Source Code
Latest 25 from a total of 59 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Claim Tokens | 382473317 | 121 days ago | IN | 0 ETH | 0.00000085 | ||||
| Claim Tokens | 381423923 | 124 days ago | IN | 0 ETH | 0.00000083 | ||||
| Claim Tokens | 380450074 | 127 days ago | IN | 0 ETH | 0.0000012 | ||||
| Claim Tokens | 380183317 | 128 days ago | IN | 0 ETH | 0.00002233 | ||||
| Claim Tokens | 379707170 | 129 days ago | IN | 0 ETH | 0.00000103 | ||||
| Claim Tokens | 379612560 | 129 days ago | IN | 0 ETH | 0.00000083 | ||||
| Claim Tokens | 379592014 | 129 days ago | IN | 0 ETH | 0.000001 | ||||
| Claim Tokens | 379584539 | 129 days ago | IN | 0 ETH | 0.000001 | ||||
| Claim Tokens | 379584042 | 129 days ago | IN | 0 ETH | 0.000001 | ||||
| Claim Tokens | 379583841 | 129 days ago | IN | 0 ETH | 0.000001 | ||||
| Claim Tokens | 379568637 | 129 days ago | IN | 0 ETH | 0.00000101 | ||||
| Finalize Leaderb... | 379544476 | 129 days ago | IN | 0 ETH | 0.00001441 | ||||
| Claim Tokens | 367887866 | 163 days ago | IN | 0 ETH | 0.00000102 | ||||
| Claim Tokens | 367858251 | 163 days ago | IN | 0 ETH | 0.00000104 | ||||
| Create Leaderboa... | 367730858 | 164 days ago | IN | 0.0002 ETH | 0.00001366 | ||||
| Claim Tokens | 367498749 | 164 days ago | IN | 0 ETH | 0.00000101 | ||||
| Claim Tokens | 367411350 | 165 days ago | IN | 0 ETH | 0.00000104 | ||||
| Claim Tokens | 367186374 | 165 days ago | IN | 0 ETH | 0.00000122 | ||||
| Claim Tokens | 367046361 | 166 days ago | IN | 0 ETH | 0.00000101 | ||||
| Claim Tokens | 367026603 | 166 days ago | IN | 0 ETH | 0.00000103 | ||||
| Finalize Leaderb... | 367017684 | 166 days ago | IN | 0 ETH | 0.00001499 | ||||
| Claim Tokens | 358487205 | 190 days ago | IN | 0 ETH | 0.00000135 | ||||
| Claim Tokens | 356048486 | 197 days ago | IN | 0 ETH | 0.00000078 | ||||
| Claim Tokens | 356048411 | 197 days ago | IN | 0 ETH | 0.00000078 | ||||
| Finalize Leaderb... | 356048212 | 197 days ago | IN | 0 ETH | 0.00000391 |
Latest 21 internal transactions
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 358487205 | 190 days ago | 0.008 ETH | ||||
| 356048486 | 197 days ago | 0.003429 ETH | ||||
| 356048411 | 197 days ago | 0.004571 ETH | ||||
| 345249085 | 229 days ago | 0.014 ETH | ||||
| 345248894 | 229 days ago | 0.009 ETH | ||||
| 345106792 | 229 days ago | 0.017 ETH | ||||
| 345069621 | 229 days ago | 0.008 ETH | ||||
| 334246098 | 261 days ago | 0.085 ETH | ||||
| 333642397 | 262 days ago | 0.008 ETH | ||||
| 333642035 | 262 days ago | 0.006 ETH | ||||
| 323601365 | 292 days ago | 0.04 ETH | ||||
| 323134268 | 293 days ago | 0.03 ETH | ||||
| 323133467 | 293 days ago | 0.015 ETH | ||||
| 323075904 | 293 days ago | 0.012 ETH | ||||
| 319154790 | 305 days ago | 0.05842105 ETH | ||||
| 312118366 | 325 days ago | 0.01578947 ETH | ||||
| 311613599 | 326 days ago | 0.00552631 ETH | ||||
| 311178834 | 328 days ago | 0.00394736 ETH | ||||
| 311127142 | 328 days ago | 0.00947368 ETH | ||||
| 310976423 | 328 days ago | 0.00710526 ETH | ||||
| 310969985 | 328 days ago | 0.01973684 ETH |
Cross-Chain Transactions
Loading...
Loading
Contract Name:
LeaderboardHub
Compiler Version
v0.8.19+commit.7dd6d404
Optimization Enabled:
Yes with 7777 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import "@openzeppelin/contracts/utils/Address.sol";
import {IERC20, SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {AccessControlEnumerable} from "@openzeppelin/contracts/access/AccessControlEnumerable.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {VRFConsumerBaseV2Plus} from "@chainlink/contracts/src/v0.8/vrf/dev/VRFConsumerBaseV2Plus.sol";
import {VRFV2PlusClient} from "@chainlink/contracts/src/v0.8/vrf/dev/libraries/VRFV2PlusClient.sol";
import {ILeaderboardHub} from "./interfaces/ILeaderboardHub.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
/// @title LeaderboardHub contract
/// @notice B2B contract allowing affiliates to create some leaderboards and reward the users.
/// Please do not create the leaderboard directly through the contract. Create it through the BetSwirl frontend.
/// @dev Rebasing tokens or transfer-fee tokens are not taken in charge.
contract LeaderboardHub is
ILeaderboardHub,
AccessControlEnumerable,
VRFConsumerBaseV2Plus,
ReentrancyGuard
{
using SafeERC20 for IERC20;
uint32 private constant TWO_DAYS = 172_800;
uint32 private constant ONE_YEAR = 31_536_000;
uint32 private constant ONE_MONTH = 2_592_000;
uint32 private MAX_WINNERS = type(uint16).max - 1;
/// @notice Maps leaderboard ID => Leaderboard.
mapping(uint64 => Leaderboard) public leaderboards;
/// @notice Total leaderboards count.
uint64 private _leaderboardsCount;
/// @notice Maximum number of random NFTs per leaderboard
uint32 public randomNFTsLimit;
/// @notice Minimum expiration time an affiliate can set while creating a leaderboard.
uint32 public minExpirationTime;
/// @notice Maps leaderboard ID => account => position.
mapping(uint64 => mapping(address => uint16)) public winnerPositions;
/// @notice Maps leaderboard ID => account => tokens claimed.
mapping(uint64 => mapping(address => uint256)) public tokensClaimed;
/// @notice Maps leaderboard ID => position => collection => NFT id => available.
mapping(uint64 => mapping(uint16 => mapping(address => mapping(uint256 => bool))))
public nftAvailable;
/// @notice Maps VRF request IDs to leaderboard ID.
mapping(uint256 => uint64) internal _leaderboardIdByVrfRequestId;
/// @notice Maps leaderboard ID -> NFTReward struct.
mapping(uint64 => NFTReward[]) public randomNFTs;
/// @notice Maps leaderboard ID -> NFTReward struct.
mapping(uint64 => NFTReward[]) public staticNFTs;
/// @notice Role associated to leaderboard manager.
bytes32 public constant LEADERBOARD_MANAGER =
keccak256("LEADERBOARD_MANAGER");
/// @notice Fee asked to affiliate to pay while creating a leaderboard (gas token).
uint256 public fee;
/// @notice Accumulated withdrawable fees.
uint256 public accumulatedFees;
/// @notice Chainlink VRF configuration state.
ChainlinkConfig private _chainlinkConfig;
modifier onlyAffiliate(uint64 leaderboardId) {
if (_msgSender() != leaderboards[leaderboardId].affiliate)
revert AccessDenied();
_;
}
/// @notice Cancels the leaderboard if the leaderboard ended more than one month ago and
/// it is still not finalized.
modifier recoveryAffiliate(uint64 leaderboardId) {
if (!_finalized(leaderboardId)) {
if (
(uint40(leaderboards[leaderboardId].endsAt) + ONE_MONTH <
block.timestamp)
) _cancelLeaderboard(leaderboardId);
else revert NotFinalized();
}
if (!_expired(leaderboardId)) revert NotExpired();
_;
}
/// @notice Initialize the contract's admin role to the deployer, and state variables.
/// @param chainlinkCoordinatorAddress Address of the Chainlink VRF Coordinator.
/// @param maxRandomNFTs Maximum number of random NFTs an affiliate can set while creating a leaderboard.
/// @param minExpirationTime_ Minimum expiration time an affiliate can set while creating a leaderboard.
/// @param fee_ Fee asked to affiliate to pay while creating a leaderboard (gas token)
constructor(
address chainlinkCoordinatorAddress,
uint32 maxRandomNFTs,
uint32 minExpirationTime_,
uint256 fee_
) VRFConsumerBaseV2Plus(chainlinkCoordinatorAddress) {
if (chainlinkCoordinatorAddress == address(0)) revert InvalidAddress();
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
setMinExpirationTime(minExpirationTime_);
setRandomNFTsLimit(maxRandomNFTs);
setFee(fee_);
}
/// @notice Creates a new leaderboard.
/// @param affiliate Address of the affiliate.
/// @param token Address of the token (zero address if gas token).
/// @param endsAt Date on which the leaderboard ends. Bets must not be counted after this date.
/// @param expirationTime Time after the leaderboard has been finalized during which players can claim their prize.
/// @param shares_ Number of shares for each winner (sorted by order).
/// @param staticNfts_ Static NFTs.
/// @param randomNfts_ Random NFTs.
/// @dev Total shares can be "0" in the case where the creator want to send only NFTs for example.
function createLeaderboard(
address affiliate,
address token,
uint32 endsAt,
uint32 expirationTime,
uint256[] calldata shares_,
NFTReward[] calldata staticNfts_,
RandomNFTRewards[] memory randomNfts_ // Memory used to fix "Stack too deep error"
) external payable {
uint256 totalWinners = shares_.length;
if (affiliate == address(0)) revert InvalidAddress();
if (totalWinners == 0) revert EmptyShares();
if (totalWinners > MAX_WINNERS) revert SharesCountTooHigh();
if (block.timestamp >= endsAt) revert EndDateTooSmall();
if (expirationTime < minExpirationTime) revert ExpirationTimeTooLow();
if (expirationTime > ONE_YEAR) revert ExpirationTimeTooHigh();
uint256 totalShares;
for (uint256 i; i < totalWinners; ) {
totalShares += shares_[i];
unchecked {
++i;
}
}
// 1. Get tokens
if (token == address(0)) {
if (msg.value != totalShares + fee) revert InvalidValueSent();
} else {
if (msg.value != fee) revert InvalidValueSent();
if (totalShares > 0) {
uint256 balanceBefore = IERC20(token).balanceOf(address(this));
IERC20(token).safeTransferFrom(
msg.sender,
address(this),
totalShares
);
uint256 balanceAfter = IERC20(token).balanceOf(address(this));
if (balanceAfter - balanceBefore != totalShares)
revert TaxedTokenNotTakenInCharge();
}
}
accumulatedFees += fee;
//2. Create leaderboard
Leaderboard memory leaderboard = Leaderboard(
affiliate,
token,
endsAt,
expirationTime,
0,
totalShares,
totalShares,
shares_,
new address[](totalWinners)
);
++_leaderboardsCount;
uint64 leaderboardId = _leaderboardsCount;
leaderboards[leaderboardId] = leaderboard;
//3. Get static NFTs
{
uint256 id;
address nftCollection;
uint16 nftPosition;
uint256 nftsLength = staticNfts_.length;
NFTReward memory nft;
for (uint256 i; i < nftsLength; ) {
nft = staticNfts_[i];
nftCollection = nft.collection;
id = nft.id;
nftPosition = nft.position;
if (totalWinners < nftPosition || nftPosition == 0)
revert InvalidPosition();
staticNFTs[leaderboardId].push(nft);
IERC721(nftCollection).transferFrom(
msg.sender,
address(this),
id
);
nftAvailable[leaderboardId][nftPosition][nftCollection][
id
] = true;
unchecked {
++i;
}
}
//4. Get random NFTs
nftsLength = randomNfts_.length;
RandomNFTRewards memory randomNft;
uint256 idsLength;
uint256 totalRandomNfts;
for (uint256 i; i < nftsLength; ) {
randomNft = randomNfts_[i];
idsLength = randomNft.ids.length;
totalRandomNfts += idsLength;
nftCollection = randomNft.collection;
for (uint256 j; j < idsLength; ) {
id = randomNft.ids[j];
randomNFTs[leaderboardId].push(
NFTReward(nftCollection, 0, id)
);
IERC721(nftCollection).transferFrom(
msg.sender,
address(this),
id
);
unchecked {
++j;
}
}
unchecked {
++i;
}
}
if (totalRandomNfts > randomNFTsLimit)
revert RandomNftLengthExceedsLimit();
}
emit LeaderboardCreated(
leaderboardId,
affiliate,
token,
endsAt,
expirationTime,
totalShares,
shares_
);
}
/// @notice Finalizes a leaderboard.
/// Once finalized, the winners will be able to claim theirs prizes (tokens + NFTs)
/// @param leaderboardId ID of the leaderboard.
/// @param winners_ Winners ranked in order.
/// @dev winners_ length could be lower than shares length in the case where not enough players are eligible.
/// Having duplicate addresses in winners_ is possible in theory.
/// If it happens, the winner will be able to get their rewards only for their last position.
/// VRF Coordinator is not called if winners_ < 2
function finalizeLeaderboard(
uint64 leaderboardId,
address[] calldata winners_
) external onlyRole(LEADERBOARD_MANAGER) {
// Not saved in memory to save some gas
Leaderboard storage leaderboard = leaderboards[leaderboardId];
if (!_ended(leaderboardId)) revert NotEnded();
if (_finalized(leaderboardId)) revert AlreadyFinalized();
uint256 winnersCount = winners_.length;
if (winnersCount > 0) {
if (winnersCount > leaderboard.shares.length)
revert WinnerCountTooHigh();
leaderboards[leaderboardId].expiresAt =
uint40(block.timestamp) +
leaderboard.expirationTime;
//1. Rank the winners
for (uint256 i; i < winnersCount; ) {
// winnerPosition starts to 1
winnerPositions[leaderboardId][winners_[i]] = uint16(i) + 1;
unchecked {
++i;
}
}
leaderboards[leaderboardId].winners = winners_;
uint32 randomNFTsCount = uint32(randomNFTs[leaderboardId].length);
//2. Distribute random NFTs
if (randomNFTsCount > 0) {
// If only one winner, do not call VRF to save some gas
if (winnersCount == 1) {
NFTReward[] storage randomNfts = randomNFTs[leaderboardId];
address collection;
uint256 nftId;
address winner = winners_[0];
for (uint256 i; i < randomNFTsCount; ) {
collection = randomNfts[i].collection;
nftId = randomNfts[i].id;
nftAvailable[leaderboardId][1][collection][
nftId
] = true;
randomNfts[i].position = 1;
emit NFTRewarded(
leaderboardId,
winner,
collection,
nftId
);
unchecked {
++i;
}
}
} else {
uint256 requestId = s_vrfCoordinator.requestRandomWords(
VRFV2PlusClient.RandomWordsRequest({
keyHash: _chainlinkConfig.keyHash,
subId: _chainlinkConfig.vrfSubId,
requestConfirmations: _chainlinkConfig
.requestConfirmations,
callbackGasLimit: _chainlinkConfig.callbackGasBase +
_chainlinkConfig.callbackGasExtraNFT *
randomNFTsCount,
numWords: 1,
extraArgs: VRFV2PlusClient._argsToBytes(
VRFV2PlusClient.ExtraArgsV1({
nativePayment: _chainlinkConfig
.nativePayment
})
)
})
);
_leaderboardIdByVrfRequestId[requestId] = leaderboardId;
}
}
} else {
/* By setting an expiresAt in the past, the leaderboard will be considered as finalized
and the affiliate will be able to retrieve the prizes immediately. */
leaderboards[leaderboardId].expiresAt = uint32(block.timestamp) - 1;
}
emit Finalized(leaderboardId, winners_);
}
/// @notice Indicates the amount of tokens claimable for an account.
/// @param leaderboardId ID of the leaderboard.
/// @param account Address of the player.
function claimable(
uint64 leaderboardId,
address account
) public view returns (uint256) {
if (!_finalized(leaderboardId) || _expired(leaderboardId)) return 0;
uint16 position = winnerPositions[leaderboardId][account];
if (position == 0) return 0;
return
leaderboards[leaderboardId].shares[position - 1] -
tokensClaimed[leaderboardId][account];
}
/// @notice Allows a player to claim their token reward.
/// @param leaderboardId ID of the leaderboard.
/// @param to Address on which to send tokens.
function claimTokens(
uint64 leaderboardId,
address payable to
) external nonReentrant {
if (to == address(0)) revert InvalidAddress();
address account = msg.sender;
uint256 _claimable = claimable(leaderboardId, account);
if (_claimable == 0) revert NothingToClaim();
tokensClaimed[leaderboardId][account] = _claimable;
// Decrease remaining amount to unclaim
leaderboards[leaderboardId].remaining -= _claimable;
address token = leaderboards[leaderboardId].token;
_transfer(to, token, _claimable);
emit Claimed(leaderboardId, account, token, _claimable);
}
/// @notice Allows an affiliate to pull unclaimed tokens of a leaderboard.
/// @param leaderboardId Id of the leaderboard.
/// @param to Address on which to send tokens.
function pullUnclaimedTokens(
uint64 leaderboardId,
address to
)
external
nonReentrant
onlyAffiliate(leaderboardId)
recoveryAffiliate(leaderboardId)
{
if (to == address(0)) revert InvalidAddress();
uint256 remaining = leaderboards[leaderboardId].remaining;
if (remaining == 0) revert NothingToClaim();
delete leaderboards[leaderboardId].remaining;
address token = leaderboards[leaderboardId].token;
_transfer(to, token, remaining);
emit Unclaimed(leaderboardId, to, token, remaining);
}
/// @notice Resolves the random NFTs distribution using the Chainlink randomness.
/// @param requestId The Chainlink request ID.
/// @param randomWords Random words list. Contains only one.
function fulfillRandomWords(
uint256 requestId,
uint256[] calldata randomWords
) internal override {
uint256 randomWord = randomWords[0];
uint64 leaderboardId = _leaderboardIdByVrfRequestId[requestId];
uint256 winnersCount = leaderboards[leaderboardId].winners.length;
address collection;
uint256 nftId;
NFTReward[] storage randomNfts = randomNFTs[leaderboardId];
uint256 nftLength = randomNfts.length;
for (uint256 i; i < nftLength; ) {
uint256 playerIndex = uint256(
keccak256(abi.encode(randomWord, i))
) % winnersCount;
address winner = leaderboards[leaderboardId].winners[playerIndex];
collection = randomNfts[i].collection;
nftId = randomNfts[i].id;
nftAvailable[leaderboardId][uint16(playerIndex + 1)][collection][
nftId
] = true;
randomNfts[i].position = uint16(playerIndex + 1);
emit NFTRewarded(leaderboardId, winner, collection, nftId);
unchecked {
++i;
}
}
// Required to avoid double call to fulfillRandomWords
delete _leaderboardIdByVrfRequestId[requestId];
}
/// @notice Allows a player to claim all their won NFTs (static + random).
/// @param leaderboardId ID of the leaderboard.
/// @param to Address on which to send the NFTs.
function claimNFTs(uint64 leaderboardId, address to) external nonReentrant {
if (!_finalized(leaderboardId)) revert NotFinalized();
if (_expired(leaderboardId)) revert Expired();
if (to == address(0)) revert InvalidAddress();
address account = msg.sender;
uint16 position = winnerPositions[leaderboardId][account];
if (position == 0) revert InvalidWinner();
// 1. Claim static NFTs
NFTReward[] memory nfts = staticNFTs[leaderboardId];
uint256 nftsLength = nfts.length;
address collection;
uint256 id;
for (uint256 i; i < nftsLength; ) {
collection = nfts[i].collection;
id = nfts[i].id;
// If the NFT is for the user's position AND the NFT has not been already claimed
if (
nfts[i].position == position &&
nftAvailable[leaderboardId][position][collection][id]
) {
_claimNFT(leaderboardId, position, collection, id, to);
}
unchecked {
i++;
}
}
// 2. Claim random NFTs
// There are some redundancy with the loop above, but it's ok because it allows to save some gas.
nfts = randomNFTs[leaderboardId];
nftsLength = nfts.length;
for (uint256 i; i < nftsLength; ) {
collection = nfts[i].collection;
id = nfts[i].id;
// If the NFT is for the user's position AND the NFT has not been already claimed
if (
nfts[i].position == position &&
nftAvailable[leaderboardId][position][collection][id]
) {
_claimNFT(leaderboardId, position, collection, id, to);
}
unchecked {
++i;
}
}
}
/// @notice Allows a player to claim a NFT they won (static or random).
/// @param leaderboardId ID of the leaderboard.
/// @param collection Address of the collection.
/// @param id ID of the NFT.
/// @param to Address on which to send the NFT.
function claimNFT(
uint64 leaderboardId,
address collection,
uint256 id,
address to
) external nonReentrant {
if (to == address(0)) revert InvalidAddress();
if (!_finalized(leaderboardId)) revert NotFinalized();
if (_expired(leaderboardId)) revert Expired();
address account = msg.sender;
uint16 position = winnerPositions[leaderboardId][account];
if (!nftAvailable[leaderboardId][position][collection][id])
revert NothingToClaim();
_claimNFT(leaderboardId, position, collection, id, to);
}
/// @notice Claims a NFT
/// @param leaderboardId ID of the leaderboard.
/// @param position Position of the winner.
/// @param collection Address of the collection.
/// @param id ID of the NFT.
/// @param to Address on which to send the NFT.
function _claimNFT(
uint64 leaderboardId,
uint16 position,
address collection,
uint256 id,
address to
) private {
delete nftAvailable[leaderboardId][position][collection][id];
IERC721(collection).safeTransferFrom(address(this), to, id);
emit NFTClaimed(leaderboardId, position, collection, id);
}
/// @notice Allows an affiliate to pull all unclaimed NFTs (static + random).
/// @param leaderboardId Id of the leaderboard.
/// @param to Address on which to send the NFTs.
/// @dev Particularly useful when Chainlink callback failed or if leaderboard has been canceled because
/// it means the random NFTs have not been randomnly distributed and then there are not unclaimable via pullUnclaimedNFT .
function pullUnclaimedNFTs(
uint64 leaderboardId,
address to
)
external
onlyAffiliate(leaderboardId)
recoveryAffiliate(leaderboardId)
nonReentrant
{
if (to == address(0)) revert InvalidAddress();
// 1. Unclaim static NFTs
NFTReward[] memory nfts = staticNFTs[leaderboardId];
uint256 nftsLength = nfts.length;
address collection;
uint256 id;
uint16 position;
NFTReward memory nft;
for (uint256 i; i < nftsLength; ) {
nft = nfts[i];
collection = nft.collection;
id = nft.id;
position = nft.position;
// If the NFT has not been claimed by the user
if (nftAvailable[leaderboardId][position][collection][id]) {
_unclaimNFT(leaderboardId, position, collection, id, to);
}
unchecked {
i++;
}
}
// 2. Unclaim random NFTs
// There are some redundancy with the loop above, but it's ok because it allows to save some gas.
nfts = randomNFTs[leaderboardId];
nftsLength = randomNFTs[leaderboardId].length;
for (uint256 i; i < nftsLength; ) {
nft = nfts[i];
collection = nft.collection;
id = nft.id;
position = nft.position;
// If the NFT has not been claimed by the user OR If the NFT has not been distributed.
if (
nftAvailable[leaderboardId][position][collection][id] ||
position == 0
) _unclaimNFT(leaderboardId, position, collection, id, to);
unchecked {
++i;
}
}
// Once deleted, the affiliate couldn't unclaim it anymore and it saves some gas.
// But in return, the NFTs will no longer be visible in the contract.
delete randomNFTs[leaderboardId];
delete staticNFTs[leaderboardId];
}
/// @notice Allows an affiliate to pull a unclaimed NFT from the winner address.
/// @param leaderboardId Id of the leaderboard.
/// @param winner Address of the winner .
/// @param collection Address of the collection.
/// @param id ID of the NFT.
/// @param to Address on which to send the NFT.
function pullUnclaimedNFTFromWinner(
uint64 leaderboardId,
address winner,
address collection,
uint256 id,
address to
) external {
uint16 position = winnerPositions[leaderboardId][winner];
pullUnclaimedNFT(leaderboardId, position, collection, id, to);
}
/// @notice Allows an affiliate to pull a unclaimed NFT from the position of the (potential) winner.
/// @param leaderboardId Id of the leaderboard.
/// @param position Position of the (potential) winner .
/// @param collection Address of the collection.
/// @param id ID of the NFT.
/// @param to Address on which to send the NFT.
function pullUnclaimedNFT(
uint64 leaderboardId,
uint16 position,
address collection,
uint256 id,
address to
)
public
onlyAffiliate(leaderboardId)
recoveryAffiliate(leaderboardId)
nonReentrant
{
if (to == address(0)) revert InvalidAddress();
if (!nftAvailable[leaderboardId][position][collection][id])
revert NothingToClaim();
_unclaimNFT(leaderboardId, position, collection, id, to);
}
/// @notice Unclaims a NFT
/// @param leaderboardId ID of the leaderboard.
/// @param position Position of the winner.
/// @param collection Address of the collection.
/// @param id ID of the NFT.
/// @param to Address on which to send the NFT.
function _unclaimNFT(
uint64 leaderboardId,
uint16 position,
address collection,
uint256 id,
address to
) private {
delete nftAvailable[leaderboardId][position][collection][id];
IERC721(collection).safeTransferFrom(address(this), to, id);
emit NFTUnclaimed(leaderboardId, to, position, collection, id);
}
/// @notice Allows an admin to withdraw accumulated fees.
/// @param to Receiver address.
function withdrawFees(
address to
) external nonReentrant onlyRole(DEFAULT_ADMIN_ROLE) {
if (to == address(0)) revert InvalidAddress();
uint256 feesToWithdraw = accumulatedFees;
if (feesToWithdraw != 0) {
delete accumulatedFees;
Address.sendValue(payable(to), feesToWithdraw);
emit FeesWithdrawn(to, feesToWithdraw);
}
}
/// @notice Allows an admin to set fee amount.
/// @param feeAmount Fee amount.
/// @dev Fee can be 0.
function setFee(uint256 feeAmount) public onlyRole(DEFAULT_ADMIN_ROLE) {
uint256 oldFee = fee;
fee = feeAmount;
emit FeeSet(oldFee, feeAmount);
}
/// @notice Allows an admin to set the random NFTS limit.
/// @param maxRandomNFTs Maximum number of random NFTs per leaderboard.
/// @dev Max random NFTs can be 0.
function setRandomNFTsLimit(
uint32 maxRandomNFTs
) public onlyRole(DEFAULT_ADMIN_ROLE) {
uint32 oldRandomNFTsLimit = randomNFTsLimit;
randomNFTsLimit = maxRandomNFTs;
emit RandomNFTsLimitSet(oldRandomNFTsLimit, maxRandomNFTs);
}
/// @notice Allows an admin to set the minimum expiration time.
/// @param minExpirationTime_ Minimum expiration time.
function setMinExpirationTime(
uint32 minExpirationTime_
) public onlyRole(DEFAULT_ADMIN_ROLE) {
if (minExpirationTime_ < TWO_DAYS) revert ExpirationTimeTooLow();
uint32 oldMinExpirationTime = minExpirationTime;
minExpirationTime = minExpirationTime_;
emit MinExpirationTimeSet(oldMinExpirationTime, minExpirationTime_);
}
/// @notice Sets the Chainlink VRF V2 configuration.
/// @param nativePayment Whether Betswirl pays VRF fees in gas token or in LINK token.
/// @param requestConfirmations How many confirmations the Chainlink node should wait before responding.
/// @param callbackGasBase How much gas is needed in the Chainlink VRF callback.
/// @param callbackGasExtraNFT How much gas is needed per random NFT in the Chainlink VRF callback (in addition to callbackGasBase)
/// @param vrfSubId Chainlink VRF subscription ID
/// @param keyHash Hash of the public key used to verify the VRF proof.
function setChainlinkConfig(
bool nativePayment,
uint16 requestConfirmations,
uint32 callbackGasBase,
uint32 callbackGasExtraNFT,
uint256 vrfSubId,
bytes32 keyHash
) external onlyRole(DEFAULT_ADMIN_ROLE) {
if (
callbackGasBase == 0 ||
callbackGasExtraNFT == 0 ||
vrfSubId == 0 ||
keyHash == 0
) revert InvalidChainlinkConfig();
_chainlinkConfig = ChainlinkConfig(
nativePayment,
requestConfirmations,
callbackGasBase,
callbackGasExtraNFT,
vrfSubId,
keyHash
);
emit ChainlinkConfigSet(
nativePayment,
requestConfirmations,
callbackGasBase,
callbackGasExtraNFT,
vrfSubId,
keyHash
);
}
/// @notice Cancels a leaderboard for emergency use only.
/// @param leaderboardId ID of the leaderboard.
function cancelLeaderboard(
uint64 leaderboardId
) external onlyRole(LEADERBOARD_MANAGER) {
if (_finalized(leaderboardId)) revert AlreadyFinalized();
_cancelLeaderboard(leaderboardId);
}
/// @notice Cancels a leaderboard.
/// A canceled leaderboard will be considered as finalized, and the creator could unclaim the prizes instantly.
/// @param leaderboardId ID of the leaderboard.
/// @dev Paid fees are not refunded.
function _cancelLeaderboard(uint64 leaderboardId) private {
/* By setting an expiresAt in the past, the leaderboard will be considered as finalized
and the affiliate will be able to retrieve the prizes immediately. */
leaderboards[leaderboardId].expiresAt = uint32(block.timestamp) - 1;
emit Canceled(leaderboardId);
}
/// @notice Transfers a specific amount of token to an address.
/// Uses native transfer or ERC20 transfer depending on the token.
/// @dev The 0x address is considered as the gas token.
/// @param user Address of destination.
/// @param token Address of the token.
/// @param amount Number of tokens.
function _transfer(address user, address token, uint256 amount) private {
if (token == address(0)) {
Address.sendValue(payable(user), amount);
} else {
IERC20(token).safeTransfer(user, amount);
}
}
function _expired(uint64 leaderboardId) internal view returns (bool) {
uint40 expiresAt = leaderboards[leaderboardId].expiresAt;
return expiresAt > 0 && block.timestamp > expiresAt;
}
function _ended(uint64 leaderboardId) internal view returns (bool) {
return block.timestamp > leaderboards[leaderboardId].endsAt;
}
function _finalized(uint64 leaderboardId) internal view returns (bool) {
return leaderboards[leaderboardId].expiresAt > 0;
}
/// @notice Winners of a leaderboard.
function winners(
uint64 leaderboardId
) external view returns (address[] memory) {
return leaderboards[leaderboardId].winners;
}
/// @notice Shares of a leaderboard.
function shares(
uint64 leaderboardId
) external view returns (uint256[] memory) {
return leaderboards[leaderboardId].shares;
}
/// @notice Number of created leaderboards.
function leaderboardsCount() external view returns (uint256) {
return _leaderboardsCount;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {ConfirmedOwnerWithProposal} from "./ConfirmedOwnerWithProposal.sol";
/// @title The ConfirmedOwner contract
/// @notice A contract with helpers for basic contract ownership.
contract ConfirmedOwner is ConfirmedOwnerWithProposal {
constructor(address newOwner) ConfirmedOwnerWithProposal(newOwner, address(0)) {}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IOwnable} from "../interfaces/IOwnable.sol";
/// @title The ConfirmedOwner contract
/// @notice A contract with helpers for basic contract ownership.
contract ConfirmedOwnerWithProposal is IOwnable {
address private s_owner;
address private s_pendingOwner;
event OwnershipTransferRequested(address indexed from, address indexed to);
event OwnershipTransferred(address indexed from, address indexed to);
constructor(address newOwner, address pendingOwner) {
// solhint-disable-next-line gas-custom-errors
require(newOwner != address(0), "Cannot set owner to zero");
s_owner = newOwner;
if (pendingOwner != address(0)) {
_transferOwnership(pendingOwner);
}
}
/// @notice Allows an owner to begin transferring ownership to a new address.
function transferOwnership(address to) public override onlyOwner {
_transferOwnership(to);
}
/// @notice Allows an ownership transfer to be completed by the recipient.
function acceptOwnership() external override {
// solhint-disable-next-line gas-custom-errors
require(msg.sender == s_pendingOwner, "Must be proposed owner");
address oldOwner = s_owner;
s_owner = msg.sender;
s_pendingOwner = address(0);
emit OwnershipTransferred(oldOwner, msg.sender);
}
/// @notice Get the current owner
function owner() public view override returns (address) {
return s_owner;
}
/// @notice validate, transfer ownership, and emit relevant events
function _transferOwnership(address to) private {
// solhint-disable-next-line gas-custom-errors
require(to != msg.sender, "Cannot transfer to self");
s_pendingOwner = to;
emit OwnershipTransferRequested(s_owner, to);
}
/// @notice validate access
function _validateOwnership() internal view {
// solhint-disable-next-line gas-custom-errors
require(msg.sender == s_owner, "Only callable by owner");
}
/// @notice Reverts if called by anyone other than the contract owner.
modifier onlyOwner() {
_validateOwnership();
_;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IOwnable {
function owner() external returns (address);
function transferOwnership(address recipient) external;
function acceptOwnership() external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {VRFV2PlusClient} from "../libraries/VRFV2PlusClient.sol";
import {IVRFSubscriptionV2Plus} from "./IVRFSubscriptionV2Plus.sol";
// Interface that enables consumers of VRFCoordinatorV2Plus to be future-proof for upgrades
// This interface is supported by subsequent versions of VRFCoordinatorV2Plus
interface IVRFCoordinatorV2Plus is IVRFSubscriptionV2Plus {
/**
* @notice Request a set of random words.
* @param req - a struct containing following fields for randomness request:
* keyHash - Corresponds to a particular oracle job which uses
* that key for generating the VRF proof. Different keyHash's have different gas price
* ceilings, so you can select a specific one to bound your maximum per request cost.
* subId - The ID of the VRF subscription. Must be funded
* with the minimum subscription balance required for the selected keyHash.
* requestConfirmations - How many blocks you'd like the
* oracle to wait before responding to the request. See SECURITY CONSIDERATIONS
* for why you may want to request more. The acceptable range is
* [minimumRequestBlockConfirmations, 200].
* callbackGasLimit - How much gas you'd like to receive in your
* fulfillRandomWords callback. Note that gasleft() inside fulfillRandomWords
* may be slightly less than this amount because of gas used calling the function
* (argument decoding etc.), so you may need to request slightly more than you expect
* to have inside fulfillRandomWords. The acceptable range is
* [0, maxGasLimit]
* numWords - The number of uint256 random values you'd like to receive
* in your fulfillRandomWords callback. Note these numbers are expanded in a
* secure way by the VRFCoordinator from a single random value supplied by the oracle.
* extraArgs - abi-encoded extra args
* @return requestId - A unique identifier of the request. Can be used to match
* a request to a response in fulfillRandomWords.
*/
function requestRandomWords(VRFV2PlusClient.RandomWordsRequest calldata req) external returns (uint256 requestId);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @notice The IVRFMigratableConsumerV2Plus interface defines the
/// @notice method required to be implemented by all V2Plus consumers.
/// @dev This interface is designed to be used in VRFConsumerBaseV2Plus.
interface IVRFMigratableConsumerV2Plus {
event CoordinatorSet(address vrfCoordinator);
/// @notice Sets the VRF Coordinator address
/// @notice This method should only be callable by the coordinator or contract owner
function setCoordinator(address vrfCoordinator) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @notice The IVRFSubscriptionV2Plus interface defines the subscription
/// @notice related methods implemented by the V2Plus coordinator.
interface IVRFSubscriptionV2Plus {
/**
* @notice Add a consumer to a VRF subscription.
* @param subId - ID of the subscription
* @param consumer - New consumer which can use the subscription
*/
function addConsumer(uint256 subId, address consumer) external;
/**
* @notice Remove a consumer from a VRF subscription.
* @param subId - ID of the subscription
* @param consumer - Consumer to remove from the subscription
*/
function removeConsumer(uint256 subId, address consumer) external;
/**
* @notice Cancel a subscription
* @param subId - ID of the subscription
* @param to - Where to send the remaining LINK to
*/
function cancelSubscription(uint256 subId, address to) external;
/**
* @notice Accept subscription owner transfer.
* @param subId - ID of the subscription
* @dev will revert if original owner of subId has
* not requested that msg.sender become the new owner.
*/
function acceptSubscriptionOwnerTransfer(uint256 subId) external;
/**
* @notice Request subscription owner transfer.
* @param subId - ID of the subscription
* @param newOwner - proposed new owner of the subscription
*/
function requestSubscriptionOwnerTransfer(uint256 subId, address newOwner) external;
/**
* @notice Create a VRF subscription.
* @return subId - A unique subscription id.
* @dev You can manage the consumer set dynamically with addConsumer/removeConsumer.
* @dev Note to fund the subscription with LINK, use transferAndCall. For example
* @dev LINKTOKEN.transferAndCall(
* @dev address(COORDINATOR),
* @dev amount,
* @dev abi.encode(subId));
* @dev Note to fund the subscription with Native, use fundSubscriptionWithNative. Be sure
* @dev to send Native with the call, for example:
* @dev COORDINATOR.fundSubscriptionWithNative{value: amount}(subId);
*/
function createSubscription() external returns (uint256 subId);
/**
* @notice Get a VRF subscription.
* @param subId - ID of the subscription
* @return balance - LINK balance of the subscription in juels.
* @return nativeBalance - native balance of the subscription in wei.
* @return reqCount - Requests count of subscription.
* @return owner - owner of the subscription.
* @return consumers - list of consumer address which are able to use this subscription.
*/
function getSubscription(
uint256 subId
)
external
view
returns (uint96 balance, uint96 nativeBalance, uint64 reqCount, address owner, address[] memory consumers);
/*
* @notice Check to see if there exists a request commitment consumers
* for all consumers and keyhashes for a given sub.
* @param subId - ID of the subscription
* @return true if there exists at least one unfulfilled request for the subscription, false
* otherwise.
*/
function pendingRequestExists(uint256 subId) external view returns (bool);
/**
* @notice Paginate through all active VRF subscriptions.
* @param startIndex index of the subscription to start from
* @param maxCount maximum number of subscriptions to return, 0 to return all
* @dev the order of IDs in the list is **not guaranteed**, therefore, if making successive calls, one
* @dev should consider keeping the blockheight constant to ensure a holistic picture of the contract state
*/
function getActiveSubscriptionIds(uint256 startIndex, uint256 maxCount) external view returns (uint256[] memory);
/**
* @notice Fund a subscription with native.
* @param subId - ID of the subscription
* @notice This method expects msg.value to be greater than or equal to 0.
*/
function fundSubscriptionWithNative(uint256 subId) external payable;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
// End consumer library.
library VRFV2PlusClient {
// extraArgs will evolve to support new features
bytes4 public constant EXTRA_ARGS_V1_TAG = bytes4(keccak256("VRF ExtraArgsV1"));
struct ExtraArgsV1 {
bool nativePayment;
}
struct RandomWordsRequest {
bytes32 keyHash;
uint256 subId;
uint16 requestConfirmations;
uint32 callbackGasLimit;
uint32 numWords;
bytes extraArgs;
}
function _argsToBytes(ExtraArgsV1 memory extraArgs) internal pure returns (bytes memory bts) {
return abi.encodeWithSelector(EXTRA_ARGS_V1_TAG, extraArgs);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import {IVRFCoordinatorV2Plus} from "./interfaces/IVRFCoordinatorV2Plus.sol";
import {IVRFMigratableConsumerV2Plus} from "./interfaces/IVRFMigratableConsumerV2Plus.sol";
import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol";
/** ****************************************************************************
* @notice Interface for contracts using VRF randomness
* *****************************************************************************
* @dev PURPOSE
*
* @dev Reggie the Random Oracle (not his real job) wants to provide randomness
* @dev to Vera the verifier in such a way that Vera can be sure he's not
* @dev making his output up to suit himself. Reggie provides Vera a public key
* @dev to which he knows the secret key. Each time Vera provides a seed to
* @dev Reggie, he gives back a value which is computed completely
* @dev deterministically from the seed and the secret key.
*
* @dev Reggie provides a proof by which Vera can verify that the output was
* @dev correctly computed once Reggie tells it to her, but without that proof,
* @dev the output is indistinguishable to her from a uniform random sample
* @dev from the output space.
*
* @dev The purpose of this contract is to make it easy for unrelated contracts
* @dev to talk to Vera the verifier about the work Reggie is doing, to provide
* @dev simple access to a verifiable source of randomness. It ensures 2 things:
* @dev 1. The fulfillment came from the VRFCoordinatorV2Plus.
* @dev 2. The consumer contract implements fulfillRandomWords.
* *****************************************************************************
* @dev USAGE
*
* @dev Calling contracts must inherit from VRFConsumerBaseV2Plus, and can
* @dev initialize VRFConsumerBaseV2Plus's attributes in their constructor as
* @dev shown:
*
* @dev contract VRFConsumerV2Plus is VRFConsumerBaseV2Plus {
* @dev constructor(<other arguments>, address _vrfCoordinator, address _subOwner)
* @dev VRFConsumerBaseV2Plus(_vrfCoordinator, _subOwner) public {
* @dev <initialization with other arguments goes here>
* @dev }
* @dev }
*
* @dev The oracle will have given you an ID for the VRF keypair they have
* @dev committed to (let's call it keyHash). Create a subscription, fund it
* @dev and your consumer contract as a consumer of it (see VRFCoordinatorInterface
* @dev subscription management functions).
* @dev Call requestRandomWords(keyHash, subId, minimumRequestConfirmations,
* @dev callbackGasLimit, numWords, extraArgs),
* @dev see (IVRFCoordinatorV2Plus for a description of the arguments).
*
* @dev Once the VRFCoordinatorV2Plus has received and validated the oracle's response
* @dev to your request, it will call your contract's fulfillRandomWords method.
*
* @dev The randomness argument to fulfillRandomWords is a set of random words
* @dev generated from your requestId and the blockHash of the request.
*
* @dev If your contract could have concurrent requests open, you can use the
* @dev requestId returned from requestRandomWords to track which response is associated
* @dev with which randomness request.
* @dev See "SECURITY CONSIDERATIONS" for principles to keep in mind,
* @dev if your contract could have multiple requests in flight simultaneously.
*
* @dev Colliding `requestId`s are cryptographically impossible as long as seeds
* @dev differ.
*
* *****************************************************************************
* @dev SECURITY CONSIDERATIONS
*
* @dev A method with the ability to call your fulfillRandomness method directly
* @dev could spoof a VRF response with any random value, so it's critical that
* @dev it cannot be directly called by anything other than this base contract
* @dev (specifically, by the VRFConsumerBaseV2Plus.rawFulfillRandomness method).
*
* @dev For your users to trust that your contract's random behavior is free
* @dev from malicious interference, it's best if you can write it so that all
* @dev behaviors implied by a VRF response are executed *during* your
* @dev fulfillRandomness method. If your contract must store the response (or
* @dev anything derived from it) and use it later, you must ensure that any
* @dev user-significant behavior which depends on that stored value cannot be
* @dev manipulated by a subsequent VRF request.
*
* @dev Similarly, both miners and the VRF oracle itself have some influence
* @dev over the order in which VRF responses appear on the blockchain, so if
* @dev your contract could have multiple VRF requests in flight simultaneously,
* @dev you must ensure that the order in which the VRF responses arrive cannot
* @dev be used to manipulate your contract's user-significant behavior.
*
* @dev Since the block hash of the block which contains the requestRandomness
* @dev call is mixed into the input to the VRF *last*, a sufficiently powerful
* @dev miner could, in principle, fork the blockchain to evict the block
* @dev containing the request, forcing the request to be included in a
* @dev different block with a different hash, and therefore a different input
* @dev to the VRF. However, such an attack would incur a substantial economic
* @dev cost. This cost scales with the number of blocks the VRF oracle waits
* @dev until it calls responds to a request. It is for this reason that
* @dev that you can signal to an oracle you'd like them to wait longer before
* @dev responding to the request (however this is not enforced in the contract
* @dev and so remains effective only in the case of unmodified oracle software).
*/
abstract contract VRFConsumerBaseV2Plus is IVRFMigratableConsumerV2Plus, ConfirmedOwner {
error OnlyCoordinatorCanFulfill(address have, address want);
error OnlyOwnerOrCoordinator(address have, address owner, address coordinator);
error ZeroAddress();
// s_vrfCoordinator should be used by consumers to make requests to vrfCoordinator
// so that coordinator reference is updated after migration
IVRFCoordinatorV2Plus public s_vrfCoordinator;
/**
* @param _vrfCoordinator address of VRFCoordinator contract
*/
constructor(address _vrfCoordinator) ConfirmedOwner(msg.sender) {
if (_vrfCoordinator == address(0)) {
revert ZeroAddress();
}
s_vrfCoordinator = IVRFCoordinatorV2Plus(_vrfCoordinator);
}
/**
* @notice fulfillRandomness handles the VRF response. Your contract must
* @notice implement it. See "SECURITY CONSIDERATIONS" above for important
* @notice principles to keep in mind when implementing your fulfillRandomness
* @notice method.
*
* @dev VRFConsumerBaseV2Plus expects its subcontracts to have a method with this
* @dev signature, and will call it once it has verified the proof
* @dev associated with the randomness. (It is triggered via a call to
* @dev rawFulfillRandomness, below.)
*
* @param requestId The Id initially returned by requestRandomness
* @param randomWords the VRF output expanded to the requested number of words
*/
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function fulfillRandomWords(uint256 requestId, uint256[] calldata randomWords) internal virtual;
// rawFulfillRandomness is called by VRFCoordinator when it receives a valid VRF
// proof. rawFulfillRandomness then calls fulfillRandomness, after validating
// the origin of the call
function rawFulfillRandomWords(uint256 requestId, uint256[] calldata randomWords) external {
if (msg.sender != address(s_vrfCoordinator)) {
revert OnlyCoordinatorCanFulfill(msg.sender, address(s_vrfCoordinator));
}
fulfillRandomWords(requestId, randomWords);
}
/**
* @inheritdoc IVRFMigratableConsumerV2Plus
*/
function setCoordinator(address _vrfCoordinator) external override onlyOwnerOrCoordinator {
if (_vrfCoordinator == address(0)) {
revert ZeroAddress();
}
s_vrfCoordinator = IVRFCoordinatorV2Plus(_vrfCoordinator);
emit CoordinatorSet(_vrfCoordinator);
}
modifier onlyOwnerOrCoordinator() {
if (msg.sender != owner() && msg.sender != address(s_vrfCoordinator)) {
revert OnlyOwnerOrCoordinator(msg.sender, owner(), address(s_vrfCoordinator));
}
_;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControl.sol)
pragma solidity ^0.8.0;
import "./IAccessControl.sol";
import "../utils/Context.sol";
import "../utils/Strings.sol";
import "../utils/introspection/ERC165.sol";
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms. This is a lightweight version that doesn't allow enumerating role
* members except through off-chain means by accessing the contract event logs. Some
* applications may benefit from on-chain enumerability, for those cases see
* {AccessControlEnumerable}.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```solidity
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```solidity
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
* to enforce additional security measures for this role.
*/
abstract contract AccessControl is Context, IAccessControl, ERC165 {
struct RoleData {
mapping(address => bool) members;
bytes32 adminRole;
}
mapping(bytes32 => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with a standardized message including the required role.
*
* The format of the revert reason is given by the following regular expression:
*
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
*
* _Available since v4.1._
*/
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
return _roles[role].members[account];
}
/**
* @dev Revert with a standard message if `_msgSender()` is missing `role`.
* Overriding this function changes the behavior of the {onlyRole} modifier.
*
* Format of the revert message is described in {_checkRole}.
*
* _Available since v4.6._
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
/**
* @dev Revert with a standard message if `account` is missing `role`.
*
* The format of the revert reason is given by the following regular expression:
*
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert(
string(
abi.encodePacked(
"AccessControl: account ",
Strings.toHexString(account),
" is missing role ",
Strings.toHexString(uint256(role), 32)
)
)
);
}
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
return _roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleGranted} event.
*/
function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleRevoked} event.
*/
function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been revoked `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*
* May emit a {RoleRevoked} event.
*/
function renounceRole(bytes32 role, address account) public virtual override {
require(account == _msgSender(), "AccessControl: can only renounce roles for self");
_revokeRole(role, account);
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event. Note that unlike {grantRole}, this function doesn't perform any
* checks on the calling account.
*
* May emit a {RoleGranted} event.
*
* [WARNING]
* ====
* This function should only be called from the constructor when setting
* up the initial roles for the system.
*
* Using this function in any other way is effectively circumventing the admin
* system imposed by {AccessControl}.
* ====
*
* NOTE: This function is deprecated in favor of {_grantRole}.
*/
function _setupRole(bytes32 role, address account) internal virtual {
_grantRole(role, account);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = getRoleAdmin(role);
_roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Grants `role` to `account`.
*
* Internal function without access restriction.
*
* May emit a {RoleGranted} event.
*/
function _grantRole(bytes32 role, address account) internal virtual {
if (!hasRole(role, account)) {
_roles[role].members[account] = true;
emit RoleGranted(role, account, _msgSender());
}
}
/**
* @dev Revokes `role` from `account`.
*
* Internal function without access restriction.
*
* May emit a {RoleRevoked} event.
*/
function _revokeRole(bytes32 role, address account) internal virtual {
if (hasRole(role, account)) {
_roles[role].members[account] = false;
emit RoleRevoked(role, account, _msgSender());
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol)
pragma solidity ^0.8.0;
import "./IAccessControlEnumerable.sol";
import "./AccessControl.sol";
import "../utils/structs/EnumerableSet.sol";
/**
* @dev Extension of {AccessControl} that allows enumerating the members of each role.
*/
abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl {
using EnumerableSet for EnumerableSet.AddressSet;
mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers;
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns one of the accounts that have `role`. `index` must be a
* value between 0 and {getRoleMemberCount}, non-inclusive.
*
* Role bearers are not sorted in any particular way, and their ordering may
* change at any point.
*
* WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
* you perform all queries on the same block. See the following
* https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
* for more information.
*/
function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) {
return _roleMembers[role].at(index);
}
/**
* @dev Returns the number of accounts that have `role`. Can be used
* together with {getRoleMember} to enumerate all bearers of a role.
*/
function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) {
return _roleMembers[role].length();
}
/**
* @dev Overload {_grantRole} to track enumerable memberships
*/
function _grantRole(bytes32 role, address account) internal virtual override {
super._grantRole(role, account);
_roleMembers[role].add(account);
}
/**
* @dev Overload {_revokeRole} to track enumerable memberships
*/
function _revokeRole(bytes32 role, address account) internal virtual override {
super._revokeRole(role, account);
_roleMembers[role].remove(account);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
pragma solidity ^0.8.0;
/**
* @dev External interface of AccessControl declared to support ERC165 detection.
*/
interface IAccessControl {
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*
* _Available since v3.1._
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call, an admin role
* bearer except when using {AccessControl-_setupRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) external view returns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) external view returns (bytes32);
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*/
function renounceRole(bytes32 role, address account) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol)
pragma solidity ^0.8.0;
import "./IAccessControl.sol";
/**
* @dev External interface of AccessControlEnumerable declared to support ERC165 detection.
*/
interface IAccessControlEnumerable is IAccessControl {
/**
* @dev Returns one of the accounts that have `role`. `index` must be a
* value between 0 and {getRoleMemberCount}, non-inclusive.
*
* Role bearers are not sorted in any particular way, and their ordering may
* change at any point.
*
* WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
* you perform all queries on the same block. See the following
* https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
* for more information.
*/
function getRoleMember(bytes32 role, uint256 index) external view returns (address);
/**
* @dev Returns the number of accounts that have `role`. Can be used
* together with {getRoleMember} to enumerate all bearers of a role.
*/
function getRoleMemberCount(bytes32 role) external view returns (uint256);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be _NOT_ENTERED
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == _ENTERED;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(IERC20 token, address spender, uint256 value) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
* Revert on invalid signature.
*/
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return
success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/IERC721.sol)
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC721 compliant contract.
*/
interface IERC721 is IERC165 {
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
* or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
* understand this adds an external call which potentially creates a reentrancy vulnerability.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external;
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the caller.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
pragma solidity ^0.8.0;
import "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*
* Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @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 == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1, "Math: mulDiv overflow");
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)
pragma solidity ^0.8.0;
import "./math/Math.sol";
import "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant _SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toString(int256 value) internal pure returns (string memory) {
return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return keccak256(bytes(a)) == keccak256(bytes(b));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.0;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position of the value in the `values` array, plus 1 because index 0
// means a value is not in the set.
mapping(bytes32 => uint256) _indexes;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slot
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
if (lastIndex != toDeleteIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastValue;
// Update the index for the moved value
set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the index for the deleted slot
delete set._indexes[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
/// @notice Defines common functionalities used across ILeaderboardAffiliate, ILeaderboardAdmin & ILeaderboardPlayer interfaces.
interface ILeaderboardHubCommon {
/// @notice NFT Reward struct.
/// @param collection Collection address of the NFT.
/// @param position Position of the winner who will receive this NFT.
/// @param id Id of the NFT.
/// @dev Position starts to 1, exept for random NFTs not yet distributed (in this case position = 0)
struct NFTReward {
address collection;
uint16 position;
uint256 id;
}
/// @notice Leaderboard struct.
/// @param affiliate Address of the affiliate.
/// @param token Address of the token.
/// @param endsAt Date on which the leaderboard ends. Bets must not be counted after this date.
/// @param expirationTime Time after the leaderboard has been finalized during which players can claim their prize.
/// @param expiresAt Indicates until when the winners can claim their prizes (if 0 it means the leaderboard is not yet finalized)
/// @param remaining Remaining shares claimable by the winners.
/// @param totalShares Total shares to be claimed by the winners.
/// @param shares Number of shares for each winner (sorted by order).
/// @param winners Winners ranked in order.
/// @dev Token is zero address if gas token. Set during finalization.
struct Leaderboard {
address affiliate;
address token;
uint32 endsAt;
uint32 expirationTime;
uint40 expiresAt;
uint256 remaining;
uint256 totalShares;
uint256[] shares;
address[] winners;
}
/// @notice Reverting error when the leaderboard is not yet finalized.
error NotFinalized();
/// @notice Reverting error when a player/affiliate try to claim something already claimed or something they don't win.
error NothingToClaim();
/// @notice Reverting when an address passed in params is the zero address.
error InvalidAddress();
}
/// @notice Defines affiliate functionalities, essentially creating leaderboard and unclaiming rewards.
interface ILeaderboardHubAffiliate is ILeaderboardHubCommon {
/// @notice Emitted after a leaderboard is created.
/// @param leaderboardId ID of the leaderboard.
/// @param affiliate Address of the affiliate.
/// @param token Address of the token.
/// @param endsAt Date on which the leaderboard ends. Bets must not be counted after this date.
/// @param expirationTime Time after the leaderboard has been finalized during which players can claim their prize.
/// @param totalShares Total shares to be claimed by the winners.
/// @param shares Number of shares for each winner (sorted by order).
event LeaderboardCreated(
uint64 indexed leaderboardId,
address indexed affiliate,
address token,
uint32 endsAt,
uint32 expirationTime,
uint256 totalShares,
uint256[] shares
);
/// @notice Emitted after the affiliate has pulled the unclaimed token.
/// @param leaderboardId ID of the leaderboard.
/// @param receiver Address of the receiver.
/// @param token Address of the token (zero address if gas token).
/// @param amount Amount claimed.
event Unclaimed(
uint256 indexed leaderboardId,
address receiver,
address token,
uint256 amount
);
/// @notice Emitted after a affiliate has pulled a not claimed NFT.
/// @param leaderboardId ID of the leaderboard.
/// @param to Address of the receiver.
/// @param position Position of the winner who didn't claim the NFT.
/// @param collection Address of the collection.
/// @param id ID of the NFT.
/// @dev Emitted for static and random NFTs.
event NFTUnclaimed(
uint256 indexed leaderboardId,
address to,
uint16 position,
address collection,
uint256 id
);
/// @notice Random NFT Rewards struct.
/// @param collection Collection address of the NFT.
/// @param ids Id Array of the NFTs.
/// @dev Used as params for createLeaderboard function.
struct RandomNFTRewards {
address collection;
uint256[] ids;
}
/// @notice Creates a new leaderboard.
/// @param affiliate Address of the affiliate.
/// @param token Address of the token (zero address if gas token).
/// @param endsAt Date on which the leaderboard ends. Bets must not be counted after this date.
/// @param expirationTime Time after the leaderboard has been finalized during which players can claim their prize.
/// @param shares_ Number of shares for each winner (sorted by order).
/// @param staticNfts_ Static NFTs.
/// @param randomNfts_ Random NFTs.
function createLeaderboard(
address affiliate,
address token,
uint32 endsAt,
uint32 expirationTime,
uint256[] calldata shares_,
NFTReward[] calldata staticNfts_,
RandomNFTRewards[] calldata randomNfts_
) external payable;
/// @notice Allows an affiliate to pull unclaimed tokens of a leaderboard.
/// @param leaderboardId Id of the leaderboard.
/// @param to Address on which to send tokens.
function pullUnclaimedTokens(uint64 leaderboardId, address to) external;
/// @notice Allows an affiliate to pull all unclaimed NFTs (static + random).
/// @param leaderboardId Id of the leaderboard.
/// @param to Address on which to send the NFTs.
/// @dev Particularly useful when Chainlink callback failed or if leaderboard has been canceled because
/// it means the random NFTs have not been randomnly distributed and then there are not unclaimable via pullUnclaimedNFT .
function pullUnclaimedNFTs(uint64 leaderboardId, address to) external;
/// @notice Allows an affiliate to pull a unclaimed NFT from the position of the (potential) winner.
/// @param leaderboardId Id of the leaderboard.
/// @param position Position of the (potential) winner .
/// @param collection Address of the collection.
/// @param id ID of the NFT.
/// @param to Address on which to send the NFT.
function pullUnclaimedNFT(
uint64 leaderboardId,
uint16 position,
address collection,
uint256 id,
address to
) external;
/// @notice Allows an affiliate to pull a unclaimed NFT from the winner address.
/// @param leaderboardId Id of the leaderboard.
/// @param winner Address of the winner .
/// @param collection Address of the collection.
/// @param id ID of the NFT.
/// @param to Address on which to send the NFT.
function pullUnclaimedNFTFromWinner(
uint64 leaderboardId,
address winner,
address collection,
uint256 id,
address to
) external;
/// @notice Reverting error when the random NFTs length exceeds the maximum limit.
error RandomNftLengthExceedsLimit();
/// @notice Reverting error when the end date is not after the current date.
error EndDateTooSmall();
/// @notice Reverting error when the expire time is lower than min expiration time.
error ExpirationTimeTooLow();
/// @notice Reverting error when the expire time is higher than one year.
error ExpirationTimeTooHigh();
/// @notice Reverting error when the value received is wrong.
error InvalidValueSent();
/// @notice Reverting error when it detects that a token has transfer taxes.
error TaxedTokenNotTakenInCharge();
/// @notice Reverting error when position exceeds the shares size or equals to 0.
error InvalidPosition();
/// @notice Reverting error when the leaderboard is not expired.
error NotExpired();
/// @notice Reverting error when sender isn't allowed.
error AccessDenied();
/// @notice Reverting error when the shares array passed in createLeaderboard is empty.
error EmptyShares();
/// @notice Reverting error when the shares array length passed in createLeaderboard is more than max uint16 value.
error SharesCountTooHigh();
}
/// @notice Defines administrative functionalities (admin & leaderboard manager).
interface ILeaderboardHubAdmin is ILeaderboardHubCommon {
/// @notice Emitted after a leaderboard is finalized.
/// @param leaderboardId ID of the leaderboard.
/// @param winners Winners ranked in order.
event Finalized(uint64 indexed leaderboardId, address[] winners);
/// @notice Emitted after an admin has changed the fee amount.
/// @param previousFeeAmount Previous fee amount.
/// @param feeAmount Fee amount.
event FeeSet(uint256 previousFeeAmount, uint256 feeAmount);
/// @notice Emitted after an admin has changed the maximum number of random NFTs per leaderboard.
/// @param previousMaxRandomNFTs Prevous maximum number of random NFTs per leaderboard.
/// @param maxRandomNFTs Maximum number of random NFTs per leaderboard.
event RandomNFTsLimitSet(
uint32 previousMaxRandomNFTs,
uint32 maxRandomNFTs
);
/// @notice Emitted after an admin has changed the minimum expiration time.
/// @param previousMinExpirationTime Previous minimum expiration time settable while creating a new leaderboard.
/// @param minExpirationTime Minimum expiration time settable while creating a new leaderboard.
event MinExpirationTimeSet(
uint32 previousMinExpirationTime,
uint32 minExpirationTime
);
/// @notice Emitted after an admin has withdrawn the accumulated fee.
/// @param receiver Fees receiver address.
/// @param fees Accumulated fees amount.
event FeesWithdrawn(address indexed receiver, uint256 fees);
/// @notice Emitted after the Chainlink config is set.
/// @param nativePayment Whether Betswirl pays VRF fees in gas token or in LINK token.
/// @param requestConfirmations How many confirmations the Chainlink node should wait before responding.
/// @param callbackGasBase How much gas is needed in the Chainlink VRF callback.
/// @param callbackGasExtraNFT How much gas is needed per random NFT in the Chainlink VRF callback (in addition to callbackGasBase)
/// @param vrfSubId Chainlink VRF subscription ID
/// @param keyHash Hash of the public key used to verify the VRF proof.
event ChainlinkConfigSet(
bool nativePayment,
uint16 requestConfirmations,
uint32 callbackGasBase,
uint32 callbackGasExtraNFT,
uint256 vrfSubId,
bytes32 keyHash
);
/// @notice Emitted after a leaderboard is canceled
/// @param leaderboardId ID of the leaderboard.
event Canceled(uint64 indexed leaderboardId);
/// @notice Chainlink VRF configuration struct.
/// @param nativePayment Whether Betswirl pays VRF fees in gas token or in LINK token.
/// @param requestConfirmations How many confirmations the Chainlink node should wait before responding.
/// @param callbackGasBase How much gas is needed in the Chainlink VRF callback.
/// @param callbackGasExtraNFT How much gas is needed per random NFT in the Chainlink VRF callback (in addition to callbackGasBase)
/// @param vrfSubId Chainlink VRF subscription ID
/// @param keyHash Hash of the public key used to verify the VRF proof.
struct ChainlinkConfig {
bool nativePayment;
uint16 requestConfirmations;
uint32 callbackGasBase;
uint32 callbackGasExtraNFT;
uint256 vrfSubId;
bytes32 keyHash;
}
/// @notice Finalizes a leaderboard.
/// Once finalized, the winners will be able to claim theirs prizes (tokens + NFTs)
/// @param leaderboardId ID of the leaderboard.
/// @param winners_ Winners ranked in order.
function finalizeLeaderboard(
uint64 leaderboardId,
address[] calldata winners_
) external;
/// @notice Allows an admin to withdraw accumulated fees.
/// @param account Receiver address.
function withdrawFees(address account) external;
/// @notice Allows an admin to set fee amount.
/// @param feeAmount Fee amount.
function setFee(uint256 feeAmount) external;
/// @notice Allows an admin to set the random NFTS limit.
/// @param maxRandomNFTs Maximum number of random NFTs per leaderboard.
function setRandomNFTsLimit(uint32 maxRandomNFTs) external;
/// @notice Allows an admin to set the minimum expiration time.
/// @param minExpirationTime_ Minimum expiration time.
function setMinExpirationTime(uint32 minExpirationTime_) external;
/// @notice Sets the Chainlink VRF V2 configuration.
/// @param nativePayment Whether Betswirl pays VRF fees in gas token or in LINK token.
/// @param requestConfirmations How many confirmations the Chainlink node should wait before responding.
/// @param callbackGasBase How much gas is needed in the Chainlink VRF callback.
/// @param callbackGasExtraNFT How much gas is needed per random NFT in the Chainlink VRF callback (in addition to callbackGasBase)
/// @param vrfSubId Chainlink VRF subscription ID
/// @param keyHash Hash of the public key used to verify the VRF proof.
function setChainlinkConfig(
bool nativePayment,
uint16 requestConfirmations,
uint32 callbackGasBase,
uint32 callbackGasExtraNFT,
uint256 vrfSubId,
bytes32 keyHash
) external;
/// @notice Cancels a leaderboard for emergency use only.
/// @param leaderboardId ID of the leaderboard.
function cancelLeaderboard(uint64 leaderboardId) external;
/// @notice Reverting error when the leaderboard is not ended.
error NotEnded();
/// @notice Reverting error when the leaderboard is already finalized.
error AlreadyFinalized();
/// @notice Reverting error when the winners count doesn't match to shares size.
error WinnerCountTooHigh();
/// @notice Reverting error when set a minimum expiration time too low.
error MinExpirationTimeTooLow();
/// @notice Reverting when one of the Chainlink param config is not valid.
error InvalidChainlinkConfig();
}
/// @notice Defines player functionalities, essentially claiming rewards
interface ILeaderboardHubPlayer is ILeaderboardHubCommon {
/// @notice Emitted after a winner has claimed some tokens.
/// @param leaderboardId ID of the leaderboard.
/// @param player Address of the winner.
/// @param token Address of the token (zero address if gas token).
/// @param amount Amount claimed.
event Claimed(
uint256 indexed leaderboardId,
address indexed player,
address token,
uint256 amount
);
/// @notice Emitted after a player has won a NFT via VRF Chainlink.
/// @param leaderboardId ID of the leaderboard.
/// @param player Address of the winner.
/// @param collection Address of the collection.
/// @param id ID of the NFT.
/// @dev The NFT is not sent when this event is emitted. Player have to claim it.
event NFTRewarded(
uint256 indexed leaderboardId,
address indexed player,
address collection,
uint256 id
);
/// @notice Emitted after a player has claimed a NFT.
/// @param leaderboardId ID of the leaderboard.
/// @param position Position of the winner.
/// @param collection Address of the collection.
/// @param id ID of the NFT.
/// @dev Emitted for static and random NFTs.
event NFTClaimed(
uint256 indexed leaderboardId,
uint16 position,
address collection,
uint256 id
);
/// @notice Allows a player to claim their token reward.
/// @param leaderboardId ID of the leaderboard.
/// @param to Address on which to send tokens.
function claimTokens(uint64 leaderboardId, address payable to) external;
/// @notice Allows a player to claim all their won NFTs (static + random).
/// @param leaderboardId ID of the leaderboard.
/// @param to Address on which to send the NFTs.
function claimNFTs(uint64 leaderboardId, address to) external;
/// @notice Allows a player to claim a NFT they won (static or random).
/// @param leaderboardId ID of the leaderboard.
/// @param collection Address of the collection.
/// @param id ID of the NFT.
/// @param to Address on which to send tokens.
function claimNFT(
uint64 leaderboardId,
address collection,
uint256 id,
address to
) external;
/// @notice Indicates the amount of tokens claimable for an account.
/// @param leaderboardId ID of the leaderboard.
/// @param account Address of the player.
function claimable(
uint64 leaderboardId,
address account
) external view returns (uint256);
/// @notice Reverting error when the leaderboard is expired.
error Expired();
/// @notice Reverting error when the position of the winner does not exist in claimNFTs.
error InvalidWinner();
}
/// @notice Aggregates all functionalities from ILeaderboardAdmin, ILeaderboardAffiliate & ILeaderboardPlayer interfaces.
interface ILeaderboardHub is
ILeaderboardHubAdmin,
ILeaderboardHubAffiliate,
ILeaderboardHubPlayer
{
/// @notice Shares of a leaderboard.
function shares(
uint64 leaderboardId
) external view returns (uint256[] memory);
/// @notice Winners of a leaderboard.
function winners(
uint64 leaderboardId
) external view returns (address[] memory);
/// @notice Number of created leaderboards.
function leaderboardsCount() external view returns (uint256);
}{
"optimizer": {
"enabled": true,
"runs": 7777
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"chainlinkCoordinatorAddress","type":"address"},{"internalType":"uint32","name":"maxRandomNFTs","type":"uint32"},{"internalType":"uint32","name":"minExpirationTime_","type":"uint32"},{"internalType":"uint256","name":"fee_","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessDenied","type":"error"},{"inputs":[],"name":"AlreadyFinalized","type":"error"},{"inputs":[],"name":"EmptyShares","type":"error"},{"inputs":[],"name":"EndDateTooSmall","type":"error"},{"inputs":[],"name":"ExpirationTimeTooHigh","type":"error"},{"inputs":[],"name":"ExpirationTimeTooLow","type":"error"},{"inputs":[],"name":"Expired","type":"error"},{"inputs":[],"name":"InvalidAddress","type":"error"},{"inputs":[],"name":"InvalidChainlinkConfig","type":"error"},{"inputs":[],"name":"InvalidPosition","type":"error"},{"inputs":[],"name":"InvalidValueSent","type":"error"},{"inputs":[],"name":"InvalidWinner","type":"error"},{"inputs":[],"name":"MinExpirationTimeTooLow","type":"error"},{"inputs":[],"name":"NotEnded","type":"error"},{"inputs":[],"name":"NotExpired","type":"error"},{"inputs":[],"name":"NotFinalized","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":[{"internalType":"address","name":"have","type":"address"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"coordinator","type":"address"}],"name":"OnlyOwnerOrCoordinator","type":"error"},{"inputs":[],"name":"RandomNftLengthExceedsLimit","type":"error"},{"inputs":[],"name":"SharesCountTooHigh","type":"error"},{"inputs":[],"name":"TaxedTokenNotTakenInCharge","type":"error"},{"inputs":[],"name":"WinnerCountTooHigh","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint64","name":"leaderboardId","type":"uint64"}],"name":"Canceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"nativePayment","type":"bool"},{"indexed":false,"internalType":"uint16","name":"requestConfirmations","type":"uint16"},{"indexed":false,"internalType":"uint32","name":"callbackGasBase","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"callbackGasExtraNFT","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"vrfSubId","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"keyHash","type":"bytes32"}],"name":"ChainlinkConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"leaderboardId","type":"uint256"},{"indexed":true,"internalType":"address","name":"player","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Claimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"vrfCoordinator","type":"address"}],"name":"CoordinatorSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"previousFeeAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"feeAmount","type":"uint256"}],"name":"FeeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"fees","type":"uint256"}],"name":"FeesWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint64","name":"leaderboardId","type":"uint64"},{"indexed":false,"internalType":"address[]","name":"winners","type":"address[]"}],"name":"Finalized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint64","name":"leaderboardId","type":"uint64"},{"indexed":true,"internalType":"address","name":"affiliate","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint32","name":"endsAt","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"expirationTime","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"totalShares","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"shares","type":"uint256[]"}],"name":"LeaderboardCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"previousMinExpirationTime","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"minExpirationTime","type":"uint32"}],"name":"MinExpirationTimeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"leaderboardId","type":"uint256"},{"indexed":false,"internalType":"uint16","name":"position","type":"uint16"},{"indexed":false,"internalType":"address","name":"collection","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"}],"name":"NFTClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"leaderboardId","type":"uint256"},{"indexed":true,"internalType":"address","name":"player","type":"address"},{"indexed":false,"internalType":"address","name":"collection","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"}],"name":"NFTRewarded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"leaderboardId","type":"uint256"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint16","name":"position","type":"uint16"},{"indexed":false,"internalType":"address","name":"collection","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"}],"name":"NFTUnclaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"OwnershipTransferRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"previousMaxRandomNFTs","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"maxRandomNFTs","type":"uint32"}],"name":"RandomNFTsLimitSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"leaderboardId","type":"uint256"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Unclaimed","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LEADERBOARD_MANAGER","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"accumulatedFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"leaderboardId","type":"uint64"}],"name":"cancelLeaderboard","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"leaderboardId","type":"uint64"},{"internalType":"address","name":"collection","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"claimNFT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"leaderboardId","type":"uint64"},{"internalType":"address","name":"to","type":"address"}],"name":"claimNFTs","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"leaderboardId","type":"uint64"},{"internalType":"address payable","name":"to","type":"address"}],"name":"claimTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"leaderboardId","type":"uint64"},{"internalType":"address","name":"account","type":"address"}],"name":"claimable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"affiliate","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint32","name":"endsAt","type":"uint32"},{"internalType":"uint32","name":"expirationTime","type":"uint32"},{"internalType":"uint256[]","name":"shares_","type":"uint256[]"},{"components":[{"internalType":"address","name":"collection","type":"address"},{"internalType":"uint16","name":"position","type":"uint16"},{"internalType":"uint256","name":"id","type":"uint256"}],"internalType":"struct ILeaderboardHubCommon.NFTReward[]","name":"staticNfts_","type":"tuple[]"},{"components":[{"internalType":"address","name":"collection","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"}],"internalType":"struct ILeaderboardHubAffiliate.RandomNFTRewards[]","name":"randomNfts_","type":"tuple[]"}],"name":"createLeaderboard","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"fee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"leaderboardId","type":"uint64"},{"internalType":"address[]","name":"winners_","type":"address[]"}],"name":"finalizeLeaderboard","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getRoleMember","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleMemberCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"","type":"uint64"}],"name":"leaderboards","outputs":[{"internalType":"address","name":"affiliate","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint32","name":"endsAt","type":"uint32"},{"internalType":"uint32","name":"expirationTime","type":"uint32"},{"internalType":"uint40","name":"expiresAt","type":"uint40"},{"internalType":"uint256","name":"remaining","type":"uint256"},{"internalType":"uint256","name":"totalShares","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"leaderboardsCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minExpirationTime","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"","type":"uint64"},{"internalType":"uint16","name":"","type":"uint16"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"nftAvailable","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"leaderboardId","type":"uint64"},{"internalType":"uint16","name":"position","type":"uint16"},{"internalType":"address","name":"collection","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"pullUnclaimedNFT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"leaderboardId","type":"uint64"},{"internalType":"address","name":"winner","type":"address"},{"internalType":"address","name":"collection","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"pullUnclaimedNFTFromWinner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"leaderboardId","type":"uint64"},{"internalType":"address","name":"to","type":"address"}],"name":"pullUnclaimedNFTs","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"leaderboardId","type":"uint64"},{"internalType":"address","name":"to","type":"address"}],"name":"pullUnclaimedTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"","type":"uint64"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"randomNFTs","outputs":[{"internalType":"address","name":"collection","type":"address"},{"internalType":"uint16","name":"position","type":"uint16"},{"internalType":"uint256","name":"id","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"randomNFTsLimit","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"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":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"s_vrfCoordinator","outputs":[{"internalType":"contract IVRFCoordinatorV2Plus","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"nativePayment","type":"bool"},{"internalType":"uint16","name":"requestConfirmations","type":"uint16"},{"internalType":"uint32","name":"callbackGasBase","type":"uint32"},{"internalType":"uint32","name":"callbackGasExtraNFT","type":"uint32"},{"internalType":"uint256","name":"vrfSubId","type":"uint256"},{"internalType":"bytes32","name":"keyHash","type":"bytes32"}],"name":"setChainlinkConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_vrfCoordinator","type":"address"}],"name":"setCoordinator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"feeAmount","type":"uint256"}],"name":"setFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"minExpirationTime_","type":"uint32"}],"name":"setMinExpirationTime","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"maxRandomNFTs","type":"uint32"}],"name":"setRandomNFTsLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"leaderboardId","type":"uint64"}],"name":"shares","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"","type":"uint64"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"staticNFTs","outputs":[{"internalType":"address","name":"collection","type":"address"},{"internalType":"uint16","name":"position","type":"uint16"},{"internalType":"uint256","name":"id","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"","type":"uint64"},{"internalType":"address","name":"","type":"address"}],"name":"tokensClaimed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"","type":"uint64"},{"internalType":"address","name":"","type":"address"}],"name":"winnerPositions","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"leaderboardId","type":"uint64"}],"name":"winners","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"withdrawFees","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
608060405262000013600161ffff6200075d565b6006805463ffffffff191661ffff929092169190911790553480156200003857600080fd5b50604051620063d9380380620063d98339810160408190526200005b916200079c565b833380600081620000b35760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600280546001600160a01b0319166001600160a01b0384811691909117909155811615620000e657620000e6816200018a565b5050506001600160a01b038116620001115760405163d92e233d60e01b815260040160405180910390fd5b600480546001600160a01b0319166001600160a01b0392831617905560016005558416620001525760405163e6c4247b60e01b815260040160405180910390fd5b6200015f60003362000236565b6200016a8262000246565b6200017583620002f2565b620001808162000367565b5050505062000945565b336001600160a01b03821603620001e45760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401620000aa565b600380546001600160a01b0319166001600160a01b03838116918217909255600254604051919216907fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae127890600090a350565b620002428282620003b3565b5050565b60006200025381620003de565b6202a30063ffffffff831610156200027e576040516324e7b4e360e21b815260040160405180910390fd5b6008805463ffffffff8481166c0100000000000000000000000081810263ffffffff60601b1985161790945560408051949093049091168084526020840191909152917f1398a97a969c92d765fc12d4c55a8296fe70182a141706cd6765919681ff441f91015b60405180910390a1505050565b6000620002ff81620003de565b6008805463ffffffff8481166801000000000000000081810263ffffffff60401b1985161790945560408051949093049091168084526020840191909152917f15e0bb40e5929263cd42074e0a96296cbffd80a90be0ecb46d84e656e90970549101620002e5565b60006200037481620003de565b600f80549083905560408051828152602081018590527f74dbbbe280ef27b79a8a0c449d5ae2ba7a31849103241d0f98df70bbc9d03e379101620002e5565b620003bf8282620003ed565b6000828152600160205260409020620003d990826200048d565b505050565b620003ea8133620004ad565b50565b6000828152602081815260408083206001600160a01b038516845290915290205460ff1662000242576000828152602081815260408083206001600160a01b03851684529091529020805460ff19166001179055620004493390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6000620004a4836001600160a01b03841662000529565b90505b92915050565b6000828152602081815260408083206001600160a01b038516845290915290205460ff166200024257620004e1816200057b565b620004ee8360206200058e565b6040516020016200050192919062000821565b60408051601f198184030181529082905262461bcd60e51b8252620000aa916004016200089a565b60008181526001830160205260408120546200057257508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155620004a7565b506000620004a7565b6060620004a76001600160a01b03831660145b606060006200059f836002620008cf565b620005ac906002620008e9565b6001600160401b03811115620005c657620005c6620008ff565b6040519080825280601f01601f191660200182016040528015620005f1576020820181803683370190505b509050600360fc1b816000815181106200060f576200060f62000915565b60200101906001600160f81b031916908160001a905350600f60fb1b8160018151811062000641576200064162000915565b60200101906001600160f81b031916908160001a905350600062000667846002620008cf565b62000674906001620008e9565b90505b6001811115620006f6576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110620006ac57620006ac62000915565b1a60f81b828281518110620006c557620006c562000915565b60200101906001600160f81b031916908160001a90535060049490941c93620006ee816200092b565b905062000677565b508315620004a45760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401620000aa565b634e487b7160e01b600052601160045260246000fd5b61ffff8281168282160390808211156200077b576200077b62000747565b5092915050565b805163ffffffff811681146200079757600080fd5b919050565b60008060008060808587031215620007b357600080fd5b84516001600160a01b0381168114620007cb57600080fd5b9350620007db6020860162000782565b9250620007eb6040860162000782565b6060959095015193969295505050565b60005b8381101562000818578181015183820152602001620007fe565b50506000910152565b7f416363657373436f6e74726f6c3a206163636f756e74200000000000000000008152600083516200085b816017850160208801620007fb565b7001034b99036b4b9b9b4b733903937b6329607d1b60179184019182015283516200088e816028840160208801620007fb565b01602801949350505050565b6020815260008251806020840152620008bb816040850160208701620007fb565b601f01601f19169190910160400192915050565b8082028115828204841417620004a757620004a762000747565b80820180821115620004a757620004a762000747565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b6000816200093d576200093d62000747565b506000190190565b615a8480620009556000396000f3fe6080604052600436106102f15760003560e01c806379ba50971161018f578063a270d4a4116100e1578063ddca3f431161008a578063f2fde38b11610064578063f2fde38b146109f6578063feb5657314610a16578063ff5c868014610a3657600080fd5b8063ddca3f4314610959578063e99c43e31461096f578063ef66ba58146109be57600080fd5b8063ca15c873116100bb578063ca15c873146108f9578063d547741f14610919578063dbd38c591461093957600080fd5b8063a270d4a414610899578063acfdbac0146108b9578063b39ffe6c146108d957600080fd5b80638ea9811711610143578063934131ed1161011d578063934131ed146108445780639eccacf614610864578063a217fddf1461088457600080fd5b80638ea98117146107c05780639010d07c146107e057806391d148541461080057600080fd5b806388d8b2a71161017457806388d8b2a71461074e5780638c422c271461076e5780638da5cb5b1461078e57600080fd5b806379ba5097146107195780638223b9481461072e57600080fd5b806329676e5c1161024857806336568abe116101fc578063587f5ed7116101d6578063587f5ed7146106c3578063694049f0146106d957806369fe0e2d146106f957600080fd5b806336568abe1461066357806342f1054e1461068357806343bc6f3e146106a357600080fd5b806331b412d71161022d57806331b412d7146105d5578063338c99f11461060257806333f44eda1461063657600080fd5b806329676e5c146105775780632f2ff15d146105b557600080fd5b8063164e68de116102aa578063241de80811610284578063241de808146104fa578063248a9ca31461051a57806327368e731461054a57600080fd5b8063164e68de14610472578063165df94c146104925780631fe543e3146104da57600080fd5b8063086e4bfd116102db578063086e4bfd146104145780630a8894c41461043d57806313520c8b1461045f57600080fd5b8062b255b0146102f657806301ffc9a7146103e4575b600080fd5b34801561030257600080fd5b5061038f610311366004614dda565b600760205260009081526040902080546001820154600283015460038401546004909401546001600160a01b03938416949383169363ffffffff74010000000000000000000000000000000000000000850481169478010000000000000000000000000000000000000000000000009004169264ffffffffff169187565b604080516001600160a01b03988916815297909616602088015263ffffffff9485169587019590955292909116606085015264ffffffffff16608084015260a083015260c082015260e0015b60405180910390f35b3480156103f057600080fd5b506104046103ff366004614df5565b610a80565b60405190151581526020016103db565b34801561042057600080fd5b5060085467ffffffffffffffff165b6040519081526020016103db565b34801561044957600080fd5b5061045d610458366004614e5e565b610adc565b005b61045d61046d366004615149565b610d2d565b34801561047e57600080fd5b5061045d61048d366004615222565b611878565b34801561049e57600080fd5b506104b26104ad36600461523f565b611936565b604080516001600160a01b03909416845261ffff9092166020840152908201526060016103db565b3480156104e657600080fd5b5061045d6104f5366004615269565b611998565b34801561050657600080fd5b5061045d6105153660046152b5565b611a07565b34801561052657600080fd5b5061042f610535366004615306565b60009081526020819052604090206001015490565b34801561055657600080fd5b5061056a610565366004614dda565b611b9f565b6040516103db919061531f565b34801561058357600080fd5b506008546105a09068010000000000000000900463ffffffff1681565b60405163ffffffff90911681526020016103db565b3480156105c157600080fd5b5061045d6105d036600461536c565b611c19565b3480156105e157600080fd5b506008546105a0906c01000000000000000000000000900463ffffffff1681565b34801561060e57600080fd5b5061042f7f165b4e4bdd6508f05266283fa4bd4fd5ebb938a5f16867c98d3e5cbad8ff56eb81565b34801561064257600080fd5b50610656610651366004614dda565b611c3e565b6040516103db919061539c565b34801561066f57600080fd5b5061045d61067e36600461536c565b611cad565b34801561068f57600080fd5b5061045d61069e3660046153e2565b611d39565b3480156106af57600080fd5b5061045d6106be366004614dda565b611ef6565b3480156106cf57600080fd5b5061042f60105481565b3480156106e557600080fd5b5061045d6106f436600461544a565b611f84565b34801561070557600080fd5b5061045d610714366004615306565b6123ef565b34801561072557600080fd5b5061045d612441565b34801561073a57600080fd5b5061045d61074936600461544a565b61250e565b34801561075a57600080fd5b5061042f61076936600461544a565b612681565b34801561077a57600080fd5b506104b261078936600461523f565b61277d565b34801561079a57600080fd5b506002546001600160a01b03165b6040516001600160a01b0390911681526020016103db565b3480156107cc57600080fd5b5061045d6107db366004615222565b612799565b3480156107ec57600080fd5b506107a86107fb366004615476565b6128d6565b34801561080c57600080fd5b5061040461081b36600461536c565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b34801561085057600080fd5b5061045d61085f366004615498565b6128f5565b34801561087057600080fd5b506004546107a8906001600160a01b031681565b34801561089057600080fd5b5061042f600081565b3480156108a557600080fd5b5061045d6108b4366004615498565b6129c6565b3480156108c557600080fd5b5061045d6108d436600461544a565b612a50565b3480156108e557600080fd5b5061045d6108f43660046154b3565b612f89565b34801561090557600080fd5b5061042f610914366004615306565b612fd0565b34801561092557600080fd5b5061045d61093436600461536c565b612fe7565b34801561094557600080fd5b5061045d61095436600461544a565b61300c565b34801561096557600080fd5b5061042f600f5481565b34801561097b57600080fd5b506109ab61098a36600461544a565b600960209081526000928352604080842090915290825290205461ffff1681565b60405161ffff90911681526020016103db565b3480156109ca57600080fd5b5061042f6109d936600461544a565b600a60209081526000928352604080842090915290825290205481565b348015610a0257600080fd5b5061045d610a11366004615222565b613280565b348015610a2257600080fd5b5061045d610a313660046154e4565b613291565b348015610a4257600080fd5b50610404610a5136600461551e565b600b60209081526000948552604080862082529385528385208152918452828420909152825290205460ff1681565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f5a05180f000000000000000000000000000000000000000000000000000000001480610ad65750610ad682613938565b92915050565b67ffffffffffffffff851660009081526007602052604090205485906001600160a01b0316336001600160a01b031614610b42576040517f4ca8886700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b85610b6f8167ffffffffffffffff1660009081526007602052604090206002015464ffffffffff16151590565b610c0b5767ffffffffffffffff81166000908152600760205260409020600101544290610bbe9062278d009074010000000000000000000000000000000000000000900463ffffffff1661559a565b64ffffffffff161015610bd957610bd4816139cf565b610c0b565b6040517f1bee0d5a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610c1481613a55565b610c4a576040517fd0404f8500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610c52613a92565b6001600160a01b038316610c92576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff87166000908152600b6020908152604080832061ffff8a16845282528083206001600160a01b0389168452825280832087845290915290205460ff16610d0d576040517f969bf72800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610d1a8787878787613aeb565b610d246001600555565b50505050505050565b836001600160a01b038a16610d6e576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80600003610da8576040517ff841798a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60065463ffffffff16811115610dea576040517f5a90209700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8763ffffffff164210610e29576040517f15d3780400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60085463ffffffff6c0100000000000000000000000090910481169088161015610e7f576040517f939ed38c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6301e1338063ffffffff88161115610ec3576040517fa9a4b7de00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000805b82811015610efd57878782818110610ee157610ee16155b8565b9050602002013582610ef391906155e7565b9150600101610ec7565b506001600160a01b038a16610f5657600f54610f1990826155e7565b3414610f51576040517f39142b5400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110fd565b600f543414610f91576040517f39142b5400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80156110fd576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000906001600160a01b038c16906370a0823190602401602060405180830381865afa158015610ff7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061101b91906155fa565b90506110326001600160a01b038c16333085613c19565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000906001600160a01b038d16906370a0823190602401602060405180830381865afa158015611092573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110b691906155fa565b9050826110c38383615613565b146110fa576040517f280034c700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505b600f546010600082825461111191906155e7565b9250508190555060006040518061012001604052808d6001600160a01b031681526020018c6001600160a01b031681526020018b63ffffffff1681526020018a63ffffffff168152602001600064ffffffffff1681526020018381526020018381526020018989808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152505050908252506020018467ffffffffffffffff8111156111c8576111c8614f67565b6040519080825280602002602001820160405280156111f1578160200160208202803683370190505b50905260088054919250906000906112129067ffffffffffffffff16615626565b82546101009290920a67ffffffffffffffff81810219909316918316021790915560085416600081815260076020908152604091829020845181547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0391821617825585830151600183018054958801516060890151929093167fffffffffffffffff000000000000000000000000000000000000000000000000909616959095177401000000000000000000000000000000000000000063ffffffff93841602177fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff16780100000000000000000000000000000000000000000000000092909116919091021790925560808401516002830180547fffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000001664ffffffffff90921691909117905560a0840151600383015560c0840151600483015560e0840151805185939261138f926005850192910190614c26565b5061010082015180516113ac916006840191602090910190614c71565b50506040805160608101825260008082526020820181905291810182905290915081908190899060005b828110156115c8578c8c828181106113f0576113f06155b8565b905060600201803603810190611406919061564d565b91508160000151945081604001519550816020015193508361ffff168a1080611431575061ffff8416155b15611468576040517fce7e065e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff87166000908152600e6020908152604080832080546001808201835591855293839020865160029095020180549387015161ffff1674010000000000000000000000000000000000000000027fffffffffffffffffffff000000000000000000000000000000000000000000009094166001600160a01b039586161793909317835585820151920191909155517f23b872dd00000000000000000000000000000000000000000000000000000000815233600482015230602482015260448101889052908616906323b872dd90606401600060405180830381600087803b15801561155b57600080fd5b505af115801561156f573d6000803e3d6000fd5b5050505067ffffffffffffffff87166000908152600b6020908152604080832061ffff8816845282528083206001600160a01b038916845282528083208984529091529020805460ff19166001908117909155016113d6565b50895160408051808201909152600081526060602082015290925060008060005b858110156117bc578d8181518110611603576116036155b8565b602002602001015193508360200151519250828261162191906155e7565b84519850915060005b838110156117b35784602001518181518110611648576116486155b8565b60200260200101519950600d60008c67ffffffffffffffff1667ffffffffffffffff16815260200190815260200160002060405180606001604052808b6001600160a01b03168152602001600061ffff1681526020018c815250908060018154018082558091505060019003906000526020600020906002020160009091909190915060008201518160000160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060208201518160000160146101000a81548161ffff021916908361ffff160217905550604082015181600101555050886001600160a01b03166323b872dd33308d6040518463ffffffff1660e01b8152600401611776939291906001600160a01b039384168152919092166020820152604081019190915260600190565b600060405180830381600087803b15801561179057600080fd5b505af11580156117a4573d6000803e3d6000fd5b5050505080600101905061162a565b506001016115e9565b5060085468010000000000000000900463ffffffff1681111561180b576040517fdceda73e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505050505050508c6001600160a01b03168167ffffffffffffffff167f96ce59e14c7cb8c2bd14fe9af6e771bfd51d9be8b63e3fdeba2d593113d9285e8e8e8e888f8f604051611861969594939291906156b7565b60405180910390a350505050505050505050505050565b611880613a92565b600061188b81613cca565b6001600160a01b0382166118cb576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60105480156119275760006010556118e38382613cd4565b826001600160a01b03167fc0819c13be868895eb93e40eaceb96de976442fa1d404e5c55f14bb65a8c489a8260405161191e91815260200190565b60405180910390a25b50506119336001600555565b50565b600e602052816000526040600020818154811061195257600080fd5b6000918252602090912060029091020180546001909101546001600160a01b03821693507401000000000000000000000000000000000000000090910461ffff16915083565b6004546001600160a01b031633146119f757600480546040517f1cf993f400000000000000000000000000000000000000000000000000000000815233928101929092526001600160a01b031660248201526044015b60405180910390fd5b611a02838383613ded565b505050565b611a0f613a92565b6001600160a01b038116611a4f576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff841660009081526007602052604090206002015464ffffffffff16611aa9576040517f1bee0d5a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611ab284613a55565b15611ae9576040517f203d82d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff841660008181526009602090815260408083203380855290835281842054948452600b835281842061ffff9095168085529483528184206001600160a01b038916855283528184208785529092529091205490919060ff16611b80576040517f969bf72800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611b8d8682878787614094565b5050611b996001600555565b50505050565b67ffffffffffffffff8116600090815260076020908152604091829020600601805483518184028101840190945280845260609392830182828015611c0d57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611bef575b50505050509050919050565b600082815260208190526040902060010154611c3481613cca565b611a0283836141ac565b67ffffffffffffffff8116600090815260076020908152604091829020600501805483518184028101840190945280845260609392830182828015611c0d57602002820191906000526020600020905b815481526020019060010190808311611c8e5750505050509050919050565b6001600160a01b0381163314611d2b5760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201527f20726f6c657320666f722073656c66000000000000000000000000000000000060648201526084016119ee565b611d3582826141ce565b5050565b6000611d4481613cca565b63ffffffff85161580611d5b575063ffffffff8416155b80611d64575082155b80611d6d575081155b15611da4576040517fe570699100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805160c0808201835289151580835261ffff8a16602080850182905263ffffffff8b8116868801819052908b16606080880182905260808089018d905260a09889018c9052601180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000ff8916176101008802177fffffffffffffffffffffffffffffffffffffffffff0000000000000000ffffff16630100000086027fffffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffff1617670100000000000000850217905560128d905560138c905589519687529386019490945296840152908201949094529283018690529082018490527f52a459681d6c26ba4c8e787b15f0e115e8c64394c2b24290170956f99825e5d6910160405180910390a150505050505050565b7f165b4e4bdd6508f05266283fa4bd4fd5ebb938a5f16867c98d3e5cbad8ff56eb611f2081613cca565b67ffffffffffffffff821660009081526007602052604090206002015464ffffffffff1615611f7b576040517f475a253500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611d35826139cf565b611f8c613a92565b67ffffffffffffffff821660009081526007602052604090206002015464ffffffffff16611fe6576040517f1bee0d5a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611fef82613a55565b15612026576040517f203d82d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038116612066576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff82166000908152600960209081526040808320338085529252822054909161ffff909116908190036120cd576040517f93a5f3c700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff84166000908152600e6020908152604080832080548251818502810185019093528083529192909190849084015b82821015612170576000848152602090819020604080516060810182526002860290920180546001600160a01b038116845274010000000000000000000000000000000000000000900461ffff1683850152600190810154918301919091529083529092019101612104565b50505050905060008151905060008060005b838110156122595784818151811061219c5761219c6155b8565b60200260200101516000015192508481815181106121bc576121bc6155b8565b60200260200101516040015191508561ffff168582815181106121e1576121e16155b8565b60200260200101516020015161ffff1614801561223f575067ffffffffffffffff89166000908152600b6020908152604080832061ffff8a16845282528083206001600160a01b0387168452825280832085845290915290205460ff165b1561225157612251898785858c614094565b600101612182565b5067ffffffffffffffff88166000908152600d6020908152604080832080548251818502810185019093528083529193909284015b828210156122fa576000848152602090819020604080516060810182526002860290920180546001600160a01b038116845274010000000000000000000000000000000000000000900461ffff168385015260019081015491830191909152908352909201910161228e565b5050505093508351925060005b838110156123de57848181518110612321576123216155b8565b6020026020010151600001519250848181518110612341576123416155b8565b60200260200101516040015191508561ffff16858281518110612366576123666155b8565b60200260200101516020015161ffff161480156123c4575067ffffffffffffffff89166000908152600b6020908152604080832061ffff8a16845282528083206001600160a01b0387168452825280832085845290915290205460ff165b156123d6576123d6898785858c614094565b600101612307565b50505050505050611d356001600555565b60006123fa81613cca565b600f80549083905560408051828152602081018590527f74dbbbe280ef27b79a8a0c449d5ae2ba7a31849103241d0f98df70bbc9d03e3791015b60405180910390a1505050565b6003546001600160a01b0316331461249b5760405162461bcd60e51b815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064016119ee565b600280547fffffffffffffffffffffffff0000000000000000000000000000000000000000808216339081179093556003805490911690556040516001600160a01b03909116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a350565b612516613a92565b6001600160a01b038116612556576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3360006125638483612681565b90508060000361259f576040517f969bf72800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff84166000818152600a602090815260408083206001600160a01b038716845282528083208590559282526007905290812060030180548392906125ec908490615613565b909155505067ffffffffffffffff84166000908152600760205260409020600101546001600160a01b03166126228482846141f0565b604080516001600160a01b0383811682526020820185905285169167ffffffffffffffff8816917fce3bcb6e219596cf26007ffdfaae8953bc3f76e3f36c0a79b23e28020da3222e910160405180910390a3505050611d356001600555565b67ffffffffffffffff821660009081526007602052604081206002015464ffffffffff1615806126b557506126b583613a55565b156126c257506000610ad6565b67ffffffffffffffff831660009081526009602090815260408083206001600160a01b038616845290915281205461ffff1690819003612706576000915050610ad6565b67ffffffffffffffff84166000818152600a602090815260408083206001600160a01b03881684528252808320549383526007909152902060050161274c600184615738565b61ffff1681548110612760576127606155b8565b90600052602060002001546127759190615613565b949350505050565b600d602052816000526040600020818154811061195257600080fd5b6002546001600160a01b031633148015906127bf57506004546001600160a01b03163314155b1561282a57336127d76002546001600160a01b031690565b600480546040517f061db9c10000000000000000000000000000000000000000000000000000000081526001600160a01b03948516928101929092529183166024820152911660448201526064016119ee565b6001600160a01b03811661286a576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600480547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527fd1a6a14209a385a964d036e404cb5cfb71f4000cdb03c9366292430787261be69060200160405180910390a150565b60008281526001602052604081206128ee908361421c565b9392505050565b600061290081613cca565b6202a30063ffffffff83161015612943576040517f939ed38c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6008805463ffffffff8481166c010000000000000000000000008181027fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff85161790945560408051949093049091168084526020840191909152917f1398a97a969c92d765fc12d4c55a8296fe70182a141706cd6765919681ff441f9101612434565b60006129d181613cca565b6008805463ffffffff848116680100000000000000008181027fffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffffff85161790945560408051949093049091168084526020840191909152917f15e0bb40e5929263cd42074e0a96296cbffd80a90be0ecb46d84e656e90970549101612434565b67ffffffffffffffff821660009081526007602052604090205482906001600160a01b0316336001600160a01b031614612ab6576040517f4ca8886700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82612ae38167ffffffffffffffff1660009081526007602052604090206002015464ffffffffff16151590565b612b485767ffffffffffffffff81166000908152600760205260409020600101544290612b329062278d009074010000000000000000000000000000000000000000900463ffffffff1661559a565b64ffffffffff161015610bd957612b48816139cf565b612b5181613a55565b612b87576040517fd0404f8500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612b8f613a92565b6001600160a01b038316612bcf576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff84166000908152600e6020908152604080832080548251818502810185019093528083529192909190849084015b82821015612c72576000848152602090819020604080516060810182526002860290920180546001600160a01b038116845274010000000000000000000000000000000000000000900461ffff1683850152600190810154918301919091529083529092019101612c06565b5050505090506000815190506000806000612cb4604051806060016040528060006001600160a01b03168152602001600061ffff168152602001600081525090565b60005b85811015612d8c57868181518110612cd157612cd16155b8565b60200260200101519150816000015194508160400151935081602001519250600b60008c67ffffffffffffffff1667ffffffffffffffff16815260200190815260200160002060008461ffff1661ffff1681526020019081526020016000206000866001600160a01b03166001600160a01b03168152602001908152602001600020600085815260200190815260200160002060009054906101000a900460ff1615612d8457612d848b8487878e613aeb565b600101612cb7565b5067ffffffffffffffff8a166000908152600d6020908152604080832080548251818502810185019093528083529193909284015b82821015612e2d576000848152602090819020604080516060810182526002860290920180546001600160a01b038116845274010000000000000000000000000000000000000000900461ffff1683850152600190810154918301919091529083529092019101612dc1565b50505067ffffffffffffffff8c166000908152600d6020526040812054929850919650505b85811015612f3457868181518110612e6c57612e6c6155b8565b60200260200101519150816000015194508160400151935081602001519250600b60008c67ffffffffffffffff1667ffffffffffffffff16815260200190815260200160002060008461ffff1661ffff1681526020019081526020016000206000866001600160a01b03166001600160a01b03168152602001908152602001600020600085815260200190815260200160002060009054906101000a900460ff1680612f1a575061ffff8316155b15612f2c57612f2c8b8487878e613aeb565b600101612e52565b5067ffffffffffffffff8a166000908152600d60205260408120612f5791614cde565b67ffffffffffffffff8a166000908152600e60205260408120612f7991614cde565b505050505050611b996001600555565b67ffffffffffffffff851660009081526009602090815260408083206001600160a01b038816845290915290205461ffff16612fc88682868686610adc565b505050505050565b6000818152600160205260408120610ad690614228565b60008281526020819052604090206001015461300281613cca565b611a0283836141ce565b613014613a92565b67ffffffffffffffff821660009081526007602052604090205482906001600160a01b0316336001600160a01b03161461307a576040517f4ca8886700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b826130a78167ffffffffffffffff1660009081526007602052604090206002015464ffffffffff16151590565b61310c5767ffffffffffffffff811660009081526007602052604090206001015442906130f69062278d009074010000000000000000000000000000000000000000900463ffffffff1661559a565b64ffffffffff161015610bd95761310c816139cf565b61311581613a55565b61314b576040517fd0404f8500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03831661318b576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff8416600090815260076020526040812060030154908190036131e2576040517f969bf72800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff851660009081526007602052604081206003810191909155600101546001600160a01b031661321b8582846141f0565b604080516001600160a01b0380881682528316602082015290810183905267ffffffffffffffff8716907fedfc9916535bec7ba1d215e222639f283bb485a546fbc7751cb3003b2487c89d9060600160405180910390a250505050611d356001600555565b613288614232565b6119338161428e565b7f165b4e4bdd6508f05266283fa4bd4fd5ebb938a5f16867c98d3e5cbad8ff56eb6132bb81613cca565b67ffffffffffffffff84166000908152600760205260409020600181015474010000000000000000000000000000000000000000900463ffffffff16421161332f576040517fd3018d1800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff851660009081526007602052604090206002015464ffffffffff161561338a576040517f475a253500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8280156138915760058201548111156133cf576040517f9172fb5300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001820154613400907801000000000000000000000000000000000000000000000000900463ffffffff164261559a565b67ffffffffffffffff8716600090815260076020526040812060020180547fffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000001664ffffffffff93909316929092179091555b818110156134f857613465816001615753565b67ffffffffffffffff8816600090815260096020526040812090888885818110613491576134916155b8565b90506020020160208101906134a69190615222565b6001600160a01b03168152602081019190915260400160002080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001661ffff92909216919091179055600101613452565b5067ffffffffffffffff86166000908152600760205260409020613520906006018686614cff565b5067ffffffffffffffff86166000908152600d602052604090205463ffffffff81161561388b57816001036137385767ffffffffffffffff87166000908152600d602052604081209080808989828161357b5761357b6155b8565b90506020020160208101906135909190615222565b905060005b8563ffffffff1681101561372e578481815481106135b5576135b56155b8565b600091825260209091206002909102015485546001600160a01b0390911694508590829081106135e7576135e76155b8565b90600052602060002090600202016001015492506001600b60008e67ffffffffffffffff1667ffffffffffffffff1681526020019081526020016000206000600161ffff1681526020019081526020016000206000866001600160a01b03166001600160a01b03168152602001908152602001600020600085815260200190815260200160002060006101000a81548160ff0219169083151502179055506001858281548110613699576136996155b8565b906000526020600020906002020160000160146101000a81548161ffff021916908361ffff160217905550816001600160a01b03168c67ffffffffffffffff167ff6e5a522fc2128fe2f9aa758c32fd30639e277a0391e3b77c242e2e140834017868660405161371e9291906001600160a01b03929092168252602082015260400190565b60405180910390a3600101613595565b505050505061388b565b6004546040805160c08101825260135481526012546020820152601154610100810461ffff16928201929092526000926001600160a01b031691639b1c385e9190606082019061379a908790670100000000000000900463ffffffff1661576e565b6011546137b491906301000000900463ffffffff16615796565b63ffffffff168152600160208083019190915260408051918201815260115460ff1615158252909101906137e790614350565b8152506040518263ffffffff1660e01b81526004016138069190615803565b6020604051808303816000875af1158015613825573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061384991906155fa565b6000908152600c6020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff8a16179055505b506138ec565b61389c600142615860565b67ffffffffffffffff8716600090815260076020526040902060020180547fffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000001663ffffffff929092169190911790555b8567ffffffffffffffff167f16d96bffc4329041e15226a1bd1b20bc0ee33f5f7bb8e11877eb917ae2c05ca3868660405161392892919061587d565b60405180910390a2505050505050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f7965db0b000000000000000000000000000000000000000000000000000000001480610ad657507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000831614610ad6565b6139da600142615860565b67ffffffffffffffff821660008181526007602052604080822060020180547fffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000001663ffffffff9590951694909417909355915190917fb7329e7cadb48d9cf876a7b7c6f4b44919ce6e8f47138d118707acfb9264983591a250565b67ffffffffffffffff811660009081526007602052604081206002015464ffffffffff1680158015906128ee575064ffffffffff16421192915050565b600260055403613ae45760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016119ee565b6002600555565b67ffffffffffffffff85166000908152600b6020908152604080832061ffff8816845282528083206001600160a01b0387811680865291845282852087865290935292819020805460ff19169055517f42842e0e0000000000000000000000000000000000000000000000000000000081523060048201529083166024820152604481018490526342842e0e90606401600060405180830381600087803b158015613b9557600080fd5b505af1158015613ba9573d6000803e3d6000fd5b5050604080516001600160a01b03808616825261ffff891660208301528716918101919091526060810185905267ffffffffffffffff881692507f6334894bb9d500f9065592453c8b0f0127e2220c83be138f2792a93fb51e932f91506080015b60405180910390a25050505050565b6040516001600160a01b0380851660248301528316604482015260648101829052611b999085907f23b872dd00000000000000000000000000000000000000000000000000000000906084015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091526143ee565b61193381336144d6565b80471015613d245760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e636500000060448201526064016119ee565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114613d71576040519150601f19603f3d011682016040523d82523d6000602084013e613d76565b606091505b5050905080611a025760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d6179206861766520726576657274656400000000000060648201526084016119ee565b600082826000818110613e0257613e026155b8565b6000878152600c602090815260408083205467ffffffffffffffff168084526007835281842060060154600d845291842080549390950296909601359650935090918291825b81811015614053576000868983604051602001613e6f929190918252602082015260400190565b6040516020818303038152906040528051906020012060001c613e9291906158cb565b67ffffffffffffffff891660009081526007602052604081206006018054929350909183908110613ec557613ec56155b8565b60009182526020909120015485546001600160a01b039091169150859084908110613ef257613ef26155b8565b600091825260209091206002909102015485546001600160a01b039091169750859084908110613f2457613f246155b8565b60009182526020808320600160029093020182015467ffffffffffffffff8d168452600b909152604083209098509091613f5e85846155e7565b61ffff168152602080820192909252604090810160009081206001600160a01b038c16825283528181208a82529092529020805460ff1916911515919091179055613faa8260016155e7565b858481548110613fbc57613fbc6155b8565b906000526020600020906002020160000160146101000a81548161ffff021916908361ffff160217905550806001600160a01b03168967ffffffffffffffff167ff6e5a522fc2128fe2f9aa758c32fd30639e277a0391e3b77c242e2e14083401789896040516140419291906001600160a01b03929092168252602082015260400190565b60405180910390a35050600101613e48565b50505060009788525050600c6020525050604090932080547fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000169055505050565b67ffffffffffffffff85166000908152600b6020908152604080832061ffff8816845282528083206001600160a01b0387811680865291845282852087865290935292819020805460ff19169055517f42842e0e0000000000000000000000000000000000000000000000000000000081523060048201529083166024820152604481018490526342842e0e90606401600060405180830381600087803b15801561413e57600080fd5b505af1158015614152573d6000803e3d6000fd5b50506040805161ffff881681526001600160a01b038716602082015290810185905267ffffffffffffffff881692507fbcdf1487c9b0f6650c1e48fce71b090ec57dba8ba1f02b1b14212a3730e91e0c9150606001613c0a565b6141b68282614549565b6000828152600160205260409020611a0290826145e7565b6141d882826145fc565b6000828152600160205260409020611a02908261467b565b6001600160a01b03821661420857611a028382613cd4565b611a026001600160a01b0383168483614690565b60006128ee83836146d9565b6000610ad6825490565b6002546001600160a01b0316331461428c5760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016119ee565b565b336001600160a01b038216036142e65760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016119ee565b600380547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03838116918217909255600254604051919216907fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae127890600090a350565b60607f92fd13387c7fe7befbc38d303d6468778fb9731bc4583f17d92989c6fcfdeaaa8260405160240161438991511515815260200190565b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915292915050565b6000614443826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166147039092919063ffffffff16565b90508051600014806144645750808060200190518101906144649190615906565b611a025760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f7420737563636565640000000000000000000000000000000000000000000060648201526084016119ee565b6000828152602081815260408083206001600160a01b038516845290915290205460ff16611d355761450781614712565b614512836020614724565b604051602001614523929190615923565b60408051601f198184030181529082905262461bcd60e51b82526119ee916004016159a4565b6000828152602081815260408083206001600160a01b038516845290915290205460ff16611d35576000828152602081815260408083206001600160a01b03851684529091529020805460ff191660011790556145a33390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b60006128ee836001600160a01b03841661494d565b6000828152602081815260408083206001600160a01b038516845290915290205460ff1615611d35576000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b60006128ee836001600160a01b03841661499c565b6040516001600160a01b038316602482015260448101829052611a029084907fa9059cbb0000000000000000000000000000000000000000000000000000000090606401613c66565b60008260000182815481106146f0576146f06155b8565b9060005260206000200154905092915050565b60606127758484600085614a96565b6060610ad66001600160a01b03831660145b606060006147338360026159b7565b61473e9060026155e7565b67ffffffffffffffff81111561475657614756614f67565b6040519080825280601f01601f191660200182016040528015614780576020820181803683370190505b5090507f3000000000000000000000000000000000000000000000000000000000000000816000815181106147b7576147b76155b8565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f78000000000000000000000000000000000000000000000000000000000000008160018151811061481a5761481a6155b8565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060006148568460026159b7565b6148619060016155e7565b90505b60018111156148fe577f303132333435363738396162636465660000000000000000000000000000000085600f16601081106148a2576148a26155b8565b1a60f81b8282815181106148b8576148b86155b8565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060049490941c936148f7816159ce565b9050614864565b5083156128ee5760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e7460448201526064016119ee565b600081815260018301602052604081205461499457508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610ad6565b506000610ad6565b60008181526001830160205260408120548015614a855760006149c0600183615613565b85549091506000906149d490600190615613565b9050818114614a395760008660000182815481106149f4576149f46155b8565b9060005260206000200154905080876000018481548110614a1757614a176155b8565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080614a4a57614a4a615a03565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610ad6565b6000915050610ad6565b5092915050565b606082471015614b0e5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c000000000000000000000000000000000000000000000000000060648201526084016119ee565b600080866001600160a01b03168587604051614b2a9190615a32565b60006040518083038185875af1925050503d8060008114614b67576040519150601f19603f3d011682016040523d82523d6000602084013e614b6c565b606091505b5091509150614b7d87838387614b88565b979650505050505050565b60608315614bf7578251600003614bf0576001600160a01b0385163b614bf05760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016119ee565b5081612775565b6127758383815115614c0c5781518083602001fd5b8060405162461bcd60e51b81526004016119ee91906159a4565b828054828255906000526020600020908101928215614c61579160200282015b82811115614c61578251825591602001919060010190614c46565b50614c6d929150614d6a565b5090565b828054828255906000526020600020908101928215614c61579160200282015b82811115614c6157825182547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03909116178255602090920191600190910190614c91565b50805460008255600202906000526020600020908101906119339190614d7f565b828054828255906000526020600020908101928215614c61579160200282015b82811115614c615781547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03843516178255602090920191600190910190614d1f565b5b80821115614c6d5760008155600101614d6b565b5b80821115614c6d5780547fffffffffffffffffffff0000000000000000000000000000000000000000000016815560006001820155600201614d80565b803567ffffffffffffffff81168114614dd557600080fd5b919050565b600060208284031215614dec57600080fd5b6128ee82614dbd565b600060208284031215614e0757600080fd5b81357fffffffff00000000000000000000000000000000000000000000000000000000811681146128ee57600080fd5b803561ffff81168114614dd557600080fd5b6001600160a01b038116811461193357600080fd5b600080600080600060a08688031215614e7657600080fd5b614e7f86614dbd565b9450614e8d60208701614e37565b93506040860135614e9d81614e49565b9250606086013591506080860135614eb481614e49565b809150509295509295909350565b803563ffffffff81168114614dd557600080fd5b60008083601f840112614ee857600080fd5b50813567ffffffffffffffff811115614f0057600080fd5b6020830191508360208260051b8501011115614f1b57600080fd5b9250929050565b60008083601f840112614f3457600080fd5b50813567ffffffffffffffff811115614f4c57600080fd5b602083019150836020606083028501011115614f1b57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715614fb957614fb9614f67565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715614fe857614fe8614f67565b604052919050565b600067ffffffffffffffff82111561500a5761500a614f67565b5060051b60200190565b600082601f83011261502557600080fd5b8135602061503a61503583614ff0565b614fbf565b828152600592831b850182019282820191908785111561505957600080fd5b8387015b8581101561513c57803567ffffffffffffffff8082111561507e5760008081fd5b818a019150604080601f19848e030112156150995760008081fd5b6150a1614f96565b888401356150ae81614e49565b815283820135838111156150c25760008081fd5b8085019450508c603f8501126150da57600092508283fd5b8884013592506150ec61503584614ff0565b83815292861b8401820192898101908e8511156151095760008081fd5b948301945b848610156151275785358252948a0194908a019061510e565b828b015250875250505092840192840161505d565b5090979650505050505050565b600080600080600080600080600060e08a8c03121561516757600080fd5b893561517281614e49565b985060208a013561518281614e49565b975061519060408b01614ec2565b965061519e60608b01614ec2565b955060808a013567ffffffffffffffff808211156151bb57600080fd5b6151c78d838e01614ed6565b909750955060a08c01359150808211156151e057600080fd5b6151ec8d838e01614f22565b909550935060c08c013591508082111561520557600080fd5b506152128c828d01615014565b9150509295985092959850929598565b60006020828403121561523457600080fd5b81356128ee81614e49565b6000806040838503121561525257600080fd5b61525b83614dbd565b946020939093013593505050565b60008060006040848603121561527e57600080fd5b83359250602084013567ffffffffffffffff81111561529c57600080fd5b6152a886828701614ed6565b9497909650939450505050565b600080600080608085870312156152cb57600080fd5b6152d485614dbd565b935060208501356152e481614e49565b92506040850135915060608501356152fb81614e49565b939692955090935050565b60006020828403121561531857600080fd5b5035919050565b6020808252825182820181905260009190848201906040850190845b818110156153605783516001600160a01b03168352928401929184019160010161533b565b50909695505050505050565b6000806040838503121561537f57600080fd5b82359150602083013561539181614e49565b809150509250929050565b6020808252825182820181905260009190848201906040850190845b81811015615360578351835292840192918401916001016153b8565b801515811461193357600080fd5b60008060008060008060c087890312156153fb57600080fd5b8635615406816153d4565b955061541460208801614e37565b945061542260408801614ec2565b935061543060608801614ec2565b92506080870135915060a087013590509295509295509295565b6000806040838503121561545d57600080fd5b61546683614dbd565b9150602083013561539181614e49565b6000806040838503121561548957600080fd5b50508035926020909101359150565b6000602082840312156154aa57600080fd5b6128ee82614ec2565b600080600080600060a086880312156154cb57600080fd5b6154d486614dbd565b94506020860135614e8d81614e49565b6000806000604084860312156154f957600080fd5b61550284614dbd565b9250602084013567ffffffffffffffff81111561529c57600080fd5b6000806000806080858703121561553457600080fd5b61553d85614dbd565b935061554b60208601614e37565b9250604085013561555b81614e49565b9396929550929360600135925050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b64ffffffffff818116838216019080821115614a8f57614a8f61556b565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b80820180821115610ad657610ad661556b565b60006020828403121561560c57600080fd5b5051919050565b81810381811115610ad657610ad661556b565b600067ffffffffffffffff8083168181036156435761564361556b565b6001019392505050565b60006060828403121561565f57600080fd5b6040516060810181811067ffffffffffffffff8211171561568257615682614f67565b604052823561569081614e49565b815261569e60208401614e37565b6020820152604083013560408201528091505092915050565b6001600160a01b0387168152600063ffffffff808816602084015280871660408401525084606083015260a060808301528260a08301527f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83111561571b57600080fd5b8260051b808560c08501379190910160c001979650505050505050565b61ffff828116828216039080821115614a8f57614a8f61556b565b61ffff818116838216019080821115614a8f57614a8f61556b565b63ffffffff81811683821602808216919082811461578e5761578e61556b565b505092915050565b63ffffffff818116838216019080821115614a8f57614a8f61556b565b60005b838110156157ce5781810151838201526020016157b6565b50506000910152565b600081518084526157ef8160208601602086016157b3565b601f01601f19169290920160200192915050565b60208152815160208201526020820151604082015261ffff60408301511660608201526000606083015163ffffffff80821660808501528060808601511660a0850152505060a083015160c08084015261277560e08401826157d7565b63ffffffff828116828216039080821115614a8f57614a8f61556b565b60208082528181018390526000908460408401835b868110156158c05782356158a581614e49565b6001600160a01b031682529183019190830190600101615892565b509695505050505050565b600082615901577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500690565b60006020828403121561591857600080fd5b81516128ee816153d4565b7f416363657373436f6e74726f6c3a206163636f756e742000000000000000000081526000835161595b8160178501602088016157b3565b7f206973206d697373696e6720726f6c652000000000000000000000000000000060179184019182015283516159988160288401602088016157b3565b01602801949350505050565b6020815260006128ee60208301846157d7565b8082028115828204841417610ad657610ad661556b565b6000816159dd576159dd61556b565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b60008251615a448184602087016157b3565b919091019291505056fea264697066735822122083946c20afe0f66cec3386926ac32781a258a708932af4506fec426a277dda6b64736f6c634300081300330000000000000000000000003c0ca683b403e37668ae3dc4fb62f4b29b6f7a3e00000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000093a800000000000000000000000000000000000000000000000000000b5e620f48000
Deployed Bytecode
0x6080604052600436106102f15760003560e01c806379ba50971161018f578063a270d4a4116100e1578063ddca3f431161008a578063f2fde38b11610064578063f2fde38b146109f6578063feb5657314610a16578063ff5c868014610a3657600080fd5b8063ddca3f4314610959578063e99c43e31461096f578063ef66ba58146109be57600080fd5b8063ca15c873116100bb578063ca15c873146108f9578063d547741f14610919578063dbd38c591461093957600080fd5b8063a270d4a414610899578063acfdbac0146108b9578063b39ffe6c146108d957600080fd5b80638ea9811711610143578063934131ed1161011d578063934131ed146108445780639eccacf614610864578063a217fddf1461088457600080fd5b80638ea98117146107c05780639010d07c146107e057806391d148541461080057600080fd5b806388d8b2a71161017457806388d8b2a71461074e5780638c422c271461076e5780638da5cb5b1461078e57600080fd5b806379ba5097146107195780638223b9481461072e57600080fd5b806329676e5c1161024857806336568abe116101fc578063587f5ed7116101d6578063587f5ed7146106c3578063694049f0146106d957806369fe0e2d146106f957600080fd5b806336568abe1461066357806342f1054e1461068357806343bc6f3e146106a357600080fd5b806331b412d71161022d57806331b412d7146105d5578063338c99f11461060257806333f44eda1461063657600080fd5b806329676e5c146105775780632f2ff15d146105b557600080fd5b8063164e68de116102aa578063241de80811610284578063241de808146104fa578063248a9ca31461051a57806327368e731461054a57600080fd5b8063164e68de14610472578063165df94c146104925780631fe543e3146104da57600080fd5b8063086e4bfd116102db578063086e4bfd146104145780630a8894c41461043d57806313520c8b1461045f57600080fd5b8062b255b0146102f657806301ffc9a7146103e4575b600080fd5b34801561030257600080fd5b5061038f610311366004614dda565b600760205260009081526040902080546001820154600283015460038401546004909401546001600160a01b03938416949383169363ffffffff74010000000000000000000000000000000000000000850481169478010000000000000000000000000000000000000000000000009004169264ffffffffff169187565b604080516001600160a01b03988916815297909616602088015263ffffffff9485169587019590955292909116606085015264ffffffffff16608084015260a083015260c082015260e0015b60405180910390f35b3480156103f057600080fd5b506104046103ff366004614df5565b610a80565b60405190151581526020016103db565b34801561042057600080fd5b5060085467ffffffffffffffff165b6040519081526020016103db565b34801561044957600080fd5b5061045d610458366004614e5e565b610adc565b005b61045d61046d366004615149565b610d2d565b34801561047e57600080fd5b5061045d61048d366004615222565b611878565b34801561049e57600080fd5b506104b26104ad36600461523f565b611936565b604080516001600160a01b03909416845261ffff9092166020840152908201526060016103db565b3480156104e657600080fd5b5061045d6104f5366004615269565b611998565b34801561050657600080fd5b5061045d6105153660046152b5565b611a07565b34801561052657600080fd5b5061042f610535366004615306565b60009081526020819052604090206001015490565b34801561055657600080fd5b5061056a610565366004614dda565b611b9f565b6040516103db919061531f565b34801561058357600080fd5b506008546105a09068010000000000000000900463ffffffff1681565b60405163ffffffff90911681526020016103db565b3480156105c157600080fd5b5061045d6105d036600461536c565b611c19565b3480156105e157600080fd5b506008546105a0906c01000000000000000000000000900463ffffffff1681565b34801561060e57600080fd5b5061042f7f165b4e4bdd6508f05266283fa4bd4fd5ebb938a5f16867c98d3e5cbad8ff56eb81565b34801561064257600080fd5b50610656610651366004614dda565b611c3e565b6040516103db919061539c565b34801561066f57600080fd5b5061045d61067e36600461536c565b611cad565b34801561068f57600080fd5b5061045d61069e3660046153e2565b611d39565b3480156106af57600080fd5b5061045d6106be366004614dda565b611ef6565b3480156106cf57600080fd5b5061042f60105481565b3480156106e557600080fd5b5061045d6106f436600461544a565b611f84565b34801561070557600080fd5b5061045d610714366004615306565b6123ef565b34801561072557600080fd5b5061045d612441565b34801561073a57600080fd5b5061045d61074936600461544a565b61250e565b34801561075a57600080fd5b5061042f61076936600461544a565b612681565b34801561077a57600080fd5b506104b261078936600461523f565b61277d565b34801561079a57600080fd5b506002546001600160a01b03165b6040516001600160a01b0390911681526020016103db565b3480156107cc57600080fd5b5061045d6107db366004615222565b612799565b3480156107ec57600080fd5b506107a86107fb366004615476565b6128d6565b34801561080c57600080fd5b5061040461081b36600461536c565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b34801561085057600080fd5b5061045d61085f366004615498565b6128f5565b34801561087057600080fd5b506004546107a8906001600160a01b031681565b34801561089057600080fd5b5061042f600081565b3480156108a557600080fd5b5061045d6108b4366004615498565b6129c6565b3480156108c557600080fd5b5061045d6108d436600461544a565b612a50565b3480156108e557600080fd5b5061045d6108f43660046154b3565b612f89565b34801561090557600080fd5b5061042f610914366004615306565b612fd0565b34801561092557600080fd5b5061045d61093436600461536c565b612fe7565b34801561094557600080fd5b5061045d61095436600461544a565b61300c565b34801561096557600080fd5b5061042f600f5481565b34801561097b57600080fd5b506109ab61098a36600461544a565b600960209081526000928352604080842090915290825290205461ffff1681565b60405161ffff90911681526020016103db565b3480156109ca57600080fd5b5061042f6109d936600461544a565b600a60209081526000928352604080842090915290825290205481565b348015610a0257600080fd5b5061045d610a11366004615222565b613280565b348015610a2257600080fd5b5061045d610a313660046154e4565b613291565b348015610a4257600080fd5b50610404610a5136600461551e565b600b60209081526000948552604080862082529385528385208152918452828420909152825290205460ff1681565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f5a05180f000000000000000000000000000000000000000000000000000000001480610ad65750610ad682613938565b92915050565b67ffffffffffffffff851660009081526007602052604090205485906001600160a01b0316336001600160a01b031614610b42576040517f4ca8886700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b85610b6f8167ffffffffffffffff1660009081526007602052604090206002015464ffffffffff16151590565b610c0b5767ffffffffffffffff81166000908152600760205260409020600101544290610bbe9062278d009074010000000000000000000000000000000000000000900463ffffffff1661559a565b64ffffffffff161015610bd957610bd4816139cf565b610c0b565b6040517f1bee0d5a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610c1481613a55565b610c4a576040517fd0404f8500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610c52613a92565b6001600160a01b038316610c92576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff87166000908152600b6020908152604080832061ffff8a16845282528083206001600160a01b0389168452825280832087845290915290205460ff16610d0d576040517f969bf72800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610d1a8787878787613aeb565b610d246001600555565b50505050505050565b836001600160a01b038a16610d6e576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80600003610da8576040517ff841798a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60065463ffffffff16811115610dea576040517f5a90209700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8763ffffffff164210610e29576040517f15d3780400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60085463ffffffff6c0100000000000000000000000090910481169088161015610e7f576040517f939ed38c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6301e1338063ffffffff88161115610ec3576040517fa9a4b7de00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000805b82811015610efd57878782818110610ee157610ee16155b8565b9050602002013582610ef391906155e7565b9150600101610ec7565b506001600160a01b038a16610f5657600f54610f1990826155e7565b3414610f51576040517f39142b5400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110fd565b600f543414610f91576040517f39142b5400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80156110fd576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000906001600160a01b038c16906370a0823190602401602060405180830381865afa158015610ff7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061101b91906155fa565b90506110326001600160a01b038c16333085613c19565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000906001600160a01b038d16906370a0823190602401602060405180830381865afa158015611092573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110b691906155fa565b9050826110c38383615613565b146110fa576040517f280034c700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505b600f546010600082825461111191906155e7565b9250508190555060006040518061012001604052808d6001600160a01b031681526020018c6001600160a01b031681526020018b63ffffffff1681526020018a63ffffffff168152602001600064ffffffffff1681526020018381526020018381526020018989808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152505050908252506020018467ffffffffffffffff8111156111c8576111c8614f67565b6040519080825280602002602001820160405280156111f1578160200160208202803683370190505b50905260088054919250906000906112129067ffffffffffffffff16615626565b82546101009290920a67ffffffffffffffff81810219909316918316021790915560085416600081815260076020908152604091829020845181547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0391821617825585830151600183018054958801516060890151929093167fffffffffffffffff000000000000000000000000000000000000000000000000909616959095177401000000000000000000000000000000000000000063ffffffff93841602177fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff16780100000000000000000000000000000000000000000000000092909116919091021790925560808401516002830180547fffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000001664ffffffffff90921691909117905560a0840151600383015560c0840151600483015560e0840151805185939261138f926005850192910190614c26565b5061010082015180516113ac916006840191602090910190614c71565b50506040805160608101825260008082526020820181905291810182905290915081908190899060005b828110156115c8578c8c828181106113f0576113f06155b8565b905060600201803603810190611406919061564d565b91508160000151945081604001519550816020015193508361ffff168a1080611431575061ffff8416155b15611468576040517fce7e065e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff87166000908152600e6020908152604080832080546001808201835591855293839020865160029095020180549387015161ffff1674010000000000000000000000000000000000000000027fffffffffffffffffffff000000000000000000000000000000000000000000009094166001600160a01b039586161793909317835585820151920191909155517f23b872dd00000000000000000000000000000000000000000000000000000000815233600482015230602482015260448101889052908616906323b872dd90606401600060405180830381600087803b15801561155b57600080fd5b505af115801561156f573d6000803e3d6000fd5b5050505067ffffffffffffffff87166000908152600b6020908152604080832061ffff8816845282528083206001600160a01b038916845282528083208984529091529020805460ff19166001908117909155016113d6565b50895160408051808201909152600081526060602082015290925060008060005b858110156117bc578d8181518110611603576116036155b8565b602002602001015193508360200151519250828261162191906155e7565b84519850915060005b838110156117b35784602001518181518110611648576116486155b8565b60200260200101519950600d60008c67ffffffffffffffff1667ffffffffffffffff16815260200190815260200160002060405180606001604052808b6001600160a01b03168152602001600061ffff1681526020018c815250908060018154018082558091505060019003906000526020600020906002020160009091909190915060008201518160000160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060208201518160000160146101000a81548161ffff021916908361ffff160217905550604082015181600101555050886001600160a01b03166323b872dd33308d6040518463ffffffff1660e01b8152600401611776939291906001600160a01b039384168152919092166020820152604081019190915260600190565b600060405180830381600087803b15801561179057600080fd5b505af11580156117a4573d6000803e3d6000fd5b5050505080600101905061162a565b506001016115e9565b5060085468010000000000000000900463ffffffff1681111561180b576040517fdceda73e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505050505050508c6001600160a01b03168167ffffffffffffffff167f96ce59e14c7cb8c2bd14fe9af6e771bfd51d9be8b63e3fdeba2d593113d9285e8e8e8e888f8f604051611861969594939291906156b7565b60405180910390a350505050505050505050505050565b611880613a92565b600061188b81613cca565b6001600160a01b0382166118cb576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60105480156119275760006010556118e38382613cd4565b826001600160a01b03167fc0819c13be868895eb93e40eaceb96de976442fa1d404e5c55f14bb65a8c489a8260405161191e91815260200190565b60405180910390a25b50506119336001600555565b50565b600e602052816000526040600020818154811061195257600080fd5b6000918252602090912060029091020180546001909101546001600160a01b03821693507401000000000000000000000000000000000000000090910461ffff16915083565b6004546001600160a01b031633146119f757600480546040517f1cf993f400000000000000000000000000000000000000000000000000000000815233928101929092526001600160a01b031660248201526044015b60405180910390fd5b611a02838383613ded565b505050565b611a0f613a92565b6001600160a01b038116611a4f576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff841660009081526007602052604090206002015464ffffffffff16611aa9576040517f1bee0d5a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611ab284613a55565b15611ae9576040517f203d82d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff841660008181526009602090815260408083203380855290835281842054948452600b835281842061ffff9095168085529483528184206001600160a01b038916855283528184208785529092529091205490919060ff16611b80576040517f969bf72800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611b8d8682878787614094565b5050611b996001600555565b50505050565b67ffffffffffffffff8116600090815260076020908152604091829020600601805483518184028101840190945280845260609392830182828015611c0d57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611bef575b50505050509050919050565b600082815260208190526040902060010154611c3481613cca565b611a0283836141ac565b67ffffffffffffffff8116600090815260076020908152604091829020600501805483518184028101840190945280845260609392830182828015611c0d57602002820191906000526020600020905b815481526020019060010190808311611c8e5750505050509050919050565b6001600160a01b0381163314611d2b5760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201527f20726f6c657320666f722073656c66000000000000000000000000000000000060648201526084016119ee565b611d3582826141ce565b5050565b6000611d4481613cca565b63ffffffff85161580611d5b575063ffffffff8416155b80611d64575082155b80611d6d575081155b15611da4576040517fe570699100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805160c0808201835289151580835261ffff8a16602080850182905263ffffffff8b8116868801819052908b16606080880182905260808089018d905260a09889018c9052601180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000ff8916176101008802177fffffffffffffffffffffffffffffffffffffffffff0000000000000000ffffff16630100000086027fffffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffff1617670100000000000000850217905560128d905560138c905589519687529386019490945296840152908201949094529283018690529082018490527f52a459681d6c26ba4c8e787b15f0e115e8c64394c2b24290170956f99825e5d6910160405180910390a150505050505050565b7f165b4e4bdd6508f05266283fa4bd4fd5ebb938a5f16867c98d3e5cbad8ff56eb611f2081613cca565b67ffffffffffffffff821660009081526007602052604090206002015464ffffffffff1615611f7b576040517f475a253500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611d35826139cf565b611f8c613a92565b67ffffffffffffffff821660009081526007602052604090206002015464ffffffffff16611fe6576040517f1bee0d5a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611fef82613a55565b15612026576040517f203d82d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038116612066576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff82166000908152600960209081526040808320338085529252822054909161ffff909116908190036120cd576040517f93a5f3c700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff84166000908152600e6020908152604080832080548251818502810185019093528083529192909190849084015b82821015612170576000848152602090819020604080516060810182526002860290920180546001600160a01b038116845274010000000000000000000000000000000000000000900461ffff1683850152600190810154918301919091529083529092019101612104565b50505050905060008151905060008060005b838110156122595784818151811061219c5761219c6155b8565b60200260200101516000015192508481815181106121bc576121bc6155b8565b60200260200101516040015191508561ffff168582815181106121e1576121e16155b8565b60200260200101516020015161ffff1614801561223f575067ffffffffffffffff89166000908152600b6020908152604080832061ffff8a16845282528083206001600160a01b0387168452825280832085845290915290205460ff165b1561225157612251898785858c614094565b600101612182565b5067ffffffffffffffff88166000908152600d6020908152604080832080548251818502810185019093528083529193909284015b828210156122fa576000848152602090819020604080516060810182526002860290920180546001600160a01b038116845274010000000000000000000000000000000000000000900461ffff168385015260019081015491830191909152908352909201910161228e565b5050505093508351925060005b838110156123de57848181518110612321576123216155b8565b6020026020010151600001519250848181518110612341576123416155b8565b60200260200101516040015191508561ffff16858281518110612366576123666155b8565b60200260200101516020015161ffff161480156123c4575067ffffffffffffffff89166000908152600b6020908152604080832061ffff8a16845282528083206001600160a01b0387168452825280832085845290915290205460ff165b156123d6576123d6898785858c614094565b600101612307565b50505050505050611d356001600555565b60006123fa81613cca565b600f80549083905560408051828152602081018590527f74dbbbe280ef27b79a8a0c449d5ae2ba7a31849103241d0f98df70bbc9d03e3791015b60405180910390a1505050565b6003546001600160a01b0316331461249b5760405162461bcd60e51b815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064016119ee565b600280547fffffffffffffffffffffffff0000000000000000000000000000000000000000808216339081179093556003805490911690556040516001600160a01b03909116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a350565b612516613a92565b6001600160a01b038116612556576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3360006125638483612681565b90508060000361259f576040517f969bf72800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff84166000818152600a602090815260408083206001600160a01b038716845282528083208590559282526007905290812060030180548392906125ec908490615613565b909155505067ffffffffffffffff84166000908152600760205260409020600101546001600160a01b03166126228482846141f0565b604080516001600160a01b0383811682526020820185905285169167ffffffffffffffff8816917fce3bcb6e219596cf26007ffdfaae8953bc3f76e3f36c0a79b23e28020da3222e910160405180910390a3505050611d356001600555565b67ffffffffffffffff821660009081526007602052604081206002015464ffffffffff1615806126b557506126b583613a55565b156126c257506000610ad6565b67ffffffffffffffff831660009081526009602090815260408083206001600160a01b038616845290915281205461ffff1690819003612706576000915050610ad6565b67ffffffffffffffff84166000818152600a602090815260408083206001600160a01b03881684528252808320549383526007909152902060050161274c600184615738565b61ffff1681548110612760576127606155b8565b90600052602060002001546127759190615613565b949350505050565b600d602052816000526040600020818154811061195257600080fd5b6002546001600160a01b031633148015906127bf57506004546001600160a01b03163314155b1561282a57336127d76002546001600160a01b031690565b600480546040517f061db9c10000000000000000000000000000000000000000000000000000000081526001600160a01b03948516928101929092529183166024820152911660448201526064016119ee565b6001600160a01b03811661286a576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600480547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527fd1a6a14209a385a964d036e404cb5cfb71f4000cdb03c9366292430787261be69060200160405180910390a150565b60008281526001602052604081206128ee908361421c565b9392505050565b600061290081613cca565b6202a30063ffffffff83161015612943576040517f939ed38c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6008805463ffffffff8481166c010000000000000000000000008181027fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff85161790945560408051949093049091168084526020840191909152917f1398a97a969c92d765fc12d4c55a8296fe70182a141706cd6765919681ff441f9101612434565b60006129d181613cca565b6008805463ffffffff848116680100000000000000008181027fffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffffff85161790945560408051949093049091168084526020840191909152917f15e0bb40e5929263cd42074e0a96296cbffd80a90be0ecb46d84e656e90970549101612434565b67ffffffffffffffff821660009081526007602052604090205482906001600160a01b0316336001600160a01b031614612ab6576040517f4ca8886700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82612ae38167ffffffffffffffff1660009081526007602052604090206002015464ffffffffff16151590565b612b485767ffffffffffffffff81166000908152600760205260409020600101544290612b329062278d009074010000000000000000000000000000000000000000900463ffffffff1661559a565b64ffffffffff161015610bd957612b48816139cf565b612b5181613a55565b612b87576040517fd0404f8500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612b8f613a92565b6001600160a01b038316612bcf576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff84166000908152600e6020908152604080832080548251818502810185019093528083529192909190849084015b82821015612c72576000848152602090819020604080516060810182526002860290920180546001600160a01b038116845274010000000000000000000000000000000000000000900461ffff1683850152600190810154918301919091529083529092019101612c06565b5050505090506000815190506000806000612cb4604051806060016040528060006001600160a01b03168152602001600061ffff168152602001600081525090565b60005b85811015612d8c57868181518110612cd157612cd16155b8565b60200260200101519150816000015194508160400151935081602001519250600b60008c67ffffffffffffffff1667ffffffffffffffff16815260200190815260200160002060008461ffff1661ffff1681526020019081526020016000206000866001600160a01b03166001600160a01b03168152602001908152602001600020600085815260200190815260200160002060009054906101000a900460ff1615612d8457612d848b8487878e613aeb565b600101612cb7565b5067ffffffffffffffff8a166000908152600d6020908152604080832080548251818502810185019093528083529193909284015b82821015612e2d576000848152602090819020604080516060810182526002860290920180546001600160a01b038116845274010000000000000000000000000000000000000000900461ffff1683850152600190810154918301919091529083529092019101612dc1565b50505067ffffffffffffffff8c166000908152600d6020526040812054929850919650505b85811015612f3457868181518110612e6c57612e6c6155b8565b60200260200101519150816000015194508160400151935081602001519250600b60008c67ffffffffffffffff1667ffffffffffffffff16815260200190815260200160002060008461ffff1661ffff1681526020019081526020016000206000866001600160a01b03166001600160a01b03168152602001908152602001600020600085815260200190815260200160002060009054906101000a900460ff1680612f1a575061ffff8316155b15612f2c57612f2c8b8487878e613aeb565b600101612e52565b5067ffffffffffffffff8a166000908152600d60205260408120612f5791614cde565b67ffffffffffffffff8a166000908152600e60205260408120612f7991614cde565b505050505050611b996001600555565b67ffffffffffffffff851660009081526009602090815260408083206001600160a01b038816845290915290205461ffff16612fc88682868686610adc565b505050505050565b6000818152600160205260408120610ad690614228565b60008281526020819052604090206001015461300281613cca565b611a0283836141ce565b613014613a92565b67ffffffffffffffff821660009081526007602052604090205482906001600160a01b0316336001600160a01b03161461307a576040517f4ca8886700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b826130a78167ffffffffffffffff1660009081526007602052604090206002015464ffffffffff16151590565b61310c5767ffffffffffffffff811660009081526007602052604090206001015442906130f69062278d009074010000000000000000000000000000000000000000900463ffffffff1661559a565b64ffffffffff161015610bd95761310c816139cf565b61311581613a55565b61314b576040517fd0404f8500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03831661318b576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff8416600090815260076020526040812060030154908190036131e2576040517f969bf72800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff851660009081526007602052604081206003810191909155600101546001600160a01b031661321b8582846141f0565b604080516001600160a01b0380881682528316602082015290810183905267ffffffffffffffff8716907fedfc9916535bec7ba1d215e222639f283bb485a546fbc7751cb3003b2487c89d9060600160405180910390a250505050611d356001600555565b613288614232565b6119338161428e565b7f165b4e4bdd6508f05266283fa4bd4fd5ebb938a5f16867c98d3e5cbad8ff56eb6132bb81613cca565b67ffffffffffffffff84166000908152600760205260409020600181015474010000000000000000000000000000000000000000900463ffffffff16421161332f576040517fd3018d1800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff851660009081526007602052604090206002015464ffffffffff161561338a576040517f475a253500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8280156138915760058201548111156133cf576040517f9172fb5300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001820154613400907801000000000000000000000000000000000000000000000000900463ffffffff164261559a565b67ffffffffffffffff8716600090815260076020526040812060020180547fffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000001664ffffffffff93909316929092179091555b818110156134f857613465816001615753565b67ffffffffffffffff8816600090815260096020526040812090888885818110613491576134916155b8565b90506020020160208101906134a69190615222565b6001600160a01b03168152602081019190915260400160002080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001661ffff92909216919091179055600101613452565b5067ffffffffffffffff86166000908152600760205260409020613520906006018686614cff565b5067ffffffffffffffff86166000908152600d602052604090205463ffffffff81161561388b57816001036137385767ffffffffffffffff87166000908152600d602052604081209080808989828161357b5761357b6155b8565b90506020020160208101906135909190615222565b905060005b8563ffffffff1681101561372e578481815481106135b5576135b56155b8565b600091825260209091206002909102015485546001600160a01b0390911694508590829081106135e7576135e76155b8565b90600052602060002090600202016001015492506001600b60008e67ffffffffffffffff1667ffffffffffffffff1681526020019081526020016000206000600161ffff1681526020019081526020016000206000866001600160a01b03166001600160a01b03168152602001908152602001600020600085815260200190815260200160002060006101000a81548160ff0219169083151502179055506001858281548110613699576136996155b8565b906000526020600020906002020160000160146101000a81548161ffff021916908361ffff160217905550816001600160a01b03168c67ffffffffffffffff167ff6e5a522fc2128fe2f9aa758c32fd30639e277a0391e3b77c242e2e140834017868660405161371e9291906001600160a01b03929092168252602082015260400190565b60405180910390a3600101613595565b505050505061388b565b6004546040805160c08101825260135481526012546020820152601154610100810461ffff16928201929092526000926001600160a01b031691639b1c385e9190606082019061379a908790670100000000000000900463ffffffff1661576e565b6011546137b491906301000000900463ffffffff16615796565b63ffffffff168152600160208083019190915260408051918201815260115460ff1615158252909101906137e790614350565b8152506040518263ffffffff1660e01b81526004016138069190615803565b6020604051808303816000875af1158015613825573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061384991906155fa565b6000908152600c6020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff8a16179055505b506138ec565b61389c600142615860565b67ffffffffffffffff8716600090815260076020526040902060020180547fffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000001663ffffffff929092169190911790555b8567ffffffffffffffff167f16d96bffc4329041e15226a1bd1b20bc0ee33f5f7bb8e11877eb917ae2c05ca3868660405161392892919061587d565b60405180910390a2505050505050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f7965db0b000000000000000000000000000000000000000000000000000000001480610ad657507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000831614610ad6565b6139da600142615860565b67ffffffffffffffff821660008181526007602052604080822060020180547fffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000001663ffffffff9590951694909417909355915190917fb7329e7cadb48d9cf876a7b7c6f4b44919ce6e8f47138d118707acfb9264983591a250565b67ffffffffffffffff811660009081526007602052604081206002015464ffffffffff1680158015906128ee575064ffffffffff16421192915050565b600260055403613ae45760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016119ee565b6002600555565b67ffffffffffffffff85166000908152600b6020908152604080832061ffff8816845282528083206001600160a01b0387811680865291845282852087865290935292819020805460ff19169055517f42842e0e0000000000000000000000000000000000000000000000000000000081523060048201529083166024820152604481018490526342842e0e90606401600060405180830381600087803b158015613b9557600080fd5b505af1158015613ba9573d6000803e3d6000fd5b5050604080516001600160a01b03808616825261ffff891660208301528716918101919091526060810185905267ffffffffffffffff881692507f6334894bb9d500f9065592453c8b0f0127e2220c83be138f2792a93fb51e932f91506080015b60405180910390a25050505050565b6040516001600160a01b0380851660248301528316604482015260648101829052611b999085907f23b872dd00000000000000000000000000000000000000000000000000000000906084015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091526143ee565b61193381336144d6565b80471015613d245760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e636500000060448201526064016119ee565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114613d71576040519150601f19603f3d011682016040523d82523d6000602084013e613d76565b606091505b5050905080611a025760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d6179206861766520726576657274656400000000000060648201526084016119ee565b600082826000818110613e0257613e026155b8565b6000878152600c602090815260408083205467ffffffffffffffff168084526007835281842060060154600d845291842080549390950296909601359650935090918291825b81811015614053576000868983604051602001613e6f929190918252602082015260400190565b6040516020818303038152906040528051906020012060001c613e9291906158cb565b67ffffffffffffffff891660009081526007602052604081206006018054929350909183908110613ec557613ec56155b8565b60009182526020909120015485546001600160a01b039091169150859084908110613ef257613ef26155b8565b600091825260209091206002909102015485546001600160a01b039091169750859084908110613f2457613f246155b8565b60009182526020808320600160029093020182015467ffffffffffffffff8d168452600b909152604083209098509091613f5e85846155e7565b61ffff168152602080820192909252604090810160009081206001600160a01b038c16825283528181208a82529092529020805460ff1916911515919091179055613faa8260016155e7565b858481548110613fbc57613fbc6155b8565b906000526020600020906002020160000160146101000a81548161ffff021916908361ffff160217905550806001600160a01b03168967ffffffffffffffff167ff6e5a522fc2128fe2f9aa758c32fd30639e277a0391e3b77c242e2e14083401789896040516140419291906001600160a01b03929092168252602082015260400190565b60405180910390a35050600101613e48565b50505060009788525050600c6020525050604090932080547fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000169055505050565b67ffffffffffffffff85166000908152600b6020908152604080832061ffff8816845282528083206001600160a01b0387811680865291845282852087865290935292819020805460ff19169055517f42842e0e0000000000000000000000000000000000000000000000000000000081523060048201529083166024820152604481018490526342842e0e90606401600060405180830381600087803b15801561413e57600080fd5b505af1158015614152573d6000803e3d6000fd5b50506040805161ffff881681526001600160a01b038716602082015290810185905267ffffffffffffffff881692507fbcdf1487c9b0f6650c1e48fce71b090ec57dba8ba1f02b1b14212a3730e91e0c9150606001613c0a565b6141b68282614549565b6000828152600160205260409020611a0290826145e7565b6141d882826145fc565b6000828152600160205260409020611a02908261467b565b6001600160a01b03821661420857611a028382613cd4565b611a026001600160a01b0383168483614690565b60006128ee83836146d9565b6000610ad6825490565b6002546001600160a01b0316331461428c5760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016119ee565b565b336001600160a01b038216036142e65760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016119ee565b600380547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03838116918217909255600254604051919216907fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae127890600090a350565b60607f92fd13387c7fe7befbc38d303d6468778fb9731bc4583f17d92989c6fcfdeaaa8260405160240161438991511515815260200190565b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915292915050565b6000614443826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166147039092919063ffffffff16565b90508051600014806144645750808060200190518101906144649190615906565b611a025760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f7420737563636565640000000000000000000000000000000000000000000060648201526084016119ee565b6000828152602081815260408083206001600160a01b038516845290915290205460ff16611d355761450781614712565b614512836020614724565b604051602001614523929190615923565b60408051601f198184030181529082905262461bcd60e51b82526119ee916004016159a4565b6000828152602081815260408083206001600160a01b038516845290915290205460ff16611d35576000828152602081815260408083206001600160a01b03851684529091529020805460ff191660011790556145a33390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b60006128ee836001600160a01b03841661494d565b6000828152602081815260408083206001600160a01b038516845290915290205460ff1615611d35576000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b60006128ee836001600160a01b03841661499c565b6040516001600160a01b038316602482015260448101829052611a029084907fa9059cbb0000000000000000000000000000000000000000000000000000000090606401613c66565b60008260000182815481106146f0576146f06155b8565b9060005260206000200154905092915050565b60606127758484600085614a96565b6060610ad66001600160a01b03831660145b606060006147338360026159b7565b61473e9060026155e7565b67ffffffffffffffff81111561475657614756614f67565b6040519080825280601f01601f191660200182016040528015614780576020820181803683370190505b5090507f3000000000000000000000000000000000000000000000000000000000000000816000815181106147b7576147b76155b8565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f78000000000000000000000000000000000000000000000000000000000000008160018151811061481a5761481a6155b8565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060006148568460026159b7565b6148619060016155e7565b90505b60018111156148fe577f303132333435363738396162636465660000000000000000000000000000000085600f16601081106148a2576148a26155b8565b1a60f81b8282815181106148b8576148b86155b8565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060049490941c936148f7816159ce565b9050614864565b5083156128ee5760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e7460448201526064016119ee565b600081815260018301602052604081205461499457508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610ad6565b506000610ad6565b60008181526001830160205260408120548015614a855760006149c0600183615613565b85549091506000906149d490600190615613565b9050818114614a395760008660000182815481106149f4576149f46155b8565b9060005260206000200154905080876000018481548110614a1757614a176155b8565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080614a4a57614a4a615a03565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610ad6565b6000915050610ad6565b5092915050565b606082471015614b0e5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c000000000000000000000000000000000000000000000000000060648201526084016119ee565b600080866001600160a01b03168587604051614b2a9190615a32565b60006040518083038185875af1925050503d8060008114614b67576040519150601f19603f3d011682016040523d82523d6000602084013e614b6c565b606091505b5091509150614b7d87838387614b88565b979650505050505050565b60608315614bf7578251600003614bf0576001600160a01b0385163b614bf05760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016119ee565b5081612775565b6127758383815115614c0c5781518083602001fd5b8060405162461bcd60e51b81526004016119ee91906159a4565b828054828255906000526020600020908101928215614c61579160200282015b82811115614c61578251825591602001919060010190614c46565b50614c6d929150614d6a565b5090565b828054828255906000526020600020908101928215614c61579160200282015b82811115614c6157825182547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03909116178255602090920191600190910190614c91565b50805460008255600202906000526020600020908101906119339190614d7f565b828054828255906000526020600020908101928215614c61579160200282015b82811115614c615781547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03843516178255602090920191600190910190614d1f565b5b80821115614c6d5760008155600101614d6b565b5b80821115614c6d5780547fffffffffffffffffffff0000000000000000000000000000000000000000000016815560006001820155600201614d80565b803567ffffffffffffffff81168114614dd557600080fd5b919050565b600060208284031215614dec57600080fd5b6128ee82614dbd565b600060208284031215614e0757600080fd5b81357fffffffff00000000000000000000000000000000000000000000000000000000811681146128ee57600080fd5b803561ffff81168114614dd557600080fd5b6001600160a01b038116811461193357600080fd5b600080600080600060a08688031215614e7657600080fd5b614e7f86614dbd565b9450614e8d60208701614e37565b93506040860135614e9d81614e49565b9250606086013591506080860135614eb481614e49565b809150509295509295909350565b803563ffffffff81168114614dd557600080fd5b60008083601f840112614ee857600080fd5b50813567ffffffffffffffff811115614f0057600080fd5b6020830191508360208260051b8501011115614f1b57600080fd5b9250929050565b60008083601f840112614f3457600080fd5b50813567ffffffffffffffff811115614f4c57600080fd5b602083019150836020606083028501011115614f1b57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715614fb957614fb9614f67565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715614fe857614fe8614f67565b604052919050565b600067ffffffffffffffff82111561500a5761500a614f67565b5060051b60200190565b600082601f83011261502557600080fd5b8135602061503a61503583614ff0565b614fbf565b828152600592831b850182019282820191908785111561505957600080fd5b8387015b8581101561513c57803567ffffffffffffffff8082111561507e5760008081fd5b818a019150604080601f19848e030112156150995760008081fd5b6150a1614f96565b888401356150ae81614e49565b815283820135838111156150c25760008081fd5b8085019450508c603f8501126150da57600092508283fd5b8884013592506150ec61503584614ff0565b83815292861b8401820192898101908e8511156151095760008081fd5b948301945b848610156151275785358252948a0194908a019061510e565b828b015250875250505092840192840161505d565b5090979650505050505050565b600080600080600080600080600060e08a8c03121561516757600080fd5b893561517281614e49565b985060208a013561518281614e49565b975061519060408b01614ec2565b965061519e60608b01614ec2565b955060808a013567ffffffffffffffff808211156151bb57600080fd5b6151c78d838e01614ed6565b909750955060a08c01359150808211156151e057600080fd5b6151ec8d838e01614f22565b909550935060c08c013591508082111561520557600080fd5b506152128c828d01615014565b9150509295985092959850929598565b60006020828403121561523457600080fd5b81356128ee81614e49565b6000806040838503121561525257600080fd5b61525b83614dbd565b946020939093013593505050565b60008060006040848603121561527e57600080fd5b83359250602084013567ffffffffffffffff81111561529c57600080fd5b6152a886828701614ed6565b9497909650939450505050565b600080600080608085870312156152cb57600080fd5b6152d485614dbd565b935060208501356152e481614e49565b92506040850135915060608501356152fb81614e49565b939692955090935050565b60006020828403121561531857600080fd5b5035919050565b6020808252825182820181905260009190848201906040850190845b818110156153605783516001600160a01b03168352928401929184019160010161533b565b50909695505050505050565b6000806040838503121561537f57600080fd5b82359150602083013561539181614e49565b809150509250929050565b6020808252825182820181905260009190848201906040850190845b81811015615360578351835292840192918401916001016153b8565b801515811461193357600080fd5b60008060008060008060c087890312156153fb57600080fd5b8635615406816153d4565b955061541460208801614e37565b945061542260408801614ec2565b935061543060608801614ec2565b92506080870135915060a087013590509295509295509295565b6000806040838503121561545d57600080fd5b61546683614dbd565b9150602083013561539181614e49565b6000806040838503121561548957600080fd5b50508035926020909101359150565b6000602082840312156154aa57600080fd5b6128ee82614ec2565b600080600080600060a086880312156154cb57600080fd5b6154d486614dbd565b94506020860135614e8d81614e49565b6000806000604084860312156154f957600080fd5b61550284614dbd565b9250602084013567ffffffffffffffff81111561529c57600080fd5b6000806000806080858703121561553457600080fd5b61553d85614dbd565b935061554b60208601614e37565b9250604085013561555b81614e49565b9396929550929360600135925050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b64ffffffffff818116838216019080821115614a8f57614a8f61556b565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b80820180821115610ad657610ad661556b565b60006020828403121561560c57600080fd5b5051919050565b81810381811115610ad657610ad661556b565b600067ffffffffffffffff8083168181036156435761564361556b565b6001019392505050565b60006060828403121561565f57600080fd5b6040516060810181811067ffffffffffffffff8211171561568257615682614f67565b604052823561569081614e49565b815261569e60208401614e37565b6020820152604083013560408201528091505092915050565b6001600160a01b0387168152600063ffffffff808816602084015280871660408401525084606083015260a060808301528260a08301527f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83111561571b57600080fd5b8260051b808560c08501379190910160c001979650505050505050565b61ffff828116828216039080821115614a8f57614a8f61556b565b61ffff818116838216019080821115614a8f57614a8f61556b565b63ffffffff81811683821602808216919082811461578e5761578e61556b565b505092915050565b63ffffffff818116838216019080821115614a8f57614a8f61556b565b60005b838110156157ce5781810151838201526020016157b6565b50506000910152565b600081518084526157ef8160208601602086016157b3565b601f01601f19169290920160200192915050565b60208152815160208201526020820151604082015261ffff60408301511660608201526000606083015163ffffffff80821660808501528060808601511660a0850152505060a083015160c08084015261277560e08401826157d7565b63ffffffff828116828216039080821115614a8f57614a8f61556b565b60208082528181018390526000908460408401835b868110156158c05782356158a581614e49565b6001600160a01b031682529183019190830190600101615892565b509695505050505050565b600082615901577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500690565b60006020828403121561591857600080fd5b81516128ee816153d4565b7f416363657373436f6e74726f6c3a206163636f756e742000000000000000000081526000835161595b8160178501602088016157b3565b7f206973206d697373696e6720726f6c652000000000000000000000000000000060179184019182015283516159988160288401602088016157b3565b01602801949350505050565b6020815260006128ee60208301846157d7565b8082028115828204841417610ad657610ad661556b565b6000816159dd576159dd61556b565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b60008251615a448184602087016157b3565b919091019291505056fea264697066735822122083946c20afe0f66cec3386926ac32781a258a708932af4506fec426a277dda6b64736f6c63430008130033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000003c0ca683b403e37668ae3dc4fb62f4b29b6f7a3e00000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000093a800000000000000000000000000000000000000000000000000000b5e620f48000
-----Decoded View---------------
Arg [0] : chainlinkCoordinatorAddress (address): 0x3C0Ca683b403E37668AE3DC4FB62F4B29B6f7a3e
Arg [1] : maxRandomNFTs (uint32): 20
Arg [2] : minExpirationTime_ (uint32): 604800
Arg [3] : fee_ (uint256): 200000000000000
-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 0000000000000000000000003c0ca683b403e37668ae3dc4fb62f4b29b6f7a3e
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000014
Arg [2] : 0000000000000000000000000000000000000000000000000000000000093a80
Arg [3] : 0000000000000000000000000000000000000000000000000000b5e620f48000
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$2,488.99
Net Worth in ETH
0.845273
Token Allocations
ETH
59.51%
BETS
9.63%
POL
8.17%
Others
22.69%
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|---|---|---|---|---|
| BASE | 38.64% | $2,943.13 | 0.3268 | $961.75 | |
| BASE | 5.50% | $2,942.52 | 0.0465 | $136.8 | |
| BASE | 5.45% | $0.000047 | 2,882,170.5001 | $135.58 | |
| BASE | 0.56% | $0.999602 | 14 | $13.99 | |
| ARB | 20.87% | $2,944.59 | 0.1764 | $519.43 | |
| ARB | 0.79% | $0.000047 | 419,162 | $19.74 | |
| POL | 8.17% | $0.127935 | 1,588.5433 | $203.23 | |
| POL | 5.38% | $0.998915 | 134 | $133.85 | |
| POL | 2.25% | $0.000046 | 1,219,202 | $55.94 | |
| AVAX | 7.18% | $12.12 | 14.7426 | $178.65 | |
| AVAX | 0.49% | $0.000047 | 260,738 | $12.28 | |
| BSC | 4.08% | $890.41 | 0.114 | $101.51 | |
| BSC | 0.65% | $0.000047 | 344,800 | $16.24 |
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.