Source Code
Overview
ETH Balance
ETH Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
TrailblazersBadgesV9
Compiler Version
v0.8.24+commit.e11b9ed9
Optimization Enabled:
Yes with 200 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
import "../trailblazers-season-2/TrailblazersS1BadgesV8.sol";
contract TrailblazersBadgesV9 is TrailblazersBadgesV8 {
/// @notice Errors
error BADGE_STILL_LOCKED(uint256 seasonId, uint256 seasonEnd, uint256 ts);
error NO_ACTIVE_SEASON();
/// @notice Updated version function
/// @return Version string
function version() external pure virtual override returns (string memory) {
return "V9";
}
/// @notice Contains the ts for each season's end, index 1 being season 1
mapping(uint256 season => uint256 endTimestamp) public seasonEndTimestamps;
/// @notice Helper variable to track the last known steason
uint256 public lastKnownSeason;
/// @notice Setter for season end timestamps
/// @param _season Season number
/// @param _timestamp Timestamp
/// @dev Only owner can set the timestamp
function setSeasonEndTimestamp(uint256 _season, uint256 _timestamp) public virtual onlyOwner {
seasonEndTimestamps[_season] = _timestamp;
if (_season > lastKnownSeason) {
lastKnownSeason = _season;
}
}
/// @notice Get the current season ID
/// @return Season ID
function getCurrentSeasonId() public view virtual returns (uint256) {
for (uint256 i = 0; i <= lastKnownSeason; i++) {
if (block.timestamp < seasonEndTimestamps[i]) {
return i;
}
}
revert NO_ACTIVE_SEASON();
}
/// @notice Get the current season's end timestamp
/// @return Season end timestamp
function getCurrentSeasonEndTimestamp() public view virtual returns (uint256) {
uint256 seasonId = getCurrentSeasonId();
return seasonEndTimestamps[seasonId];
}
/// @notice Modifier to ensure a badge isn't locked on a recruitment for that season
/// @param _tokenId Badge token id
modifier isNotLockedV9(uint256 _tokenId) virtual {
uint256 unlockTimestamp = unlockTimestamps[_tokenId];
uint256 seasonEndTimestamp = getCurrentSeasonEndTimestamp();
if (
unlockTimestamp > 0 && block.timestamp < seasonEndTimestamp
&& block.timestamp < unlockTimestamp
) {
revert BADGE_STILL_LOCKED(getCurrentSeasonId(), seasonEndTimestamp, block.timestamp);
}
_;
}
/// @notice Overwritten update function that prevents locked badges from being transferred
/// @param to Address to transfer badge to
/// @param tokenId Badge token id
/// @param auth Address to authorize transfer
/// @return Address of the recipient
function _update(
address to,
uint256 tokenId,
address auth
)
internal
virtual
override
isNotLockedV9(tokenId)
returns (address)
{
return TrailblazersBadgesV3._update(to, tokenId, auth);
}
/// @notice Start recruitment for a badge
/// @param _badgeId Badge ID
/// @param _tokenId Token ID
function startRecruitment(
uint256 _badgeId,
uint256 _tokenId
)
public
virtual
override
isNotLockedV9(_tokenId)
{
if (recruitmentLockDuration == 0) {
revert RECRUITMENT_LOCK_DURATION_NOT_SET();
}
if (ownerOf(_tokenId) != _msgSender()) {
revert NOT_OWNER();
}
uint256 seasonEndTimestamp = getCurrentSeasonEndTimestamp();
unlockTimestamps[_tokenId] = seasonEndTimestamp;
recruitmentContractV2.startRecruitment(_msgSender(), _badgeId, _tokenId);
}
/// @notice Check if a badge is locked
/// @param tokenId Badge token id
/// @return True if locked
function isLocked(uint256 tokenId) public view virtual override returns (bool) {
uint256 seasonEndTimestamp = getCurrentSeasonEndTimestamp();
if (unlockTimestamps[tokenId] > 0 && block.timestamp < seasonEndTimestamp) {
return true;
}
return false;
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
import "./TrailblazersS1BadgesV7.sol";
contract TrailblazersBadgesV8 is TrailblazersBadgesV7 {
/// @notice Updated version function
/// @return Version string
function version() external pure virtual override returns (string memory) {
return "V8";
}
/// @notice Modifier to ensure a badge isn't locked on a recruitment for that season
/// @param tokenId Badge token id
modifier isNotLockedV8(uint256 tokenId) virtual {
if (unlockTimestamps[tokenId] > 0 && block.timestamp < season2EndTimestamp) {
// s2
revert BADGE_LOCKED();
} else if (
unlockTimestamps[tokenId] == season3EndTimestamp
&& block.timestamp > season2EndTimestamp && block.timestamp < season3EndTimestamp
) {
// s3
revert BADGE_LOCKED_SEASON_2();
}
_;
}
/// @notice Overwritten update function that prevents locked badges from being transferred
/// @param to Address to transfer badge to
/// @param tokenId Badge token id
/// @param auth Address to authorize transfer
/// @return Address of the recipient
function _update(
address to,
uint256 tokenId,
address auth
)
internal
virtual
override
isNotLockedV8(tokenId)
returns (address)
{
return TrailblazersBadgesV3._update(to, tokenId, auth);
}
/// @notice Start recruitment for a badge
/// @param _badgeId Badge ID
/// @param _tokenId Token ID
function startRecruitment(
uint256 _badgeId,
uint256 _tokenId
)
public
virtual
override
isNotLockedV8(_tokenId)
{
if (recruitmentLockDuration == 0) {
revert RECRUITMENT_LOCK_DURATION_NOT_SET();
}
if (ownerOf(_tokenId) != _msgSender()) {
revert NOT_OWNER();
}
if (block.timestamp < season2EndTimestamp) {
unlockTimestamps[_tokenId] = season2EndTimestamp;
} else {
unlockTimestamps[_tokenId] = season3EndTimestamp;
}
recruitmentContractV2.startRecruitment(_msgSender(), _badgeId, _tokenId);
}
function isLocked(uint256 tokenId) public view virtual returns (bool) {
if (unlockTimestamps[tokenId] > 0 && block.timestamp < season2EndTimestamp) {
// s2
return true;
} else if (
unlockTimestamps[tokenId] == season3EndTimestamp
&& block.timestamp > season2EndTimestamp && block.timestamp < season3EndTimestamp
) {
// s3
return true;
}
return false;
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
import "./TrailblazersS1BadgesV6.sol";
import "./BadgeRecruitment.sol";
import "./BadgeRecruitmentV2.sol";
contract TrailblazersBadgesV7 is TrailblazersBadgesV6 {
/// @notice Updated version function
/// @return Version string
function version() external pure virtual override returns (string memory) {
return "V7";
}
/// @notice Modifier to ensure a badge isn't locked on a recruitment for that season
/// @param tokenId Badge token id
modifier isNotLockedV7(uint256 tokenId) virtual {
if (unlockTimestamps[tokenId] > 0 && block.timestamp < season2EndTimestamp) {
// s2
revert BADGE_LOCKED();
} else if (
unlockTimestamps[tokenId] == season3EndTimestamp
&& block.timestamp > season2EndTimestamp && block.timestamp < season3EndTimestamp
) {
// s3
revert BADGE_LOCKED_SEASON_2();
}
_;
}
/// @notice Overwritten update function that prevents locked badges from being transferred
/// @param to Address to transfer badge to
/// @param tokenId Badge token id
/// @param auth Address to authorize transfer
/// @return Address of the recipient
function _update(
address to,
uint256 tokenId,
address auth
)
internal
virtual
override
isNotLockedV7(tokenId)
returns (address)
{
return super._update(to, tokenId, auth);
}
/// @notice Start recruitment for a badge
/// @param _badgeId Badge ID
/// @param _tokenId Token ID
function startRecruitment(
uint256 _badgeId,
uint256 _tokenId
)
public
virtual
override
isNotLockedV7(_tokenId)
{
if (recruitmentLockDuration == 0) {
revert RECRUITMENT_LOCK_DURATION_NOT_SET();
}
if (ownerOf(_tokenId) != _msgSender()) {
revert NOT_OWNER();
}
if (block.timestamp < season2EndTimestamp) {
unlockTimestamps[_tokenId] = season2EndTimestamp;
} else {
unlockTimestamps[_tokenId] = season3EndTimestamp;
}
recruitmentContractV2.startRecruitment(_msgSender(), _badgeId, _tokenId);
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
import "./TrailblazersS1BadgesV5.sol";
import "./BadgeRecruitment.sol";
import "./BadgeRecruitmentV2.sol";
contract TrailblazersBadgesV6 is TrailblazersBadgesV5 {
/// @notice Updated version function
/// @return Version string
function version() external pure virtual override returns (string memory) {
return "V6";
}
/// @notice Errors
error BADGE_LOCKED_SEASON_2();
/// @notice Season end timestamps
uint256 public season2EndTimestamp; // 1734350400
uint256 public season3EndTimestamp; // 1742212800
/// @notice Setter for season 2 end timestamps
/// @param _ts Timestamp
/// @dev Only owner can set the timestamp
function setSeason2EndTimestamp(uint256 _ts) public virtual onlyOwner {
season2EndTimestamp = _ts;
}
/// @notice Setter for season 3 end timestamps
/// @param _ts Timestamp
/// @dev Only owner can set the timestamp
function setSeason3EndTimestamp(uint256 _ts) public virtual onlyOwner {
season3EndTimestamp = _ts;
}
/// @notice Modifier to ensure a badge isn't locked on a recruitment for that season
/// @param tokenId Badge token id
modifier isNotLocked(uint256 tokenId) virtual {
if (unlockTimestamps[tokenId] > 0 && block.timestamp < season2EndTimestamp) {
// s2
revert BADGE_LOCKED();
} else if (
unlockTimestamps[tokenId] == season3EndTimestamp
&& block.timestamp > season2EndTimestamp && block.timestamp < season3EndTimestamp
) {
// s3
revert BADGE_LOCKED_SEASON_2();
}
_;
}
/// @notice Overwritten update function that prevents locked badges from being transferred
/// @param to Address to transfer badge to
/// @param tokenId Badge token id
/// @param auth Address to authorize transfer
/// @return Address of the recipient
function _update(
address to,
uint256 tokenId,
address auth
)
internal
virtual
override
isNotLocked(tokenId)
returns (address)
{
return super._update(to, tokenId, auth);
}
/// @notice Start recruitment for a badge
/// @param _badgeId Badge ID
/// @param _tokenId Token ID
function startRecruitment(
uint256 _badgeId,
uint256 _tokenId
)
public
virtual
override
isNotLocked(_tokenId)
{
if (recruitmentLockDuration == 0) {
revert RECRUITMENT_LOCK_DURATION_NOT_SET();
}
if (ownerOf(_tokenId) != _msgSender()) {
revert NOT_OWNER();
}
if (block.timestamp < season2EndTimestamp) {
unlockTimestamps[_tokenId] = season2EndTimestamp;
} else {
unlockTimestamps[_tokenId] = season3EndTimestamp;
}
recruitmentContractV2.startRecruitment(_msgSender(), _badgeId, _tokenId);
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
import "@openzeppelin/contracts-upgradeable/token/ERC1155/extensions/ERC1155SupplyUpgradeable.sol";
import "../trailblazers-badges/ECDSAWhitelist.sol";
import "@taiko/blacklist/IMinimalBlacklist.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC1155/ERC1155Upgradeable.sol";
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/utils/ERC721HolderUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
import "./TrailblazersS1BadgesV4.sol";
import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "./TrailblazersBadgesS2.sol";
contract BadgeRecruitment is
UUPSUpgradeable,
Ownable2StepUpgradeable,
AccessControlUpgradeable,
ERC721HolderUpgradeable
{
/// @notice Season 1 Badges ERC721 contract
TrailblazersBadgesV4 public s1Badges;
/// @notice badges role key
bytes32 public constant S1_BADGES_ROLE = keccak256("S1_BADGES_ROLE");
/// @notice Season 2 Badges ERC1155 contract
TrailblazersBadgesS2 public s2Badges;
/// @notice Wallet authorized to sign as a source of randomness
address public randomSigner;
/// @notice Current recruitment cycle
uint256 public recruitmentCycleId;
/// @notice Mapping of unique user-per-mint-per-cycle
mapping(
uint256 recruitmentCycle
=> mapping(
address minter
=> mapping(
uint256 s1BadgeId
=> mapping(RecruitmentType recruitmentType => bool mintEnded)
)
)
) public recruitmentCycleUniqueMints;
/// @notice User experience points
mapping(address user => uint256 experience) public userExperience;
/// @notice Influence colors available
enum InfluenceColor {
Undefined, // unused
Whale, // based, pink
Minnow // boosted, purple
}
/// @notice Recruitment types
enum RecruitmentType {
Undefined,
Claim,
Migration
}
/// @notice Hash types
enum HashType {
Undefined,
Start,
End,
Influence
}
/// @notice Configuration struct
struct Config {
uint256 cooldownRecruitment;
uint256 cooldownInfluence;
uint256 influenceWeightPercent;
uint256 baseMaxInfluences;
uint256 maxInfluencesDivider;
uint256 defaultCycleDuration;
}
/// @notice Current config
Config private config;
/// @notice Recruitment struct
struct Recruitment {
uint256 recruitmentCycle;
address user;
uint256 s1BadgeId;
uint256 s1TokenId;
uint256 s2TokenId;
uint256 cooldownExpiration;
uint256 influenceExpiration;
uint256 whaleInfluences;
uint256 minnowInfluences;
}
/// @notice Recruitment Cycle struct
struct RecruitmentCycle {
uint256 cycleId;
uint256 startTime;
uint256 endTime;
uint256[] s1BadgeIds;
}
/// @notice Recruitment cycles
mapping(uint256 cycleId => RecruitmentCycle recruitmentCycle) public recruitmentCycles;
/// @notice Recruitments per user
mapping(address _user => Recruitment[] _recruitment) public recruitments;
/// @notice Gap for upgrade safety
uint256[43] private __gap;
/// @notice Errors
error MAX_INFLUENCES_REACHED();
error RECRUITMENT_NOT_STARTED();
error RECRUITMENT_ALREADY_STARTED();
error INFLUENCE_IN_PROGRESS();
error RECRUITMENT_NOT_READY();
error RECRUITMENT_NOT_ENABLED();
error TOKEN_NOT_OWNED();
error NOT_RANDOM_SIGNER();
error ALREADY_MIGRATED_IN_CYCLE();
error HASH_MISMATCH();
error NOT_S1_CONTRACT();
error EXP_TOO_LOW();
error INVALID_INFLUENCE_COLOR();
error CURRENT_CYCLE_NOT_OVER();
/// @notice Events
event RecruitmentCycleToggled(
uint256 indexed recruitmentCycleId,
uint256 indexed startTime,
uint256 indexed endTime,
uint256[] s1BadgeIds,
bool enabled
);
event RecruitmentUpdated(
uint256 indexed recruitmentCycle,
address indexed user,
uint256 s1BadgeId,
uint256 s1TokenId,
uint256 s2TokenId,
uint256 cooldownExpiration,
uint256 influenceExpiration,
uint256 whaleInfluences,
uint256 minnowInfluences
);
event RecruitmentComplete(
uint256 indexed recruitmentCycle,
address indexed user,
uint256 s1TokenId,
uint256 s2TokenId,
uint256 finalColor
);
/// @notice Check if the message sender has an active recruitment
modifier isMigrating() {
Recruitment memory recruitment_ = getActiveRecruitmentFor(_msgSender());
if (recruitment_.cooldownExpiration == 0) {
revert RECRUITMENT_NOT_STARTED();
}
_;
}
/// @notice Reverts if sender is already migrating
modifier isNotMigrating(address _user) {
if (
recruitments[_user].length > 0
&& recruitments[_user][recruitments[_user].length - 1].cooldownExpiration
> block.timestamp
) {
revert RECRUITMENT_ALREADY_STARTED();
}
_;
}
/// @notice Reverts if recruitments aren't enabled for that badge
/// @param _s1BadgeId The badge ID
modifier recruitmentOpen(uint256 _s1BadgeId) {
RecruitmentCycle memory cycle_ = recruitmentCycles[recruitmentCycleId];
if (cycle_.startTime > block.timestamp || cycle_.endTime < block.timestamp) {
revert RECRUITMENT_NOT_ENABLED();
}
bool found_ = false;
for (uint256 i = 0; i < cycle_.s1BadgeIds.length; i++) {
if (cycle_.s1BadgeIds[i] == _s1BadgeId) {
found_ = true;
break;
}
}
if (!found_) {
revert RECRUITMENT_NOT_ENABLED();
}
_;
}
/// @notice Limits recruitments to one per user, badge and cycle
/// @param _s1BadgeId The badge ID
/// @param _minter The minter address
/// @param _recruitmentType The recruitment type
modifier hasntMigratedInCycle(
uint256 _s1BadgeId,
address _minter,
RecruitmentType _recruitmentType
) {
// check that the minter hasn't used the recruitment within this cycle
if (recruitmentCycleUniqueMints[recruitmentCycleId][_minter][_s1BadgeId][_recruitmentType])
{
revert ALREADY_MIGRATED_IN_CYCLE();
}
_;
}
/// @notice Contract initializer
/// @param _s1Badges The Season 1 Badges contract address
/// @param _s2Badges The Season 2 Badges contract address
/// @param _randomSigner The random signer address
/// @param _config The initial configuration
function initialize(
address _s1Badges,
address _s2Badges,
address _randomSigner,
Config memory _config
)
external
initializer
{
_transferOwnership(_msgSender());
__Context_init();
_grantRole(DEFAULT_ADMIN_ROLE, _msgSender());
s1Badges = TrailblazersBadgesV4(_s1Badges);
_grantRole(S1_BADGES_ROLE, _s1Badges);
s2Badges = TrailblazersBadgesS2(_s2Badges);
randomSigner = _randomSigner;
config = _config;
}
/// @notice Upgrade configuration
/// @param _config The new configuration
function setConfig(Config memory _config) external onlyRole(DEFAULT_ADMIN_ROLE) {
config = _config;
}
/// @notice Get the current configuration
/// @return The current configuration
function getConfig() external view returns (Config memory) {
return config;
}
/// @notice Disable all current recruitments
/// @dev Bypasses the default date checks
function forceDisableRecruitments() internal onlyRole(DEFAULT_ADMIN_ROLE) {
recruitmentCycles[recruitmentCycleId].endTime = block.timestamp;
}
/// @notice Enable recruitments for a set of badges
/// @param _startTime The start time of the recruitment cycle
/// @param _endTime The end time of the recruitment cycle
/// @param _s1BadgeIds The badge IDs to enable
function _enableRecruitments(
uint256 _startTime,
uint256 _endTime,
uint256[] calldata _s1BadgeIds
)
internal
{
if (
recruitmentCycleId > 0
&& recruitmentCycles[recruitmentCycleId].endTime > block.timestamp
) {
revert CURRENT_CYCLE_NOT_OVER();
}
// emit disabled badges
emit RecruitmentCycleToggled(
recruitmentCycleId,
recruitmentCycles[recruitmentCycleId].startTime,
recruitmentCycles[recruitmentCycleId].endTime,
recruitmentCycles[recruitmentCycleId].s1BadgeIds,
false
);
recruitmentCycleId++;
recruitmentCycles[recruitmentCycleId] =
RecruitmentCycle(recruitmentCycleId, _startTime, _endTime, _s1BadgeIds);
// emit enabled badges
emit RecruitmentCycleToggled(recruitmentCycleId, _startTime, _endTime, _s1BadgeIds, true);
}
/// @notice Enable recruitments for a set of badges
/// @param _s1BadgeIds The badge IDs to enable
/// @dev Can be called only by the contract owner/admin
function enableRecruitments(uint256[] calldata _s1BadgeIds)
external
onlyRole(DEFAULT_ADMIN_ROLE)
{
_enableRecruitments(
block.timestamp, block.timestamp + config.defaultCycleDuration, _s1BadgeIds
);
}
/// @notice Enable recruitments for a set of badges
/// @param _startTime The start time of the recruitment cycle
/// @param _endTime The end time of the recruitment cycle
/// @param _s1BadgeIds The badge IDs to enable
/// @dev Can be called only by the contract owner/admin
function enableRecruitments(
uint256 _startTime,
uint256 _endTime,
uint256[] calldata _s1BadgeIds
)
external
onlyRole(DEFAULT_ADMIN_ROLE)
{
_enableRecruitments(_startTime, _endTime, _s1BadgeIds);
}
/// @notice Get the current recruitment cycle
/// @return The current recruitment cycle
function getRecruitmentCycle(uint256 _cycleId)
external
view
returns (RecruitmentCycle memory)
{
return recruitmentCycles[_cycleId];
}
/// @notice Internal logic to start a recruitment
/// @param _user The user address
/// @param _s1BadgeId The badge ID
/// @param _s1TokenId The badge token ID
/// @param _recruitmentType The recruitment type
function _startRecruitment(
address _user,
uint256 _s1BadgeId,
uint256 _s1TokenId,
RecruitmentType _recruitmentType
)
internal
virtual
{
Recruitment memory _recruitment = Recruitment(
recruitmentCycleId, // recruitmentCycle
_user, // user
_s1BadgeId,
_s1TokenId,
0, // s2TokenId, unset
block.timestamp + config.cooldownRecruitment, // cooldownExpiration
0, // influenceExpiration, unset
0, // whaleInfluences
0 // minnowInfluences
);
recruitments[_user].push(_recruitment);
recruitmentCycleUniqueMints[recruitmentCycleId][_user][_s1BadgeId][_recruitmentType] = true;
emit RecruitmentUpdated(
_recruitment.recruitmentCycle,
_recruitment.user,
_recruitment.s1BadgeId,
_recruitment.s1TokenId,
_recruitment.s2TokenId,
_recruitment.cooldownExpiration,
_recruitment.influenceExpiration,
_recruitment.whaleInfluences,
_recruitment.minnowInfluences
);
}
/// @notice Start a recruitment for a badge using the user's experience points
/// @param _hash The hash to sign of the signature
/// @param _v The signature V field
/// @param _r The signature R field
/// @param _s The signature S field
/// @param _exp The user's experience points
function startRecruitment(
bytes32 _hash,
uint8 _v,
bytes32 _r,
bytes32 _s,
uint256 _exp
)
external
virtual
isNotMigrating(_msgSender())
{
bytes32 calculatedHash_ = generateClaimHash(HashType.Start, _msgSender(), _exp);
if (calculatedHash_ != _hash) {
revert HASH_MISMATCH();
}
(address recovered_,,) = ECDSA.tryRecover(_hash, _v, _r, _s);
if (recovered_ != randomSigner) {
revert NOT_RANDOM_SIGNER();
}
if (_exp < userExperience[_msgSender()]) {
revert EXP_TOO_LOW();
}
userExperience[_msgSender()] = _exp;
RecruitmentCycle memory cycle_ = recruitmentCycles[recruitmentCycleId];
if (cycle_.startTime > block.timestamp || cycle_.endTime < block.timestamp) {
revert RECRUITMENT_NOT_ENABLED();
}
uint256 randomSeed_ = randomFromSignature(_hash, _v, _r, _s);
uint256 s1BadgeId_ = cycle_.s1BadgeIds[randomSeed_ % cycle_.s1BadgeIds.length];
if (
recruitmentCycleUniqueMints[recruitmentCycleId][_msgSender()][s1BadgeId_][RecruitmentType
.Claim]
) {
revert ALREADY_MIGRATED_IN_CYCLE();
}
_startRecruitment(_msgSender(), s1BadgeId_, 0, RecruitmentType.Claim);
}
/// @notice Start a recruitment for a badge using the user's experience points
/// @param _hash The hash to sign of the signature
/// @param _v The signature V field
/// @param _r The signature R field
/// @param _s The signature S field
/// @param _exp The user's experience points
/// @param _s1BadgeId The badge ID (s1)
function startRecruitment(
bytes32 _hash,
uint8 _v,
bytes32 _r,
bytes32 _s,
uint256 _exp,
uint256 _s1BadgeId
)
external
virtual
isNotMigrating(_msgSender())
recruitmentOpen(_s1BadgeId)
hasntMigratedInCycle(_s1BadgeId, _msgSender(), RecruitmentType.Claim)
{
bytes32 calculatedHash_ = generateClaimHash(HashType.Start, _msgSender(), _s1BadgeId);
if (calculatedHash_ != _hash) {
revert HASH_MISMATCH();
}
(address recovered_,,) = ECDSA.tryRecover(_hash, _v, _r, _s);
if (recovered_ != randomSigner) {
revert NOT_RANDOM_SIGNER();
}
if (_exp < userExperience[_msgSender()]) {
revert EXP_TOO_LOW();
}
userExperience[_msgSender()] = _exp;
_startRecruitment(_msgSender(), _s1BadgeId, 0, RecruitmentType.Claim);
}
/// @notice Start a recruitment for a badge
/// @param _s1BadgeId The badge ID (s1)
/// @dev Not all badges are eligible for recruitment at the same time
/// @dev Defines a cooldown for the recruitment to be complete
/// @dev the cooldown is lesser the higher the Pass Tier
/// @dev Must be called from the s1 badges contract
function startRecruitment(
address _user,
uint256 _s1BadgeId
)
external
virtual
onlyRole(S1_BADGES_ROLE)
recruitmentOpen(_s1BadgeId)
isNotMigrating(_user)
hasntMigratedInCycle(_s1BadgeId, _user, RecruitmentType.Migration)
{
uint256 s1TokenId_ = s1Badges.getTokenId(_user, _s1BadgeId);
if (s1Badges.ownerOf(s1TokenId_) != _user) {
revert TOKEN_NOT_OWNED();
}
_startRecruitment(_user, _s1BadgeId, s1TokenId_, RecruitmentType.Migration);
}
/// @notice Get the active recruitment for a user
/// @param _user The user address
/// @return The active recruitment
function getActiveRecruitmentFor(address _user) public view returns (Recruitment memory) {
if (recruitments[_user].length == 0) {
revert RECRUITMENT_NOT_STARTED();
}
return recruitments[_user][recruitments[_user].length - 1];
}
/// @notice Update a recruitment
/// @param _recruitment The updated recruitment
function _updateRecruitment(Recruitment memory _recruitment) internal virtual {
recruitments[_recruitment.user][recruitments[_recruitment.user].length - 1] = _recruitment;
emit RecruitmentUpdated(
_recruitment.recruitmentCycle,
_recruitment.user,
_recruitment.s1BadgeId,
_recruitment.s1TokenId,
_recruitment.s2TokenId,
_recruitment.cooldownExpiration,
_recruitment.influenceExpiration,
_recruitment.whaleInfluences,
_recruitment.minnowInfluences
);
}
/// @notice Get the maximum number of influences for a given experience
/// @param _exp The user's experience points
function maxInfluences(uint256 _exp) public view virtual returns (uint256 value) {
value = 0; // _exp / config.maxInfluencesDivider;
value += config.baseMaxInfluences;
return value;
}
/// @notice Influence (alter) the chances during a recruitment
/// @param _hash The hash to sign
/// @param _v signature V field
/// @param _r signature R field
/// @param _s signature S field
/// @param _influenceColor the influence's color
/// @dev Can be called only during an active recruitment
/// @dev Implements a cooldown before allowing to re-influence
/// @dev The max influence amount is determined by Pass Tier
function influenceRecruitment(
bytes32 _hash,
uint8 _v,
bytes32 _r,
bytes32 _s,
uint256 _exp,
InfluenceColor _influenceColor
)
external
isMigrating
{
bytes32 calculatedHash_ = generateClaimHash(HashType.Influence, _msgSender(), _exp);
if (calculatedHash_ != _hash) {
revert HASH_MISMATCH();
}
(address recovered_,,) = ECDSA.tryRecover(_hash, _v, _r, _s);
if (recovered_ != randomSigner) revert NOT_RANDOM_SIGNER();
Recruitment memory recruitment_ = getActiveRecruitmentFor(_msgSender());
if ((recruitment_.whaleInfluences + recruitment_.minnowInfluences) >= maxInfluences(_exp)) {
revert MAX_INFLUENCES_REACHED();
}
if (recruitment_.influenceExpiration > block.timestamp) {
revert INFLUENCE_IN_PROGRESS();
}
// apply the influence, and reset the other
if (_influenceColor == InfluenceColor.Whale) {
recruitment_.whaleInfluences++;
recruitment_.minnowInfluences = 0;
} else if (_influenceColor == InfluenceColor.Minnow) {
recruitment_.minnowInfluences++;
recruitment_.whaleInfluences = 0;
} else {
revert INVALID_INFLUENCE_COLOR();
}
recruitment_.influenceExpiration = block.timestamp + config.cooldownInfluence;
_updateRecruitment(recruitment_);
}
/// @notice End a recruitment
/// @param _hash The hash to sign
/// @param _v signature V field
/// @param _r signature R field
/// @param _s signature S field
/// @param _exp The user's experience points
/// @dev Can be called only during an active recruitment, after the cooldown is over
/// @dev The final color is determined randomly, and affected by the influence amounts
function endRecruitment(
bytes32 _hash,
uint8 _v,
bytes32 _r,
bytes32 _s,
uint256 _exp
)
external
isMigrating
{
Recruitment memory recruitment_ = getActiveRecruitmentFor(_msgSender());
if (recruitment_.influenceExpiration > block.timestamp) {
revert INFLUENCE_IN_PROGRESS();
}
// check if the cooldown is over
if (recruitment_.cooldownExpiration > block.timestamp) {
revert RECRUITMENT_NOT_READY();
}
// ensure the hash corresponds to the start time
bytes32 calculatedHash_ = generateClaimHash(HashType.End, _msgSender(), _exp);
if (calculatedHash_ != _hash) {
revert HASH_MISMATCH();
}
uint256 randomSeed_ = randomFromSignature(_hash, _v, _r, _s);
uint256 whaleWeight_ = 50 + recruitment_.whaleInfluences * config.influenceWeightPercent;
uint256 minnowWeight_ = 50 + recruitment_.minnowInfluences * config.influenceWeightPercent;
uint256 totalWeight_ = whaleWeight_ + minnowWeight_;
uint256 randomValue = randomSeed_ % totalWeight_;
TrailblazersBadgesS2.MovementType finalColor_;
if (randomValue < minnowWeight_) {
finalColor_ = TrailblazersBadgesS2.MovementType.Minnow;
} else {
finalColor_ = TrailblazersBadgesS2.MovementType.Whale;
}
uint256 s1BadgeId_ = recruitment_.s1BadgeId;
// mint the badge
s2Badges.mint(_msgSender(), TrailblazersBadgesS2.BadgeType(s1BadgeId_), finalColor_);
uint256 s2TokenId_ = s2Badges.totalSupply();
recruitment_.s2TokenId = s2TokenId_;
recruitment_.cooldownExpiration = 0;
recruitment_.influenceExpiration = 0;
_updateRecruitment(recruitment_);
emit RecruitmentComplete(
recruitment_.recruitmentCycle,
recruitment_.user,
recruitment_.s1TokenId,
recruitment_.s2TokenId,
uint256(finalColor_)
);
}
/// @notice Generate a unique hash for each recruitment uniquely
/// @param _user The user address
/// @param _exp The users experience points
/// @return _hash The unique hash
function generateClaimHash(
HashType _hashType,
address _user,
uint256 _exp
)
public
pure
returns (bytes32)
{
return keccak256(abi.encodePacked(_hashType, _user, _exp));
}
/// @notice Check if a recruitment is active for a user
/// @param _user The user address
/// @return Whether the user has an active recruitment
function isRecruitmentActive(address _user) public view returns (bool) {
if (recruitments[_user].length == 0) {
return false;
}
Recruitment memory recruitment_ = getActiveRecruitmentFor(_user);
return recruitment_.cooldownExpiration != 0;
}
/// @notice Generates a random number from a signature
/// @param _hash The hash to sign
/// @param _v signature V field
/// @param _r signature R field
/// @param _s signature S field
/// @return _random The pseudo-random number
function randomFromSignature(
bytes32 _hash,
uint8 _v,
bytes32 _r,
bytes32 _s
)
public
view
returns (uint256 _random)
{
(address recovered_,,) = ECDSA.tryRecover(_hash, _v, _r, _s);
if (recovered_ != randomSigner) revert NOT_RANDOM_SIGNER();
return uint256(keccak256(abi.encodePacked(_r, _s, _v)));
}
/// @notice Check if a influence is active for a user
/// @param _user The user address
/// @return Whether the user has an active influence
function isInfluenceActive(address _user) public view returns (bool) {
Recruitment memory recruitment_ = getActiveRecruitmentFor(_user);
return recruitment_.influenceExpiration > block.timestamp;
}
/// @notice Get the recruitment influence counts for a user
/// @param _user The user address
/// @return _whaleInfluences The Whale influence count
/// @return _minnowInfluences The Minnow influence count
function getRecruitmentInfluences(address _user)
public
view
returns (uint256 _whaleInfluences, uint256 _minnowInfluences)
{
if (!isRecruitmentActive(_user)) {
revert RECRUITMENT_NOT_STARTED();
}
Recruitment memory recruitment_ = getActiveRecruitmentFor(_user);
return (recruitment_.whaleInfluences, recruitment_.minnowInfluences);
}
/// @notice supportsInterface implementation
/// @param _interfaceId The interface ID
/// @return Whether the interface is supported
function supportsInterface(bytes4 _interfaceId) public view override returns (bool) {
return super.supportsInterface(_interfaceId);
}
/// @notice Internal method to authorize an upgrade
function _authorizeUpgrade(address) internal virtual override onlyOwner { }
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
import "./BadgeRecruitment.sol";
contract BadgeRecruitmentV2 is BadgeRecruitment {
/// @notice Events
event RecruitmentReset(
uint256 indexed cycleId, address indexed user, uint256 indexed s1TokenId, uint256 s1BadgeId
);
/// @notice Errors
error RECRUITMENT_ALREADY_COMPLETED();
error RECRUITMENT_NOT_FOUND();
error NOT_ENOUGH_TIME_LEFT();
modifier recruitmentHasTimeLeft(address _user) {
uint256 endCycleTime = recruitmentCycles[recruitmentCycleId].endTime;
uint256 potentialRecruitmentEndTime = block.timestamp + this.getConfig().cooldownRecruitment;
if (potentialRecruitmentEndTime > endCycleTime) {
revert NOT_ENOUGH_TIME_LEFT();
}
_;
}
/// @notice Updated version function
function version() external pure virtual returns (string memory) {
return "V2";
}
/// @notice Start a recruitment for a badge
/// @param _s1BadgeId The badge ID (s1)
/// @dev Not all badges are eligible for recruitment at the same time
/// @dev Defines a cooldown for the recruitment to be complete
/// @dev the cooldown is lesser the higher the Pass Tier
/// @dev Must be called from the s1 badges contract
function startRecruitment(
address _user,
uint256 _s1BadgeId,
uint256 _s1TokenId
)
external
virtual
onlyRole(S1_BADGES_ROLE)
recruitmentOpen(_s1BadgeId)
isNotMigrating(_user)
hasntMigratedInCycle(_s1BadgeId, _user, RecruitmentType.Migration)
recruitmentHasTimeLeft(_user)
{
if (s1Badges.ownerOf(_s1TokenId) != _user) {
revert TOKEN_NOT_OWNED();
}
_startRecruitment(_user, _s1BadgeId, _s1TokenId, RecruitmentType.Migration);
}
/// @notice Disable all current recruitments
/// @dev Bypasses the default date checks
function forceDisableAllRecruitments() external virtual onlyRole(DEFAULT_ADMIN_ROLE) {
forceDisableRecruitments();
emit RecruitmentCycleToggled(
recruitmentCycleId,
recruitmentCycles[recruitmentCycleId].startTime,
recruitmentCycles[recruitmentCycleId].endTime,
recruitmentCycles[recruitmentCycleId].s1BadgeIds,
false
);
}
/// @notice Get the active recruitment for a user
/// @param _user The user address
/// @return The active recruitment
function getActiveRecruitmentsFor(address _user) public view returns (Recruitment[] memory) {
if (recruitments[_user].length == 0) {
revert RECRUITMENT_NOT_STARTED();
}
return recruitments[_user];
}
/// @notice Reset a recruitment that hasn't been completed
/// @param _user The user address
/// @param _s1TokenId The s1 token ID
/// @param _s1BadgeId The s1 badge ID
/// @param _recruitmentCycle The recruitment index
/// @dev Must be called from the s1 badges contract
function resetRecruitment(
address _user,
uint256 _s1TokenId,
uint256 _s1BadgeId,
uint256 _recruitmentCycle
)
public
virtual
onlyRole(S1_BADGES_ROLE)
{
if (
!recruitmentCycleUniqueMints[_recruitmentCycle][_user][_s1BadgeId][RecruitmentType
.Migration]
&& !recruitmentCycleUniqueMints[_recruitmentCycle][_user][_s1BadgeId][RecruitmentType.Claim]
&& !recruitmentCycleUniqueMints[_recruitmentCycle][_user][_s1BadgeId][RecruitmentType
.Undefined]
) {
revert RECRUITMENT_NOT_FOUND();
}
bool found = false;
for (uint256 i = 0; i < recruitments[_user].length; i++) {
if (
recruitments[_user][i].recruitmentCycle == _recruitmentCycle
&& recruitments[_user][i].s1TokenId == _s1TokenId
&& recruitments[_user][i].s2TokenId == 0
) {
delete recruitments[_user][i];
found = true;
break;
}
}
if (!found) {
revert RECRUITMENT_NOT_FOUND();
}
recruitmentCycleUniqueMints[_recruitmentCycle][_user][_s1BadgeId][RecruitmentType.Undefined]
= false;
recruitmentCycleUniqueMints[_recruitmentCycle][_user][_s1BadgeId][RecruitmentType.Claim] =
false;
recruitmentCycleUniqueMints[_recruitmentCycle][_user][_s1BadgeId][RecruitmentType.Migration]
= false;
emit RecruitmentReset(_recruitmentCycle, _user, _s1TokenId, _s1BadgeId);
}
/// @notice Set the s2 badges contract
/// @param _s2Badges The s2 badges contract address
/// @dev Must be called from the admin account
function setS2BadgesContract(address _s2Badges) external virtual onlyRole(DEFAULT_ADMIN_ROLE) {
s2Badges = TrailblazersBadgesS2(_s2Badges);
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
import "./TrailblazersS1BadgesV4.sol";
import "./BadgeRecruitment.sol";
import "./BadgeRecruitmentV2.sol";
contract TrailblazersBadgesV5 is TrailblazersBadgesV4 {
/// @notice Errors
error RECRUITMENT_ALREADY_COMPLETED();
error NOT_OWNER();
error NOT_IMPLEMENTED();
error RECRUITMENT_NOT_FOUND();
/// @notice Updated version function
/// @return Version string
function version() external pure virtual override returns (string memory) {
return "V5";
}
/// @notice Recruitment contract
BadgeRecruitmentV2 public recruitmentContractV2;
/// @notice Setter for recruitment contract
function setRecruitmentContractV2(address _recruitmentContractV2) public onlyOwner {
recruitmentContractV2 = BadgeRecruitmentV2(_recruitmentContractV2);
}
/// @notice Start recruitment for a badge
/// @param _badgeId Badge ID
/// @param _tokenId Token ID
function startRecruitment(uint256 _badgeId, uint256 _tokenId) public virtual {
if (recruitmentLockDuration == 0) {
revert RECRUITMENT_LOCK_DURATION_NOT_SET();
}
if (ownerOf(_tokenId) != _msgSender()) {
revert NOT_OWNER();
}
if (unlockTimestamps[_tokenId] > block.timestamp) {
revert BADGE_LOCKED();
}
unlockTimestamps[_tokenId] = block.timestamp + recruitmentLockDuration;
recruitmentContractV2.startRecruitment(_msgSender(), _badgeId, _tokenId);
}
/// @notice Deprecated of legacy function
function startRecruitment(uint256 /*_badgeId*/ ) public virtual override {
revert NOT_IMPLEMENTED();
}
/// @notice Reset an ongoing migration
/// @param _tokenId Token ID
/// @param _badgeId Badge ID
/// @param _cycleId Cycle ID
/// @dev Only the owner of the token can reset the migration
function resetMigration(uint256 _tokenId, uint256 _badgeId, uint256 _cycleId) public virtual {
if (ownerOf(_tokenId) != _msgSender()) {
revert NOT_OWNER();
}
recruitmentContractV2.resetRecruitment(_msgSender(), _tokenId, _badgeId, _cycleId);
unlockTimestamps[_tokenId] = 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC1155/extensions/ERC1155Supply.sol)
pragma solidity ^0.8.20;
import {ERC1155Upgradeable} from "../ERC1155Upgradeable.sol";
import {Initializable} from "../../../proxy/utils/Initializable.sol";
/**
* @dev Extension of ERC1155 that adds tracking of total supply per id.
*
* Useful for scenarios where Fungible and Non-fungible tokens have to be
* clearly identified. Note: While a totalSupply of 1 might mean the
* corresponding is an NFT, there is no guarantees that no other token with the
* same id are not going to be minted.
*
* NOTE: This contract implies a global limit of 2**256 - 1 to the number of tokens
* that can be minted.
*
* CAUTION: This extension should not be added in an upgrade to an already deployed contract.
*/
abstract contract ERC1155SupplyUpgradeable is Initializable, ERC1155Upgradeable {
/// @custom:storage-location erc7201:openzeppelin.storage.ERC1155Supply
struct ERC1155SupplyStorage {
mapping(uint256 id => uint256) _totalSupply;
uint256 _totalSupplyAll;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ERC1155Supply")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant ERC1155SupplyStorageLocation = 0x4a593662ee04d27b6a00ebb31be7fe0c102c2ade82a7c5d764f2df05dc4e2800;
function _getERC1155SupplyStorage() private pure returns (ERC1155SupplyStorage storage $) {
assembly {
$.slot := ERC1155SupplyStorageLocation
}
}
function __ERC1155Supply_init() internal onlyInitializing {
}
function __ERC1155Supply_init_unchained() internal onlyInitializing {
}
/**
* @dev Total value of tokens in with a given id.
*/
function totalSupply(uint256 id) public view virtual returns (uint256) {
ERC1155SupplyStorage storage $ = _getERC1155SupplyStorage();
return $._totalSupply[id];
}
/**
* @dev Total value of tokens.
*/
function totalSupply() public view virtual returns (uint256) {
ERC1155SupplyStorage storage $ = _getERC1155SupplyStorage();
return $._totalSupplyAll;
}
/**
* @dev Indicates whether any token exist with a given id, or not.
*/
function exists(uint256 id) public view virtual returns (bool) {
return totalSupply(id) > 0;
}
/**
* @dev See {ERC1155-_update}.
*/
function _update(
address from,
address to,
uint256[] memory ids,
uint256[] memory values
) internal virtual override {
ERC1155SupplyStorage storage $ = _getERC1155SupplyStorage();
super._update(from, to, ids, values);
if (from == address(0)) {
uint256 totalMintValue = 0;
for (uint256 i = 0; i < ids.length; ++i) {
uint256 value = values[i];
// Overflow check required: The rest of the code assumes that totalSupply never overflows
$._totalSupply[ids[i]] += value;
totalMintValue += value;
}
// Overflow check required: The rest of the code assumes that totalSupplyAll never overflows
$._totalSupplyAll += totalMintValue;
}
if (to == address(0)) {
uint256 totalBurnValue = 0;
for (uint256 i = 0; i < ids.length; ++i) {
uint256 value = values[i];
unchecked {
// Overflow not possible: values[i] <= balanceOf(from, ids[i]) <= totalSupply(ids[i])
$._totalSupply[ids[i]] -= value;
// Overflow not possible: sum_i(values[i]) <= sum_i(totalSupply(ids[i])) <= totalSupplyAll
totalBurnValue += value;
}
}
unchecked {
// Overflow not possible: totalBurnValue = sum_i(values[i]) <= sum_i(totalSupply(ids[i])) <= totalSupplyAll
$._totalSupplyAll -= totalBurnValue;
}
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
import { UUPSUpgradeable } from
"@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import { Ownable2StepUpgradeable } from
"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
import { ContextUpgradeable } from
"@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
import { IMinimalBlacklist } from "@taiko/blacklist/IMinimalBlacklist.sol";
import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
/// @title ECDSAWhitelist
/// @dev Signature-driven mint whitelist
/// @custom:security-contact [email protected]
contract ECDSAWhitelist is ContextUpgradeable, UUPSUpgradeable, Ownable2StepUpgradeable {
event MintSignerUpdated(address _mintSigner);
event MintConsumed(address _minter, uint256 _tokenId);
event BlacklistUpdated(address _blacklist);
error MINTS_EXCEEDED();
error ADDRESS_BLACKLISTED();
error ONLY_MINT_SIGNER();
/// @notice Mint signer address
address public mintSigner;
/// @notice Tracker for minted signatures
mapping(bytes32 signatureHash => bool hasMinted) public minted;
/// @notice Blackist address
IMinimalBlacklist public blacklist;
/// @notice Gap for upgrade safety
uint256[47] private __gap;
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
/// @notice Modifier to restrict access to the mint signer
modifier onlyMintSigner() {
if (msg.sender != mintSigner) revert ONLY_MINT_SIGNER();
_;
}
/// @notice Update the blacklist address
/// @param _blacklist The new blacklist address
function updateBlacklist(IMinimalBlacklist _blacklist) external onlyOwner {
blacklist = _blacklist;
emit BlacklistUpdated(address(_blacklist));
}
/// @notice Update the mint signer address
/// @param _mintSigner The new mint signer address
function updateMintSigner(address _mintSigner) public onlyOwner {
mintSigner = _mintSigner;
emit MintSignerUpdated(_mintSigner);
}
/// @notice Contract initializer
/// @param _owner Contract owner
/// @param _mintSigner Mint signer address
/// @param _blacklist Blacklist address
function initialize(
address _owner,
address _mintSigner,
IMinimalBlacklist _blacklist
)
external
initializer
{
__ECDSAWhitelist_init(_owner, _mintSigner, _blacklist);
}
/// @notice Generate a standardized hash for externally signing
/// @param _minter Address of the minter
/// @param _tokenId ID for the token to mint
function getHash(address _minter, uint256 _tokenId) public pure returns (bytes32) {
return keccak256(bytes.concat(keccak256(abi.encode(_minter, _tokenId))));
}
/// @notice Internal method to verify valid signatures
/// @param _signature Signature to verify
/// @param _minter Address of the minter
/// @param _tokenId ID for the token to mint
/// @return Whether the signature is valid
function _isSignatureValid(
bytes memory _signature,
address _minter,
uint256 _tokenId
)
internal
view
returns (bool)
{
bytes32 _hash = getHash(_minter, _tokenId);
(address _recovered,,) = ECDSA.tryRecover(_hash, _signature);
return _recovered == mintSigner;
}
/// @notice Check if a wallet can mint
/// @param _signature Signature to verify
/// @param _minter Address of the minter
/// @param _tokenId ID for the token to mint
/// @return Whether the wallet can mint
function canMint(
bytes memory _signature,
address _minter,
uint256 _tokenId
)
public
view
returns (bool)
{
if (blacklist.isBlacklisted(_minter)) revert ADDRESS_BLACKLISTED();
if (minted[keccak256(_signature)]) return false;
return _isSignatureValid(_signature, _minter, _tokenId);
}
/// @notice Internal initializer
/// @param _owner Contract owner
/// @param _mintSigner Mint signer address
/// @param _blacklist Blacklist address
function __ECDSAWhitelist_init(
address _owner,
address _mintSigner,
IMinimalBlacklist _blacklist
)
internal
{
_transferOwnership(_owner == address(0) ? msg.sender : _owner);
__Context_init();
mintSigner = _mintSigner;
blacklist = _blacklist;
}
/// @notice Internal method to consume a mint
/// @param _signature Signature to verify
/// @param _minter Address of the minter
/// @param _tokenId ID for the token to mint
function _consumeMint(bytes memory _signature, address _minter, uint256 _tokenId) internal {
if (!canMint(_signature, _minter, _tokenId)) revert MINTS_EXCEEDED();
minted[keccak256(_signature)] = true;
emit MintConsumed(_minter, _tokenId);
}
/// @notice Internal method to authorize an upgrade
function _authorizeUpgrade(address) internal virtual override onlyOwner { }
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
/// @title Minimal Blacklist Interface
interface IMinimalBlacklist {
function isBlacklisted(address _account) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)
pragma solidity ^0.8.20;
import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant HEX_DIGITS = "0123456789abcdef";
uint8 private constant ADDRESS_LENGTH = 20;
/**
* @dev The `value` string doesn't fit in the specified `length`.
*/
error StringsInsufficientHexLength(uint256 value, uint256 length);
/**
* @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), HEX_DIGITS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toStringSigned(int256 value) internal pure returns (string memory) {
return string.concat(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) {
uint256 localValue = value;
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] = HEX_DIGITS[localValue & 0xf];
localValue >>= 4;
}
if (localValue != 0) {
revert StringsInsufficientHexLength(value, length);
}
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 bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/UUPSUpgradeable.sol)
pragma solidity ^0.8.20;
import {IERC1822Proxiable} from "@openzeppelin/contracts/interfaces/draft-IERC1822.sol";
import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol";
import {Initializable} from "./Initializable.sol";
/**
* @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
* {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
*
* A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
* reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
* `UUPSUpgradeable` with a custom implementation of upgrades.
*
* The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
*/
abstract contract UUPSUpgradeable is Initializable, IERC1822Proxiable {
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
address private immutable __self = address(this);
/**
* @dev The version of the upgrade interface of the contract. If this getter is missing, both `upgradeTo(address)`
* and `upgradeToAndCall(address,bytes)` are present, and `upgradeTo` must be used if no function should be called,
* while `upgradeToAndCall` will invoke the `receive` function if the second argument is the empty byte string.
* If the getter returns `"5.0.0"`, only `upgradeToAndCall(address,bytes)` is present, and the second argument must
* be the empty byte string if no function should be called, making it impossible to invoke the `receive` function
* during an upgrade.
*/
string public constant UPGRADE_INTERFACE_VERSION = "5.0.0";
/**
* @dev The call is from an unauthorized context.
*/
error UUPSUnauthorizedCallContext();
/**
* @dev The storage `slot` is unsupported as a UUID.
*/
error UUPSUnsupportedProxiableUUID(bytes32 slot);
/**
* @dev Check that the execution is being performed through a delegatecall call and that the execution context is
* a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case
* for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
* function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
* fail.
*/
modifier onlyProxy() {
_checkProxy();
_;
}
/**
* @dev Check that the execution is not being performed through a delegate call. This allows a function to be
* callable on the implementing contract but not through proxies.
*/
modifier notDelegated() {
_checkNotDelegated();
_;
}
function __UUPSUpgradeable_init() internal onlyInitializing {
}
function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
}
/**
* @dev Implementation of the ERC1822 {proxiableUUID} function. This returns the storage slot used by the
* implementation. It is used to validate the implementation's compatibility when performing an upgrade.
*
* IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
* bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
* function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
*/
function proxiableUUID() external view virtual notDelegated returns (bytes32) {
return ERC1967Utils.IMPLEMENTATION_SLOT;
}
/**
* @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
* encoded in `data`.
*
* Calls {_authorizeUpgrade}.
*
* Emits an {Upgraded} event.
*
* @custom:oz-upgrades-unsafe-allow-reachable delegatecall
*/
function upgradeToAndCall(address newImplementation, bytes memory data) public payable virtual onlyProxy {
_authorizeUpgrade(newImplementation);
_upgradeToAndCallUUPS(newImplementation, data);
}
/**
* @dev Reverts if the execution is not performed via delegatecall or the execution
* context is not of a proxy with an ERC1967-compliant implementation pointing to self.
* See {_onlyProxy}.
*/
function _checkProxy() internal view virtual {
if (
address(this) == __self || // Must be called through delegatecall
ERC1967Utils.getImplementation() != __self // Must be called through an active proxy
) {
revert UUPSUnauthorizedCallContext();
}
}
/**
* @dev Reverts if the execution is performed via delegatecall.
* See {notDelegated}.
*/
function _checkNotDelegated() internal view virtual {
if (address(this) != __self) {
// Must not be called through delegatecall
revert UUPSUnauthorizedCallContext();
}
}
/**
* @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
* {upgradeToAndCall}.
*
* Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
*
* ```solidity
* function _authorizeUpgrade(address) internal onlyOwner {}
* ```
*/
function _authorizeUpgrade(address newImplementation) internal virtual;
/**
* @dev Performs an implementation upgrade with a security check for UUPS proxies, and additional setup call.
*
* As a security check, {proxiableUUID} is invoked in the new implementation, and the return value
* is expected to be the implementation slot in ERC1967.
*
* Emits an {IERC1967-Upgraded} event.
*/
function _upgradeToAndCallUUPS(address newImplementation, bytes memory data) private {
try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
if (slot != ERC1967Utils.IMPLEMENTATION_SLOT) {
revert UUPSUnsupportedProxiableUUID(slot);
}
ERC1967Utils.upgradeToAndCall(newImplementation, data);
} catch {
// The implementation is not UUPS
revert ERC1967Utils.ERC1967InvalidImplementation(newImplementation);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable2Step.sol)
pragma solidity ^0.8.20;
import {OwnableUpgradeable} from "./OwnableUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which provides access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is specified at deployment time in the constructor for `Ownable`. This
* can later be changed with {transferOwnership} and {acceptOwnership}.
*
* This module is used through inheritance. It will make available all functions
* from parent (Ownable).
*/
abstract contract Ownable2StepUpgradeable is Initializable, OwnableUpgradeable {
/// @custom:storage-location erc7201:openzeppelin.storage.Ownable2Step
struct Ownable2StepStorage {
address _pendingOwner;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable2Step")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant Ownable2StepStorageLocation = 0x237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c00;
function _getOwnable2StepStorage() private pure returns (Ownable2StepStorage storage $) {
assembly {
$.slot := Ownable2StepStorageLocation
}
}
event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
function __Ownable2Step_init() internal onlyInitializing {
}
function __Ownable2Step_init_unchained() internal onlyInitializing {
}
/**
* @dev Returns the address of the pending owner.
*/
function pendingOwner() public view virtual returns (address) {
Ownable2StepStorage storage $ = _getOwnable2StepStorage();
return $._pendingOwner;
}
/**
* @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual override onlyOwner {
Ownable2StepStorage storage $ = _getOwnable2StepStorage();
$._pendingOwner = newOwner;
emit OwnershipTransferStarted(owner(), newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual override {
Ownable2StepStorage storage $ = _getOwnable2StepStorage();
delete $._pendingOwner;
super._transferOwnership(newOwner);
}
/**
* @dev The new owner accepts the ownership transfer.
*/
function acceptOwnership() public virtual {
address sender = _msgSender();
if (pendingOwner() != sender) {
revert OwnableUnauthorizedAccount(sender);
}
_transferOwnership(sender);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @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 ContextUpgradeable is Initializable {
function __Context_init() internal onlyInitializing {
}
function __Context_init_unchained() internal onlyInitializing {
}
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.20;
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS
}
/**
* @dev The signature derives the `address(0)`.
*/
error ECDSAInvalidSignature();
/**
* @dev The signature has an invalid length.
*/
error ECDSAInvalidSignatureLength(uint256 length);
/**
* @dev The signature has an S value that is in the upper half order.
*/
error ECDSAInvalidSignatureS(bytes32 s);
/**
* @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
* return address(0) without also returning an error description. Errors are documented using an enum (error type)
* and a bytes32 providing additional information about the error.
*
* If no error is returned, then the address can be used for verification purposes.
*
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*/
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
/// @solidity memory-safe-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*/
function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) {
unchecked {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
// We do not check for an overflow here since the shift operation results in 0 or 1.
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*/
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address, RecoverError, bytes32) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS, s);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature, bytes32(0));
}
return (signer, RecoverError.NoError, bytes32(0));
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
*/
function _throwError(RecoverError error, bytes32 errorArg) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert ECDSAInvalidSignature();
} else if (error == RecoverError.InvalidSignatureLength) {
revert ECDSAInvalidSignatureLength(uint256(errorArg));
} else if (error == RecoverError.InvalidSignatureS) {
revert ECDSAInvalidSignatureS(errorArg);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC1155/ERC1155.sol)
pragma solidity ^0.8.20;
import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol";
import {IERC1155MetadataURI} from "@openzeppelin/contracts/token/ERC1155/extensions/IERC1155MetadataURI.sol";
import {ContextUpgradeable} from "../../utils/ContextUpgradeable.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {ERC165Upgradeable} from "../../utils/introspection/ERC165Upgradeable.sol";
import {Arrays} from "@openzeppelin/contracts/utils/Arrays.sol";
import {IERC1155Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";
/**
* @dev Implementation of the basic standard multi-token.
* See https://eips.ethereum.org/EIPS/eip-1155
* Originally based on code by Enjin: https://github.com/enjin/erc-1155
*/
abstract contract ERC1155Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeable, IERC1155, IERC1155MetadataURI, IERC1155Errors {
using Arrays for uint256[];
using Arrays for address[];
/// @custom:storage-location erc7201:openzeppelin.storage.ERC1155
struct ERC1155Storage {
mapping(uint256 id => mapping(address account => uint256)) _balances;
mapping(address account => mapping(address operator => bool)) _operatorApprovals;
// Used as the URI for all token types by relying on ID substitution, e.g. https://token-cdn-domain/{id}.json
string _uri;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ERC1155")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant ERC1155StorageLocation = 0x88be536d5240c274a3b1d3a1be54482fd9caa294f08c62a7cde569f49a3c4500;
function _getERC1155Storage() private pure returns (ERC1155Storage storage $) {
assembly {
$.slot := ERC1155StorageLocation
}
}
/**
* @dev See {_setURI}.
*/
function __ERC1155_init(string memory uri_) internal onlyInitializing {
__ERC1155_init_unchained(uri_);
}
function __ERC1155_init_unchained(string memory uri_) internal onlyInitializing {
_setURI(uri_);
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165Upgradeable, IERC165) returns (bool) {
return
interfaceId == type(IERC1155).interfaceId ||
interfaceId == type(IERC1155MetadataURI).interfaceId ||
super.supportsInterface(interfaceId);
}
/**
* @dev See {IERC1155MetadataURI-uri}.
*
* This implementation returns the same URI for *all* token types. It relies
* on the token type ID substitution mechanism
* https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
*
* Clients calling this function must replace the `\{id\}` substring with the
* actual token type ID.
*/
function uri(uint256 /* id */) public view virtual returns (string memory) {
ERC1155Storage storage $ = _getERC1155Storage();
return $._uri;
}
/**
* @dev See {IERC1155-balanceOf}.
*/
function balanceOf(address account, uint256 id) public view virtual returns (uint256) {
ERC1155Storage storage $ = _getERC1155Storage();
return $._balances[id][account];
}
/**
* @dev See {IERC1155-balanceOfBatch}.
*
* Requirements:
*
* - `accounts` and `ids` must have the same length.
*/
function balanceOfBatch(
address[] memory accounts,
uint256[] memory ids
) public view virtual returns (uint256[] memory) {
if (accounts.length != ids.length) {
revert ERC1155InvalidArrayLength(ids.length, accounts.length);
}
uint256[] memory batchBalances = new uint256[](accounts.length);
for (uint256 i = 0; i < accounts.length; ++i) {
batchBalances[i] = balanceOf(accounts.unsafeMemoryAccess(i), ids.unsafeMemoryAccess(i));
}
return batchBalances;
}
/**
* @dev See {IERC1155-setApprovalForAll}.
*/
function setApprovalForAll(address operator, bool approved) public virtual {
_setApprovalForAll(_msgSender(), operator, approved);
}
/**
* @dev See {IERC1155-isApprovedForAll}.
*/
function isApprovedForAll(address account, address operator) public view virtual returns (bool) {
ERC1155Storage storage $ = _getERC1155Storage();
return $._operatorApprovals[account][operator];
}
/**
* @dev See {IERC1155-safeTransferFrom}.
*/
function safeTransferFrom(address from, address to, uint256 id, uint256 value, bytes memory data) public virtual {
address sender = _msgSender();
if (from != sender && !isApprovedForAll(from, sender)) {
revert ERC1155MissingApprovalForAll(sender, from);
}
_safeTransferFrom(from, to, id, value, data);
}
/**
* @dev See {IERC1155-safeBatchTransferFrom}.
*/
function safeBatchTransferFrom(
address from,
address to,
uint256[] memory ids,
uint256[] memory values,
bytes memory data
) public virtual {
address sender = _msgSender();
if (from != sender && !isApprovedForAll(from, sender)) {
revert ERC1155MissingApprovalForAll(sender, from);
}
_safeBatchTransferFrom(from, to, ids, values, data);
}
/**
* @dev Transfers a `value` amount of tokens of type `id` from `from` to `to`. Will mint (or burn) if `from`
* (or `to`) is the zero address.
*
* Emits a {TransferSingle} event if the arrays contain one element, and {TransferBatch} otherwise.
*
* Requirements:
*
* - If `to` refers to a smart contract, it must implement either {IERC1155Receiver-onERC1155Received}
* or {IERC1155Receiver-onERC1155BatchReceived} and return the acceptance magic value.
* - `ids` and `values` must have the same length.
*
* NOTE: The ERC-1155 acceptance check is not performed in this function. See {_updateWithAcceptanceCheck} instead.
*/
function _update(address from, address to, uint256[] memory ids, uint256[] memory values) internal virtual {
ERC1155Storage storage $ = _getERC1155Storage();
if (ids.length != values.length) {
revert ERC1155InvalidArrayLength(ids.length, values.length);
}
address operator = _msgSender();
for (uint256 i = 0; i < ids.length; ++i) {
uint256 id = ids.unsafeMemoryAccess(i);
uint256 value = values.unsafeMemoryAccess(i);
if (from != address(0)) {
uint256 fromBalance = $._balances[id][from];
if (fromBalance < value) {
revert ERC1155InsufficientBalance(from, fromBalance, value, id);
}
unchecked {
// Overflow not possible: value <= fromBalance
$._balances[id][from] = fromBalance - value;
}
}
if (to != address(0)) {
$._balances[id][to] += value;
}
}
if (ids.length == 1) {
uint256 id = ids.unsafeMemoryAccess(0);
uint256 value = values.unsafeMemoryAccess(0);
emit TransferSingle(operator, from, to, id, value);
} else {
emit TransferBatch(operator, from, to, ids, values);
}
}
/**
* @dev Version of {_update} that performs the token acceptance check by calling
* {IERC1155Receiver-onERC1155Received} or {IERC1155Receiver-onERC1155BatchReceived} on the receiver address if it
* contains code (eg. is a smart contract at the moment of execution).
*
* IMPORTANT: Overriding this function is discouraged because it poses a reentrancy risk from the receiver. So any
* update to the contract state after this function would break the check-effect-interaction pattern. Consider
* overriding {_update} instead.
*/
function _updateWithAcceptanceCheck(
address from,
address to,
uint256[] memory ids,
uint256[] memory values,
bytes memory data
) internal virtual {
_update(from, to, ids, values);
if (to != address(0)) {
address operator = _msgSender();
if (ids.length == 1) {
uint256 id = ids.unsafeMemoryAccess(0);
uint256 value = values.unsafeMemoryAccess(0);
_doSafeTransferAcceptanceCheck(operator, from, to, id, value, data);
} else {
_doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, values, data);
}
}
}
/**
* @dev Transfers a `value` tokens of token type `id` from `from` to `to`.
*
* Emits a {TransferSingle} event.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - `from` must have a balance of tokens of type `id` of at least `value` amount.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
* acceptance magic value.
*/
function _safeTransferFrom(address from, address to, uint256 id, uint256 value, bytes memory data) internal {
if (to == address(0)) {
revert ERC1155InvalidReceiver(address(0));
}
if (from == address(0)) {
revert ERC1155InvalidSender(address(0));
}
(uint256[] memory ids, uint256[] memory values) = _asSingletonArrays(id, value);
_updateWithAcceptanceCheck(from, to, ids, values, data);
}
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_safeTransferFrom}.
*
* Emits a {TransferBatch} event.
*
* Requirements:
*
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
* acceptance magic value.
* - `ids` and `values` must have the same length.
*/
function _safeBatchTransferFrom(
address from,
address to,
uint256[] memory ids,
uint256[] memory values,
bytes memory data
) internal {
if (to == address(0)) {
revert ERC1155InvalidReceiver(address(0));
}
if (from == address(0)) {
revert ERC1155InvalidSender(address(0));
}
_updateWithAcceptanceCheck(from, to, ids, values, data);
}
/**
* @dev Sets a new URI for all token types, by relying on the token type ID
* substitution mechanism
* https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
*
* By this mechanism, any occurrence of the `\{id\}` substring in either the
* URI or any of the values in the JSON file at said URI will be replaced by
* clients with the token type ID.
*
* For example, the `https://token-cdn-domain/\{id\}.json` URI would be
* interpreted by clients as
* `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json`
* for token type ID 0x4cce0.
*
* See {uri}.
*
* Because these URIs cannot be meaningfully represented by the {URI} event,
* this function emits no events.
*/
function _setURI(string memory newuri) internal virtual {
ERC1155Storage storage $ = _getERC1155Storage();
$._uri = newuri;
}
/**
* @dev Creates a `value` amount of tokens of type `id`, and assigns them to `to`.
*
* Emits a {TransferSingle} event.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
* acceptance magic value.
*/
function _mint(address to, uint256 id, uint256 value, bytes memory data) internal {
if (to == address(0)) {
revert ERC1155InvalidReceiver(address(0));
}
(uint256[] memory ids, uint256[] memory values) = _asSingletonArrays(id, value);
_updateWithAcceptanceCheck(address(0), to, ids, values, data);
}
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}.
*
* Emits a {TransferBatch} event.
*
* Requirements:
*
* - `ids` and `values` must have the same length.
* - `to` cannot be the zero address.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
* acceptance magic value.
*/
function _mintBatch(address to, uint256[] memory ids, uint256[] memory values, bytes memory data) internal {
if (to == address(0)) {
revert ERC1155InvalidReceiver(address(0));
}
_updateWithAcceptanceCheck(address(0), to, ids, values, data);
}
/**
* @dev Destroys a `value` amount of tokens of type `id` from `from`
*
* Emits a {TransferSingle} event.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `from` must have at least `value` amount of tokens of type `id`.
*/
function _burn(address from, uint256 id, uint256 value) internal {
if (from == address(0)) {
revert ERC1155InvalidSender(address(0));
}
(uint256[] memory ids, uint256[] memory values) = _asSingletonArrays(id, value);
_updateWithAcceptanceCheck(from, address(0), ids, values, "");
}
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}.
*
* Emits a {TransferBatch} event.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `from` must have at least `value` amount of tokens of type `id`.
* - `ids` and `values` must have the same length.
*/
function _burnBatch(address from, uint256[] memory ids, uint256[] memory values) internal {
if (from == address(0)) {
revert ERC1155InvalidSender(address(0));
}
_updateWithAcceptanceCheck(from, address(0), ids, values, "");
}
/**
* @dev Approve `operator` to operate on all of `owner` tokens
*
* Emits an {ApprovalForAll} event.
*
* Requirements:
*
* - `operator` cannot be the zero address.
*/
function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {
ERC1155Storage storage $ = _getERC1155Storage();
if (operator == address(0)) {
revert ERC1155InvalidOperator(address(0));
}
$._operatorApprovals[owner][operator] = approved;
emit ApprovalForAll(owner, operator, approved);
}
/**
* @dev Performs an acceptance check by calling {IERC1155-onERC1155Received} on the `to` address
* if it contains code at the moment of execution.
*/
function _doSafeTransferAcceptanceCheck(
address operator,
address from,
address to,
uint256 id,
uint256 value,
bytes memory data
) private {
if (to.code.length > 0) {
try IERC1155Receiver(to).onERC1155Received(operator, from, id, value, data) returns (bytes4 response) {
if (response != IERC1155Receiver.onERC1155Received.selector) {
// Tokens rejected
revert ERC1155InvalidReceiver(to);
}
} catch (bytes memory reason) {
if (reason.length == 0) {
// non-ERC1155Receiver implementer
revert ERC1155InvalidReceiver(to);
} else {
/// @solidity memory-safe-assembly
assembly {
revert(add(32, reason), mload(reason))
}
}
}
}
}
/**
* @dev Performs a batch acceptance check by calling {IERC1155-onERC1155BatchReceived} on the `to` address
* if it contains code at the moment of execution.
*/
function _doSafeBatchTransferAcceptanceCheck(
address operator,
address from,
address to,
uint256[] memory ids,
uint256[] memory values,
bytes memory data
) private {
if (to.code.length > 0) {
try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, values, data) returns (
bytes4 response
) {
if (response != IERC1155Receiver.onERC1155BatchReceived.selector) {
// Tokens rejected
revert ERC1155InvalidReceiver(to);
}
} catch (bytes memory reason) {
if (reason.length == 0) {
// non-ERC1155Receiver implementer
revert ERC1155InvalidReceiver(to);
} else {
/// @solidity memory-safe-assembly
assembly {
revert(add(32, reason), mload(reason))
}
}
}
}
}
/**
* @dev Creates an array in memory with only one value for each of the elements provided.
*/
function _asSingletonArrays(
uint256 element1,
uint256 element2
) private pure returns (uint256[] memory array1, uint256[] memory array2) {
/// @solidity memory-safe-assembly
assembly {
// Load the free memory pointer
array1 := mload(0x40)
// Set array length to 1
mstore(array1, 1)
// Store the single element at the next word after the length (where content starts)
mstore(add(array1, 0x20), element1)
// Repeat for next array locating it right after the first array
array2 := add(array1, 0x40)
mstore(array2, 1)
mstore(add(array2, 0x20), element2)
// Update the free memory pointer by pointing after the second array
mstore(0x40, add(array2, 0x40))
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/ERC721.sol)
pragma solidity ^0.8.20;
import {IERC721} from "./IERC721.sol";
import {IERC721Receiver} from "./IERC721Receiver.sol";
import {IERC721Metadata} from "./extensions/IERC721Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {Strings} from "../../utils/Strings.sol";
import {IERC165, ERC165} from "../../utils/introspection/ERC165.sol";
import {IERC721Errors} from "../../interfaces/draft-IERC6093.sol";
/**
* @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
* the Metadata extension, but not including the Enumerable extension, which is available separately as
* {ERC721Enumerable}.
*/
abstract contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Errors {
using Strings for uint256;
// Token name
string private _name;
// Token symbol
string private _symbol;
mapping(uint256 tokenId => address) private _owners;
mapping(address owner => uint256) private _balances;
mapping(uint256 tokenId => address) private _tokenApprovals;
mapping(address owner => mapping(address operator => bool)) private _operatorApprovals;
/**
* @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return
interfaceId == type(IERC721).interfaceId ||
interfaceId == type(IERC721Metadata).interfaceId ||
super.supportsInterface(interfaceId);
}
/**
* @dev See {IERC721-balanceOf}.
*/
function balanceOf(address owner) public view virtual returns (uint256) {
if (owner == address(0)) {
revert ERC721InvalidOwner(address(0));
}
return _balances[owner];
}
/**
* @dev See {IERC721-ownerOf}.
*/
function ownerOf(uint256 tokenId) public view virtual returns (address) {
return _requireOwned(tokenId);
}
/**
* @dev See {IERC721Metadata-name}.
*/
function name() public view virtual returns (string memory) {
return _name;
}
/**
* @dev See {IERC721Metadata-symbol}.
*/
function symbol() public view virtual returns (string memory) {
return _symbol;
}
/**
* @dev See {IERC721Metadata-tokenURI}.
*/
function tokenURI(uint256 tokenId) public view virtual returns (string memory) {
_requireOwned(tokenId);
string memory baseURI = _baseURI();
return bytes(baseURI).length > 0 ? string.concat(baseURI, tokenId.toString()) : "";
}
/**
* @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
* token will be the concatenation of the `baseURI` and the `tokenId`. Empty
* by default, can be overridden in child contracts.
*/
function _baseURI() internal view virtual returns (string memory) {
return "";
}
/**
* @dev See {IERC721-approve}.
*/
function approve(address to, uint256 tokenId) public virtual {
_approve(to, tokenId, _msgSender());
}
/**
* @dev See {IERC721-getApproved}.
*/
function getApproved(uint256 tokenId) public view virtual returns (address) {
_requireOwned(tokenId);
return _getApproved(tokenId);
}
/**
* @dev See {IERC721-setApprovalForAll}.
*/
function setApprovalForAll(address operator, bool approved) public virtual {
_setApprovalForAll(_msgSender(), operator, approved);
}
/**
* @dev See {IERC721-isApprovedForAll}.
*/
function isApprovedForAll(address owner, address operator) public view virtual returns (bool) {
return _operatorApprovals[owner][operator];
}
/**
* @dev See {IERC721-transferFrom}.
*/
function transferFrom(address from, address to, uint256 tokenId) public virtual {
if (to == address(0)) {
revert ERC721InvalidReceiver(address(0));
}
// Setting an "auth" arguments enables the `_isAuthorized` check which verifies that the token exists
// (from != 0). Therefore, it is not needed to verify that the return value is not 0 here.
address previousOwner = _update(to, tokenId, _msgSender());
if (previousOwner != from) {
revert ERC721IncorrectOwner(from, tokenId, previousOwner);
}
}
/**
* @dev See {IERC721-safeTransferFrom}.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) public {
safeTransferFrom(from, to, tokenId, "");
}
/**
* @dev See {IERC721-safeTransferFrom}.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual {
transferFrom(from, to, tokenId);
_checkOnERC721Received(from, to, tokenId, data);
}
/**
* @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist
*
* IMPORTANT: Any overrides to this function that add ownership of tokens not tracked by the
* core ERC721 logic MUST be matched with the use of {_increaseBalance} to keep balances
* consistent with ownership. The invariant to preserve is that for any address `a` the value returned by
* `balanceOf(a)` must be equal to the number of tokens such that `_ownerOf(tokenId)` is `a`.
*/
function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
return _owners[tokenId];
}
/**
* @dev Returns the approved address for `tokenId`. Returns 0 if `tokenId` is not minted.
*/
function _getApproved(uint256 tokenId) internal view virtual returns (address) {
return _tokenApprovals[tokenId];
}
/**
* @dev Returns whether `spender` is allowed to manage `owner`'s tokens, or `tokenId` in
* particular (ignoring whether it is owned by `owner`).
*
* WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this
* assumption.
*/
function _isAuthorized(address owner, address spender, uint256 tokenId) internal view virtual returns (bool) {
return
spender != address(0) &&
(owner == spender || isApprovedForAll(owner, spender) || _getApproved(tokenId) == spender);
}
/**
* @dev Checks if `spender` can operate on `tokenId`, assuming the provided `owner` is the actual owner.
* Reverts if `spender` does not have approval from the provided `owner` for the given token or for all its assets
* the `spender` for the specific `tokenId`.
*
* WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this
* assumption.
*/
function _checkAuthorized(address owner, address spender, uint256 tokenId) internal view virtual {
if (!_isAuthorized(owner, spender, tokenId)) {
if (owner == address(0)) {
revert ERC721NonexistentToken(tokenId);
} else {
revert ERC721InsufficientApproval(spender, tokenId);
}
}
}
/**
* @dev Unsafe write access to the balances, used by extensions that "mint" tokens using an {ownerOf} override.
*
* NOTE: the value is limited to type(uint128).max. This protect against _balance overflow. It is unrealistic that
* a uint256 would ever overflow from increments when these increments are bounded to uint128 values.
*
* WARNING: Increasing an account's balance using this function tends to be paired with an override of the
* {_ownerOf} function to resolve the ownership of the corresponding tokens so that balances and ownership
* remain consistent with one another.
*/
function _increaseBalance(address account, uint128 value) internal virtual {
unchecked {
_balances[account] += value;
}
}
/**
* @dev Transfers `tokenId` from its current owner to `to`, or alternatively mints (or burns) if the current owner
* (or `to`) is the zero address. Returns the owner of the `tokenId` before the update.
*
* The `auth` argument is optional. If the value passed is non 0, then this function will check that
* `auth` is either the owner of the token, or approved to operate on the token (by the owner).
*
* Emits a {Transfer} event.
*
* NOTE: If overriding this function in a way that tracks balances, see also {_increaseBalance}.
*/
function _update(address to, uint256 tokenId, address auth) internal virtual returns (address) {
address from = _ownerOf(tokenId);
// Perform (optional) operator check
if (auth != address(0)) {
_checkAuthorized(from, auth, tokenId);
}
// Execute the update
if (from != address(0)) {
// Clear approval. No need to re-authorize or emit the Approval event
_approve(address(0), tokenId, address(0), false);
unchecked {
_balances[from] -= 1;
}
}
if (to != address(0)) {
unchecked {
_balances[to] += 1;
}
}
_owners[tokenId] = to;
emit Transfer(from, to, tokenId);
return from;
}
/**
* @dev Mints `tokenId` and transfers it to `to`.
*
* WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
*
* Requirements:
*
* - `tokenId` must not exist.
* - `to` cannot be the zero address.
*
* Emits a {Transfer} event.
*/
function _mint(address to, uint256 tokenId) internal {
if (to == address(0)) {
revert ERC721InvalidReceiver(address(0));
}
address previousOwner = _update(to, tokenId, address(0));
if (previousOwner != address(0)) {
revert ERC721InvalidSender(address(0));
}
}
/**
* @dev Mints `tokenId`, transfers it to `to` and checks for `to` acceptance.
*
* Requirements:
*
* - `tokenId` must not exist.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function _safeMint(address to, uint256 tokenId) internal {
_safeMint(to, tokenId, "");
}
/**
* @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
* forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
*/
function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual {
_mint(to, tokenId);
_checkOnERC721Received(address(0), to, tokenId, data);
}
/**
* @dev Destroys `tokenId`.
* The approval is cleared when the token is burned.
* This is an internal function that does not check if the sender is authorized to operate on the token.
*
* Requirements:
*
* - `tokenId` must exist.
*
* Emits a {Transfer} event.
*/
function _burn(uint256 tokenId) internal {
address previousOwner = _update(address(0), tokenId, address(0));
if (previousOwner == address(0)) {
revert ERC721NonexistentToken(tokenId);
}
}
/**
* @dev Transfers `tokenId` from `from` to `to`.
* As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
*
* Emits a {Transfer} event.
*/
function _transfer(address from, address to, uint256 tokenId) internal {
if (to == address(0)) {
revert ERC721InvalidReceiver(address(0));
}
address previousOwner = _update(to, tokenId, address(0));
if (previousOwner == address(0)) {
revert ERC721NonexistentToken(tokenId);
} else if (previousOwner != from) {
revert ERC721IncorrectOwner(from, tokenId, previousOwner);
}
}
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking that contract recipients
* are aware of the ERC721 standard to prevent tokens from being forever locked.
*
* `data` is additional data, it has no specified format and it is sent in call to `to`.
*
* This internal function is like {safeTransferFrom} in the sense that it invokes
* {IERC721Receiver-onERC721Received} on the receiver, and can be used to e.g.
* implement alternative mechanisms to perform token transfer, such as signature-based.
*
* Requirements:
*
* - `tokenId` token must exist and be owned by `from`.
* - `to` cannot be the zero address.
* - `from` cannot be the zero address.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function _safeTransfer(address from, address to, uint256 tokenId) internal {
_safeTransfer(from, to, tokenId, "");
}
/**
* @dev Same as {xref-ERC721-_safeTransfer-address-address-uint256-}[`_safeTransfer`], with an additional `data` parameter which is
* forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
*/
function _safeTransfer(address from, address to, uint256 tokenId, bytes memory data) internal virtual {
_transfer(from, to, tokenId);
_checkOnERC721Received(from, to, tokenId, data);
}
/**
* @dev Approve `to` to operate on `tokenId`
*
* The `auth` argument is optional. If the value passed is non 0, then this function will check that `auth` is
* either the owner of the token, or approved to operate on all tokens held by this owner.
*
* Emits an {Approval} event.
*
* Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
*/
function _approve(address to, uint256 tokenId, address auth) internal {
_approve(to, tokenId, auth, true);
}
/**
* @dev Variant of `_approve` with an optional flag to enable or disable the {Approval} event. The event is not
* emitted in the context of transfers.
*/
function _approve(address to, uint256 tokenId, address auth, bool emitEvent) internal virtual {
// Avoid reading the owner unless necessary
if (emitEvent || auth != address(0)) {
address owner = _requireOwned(tokenId);
// We do not use _isAuthorized because single-token approvals should not be able to call approve
if (auth != address(0) && owner != auth && !isApprovedForAll(owner, auth)) {
revert ERC721InvalidApprover(auth);
}
if (emitEvent) {
emit Approval(owner, to, tokenId);
}
}
_tokenApprovals[tokenId] = to;
}
/**
* @dev Approve `operator` to operate on all of `owner` tokens
*
* Requirements:
* - operator can't be the address zero.
*
* Emits an {ApprovalForAll} event.
*/
function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {
if (operator == address(0)) {
revert ERC721InvalidOperator(operator);
}
_operatorApprovals[owner][operator] = approved;
emit ApprovalForAll(owner, operator, approved);
}
/**
* @dev Reverts if the `tokenId` doesn't have a current owner (it hasn't been minted, or it has been burned).
* Returns the owner.
*
* Overrides to ownership logic should be done to {_ownerOf}.
*/
function _requireOwned(uint256 tokenId) internal view returns (address) {
address owner = _ownerOf(tokenId);
if (owner == address(0)) {
revert ERC721NonexistentToken(tokenId);
}
return owner;
}
/**
* @dev Private function to invoke {IERC721Receiver-onERC721Received} on a target address. This will revert if the
* recipient doesn't accept the token transfer. The call is not executed if the target address is not a contract.
*
* @param from address representing the previous owner of the given token ID
* @param to target address that will receive the tokens
* @param tokenId uint256 ID of the token to be transferred
* @param data bytes optional data to send along with the call
*/
function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory data) private {
if (to.code.length > 0) {
try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
if (retval != IERC721Receiver.onERC721Received.selector) {
revert ERC721InvalidReceiver(to);
}
} catch (bytes memory reason) {
if (reason.length == 0) {
revert ERC721InvalidReceiver(to);
} else {
/// @solidity memory-safe-assembly
assembly {
revert(add(32, reason), mload(reason))
}
}
}
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)
pragma solidity ^0.8.20;
import {IAccessControl} from "./IAccessControl.sol";
import {Context} from "../utils/Context.sol";
import {ERC165} from "../utils/introspection/ERC165.sol";
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms. This is a lightweight version that doesn't allow enumerating role
* members except through off-chain means by accessing the contract event logs. Some
* applications may benefit from on-chain enumerability, for those cases see
* {AccessControlEnumerable}.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```solidity
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```solidity
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
* to enforce additional security measures for this role.
*/
abstract contract AccessControl is Context, IAccessControl, ERC165 {
struct RoleData {
mapping(address account => bool) hasRole;
bytes32 adminRole;
}
mapping(bytes32 role => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with an {AccessControlUnauthorizedAccount} error including the required role.
*/
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view virtual returns (bool) {
return _roles[role].hasRole[account];
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
* is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
* is missing `role`.
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert AccessControlUnauthorizedAccount(account, role);
}
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
return _roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleGranted} event.
*/
function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleRevoked} event.
*/
function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been revoked `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `callerConfirmation`.
*
* May emit a {RoleRevoked} event.
*/
function renounceRole(bytes32 role, address callerConfirmation) public virtual {
if (callerConfirmation != _msgSender()) {
revert AccessControlBadConfirmation();
}
_revokeRole(role, callerConfirmation);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = getRoleAdmin(role);
_roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
*
* Internal function without access restriction.
*
* May emit a {RoleGranted} event.
*/
function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
if (!hasRole(role, account)) {
_roles[role].hasRole[account] = true;
emit RoleGranted(role, account, _msgSender());
return true;
} else {
return false;
}
}
/**
* @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
*
* Internal function without access restriction.
*
* May emit a {RoleRevoked} event.
*/
function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
if (hasRole(role, account)) {
_roles[role].hasRole[account] = false;
emit RoleRevoked(role, account, _msgSender());
return true;
} else {
return false;
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/ERC721Enumerable.sol)
pragma solidity ^0.8.20;
import {ERC721Upgradeable} from "../ERC721Upgradeable.sol";
import {IERC721Enumerable} from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {Initializable} from "../../../proxy/utils/Initializable.sol";
/**
* @dev This implements an optional extension of {ERC721} defined in the EIP that adds enumerability
* of all the token ids in the contract as well as all token ids owned by each account.
*
* CAUTION: `ERC721` extensions that implement custom `balanceOf` logic, such as `ERC721Consecutive`,
* interfere with enumerability and should not be used together with `ERC721Enumerable`.
*/
abstract contract ERC721EnumerableUpgradeable is Initializable, ERC721Upgradeable, IERC721Enumerable {
/// @custom:storage-location erc7201:openzeppelin.storage.ERC721Enumerable
struct ERC721EnumerableStorage {
mapping(address owner => mapping(uint256 index => uint256)) _ownedTokens;
mapping(uint256 tokenId => uint256) _ownedTokensIndex;
uint256[] _allTokens;
mapping(uint256 tokenId => uint256) _allTokensIndex;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ERC721Enumerable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant ERC721EnumerableStorageLocation = 0x645e039705490088daad89bae25049a34f4a9072d398537b1ab2425f24cbed00;
function _getERC721EnumerableStorage() private pure returns (ERC721EnumerableStorage storage $) {
assembly {
$.slot := ERC721EnumerableStorageLocation
}
}
/**
* @dev An `owner`'s token query was out of bounds for `index`.
*
* NOTE: The owner being `address(0)` indicates a global out of bounds index.
*/
error ERC721OutOfBoundsIndex(address owner, uint256 index);
/**
* @dev Batch mint is not allowed.
*/
error ERC721EnumerableForbiddenBatchMint();
function __ERC721Enumerable_init() internal onlyInitializing {
}
function __ERC721Enumerable_init_unchained() internal onlyInitializing {
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC721Upgradeable) returns (bool) {
return interfaceId == type(IERC721Enumerable).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.
*/
function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual returns (uint256) {
ERC721EnumerableStorage storage $ = _getERC721EnumerableStorage();
if (index >= balanceOf(owner)) {
revert ERC721OutOfBoundsIndex(owner, index);
}
return $._ownedTokens[owner][index];
}
/**
* @dev See {IERC721Enumerable-totalSupply}.
*/
function totalSupply() public view virtual returns (uint256) {
ERC721EnumerableStorage storage $ = _getERC721EnumerableStorage();
return $._allTokens.length;
}
/**
* @dev See {IERC721Enumerable-tokenByIndex}.
*/
function tokenByIndex(uint256 index) public view virtual returns (uint256) {
ERC721EnumerableStorage storage $ = _getERC721EnumerableStorage();
if (index >= totalSupply()) {
revert ERC721OutOfBoundsIndex(address(0), index);
}
return $._allTokens[index];
}
/**
* @dev See {ERC721-_update}.
*/
function _update(address to, uint256 tokenId, address auth) internal virtual override returns (address) {
address previousOwner = super._update(to, tokenId, auth);
if (previousOwner == address(0)) {
_addTokenToAllTokensEnumeration(tokenId);
} else if (previousOwner != to) {
_removeTokenFromOwnerEnumeration(previousOwner, tokenId);
}
if (to == address(0)) {
_removeTokenFromAllTokensEnumeration(tokenId);
} else if (previousOwner != to) {
_addTokenToOwnerEnumeration(to, tokenId);
}
return previousOwner;
}
/**
* @dev Private function to add a token to this extension's ownership-tracking data structures.
* @param to address representing the new owner of the given token ID
* @param tokenId uint256 ID of the token to be added to the tokens list of the given address
*/
function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
ERC721EnumerableStorage storage $ = _getERC721EnumerableStorage();
uint256 length = balanceOf(to) - 1;
$._ownedTokens[to][length] = tokenId;
$._ownedTokensIndex[tokenId] = length;
}
/**
* @dev Private function to add a token to this extension's token tracking data structures.
* @param tokenId uint256 ID of the token to be added to the tokens list
*/
function _addTokenToAllTokensEnumeration(uint256 tokenId) private {
ERC721EnumerableStorage storage $ = _getERC721EnumerableStorage();
$._allTokensIndex[tokenId] = $._allTokens.length;
$._allTokens.push(tokenId);
}
/**
* @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that
* while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for
* gas optimizations e.g. when performing a transfer operation (avoiding double writes).
* This has O(1) time complexity, but alters the order of the _ownedTokens array.
* @param from address representing the previous owner of the given token ID
* @param tokenId uint256 ID of the token to be removed from the tokens list of the given address
*/
function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private {
ERC721EnumerableStorage storage $ = _getERC721EnumerableStorage();
// To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and
// then delete the last slot (swap and pop).
uint256 lastTokenIndex = balanceOf(from);
uint256 tokenIndex = $._ownedTokensIndex[tokenId];
// When the token to delete is the last token, the swap operation is unnecessary
if (tokenIndex != lastTokenIndex) {
uint256 lastTokenId = $._ownedTokens[from][lastTokenIndex];
$._ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
$._ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
}
// This also deletes the contents at the last position of the array
delete $._ownedTokensIndex[tokenId];
delete $._ownedTokens[from][lastTokenIndex];
}
/**
* @dev Private function to remove a token from this extension's token tracking data structures.
* This has O(1) time complexity, but alters the order of the _allTokens array.
* @param tokenId uint256 ID of the token to be removed from the tokens list
*/
function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private {
ERC721EnumerableStorage storage $ = _getERC721EnumerableStorage();
// To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and
// then delete the last slot (swap and pop).
uint256 lastTokenIndex = $._allTokens.length - 1;
uint256 tokenIndex = $._allTokensIndex[tokenId];
// When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so
// rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding
// an 'if' statement (like in _removeTokenFromOwnerEnumeration)
uint256 lastTokenId = $._allTokens[lastTokenIndex];
$._allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
$._allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
// This also deletes the contents at the last position of the array
delete $._allTokensIndex[tokenId];
$._allTokens.pop();
}
/**
* See {ERC721-_increaseBalance}. We need that to account tokens that were minted in batch
*/
function _increaseBalance(address account, uint128 amount) internal virtual override {
if (amount > 0) {
revert ERC721EnumerableForbiddenBatchMint();
}
super._increaseBalance(account, amount);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/utils/ERC721Holder.sol)
pragma solidity ^0.8.20;
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import {Initializable} from "../../../proxy/utils/Initializable.sol";
/**
* @dev Implementation of the {IERC721Receiver} interface.
*
* Accepts all token transfers.
* Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or
* {IERC721-setApprovalForAll}.
*/
abstract contract ERC721HolderUpgradeable is Initializable, IERC721Receiver {
function __ERC721Holder_init() internal onlyInitializing {
}
function __ERC721Holder_init_unchained() internal onlyInitializing {
}
/**
* @dev See {IERC721Receiver-onERC721Received}.
*
* Always returns `IERC721Receiver.onERC721Received.selector`.
*/
function onERC721Received(address, address, uint256, bytes memory) public virtual returns (bytes4) {
return this.onERC721Received.selector;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)
pragma solidity ^0.8.20;
import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {ERC165Upgradeable} from "../utils/introspection/ERC165Upgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.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 AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControl, ERC165Upgradeable {
struct RoleData {
mapping(address account => bool) hasRole;
bytes32 adminRole;
}
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/// @custom:storage-location erc7201:openzeppelin.storage.AccessControl
struct AccessControlStorage {
mapping(bytes32 role => RoleData) _roles;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.AccessControl")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant AccessControlStorageLocation = 0x02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800;
function _getAccessControlStorage() private pure returns (AccessControlStorage storage $) {
assembly {
$.slot := AccessControlStorageLocation
}
}
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with an {AccessControlUnauthorizedAccount} error including the required role.
*/
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
function __AccessControl_init() internal onlyInitializing {
}
function __AccessControl_init_unchained() internal onlyInitializing {
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view virtual returns (bool) {
AccessControlStorage storage $ = _getAccessControlStorage();
return $._roles[role].hasRole[account];
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
* is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
* is missing `role`.
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert AccessControlUnauthorizedAccount(account, role);
}
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
AccessControlStorage storage $ = _getAccessControlStorage();
return $._roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleGranted} event.
*/
function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleRevoked} event.
*/
function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been revoked `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `callerConfirmation`.
*
* May emit a {RoleRevoked} event.
*/
function renounceRole(bytes32 role, address callerConfirmation) public virtual {
if (callerConfirmation != _msgSender()) {
revert AccessControlBadConfirmation();
}
_revokeRole(role, callerConfirmation);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
AccessControlStorage storage $ = _getAccessControlStorage();
bytes32 previousAdminRole = getRoleAdmin(role);
$._roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
*
* Internal function without access restriction.
*
* May emit a {RoleGranted} event.
*/
function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
AccessControlStorage storage $ = _getAccessControlStorage();
if (!hasRole(role, account)) {
$._roles[role].hasRole[account] = true;
emit RoleGranted(role, account, _msgSender());
return true;
} else {
return false;
}
}
/**
* @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
*
* Internal function without access restriction.
*
* May emit a {RoleRevoked} event.
*/
function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
AccessControlStorage storage $ = _getAccessControlStorage();
if (hasRole(role, account)) {
$._roles[role].hasRole[account] = false;
emit RoleRevoked(role, account, _msgSender());
return true;
} else {
return false;
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol)
pragma solidity ^0.8.20;
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
/// @custom:storage-location erc7201:openzeppelin.storage.Pausable
struct PausableStorage {
bool _paused;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Pausable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant PausableStorageLocation = 0xcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300;
function _getPausableStorage() private pure returns (PausableStorage storage $) {
assembly {
$.slot := PausableStorageLocation
}
}
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
/**
* @dev The operation failed because the contract is paused.
*/
error EnforcedPause();
/**
* @dev The operation failed because the contract is not paused.
*/
error ExpectedPause();
/**
* @dev Initializes the contract in unpaused state.
*/
function __Pausable_init() internal onlyInitializing {
__Pausable_init_unchained();
}
function __Pausable_init_unchained() internal onlyInitializing {
PausableStorage storage $ = _getPausableStorage();
$._paused = false;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
PausableStorage storage $ = _getPausableStorage();
return $._paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
if (paused()) {
revert EnforcedPause();
}
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
if (!paused()) {
revert ExpectedPause();
}
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
PausableStorage storage $ = _getPausableStorage();
$._paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
PausableStorage storage $ = _getPausableStorage();
$._paused = false;
emit Unpaused(_msgSender());
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
import "../trailblazers-badges/TrailblazersBadgesV3.sol";
import "./BadgeRecruitment.sol";
contract TrailblazersBadgesV4 is TrailblazersBadgesV3 {
/// @notice Duration for which a s1 badge is locked after recruitment is started
uint256 public recruitmentLockDuration;
/// @notice BadgeRecruitment contract
BadgeRecruitment public recruitmentContract;
/// @notice Mapping of badge token id to unlock timestamp
mapping(uint256 tokenId => uint256 unlockTimestamp) public unlockTimestamps;
/// @notice Errors
error BADGE_LOCKED();
error RECRUITMENT_LOCK_DURATION_NOT_SET();
/// @notice Updated version function
/// @return Version string
function version() external pure virtual override returns (string memory) {
return "V4";
}
/// @notice Overwritten update function that prevents locked badges from being transferred
/// @param to Address to transfer badge to
/// @param tokenId Badge token id
/// @param auth Address to authorize transfer
/// @return Address of the recipient
function _update(
address to,
uint256 tokenId,
address auth
)
internal
virtual
override
returns (address)
{
if (unlockTimestamps[tokenId] > block.timestamp) {
revert BADGE_LOCKED();
}
return super._update(to, tokenId, auth);
}
/// @notice Set recruitment contract
/// @param _recruitmentContract Address of the recruitment contract
/// @dev Only owner
function setRecruitmentContract(address _recruitmentContract) public onlyOwner {
recruitmentContract = BadgeRecruitment(_recruitmentContract);
}
/// @notice Set recruitment lock duration
/// @param _duration Duration in seconds
/// @dev Only owner
function setRecruitmentLockDuration(uint256 _duration) public onlyOwner {
recruitmentLockDuration = _duration;
}
/// @notice Start recruitment for a badge
/// @param _badgeId Badge id
function startRecruitment(uint256 _badgeId) public virtual {
if (recruitmentLockDuration == 0) {
revert RECRUITMENT_LOCK_DURATION_NOT_SET();
}
uint256 tokenId = getTokenId(_msgSender(), _badgeId);
unlockTimestamps[tokenId] = block.timestamp + recruitmentLockDuration;
recruitmentContract.startRecruitment(_msgSender(), _badgeId);
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
import "@openzeppelin/contracts-upgradeable/token/ERC1155/extensions/ERC1155SupplyUpgradeable.sol";
import "../trailblazers-badges/ECDSAWhitelist.sol";
import "@taiko/blacklist/IMinimalBlacklist.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC1155/ERC1155Upgradeable.sol";
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Pausable.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/utils/ERC721HolderUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
import "./TrailblazersS1BadgesV4.sol";
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
contract TrailblazersBadgesS2 is
ContextUpgradeable,
UUPSUpgradeable,
Ownable2StepUpgradeable,
AccessControlUpgradeable,
ERC1155SupplyUpgradeable
{
/// @notice Badge types
enum BadgeType {
Ravers, // s1 id: 0
Robots, // s1 id: 1
Bouncers, // s1 id: 2
Masters, // s1 id: 3
Monks, // s1 id: 4
Androids, // s1 id: 5
Drummers, // s1 id: 6
Shinto // s1 id: 7
}
/// @notice Movement types
enum MovementType {
Undefined, // unused
Whale, // s1 based/pink
Minnow // s1 boosted/purple
}
/// @notice Badge struct
struct Badge {
uint256 tokenId;
BadgeType badgeType;
MovementType movementType;
}
/// @notice Badge mapping
mapping(uint256 tokenId => Badge badge) private badges;
/// @notice User, Badge, and Movement relation to tokenId
mapping(
address user
=> mapping(BadgeType badgeType => mapping(MovementType movementType => uint256 tokenId))
) private userBadges;
/// @notice Badge URI template
string public uriTemplate;
/// @notice Minter address; BadgeMigration contract
address public minter;
/// @notice Minter role
bytes32 public constant MINTER_ROLE = keccak256("MINTER");
/// @notice Gap for upgrade safety
uint256[43] private __gap;
/// @notice Errors
error NOT_MINTER();
error TOKEN_NOT_MINTED();
/// @notice Initialize the contract
/// @param _minter The minter address
/// @param _uriTemplate The badge URI template
function initialize(
address _minter,
string calldata _uriTemplate
)
external
virtual
initializer
{
__ERC1155_init("");
__ERC1155Supply_init();
_transferOwnership(_msgSender());
__Context_init();
_grantRole(DEFAULT_ADMIN_ROLE, _msgSender());
_grantRole(MINTER_ROLE, _minter);
minter = _minter;
uriTemplate = _uriTemplate;
}
/// @notice Set the minter address
/// @param _minter The minter address
/// @dev Only the owner can call this function
function setMinter(address _minter) external virtual onlyRole(DEFAULT_ADMIN_ROLE) {
minter = _minter;
_grantRole(MINTER_ROLE, _minter);
}
/// @notice Mint a badge
/// @param _to The address to mint the badge to
/// @param _badgeType The badge type
/// @param _movementType The movement type
/// @dev Only the minter can call this function
function mint(
address _to,
BadgeType _badgeType,
MovementType _movementType
)
external
virtual
onlyRole(MINTER_ROLE)
{
uint256 tokenId_ = totalSupply() + 1;
Badge memory badge_ = Badge(tokenId_, _badgeType, _movementType);
_mint(_to, tokenId_, 1, "");
badges[tokenId_] = badge_;
}
/// @notice Internal method to assemble URIs
/// @param _badgeType The badge type
/// @param _movementType The movement type
/// @return The URI
function _uri(
BadgeType _badgeType,
MovementType _movementType
)
internal
view
virtual
returns (string memory)
{
string memory badgeType_ = Strings.toString(uint256(_badgeType));
string memory movementType_ = Strings.toString(uint256(_movementType));
return string(abi.encodePacked(uriTemplate, "/", movementType_, "/", badgeType_));
}
/// @notice Retrieve the URI for a badge given the type & movement
/// @param _badgeType The badge type
/// @param _movementType The movement type
/// @return The URI
function uri(
BadgeType _badgeType,
MovementType _movementType
)
external
view
virtual
returns (string memory)
{
return _uri(_badgeType, _movementType);
}
/// @notice Retrieve the URI for a badge given the token ID
/// @param _tokenId The token ID
/// @return The URI
function uri(uint256 _tokenId) public view virtual override returns (string memory) {
if (_tokenId > totalSupply()) {
revert TOKEN_NOT_MINTED();
}
Badge memory badge_ = badges[_tokenId];
return _uri(badge_.badgeType, badge_.movementType);
}
/// @notice Retrieve a badge
/// @param _tokenId The token ID
/// @return The badge
function getBadge(uint256 _tokenId) external view virtual returns (Badge memory) {
if (_tokenId < totalSupply()) {
revert TOKEN_NOT_MINTED();
}
return badges[_tokenId];
}
/// @notice supportsInterface implementation
/// @param _interfaceId The interface ID
/// @return Whether the interface is supported
function supportsInterface(bytes4 _interfaceId)
public
view
virtual
override(AccessControlUpgradeable, ERC1155Upgradeable)
returns (bool)
{
return super.supportsInterface(_interfaceId);
}
/// @notice Internal method to authorize an upgrade
function _authorizeUpgrade(address) internal virtual override onlyOwner { }
// v2
function version() public pure returns (string memory) {
return "v2";
}
function setUri(string memory __uri) public onlyRole(DEFAULT_ADMIN_ROLE) {
uriTemplate = __uri;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.20;
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Storage of the initializable contract.
*
* It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
* when using with upgradeable contracts.
*
* @custom:storage-location erc7201:openzeppelin.storage.Initializable
*/
struct InitializableStorage {
/**
* @dev Indicates that the contract has been initialized.
*/
uint64 _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool _initializing;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
/**
* @dev The contract is already initialized.
*/
error InvalidInitialization();
/**
* @dev The contract is not initializing.
*/
error NotInitializing();
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint64 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
* number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
* production.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
// Cache values to avoid duplicated sloads
bool isTopLevelCall = !$._initializing;
uint64 initialized = $._initialized;
// Allowed calls:
// - initialSetup: the contract is not in the initializing state and no previous version was
// initialized
// - construction: the contract is initialized at version 1 (no reininitialization) and the
// current contract is just being deployed
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;
if (!initialSetup && !construction) {
revert InvalidInitialization();
}
$._initialized = 1;
if (isTopLevelCall) {
$._initializing = true;
}
_;
if (isTopLevelCall) {
$._initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint64 version) {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing || $._initialized >= version) {
revert InvalidInitialization();
}
$._initialized = version;
$._initializing = true;
_;
$._initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
_checkInitializing();
_;
}
/**
* @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
*/
function _checkInitializing() internal view virtual {
if (!_isInitializing()) {
revert NotInitializing();
}
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing) {
revert InvalidInitialization();
}
if ($._initialized != type(uint64).max) {
$._initialized = type(uint64).max;
emit Initialized(type(uint64).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint64) {
return _getInitializableStorage()._initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _getInitializableStorage()._initializing;
}
/**
* @dev Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
assembly {
$.slot := INITIALIZABLE_STORAGE
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Muldiv operation overflow.
*/
error MathOverflowedMulDiv();
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @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 towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
return a / b;
}
// (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 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^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 (unsignedRoundsUp(rounding) && 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
* towards zero.
*
* 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 + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
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 + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
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 + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.20;
/**
* @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 v5.0.0) (interfaces/draft-IERC1822.sol)
pragma solidity ^0.8.20;
/**
* @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
* proxy whose upgrades are fully controlled by the current implementation.
*/
interface IERC1822Proxiable {
/**
* @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
* address.
*
* IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
* bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
* function revert if invoked through a proxy.
*/
function proxiableUUID() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/ERC1967/ERC1967Utils.sol)
pragma solidity ^0.8.20;
import {IBeacon} from "../beacon/IBeacon.sol";
import {Address} from "../../utils/Address.sol";
import {StorageSlot} from "../../utils/StorageSlot.sol";
/**
* @dev This abstract contract provides getters and event emitting update functions for
* https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
*/
library ERC1967Utils {
// We re-declare ERC-1967 events here because they can't be used directly from IERC1967.
// This will be fixed in Solidity 0.8.21. At that point we should remove these events.
/**
* @dev Emitted when the implementation is upgraded.
*/
event Upgraded(address indexed implementation);
/**
* @dev Emitted when the admin account has changed.
*/
event AdminChanged(address previousAdmin, address newAdmin);
/**
* @dev Emitted when the beacon is changed.
*/
event BeaconUpgraded(address indexed beacon);
/**
* @dev Storage slot with the address of the current implementation.
* This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/**
* @dev The `implementation` of the proxy is invalid.
*/
error ERC1967InvalidImplementation(address implementation);
/**
* @dev The `admin` of the proxy is invalid.
*/
error ERC1967InvalidAdmin(address admin);
/**
* @dev The `beacon` of the proxy is invalid.
*/
error ERC1967InvalidBeacon(address beacon);
/**
* @dev An upgrade function sees `msg.value > 0` that may be lost.
*/
error ERC1967NonPayable();
/**
* @dev Returns the current implementation address.
*/
function getImplementation() internal view returns (address) {
return StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 implementation slot.
*/
function _setImplementation(address newImplementation) private {
if (newImplementation.code.length == 0) {
revert ERC1967InvalidImplementation(newImplementation);
}
StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation;
}
/**
* @dev Performs implementation upgrade with additional setup call if data is nonempty.
* This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
* to avoid stuck value in the contract.
*
* Emits an {IERC1967-Upgraded} event.
*/
function upgradeToAndCall(address newImplementation, bytes memory data) internal {
_setImplementation(newImplementation);
emit Upgraded(newImplementation);
if (data.length > 0) {
Address.functionDelegateCall(newImplementation, data);
} else {
_checkNonPayable();
}
}
/**
* @dev Storage slot with the admin of the contract.
* This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
/**
* @dev Returns the current admin.
*
* TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using
* the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
* `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
*/
function getAdmin() internal view returns (address) {
return StorageSlot.getAddressSlot(ADMIN_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 admin slot.
*/
function _setAdmin(address newAdmin) private {
if (newAdmin == address(0)) {
revert ERC1967InvalidAdmin(address(0));
}
StorageSlot.getAddressSlot(ADMIN_SLOT).value = newAdmin;
}
/**
* @dev Changes the admin of the proxy.
*
* Emits an {IERC1967-AdminChanged} event.
*/
function changeAdmin(address newAdmin) internal {
emit AdminChanged(getAdmin(), newAdmin);
_setAdmin(newAdmin);
}
/**
* @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
* This is the keccak-256 hash of "eip1967.proxy.beacon" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
/**
* @dev Returns the current beacon.
*/
function getBeacon() internal view returns (address) {
return StorageSlot.getAddressSlot(BEACON_SLOT).value;
}
/**
* @dev Stores a new beacon in the EIP1967 beacon slot.
*/
function _setBeacon(address newBeacon) private {
if (newBeacon.code.length == 0) {
revert ERC1967InvalidBeacon(newBeacon);
}
StorageSlot.getAddressSlot(BEACON_SLOT).value = newBeacon;
address beaconImplementation = IBeacon(newBeacon).implementation();
if (beaconImplementation.code.length == 0) {
revert ERC1967InvalidImplementation(beaconImplementation);
}
}
/**
* @dev Change the beacon and trigger a setup call if data is nonempty.
* This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
* to avoid stuck value in the contract.
*
* Emits an {IERC1967-BeaconUpgraded} event.
*
* CAUTION: Invoking this function has no effect on an instance of {BeaconProxy} since v5, since
* it uses an immutable beacon without looking at the value of the ERC-1967 beacon slot for
* efficiency.
*/
function upgradeBeaconToAndCall(address newBeacon, bytes memory data) internal {
_setBeacon(newBeacon);
emit BeaconUpgraded(newBeacon);
if (data.length > 0) {
Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
} else {
_checkNonPayable();
}
}
/**
* @dev Reverts if `msg.value` is not zero. It can be used to avoid `msg.value` stuck in the contract
* if an upgrade doesn't perform an initialization call.
*/
function _checkNonPayable() private {
if (msg.value > 0) {
revert ERC1967NonPayable();
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
/// @custom:storage-location erc7201:openzeppelin.storage.Ownable
struct OwnableStorage {
address _owner;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant OwnableStorageLocation = 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300;
function _getOwnableStorage() private pure returns (OwnableStorage storage $) {
assembly {
$.slot := OwnableStorageLocation
}
}
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
function __Ownable_init(address initialOwner) internal onlyInitializing {
__Ownable_init_unchained(initialOwner);
}
function __Ownable_init_unchained(address initialOwner) internal onlyInitializing {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
OwnableStorage storage $ = _getOwnableStorage();
return $._owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
OwnableStorage storage $ = _getOwnableStorage();
address oldOwner = $._owner;
$._owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (token/ERC1155/IERC1155.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC1155 compliant contract, as defined in the
* https://eips.ethereum.org/EIPS/eip-1155[EIP].
*/
interface IERC1155 is IERC165 {
/**
* @dev Emitted when `value` amount of tokens of type `id` are transferred from `from` to `to` by `operator`.
*/
event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
/**
* @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
* transfers.
*/
event TransferBatch(
address indexed operator,
address indexed from,
address indexed to,
uint256[] ids,
uint256[] values
);
/**
* @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
* `approved`.
*/
event ApprovalForAll(address indexed account, address indexed operator, bool approved);
/**
* @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
*
* If an {URI} event was emitted for `id`, the standard
* https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
* returned by {IERC1155MetadataURI-uri}.
*/
event URI(string value, uint256 indexed id);
/**
* @dev Returns the value of tokens of token type `id` owned by `account`.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function balanceOf(address account, uint256 id) external view returns (uint256);
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
*
* Requirements:
*
* - `accounts` and `ids` must have the same length.
*/
function balanceOfBatch(
address[] calldata accounts,
uint256[] calldata ids
) external view returns (uint256[] memory);
/**
* @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
*
* Emits an {ApprovalForAll} event.
*
* Requirements:
*
* - `operator` cannot be the caller.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
*
* See {setApprovalForAll}.
*/
function isApprovedForAll(address account, address operator) external view returns (bool);
/**
* @dev Transfers a `value` amount of tokens of type `id` from `from` to `to`.
*
* WARNING: This function can potentially allow a reentrancy attack when transferring tokens
* to an untrusted contract, when invoking {onERC1155Received} on the receiver.
* Ensure to follow the checks-effects-interactions pattern and consider employing
* reentrancy guards when interacting with untrusted contracts.
*
* Emits a {TransferSingle} event.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - If the caller is not `from`, it must have been approved to spend ``from``'s tokens via {setApprovalForAll}.
* - `from` must have a balance of tokens of type `id` of at least `value` amount.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
* acceptance magic value.
*/
function safeTransferFrom(address from, address to, uint256 id, uint256 value, bytes calldata data) external;
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
*
* WARNING: This function can potentially allow a reentrancy attack when transferring tokens
* to an untrusted contract, when invoking {onERC1155BatchReceived} on the receiver.
* Ensure to follow the checks-effects-interactions pattern and consider employing
* reentrancy guards when interacting with untrusted contracts.
*
* Emits either a {TransferSingle} or a {TransferBatch} event, depending on the length of the array arguments.
*
* Requirements:
*
* - `ids` and `values` must have the same length.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
* acceptance magic value.
*/
function safeBatchTransferFrom(
address from,
address to,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC1155/IERC1155Receiver.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
/**
* @dev Interface that must be implemented by smart contracts in order to receive
* ERC-1155 token transfers.
*/
interface IERC1155Receiver is IERC165 {
/**
* @dev Handles the receipt of a single ERC1155 token type. This function is
* called at the end of a `safeTransferFrom` after the balance has been updated.
*
* NOTE: To accept the transfer, this must return
* `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
* (i.e. 0xf23a6e61, or its own function selector).
*
* @param operator The address which initiated the transfer (i.e. msg.sender)
* @param from The address which previously owned the token
* @param id The ID of the token being transferred
* @param value The amount of tokens being transferred
* @param data Additional data with no specified format
* @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
*/
function onERC1155Received(
address operator,
address from,
uint256 id,
uint256 value,
bytes calldata data
) external returns (bytes4);
/**
* @dev Handles the receipt of a multiple ERC1155 token types. This function
* is called at the end of a `safeBatchTransferFrom` after the balances have
* been updated.
*
* NOTE: To accept the transfer(s), this must return
* `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
* (i.e. 0xbc197c81, or its own function selector).
*
* @param operator The address which initiated the batch transfer (i.e. msg.sender)
* @param from The address which previously owned the token
* @param ids An array containing ids of each token being transferred (order and length must match values array)
* @param values An array containing amounts of each token being transferred (order and length must match ids array)
* @param data Additional data with no specified format
* @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
*/
function onERC1155BatchReceived(
address operator,
address from,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
) external returns (bytes4);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC1155/extensions/IERC1155MetadataURI.sol)
pragma solidity ^0.8.20;
import {IERC1155} from "../IERC1155.sol";
/**
* @dev Interface of the optional ERC1155MetadataExtension interface, as defined
* in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[EIP].
*/
interface IERC1155MetadataURI is IERC1155 {
/**
* @dev Returns the URI for token type `id`.
*
* If the `\{id\}` substring is present in the URI, it must be replaced by
* clients with the actual token type ID.
*/
function uri(uint256 id) external view returns (string memory);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @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 v5.0.0) (utils/introspection/ERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {Initializable} from "../../proxy/utils/Initializable.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);
* }
* ```
*/
abstract contract ERC165Upgradeable is Initializable, IERC165 {
function __ERC165_init() internal onlyInitializing {
}
function __ERC165_init_unchained() internal onlyInitializing {
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Arrays.sol)
pragma solidity ^0.8.20;
import {StorageSlot} from "./StorageSlot.sol";
import {Math} from "./math/Math.sol";
/**
* @dev Collection of functions related to array types.
*/
library Arrays {
using StorageSlot for bytes32;
/**
* @dev Searches a sorted `array` and returns the first index that contains
* a value greater or equal to `element`. If no such index exists (i.e. all
* values in the array are strictly less than `element`), the array length is
* returned. Time complexity O(log n).
*
* `array` is expected to be sorted in ascending order, and to contain no
* repeated elements.
*/
function findUpperBound(uint256[] storage array, uint256 element) internal view returns (uint256) {
uint256 low = 0;
uint256 high = array.length;
if (high == 0) {
return 0;
}
while (low < high) {
uint256 mid = Math.average(low, high);
// Note that mid will always be strictly less than high (i.e. it will be a valid array index)
// because Math.average rounds towards zero (it does integer division with truncation).
if (unsafeAccess(array, mid).value > element) {
high = mid;
} else {
low = mid + 1;
}
}
// At this point `low` is the exclusive upper bound. We will return the inclusive upper bound.
if (low > 0 && unsafeAccess(array, low - 1).value == element) {
return low - 1;
} else {
return low;
}
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeAccess(address[] storage arr, uint256 pos) internal pure returns (StorageSlot.AddressSlot storage) {
bytes32 slot;
// We use assembly to calculate the storage slot of the element at index `pos` of the dynamic array `arr`
// following https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays.
/// @solidity memory-safe-assembly
assembly {
mstore(0, arr.slot)
slot := add(keccak256(0, 0x20), pos)
}
return slot.getAddressSlot();
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeAccess(bytes32[] storage arr, uint256 pos) internal pure returns (StorageSlot.Bytes32Slot storage) {
bytes32 slot;
// We use assembly to calculate the storage slot of the element at index `pos` of the dynamic array `arr`
// following https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays.
/// @solidity memory-safe-assembly
assembly {
mstore(0, arr.slot)
slot := add(keccak256(0, 0x20), pos)
}
return slot.getBytes32Slot();
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeAccess(uint256[] storage arr, uint256 pos) internal pure returns (StorageSlot.Uint256Slot storage) {
bytes32 slot;
// We use assembly to calculate the storage slot of the element at index `pos` of the dynamic array `arr`
// following https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays.
/// @solidity memory-safe-assembly
assembly {
mstore(0, arr.slot)
slot := add(keccak256(0, 0x20), pos)
}
return slot.getUint256Slot();
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeMemoryAccess(uint256[] memory arr, uint256 pos) internal pure returns (uint256 res) {
assembly {
res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
}
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeMemoryAccess(address[] memory arr, uint256 pos) internal pure returns (address res) {
assembly {
res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard ERC20 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
*/
interface IERC20Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC20InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC20InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
* @param spender Address that may be allowed to operate on tokens without being their owner.
* @param allowance Amount of tokens a `spender` is allowed to operate with.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC20InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `spender` to be approved. Used in approvals.
* @param spender Address that may be allowed to operate on tokens without being their owner.
*/
error ERC20InvalidSpender(address spender);
}
/**
* @dev Standard ERC721 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
*/
interface IERC721Errors {
/**
* @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
* Used in balance queries.
* @param owner Address of the current owner of a token.
*/
error ERC721InvalidOwner(address owner);
/**
* @dev Indicates a `tokenId` whose `owner` is the zero address.
* @param tokenId Identifier number of a token.
*/
error ERC721NonexistentToken(uint256 tokenId);
/**
* @dev Indicates an error related to the ownership over a particular token. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param tokenId Identifier number of a token.
* @param owner Address of the current owner of a token.
*/
error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC721InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC721InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param tokenId Identifier number of a token.
*/
error ERC721InsufficientApproval(address operator, uint256 tokenId);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC721InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC721InvalidOperator(address operator);
}
/**
* @dev Standard ERC1155 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
*/
interface IERC1155Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
* @param tokenId Identifier number of a token.
*/
error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC1155InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC1155InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param owner Address of the current owner of a token.
*/
error ERC1155MissingApprovalForAll(address operator, address owner);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC1155InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC1155InvalidOperator(address operator);
/**
* @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
* Used in batch transfers.
* @param idsLength Length of the array of token identifiers
* @param valuesLength Length of the array of token amounts
*/
error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an 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 address zero.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721Receiver.sol)
pragma solidity ^0.8.20;
/**
* @title ERC721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC721 asset contracts.
*/
interface IERC721Receiver {
/**
* @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
* by `operator` from `from`, this function is called.
*
* It must return its Solidity selector to confirm the token transfer.
* If any other value is returned or the interface is not implemented by the recipient, the transfer will be
* reverted.
*
* The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
*/
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/IERC721Metadata.sol)
pragma solidity ^0.8.20;
import {IERC721} from "../IERC721.sol";
/**
* @title ERC-721 Non-Fungible Token Standard, optional metadata extension
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
interface IERC721Metadata is IERC721 {
/**
* @dev Returns the token collection name.
*/
function name() external view returns (string memory);
/**
* @dev Returns the token collection symbol.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
*/
function tokenURI(uint256 tokenId) external view returns (string memory);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement 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);
* }
* ```
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/IAccessControl.sol)
pragma solidity ^0.8.20;
/**
* @dev External interface of AccessControl declared to support ERC165 detection.
*/
interface IAccessControl {
/**
* @dev The `account` is missing a role.
*/
error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);
/**
* @dev The caller of a function is not the expected one.
*
* NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
*/
error AccessControlBadConfirmation();
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call, 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 `callerConfirmation`.
*/
function renounceRole(bytes32 role, address callerConfirmation) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/ERC721.sol)
pragma solidity ^0.8.20;
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import {IERC721Metadata} from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol";
import {ContextUpgradeable} from "../../utils/ContextUpgradeable.sol";
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {ERC165Upgradeable} from "../../utils/introspection/ERC165Upgradeable.sol";
import {IERC721Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";
/**
* @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
* the Metadata extension, but not including the Enumerable extension, which is available separately as
* {ERC721Enumerable}.
*/
abstract contract ERC721Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeable, IERC721, IERC721Metadata, IERC721Errors {
using Strings for uint256;
/// @custom:storage-location erc7201:openzeppelin.storage.ERC721
struct ERC721Storage {
// Token name
string _name;
// Token symbol
string _symbol;
mapping(uint256 tokenId => address) _owners;
mapping(address owner => uint256) _balances;
mapping(uint256 tokenId => address) _tokenApprovals;
mapping(address owner => mapping(address operator => bool)) _operatorApprovals;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ERC721")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant ERC721StorageLocation = 0x80bb2b638cc20bc4d0a60d66940f3ab4a00c1d7b313497ca82fb0b4ab0079300;
function _getERC721Storage() private pure returns (ERC721Storage storage $) {
assembly {
$.slot := ERC721StorageLocation
}
}
/**
* @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
*/
function __ERC721_init(string memory name_, string memory symbol_) internal onlyInitializing {
__ERC721_init_unchained(name_, symbol_);
}
function __ERC721_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {
ERC721Storage storage $ = _getERC721Storage();
$._name = name_;
$._symbol = symbol_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165Upgradeable, IERC165) returns (bool) {
return
interfaceId == type(IERC721).interfaceId ||
interfaceId == type(IERC721Metadata).interfaceId ||
super.supportsInterface(interfaceId);
}
/**
* @dev See {IERC721-balanceOf}.
*/
function balanceOf(address owner) public view virtual returns (uint256) {
ERC721Storage storage $ = _getERC721Storage();
if (owner == address(0)) {
revert ERC721InvalidOwner(address(0));
}
return $._balances[owner];
}
/**
* @dev See {IERC721-ownerOf}.
*/
function ownerOf(uint256 tokenId) public view virtual returns (address) {
return _requireOwned(tokenId);
}
/**
* @dev See {IERC721Metadata-name}.
*/
function name() public view virtual returns (string memory) {
ERC721Storage storage $ = _getERC721Storage();
return $._name;
}
/**
* @dev See {IERC721Metadata-symbol}.
*/
function symbol() public view virtual returns (string memory) {
ERC721Storage storage $ = _getERC721Storage();
return $._symbol;
}
/**
* @dev See {IERC721Metadata-tokenURI}.
*/
function tokenURI(uint256 tokenId) public view virtual returns (string memory) {
_requireOwned(tokenId);
string memory baseURI = _baseURI();
return bytes(baseURI).length > 0 ? string.concat(baseURI, tokenId.toString()) : "";
}
/**
* @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
* token will be the concatenation of the `baseURI` and the `tokenId`. Empty
* by default, can be overridden in child contracts.
*/
function _baseURI() internal view virtual returns (string memory) {
return "";
}
/**
* @dev See {IERC721-approve}.
*/
function approve(address to, uint256 tokenId) public virtual {
_approve(to, tokenId, _msgSender());
}
/**
* @dev See {IERC721-getApproved}.
*/
function getApproved(uint256 tokenId) public view virtual returns (address) {
_requireOwned(tokenId);
return _getApproved(tokenId);
}
/**
* @dev See {IERC721-setApprovalForAll}.
*/
function setApprovalForAll(address operator, bool approved) public virtual {
_setApprovalForAll(_msgSender(), operator, approved);
}
/**
* @dev See {IERC721-isApprovedForAll}.
*/
function isApprovedForAll(address owner, address operator) public view virtual returns (bool) {
ERC721Storage storage $ = _getERC721Storage();
return $._operatorApprovals[owner][operator];
}
/**
* @dev See {IERC721-transferFrom}.
*/
function transferFrom(address from, address to, uint256 tokenId) public virtual {
if (to == address(0)) {
revert ERC721InvalidReceiver(address(0));
}
// Setting an "auth" arguments enables the `_isAuthorized` check which verifies that the token exists
// (from != 0). Therefore, it is not needed to verify that the return value is not 0 here.
address previousOwner = _update(to, tokenId, _msgSender());
if (previousOwner != from) {
revert ERC721IncorrectOwner(from, tokenId, previousOwner);
}
}
/**
* @dev See {IERC721-safeTransferFrom}.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) public {
safeTransferFrom(from, to, tokenId, "");
}
/**
* @dev See {IERC721-safeTransferFrom}.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual {
transferFrom(from, to, tokenId);
_checkOnERC721Received(from, to, tokenId, data);
}
/**
* @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist
*
* IMPORTANT: Any overrides to this function that add ownership of tokens not tracked by the
* core ERC721 logic MUST be matched with the use of {_increaseBalance} to keep balances
* consistent with ownership. The invariant to preserve is that for any address `a` the value returned by
* `balanceOf(a)` must be equal to the number of tokens such that `_ownerOf(tokenId)` is `a`.
*/
function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
ERC721Storage storage $ = _getERC721Storage();
return $._owners[tokenId];
}
/**
* @dev Returns the approved address for `tokenId`. Returns 0 if `tokenId` is not minted.
*/
function _getApproved(uint256 tokenId) internal view virtual returns (address) {
ERC721Storage storage $ = _getERC721Storage();
return $._tokenApprovals[tokenId];
}
/**
* @dev Returns whether `spender` is allowed to manage `owner`'s tokens, or `tokenId` in
* particular (ignoring whether it is owned by `owner`).
*
* WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this
* assumption.
*/
function _isAuthorized(address owner, address spender, uint256 tokenId) internal view virtual returns (bool) {
return
spender != address(0) &&
(owner == spender || isApprovedForAll(owner, spender) || _getApproved(tokenId) == spender);
}
/**
* @dev Checks if `spender` can operate on `tokenId`, assuming the provided `owner` is the actual owner.
* Reverts if `spender` does not have approval from the provided `owner` for the given token or for all its assets
* the `spender` for the specific `tokenId`.
*
* WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this
* assumption.
*/
function _checkAuthorized(address owner, address spender, uint256 tokenId) internal view virtual {
if (!_isAuthorized(owner, spender, tokenId)) {
if (owner == address(0)) {
revert ERC721NonexistentToken(tokenId);
} else {
revert ERC721InsufficientApproval(spender, tokenId);
}
}
}
/**
* @dev Unsafe write access to the balances, used by extensions that "mint" tokens using an {ownerOf} override.
*
* NOTE: the value is limited to type(uint128).max. This protect against _balance overflow. It is unrealistic that
* a uint256 would ever overflow from increments when these increments are bounded to uint128 values.
*
* WARNING: Increasing an account's balance using this function tends to be paired with an override of the
* {_ownerOf} function to resolve the ownership of the corresponding tokens so that balances and ownership
* remain consistent with one another.
*/
function _increaseBalance(address account, uint128 value) internal virtual {
ERC721Storage storage $ = _getERC721Storage();
unchecked {
$._balances[account] += value;
}
}
/**
* @dev Transfers `tokenId` from its current owner to `to`, or alternatively mints (or burns) if the current owner
* (or `to`) is the zero address. Returns the owner of the `tokenId` before the update.
*
* The `auth` argument is optional. If the value passed is non 0, then this function will check that
* `auth` is either the owner of the token, or approved to operate on the token (by the owner).
*
* Emits a {Transfer} event.
*
* NOTE: If overriding this function in a way that tracks balances, see also {_increaseBalance}.
*/
function _update(address to, uint256 tokenId, address auth) internal virtual returns (address) {
ERC721Storage storage $ = _getERC721Storage();
address from = _ownerOf(tokenId);
// Perform (optional) operator check
if (auth != address(0)) {
_checkAuthorized(from, auth, tokenId);
}
// Execute the update
if (from != address(0)) {
// Clear approval. No need to re-authorize or emit the Approval event
_approve(address(0), tokenId, address(0), false);
unchecked {
$._balances[from] -= 1;
}
}
if (to != address(0)) {
unchecked {
$._balances[to] += 1;
}
}
$._owners[tokenId] = to;
emit Transfer(from, to, tokenId);
return from;
}
/**
* @dev Mints `tokenId` and transfers it to `to`.
*
* WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
*
* Requirements:
*
* - `tokenId` must not exist.
* - `to` cannot be the zero address.
*
* Emits a {Transfer} event.
*/
function _mint(address to, uint256 tokenId) internal {
if (to == address(0)) {
revert ERC721InvalidReceiver(address(0));
}
address previousOwner = _update(to, tokenId, address(0));
if (previousOwner != address(0)) {
revert ERC721InvalidSender(address(0));
}
}
/**
* @dev Mints `tokenId`, transfers it to `to` and checks for `to` acceptance.
*
* Requirements:
*
* - `tokenId` must not exist.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function _safeMint(address to, uint256 tokenId) internal {
_safeMint(to, tokenId, "");
}
/**
* @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
* forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
*/
function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual {
_mint(to, tokenId);
_checkOnERC721Received(address(0), to, tokenId, data);
}
/**
* @dev Destroys `tokenId`.
* The approval is cleared when the token is burned.
* This is an internal function that does not check if the sender is authorized to operate on the token.
*
* Requirements:
*
* - `tokenId` must exist.
*
* Emits a {Transfer} event.
*/
function _burn(uint256 tokenId) internal {
address previousOwner = _update(address(0), tokenId, address(0));
if (previousOwner == address(0)) {
revert ERC721NonexistentToken(tokenId);
}
}
/**
* @dev Transfers `tokenId` from `from` to `to`.
* As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
*
* Emits a {Transfer} event.
*/
function _transfer(address from, address to, uint256 tokenId) internal {
if (to == address(0)) {
revert ERC721InvalidReceiver(address(0));
}
address previousOwner = _update(to, tokenId, address(0));
if (previousOwner == address(0)) {
revert ERC721NonexistentToken(tokenId);
} else if (previousOwner != from) {
revert ERC721IncorrectOwner(from, tokenId, previousOwner);
}
}
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking that contract recipients
* are aware of the ERC721 standard to prevent tokens from being forever locked.
*
* `data` is additional data, it has no specified format and it is sent in call to `to`.
*
* This internal function is like {safeTransferFrom} in the sense that it invokes
* {IERC721Receiver-onERC721Received} on the receiver, and can be used to e.g.
* implement alternative mechanisms to perform token transfer, such as signature-based.
*
* Requirements:
*
* - `tokenId` token must exist and be owned by `from`.
* - `to` cannot be the zero address.
* - `from` cannot be the zero address.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function _safeTransfer(address from, address to, uint256 tokenId) internal {
_safeTransfer(from, to, tokenId, "");
}
/**
* @dev Same as {xref-ERC721-_safeTransfer-address-address-uint256-}[`_safeTransfer`], with an additional `data` parameter which is
* forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
*/
function _safeTransfer(address from, address to, uint256 tokenId, bytes memory data) internal virtual {
_transfer(from, to, tokenId);
_checkOnERC721Received(from, to, tokenId, data);
}
/**
* @dev Approve `to` to operate on `tokenId`
*
* The `auth` argument is optional. If the value passed is non 0, then this function will check that `auth` is
* either the owner of the token, or approved to operate on all tokens held by this owner.
*
* Emits an {Approval} event.
*
* Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
*/
function _approve(address to, uint256 tokenId, address auth) internal {
_approve(to, tokenId, auth, true);
}
/**
* @dev Variant of `_approve` with an optional flag to enable or disable the {Approval} event. The event is not
* emitted in the context of transfers.
*/
function _approve(address to, uint256 tokenId, address auth, bool emitEvent) internal virtual {
ERC721Storage storage $ = _getERC721Storage();
// Avoid reading the owner unless necessary
if (emitEvent || auth != address(0)) {
address owner = _requireOwned(tokenId);
// We do not use _isAuthorized because single-token approvals should not be able to call approve
if (auth != address(0) && owner != auth && !isApprovedForAll(owner, auth)) {
revert ERC721InvalidApprover(auth);
}
if (emitEvent) {
emit Approval(owner, to, tokenId);
}
}
$._tokenApprovals[tokenId] = to;
}
/**
* @dev Approve `operator` to operate on all of `owner` tokens
*
* Requirements:
* - operator can't be the address zero.
*
* Emits an {ApprovalForAll} event.
*/
function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {
ERC721Storage storage $ = _getERC721Storage();
if (operator == address(0)) {
revert ERC721InvalidOperator(operator);
}
$._operatorApprovals[owner][operator] = approved;
emit ApprovalForAll(owner, operator, approved);
}
/**
* @dev Reverts if the `tokenId` doesn't have a current owner (it hasn't been minted, or it has been burned).
* Returns the owner.
*
* Overrides to ownership logic should be done to {_ownerOf}.
*/
function _requireOwned(uint256 tokenId) internal view returns (address) {
address owner = _ownerOf(tokenId);
if (owner == address(0)) {
revert ERC721NonexistentToken(tokenId);
}
return owner;
}
/**
* @dev Private function to invoke {IERC721Receiver-onERC721Received} on a target address. This will revert if the
* recipient doesn't accept the token transfer. The call is not executed if the target address is not a contract.
*
* @param from address representing the previous owner of the given token ID
* @param to target address that will receive the tokens
* @param tokenId uint256 ID of the token to be transferred
* @param data bytes optional data to send along with the call
*/
function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory data) private {
if (to.code.length > 0) {
try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
if (retval != IERC721Receiver.onERC721Received.selector) {
revert ERC721InvalidReceiver(to);
}
} catch (bytes memory reason) {
if (reason.length == 0) {
revert ERC721InvalidReceiver(to);
} else {
/// @solidity memory-safe-assembly
assembly {
revert(add(32, reason), mload(reason))
}
}
}
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/IERC721Enumerable.sol)
pragma solidity ^0.8.20;
import {IERC721} from "../IERC721.sol";
/**
* @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
interface IERC721Enumerable is IERC721 {
/**
* @dev Returns the total amount of tokens stored by the contract.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns a token ID owned by `owner` at a given `index` of its token list.
* Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
*/
function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);
/**
* @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
* Use along with {totalSupply} to enumerate all tokens.
*/
function tokenByIndex(uint256 index) external view returns (uint256);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
import "./TrailblazersBadges.sol";
contract TrailblazersBadgesV3 is TrailblazersBadges {
function version() external pure virtual returns (string memory) {
return "V3";
}
function _update(
address to,
uint256 tokenId,
address auth
)
internal
virtual
override
returns (address)
{
if (blacklist.isBlacklisted(_msgSender())) revert ADDRESS_BLACKLISTED();
return super._update(to, tokenId, auth);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/ERC721Pausable.sol)
pragma solidity ^0.8.20;
import {ERC721} from "../ERC721.sol";
import {Pausable} from "../../../utils/Pausable.sol";
/**
* @dev ERC721 token with pausable token transfers, minting and burning.
*
* Useful for scenarios such as preventing trades until the end of an evaluation
* period, or having an emergency switch for freezing all token transfers in the
* event of a large bug.
*
* IMPORTANT: This contract does not include public pause and unpause functions. In
* addition to inheriting this contract, you must define both functions, invoking the
* {Pausable-_pause} and {Pausable-_unpause} internal functions, with appropriate
* access control, e.g. using {AccessControl} or {Ownable}. Not doing so will
* make the contract pause mechanism of the contract unreachable, and thus unusable.
*/
abstract contract ERC721Pausable is ERC721, Pausable {
/**
* @dev See {ERC721-_update}.
*
* Requirements:
*
* - the contract must not be paused.
*/
function _update(
address to,
uint256 tokenId,
address auth
) internal virtual override whenNotPaused returns (address) {
return super._update(to, tokenId, auth);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/IBeacon.sol)
pragma solidity ^0.8.20;
/**
* @dev This is the interface that {BeaconProxy} expects of its beacon.
*/
interface IBeacon {
/**
* @dev Must return an address that can be used as a delegate call target.
*
* {UpgradeableBeacon} will check that this address is a contract.
*/
function implementation() external view returns (address);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error AddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedInnerCall();
/**
* @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.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
/**
* @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 or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {FailedInnerCall} error.
*
* 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.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @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`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
* unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {FailedInnerCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
*/
function _revert(bytes memory returndata) 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 FailedInnerCall();
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
pragma solidity ^0.8.20;
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC1967 implementation slot:
* ```solidity
* contract ERC1967 {
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(newImplementation.code.length > 0);
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
struct StringSlot {
string value;
}
struct BytesSlot {
bytes value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` with member `value` located at `slot`.
*/
function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` representation of the string storage pointer `store`.
*/
function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
/**
* @dev Returns an `BytesSlot` with member `value` located at `slot`.
*/
function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
*/
function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
import { ERC721EnumerableUpgradeable } from
"@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol";
import { ECDSAWhitelist } from "./ECDSAWhitelist.sol";
import { IMinimalBlacklist } from "@taiko/blacklist/IMinimalBlacklist.sol";
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
contract TrailblazersBadges is ERC721EnumerableUpgradeable, ECDSAWhitelist {
/// @notice Movement IDs
uint256 public constant MOVEMENT_NEUTRAL = 0;
uint256 public constant MOVEMENT_BASED = 1;
uint256 public constant MOVEMENT_BOOSTED = 2;
/// @notice Badge IDs
uint256 public constant BADGE_RAVERS = 0;
uint256 public constant BADGE_ROBOTS = 1;
uint256 public constant BADGE_BOUNCERS = 2;
uint256 public constant BADGE_MASTERS = 3;
uint256 public constant BADGE_MONKS = 4;
uint256 public constant BADGE_DRUMMERS = 5;
uint256 public constant BADGE_ANDROIDS = 6;
uint256 public constant BADGE_SHINTO = 7;
/// @notice Base URI required to interact with IPFS
string private _baseURIExtended;
/// @notice Token ID to badge ID mapping
mapping(uint256 _tokenId => uint256 _badgeId) public badges;
/// @notice Wallet-to-Movement mapping
mapping(address _user => uint256 _movement) public movements;
/// @notice Wallet to badge ID, token ID mapping
mapping(address _user => mapping(uint256 _badgeId => uint256 _tokenId)) public userBadges;
/// @notice Movement to badge ID, token ID mapping
mapping(bytes32 movementBadgeHash => uint256[2] movementBadge) public movementBadges;
/// @notice Gap for upgrade safety
uint256[43] private __gap;
error MINTER_NOT_WHITELISTED();
error INVALID_INPUT();
error INVALID_BADGE_ID();
error INVALID_MOVEMENT_ID();
event BadgeCreated(uint256 _tokenId, address _minter, uint256 _badgeId);
event MovementSet(address _user, uint256 _movementId);
event UriSet(string _uri);
/// @notice Contract initializer
/// @param _owner Contract owner
/// @param _rootURI Base URI for the token metadata
/// @param _mintSigner The address that can authorize minting badges
/// @param _blacklistAddress The address of the blacklist contract
function initialize(
address _owner,
string memory _rootURI,
address _mintSigner,
IMinimalBlacklist _blacklistAddress
)
external
initializer
{
__ERC721_init("Trailblazers Badges", "TBB");
_baseURIExtended = _rootURI;
__ECDSAWhitelist_init(_owner, _mintSigner, _blacklistAddress);
}
/// @notice Ensure update of userBadges on transfers
/// @param to The address to transfer to
/// @param tokenId The token id to transfer
/// @param auth The authorizer of the transfer
function _update(
address to,
uint256 tokenId,
address auth
)
internal
virtual
override
returns (address)
{
userBadges[_ownerOf(tokenId)][badges[tokenId]] = 0;
userBadges[to][badges[tokenId]] = tokenId;
return super._update(to, tokenId, auth);
}
/// @notice Update the base URI
/// @param _uri The new base URI
function setUri(string memory _uri) public onlyOwner {
_baseURIExtended = _uri;
emit UriSet(_uri);
}
/// @notice Get the URI for a tokenId
/// @param _tokenId The badge ID
/// @return URI The URI for the badge
function tokenURI(uint256 _tokenId) public view override returns (string memory) {
uint256 movementId = movements[ownerOf(_tokenId)];
uint256 badgeId = badges[_tokenId];
return string(
abi.encodePacked(
_baseURIExtended, "/", Strings.toString(movementId), "/", Strings.toString(badgeId)
)
);
}
/// @notice Mint a badge from the calling wallet
/// @param _signature The signature authorizing the mint
/// @param _badgeId The badge ID to mint
function mint(bytes memory _signature, uint256 _badgeId) public {
_mintBadgeTo(_signature, _msgSender(), _badgeId);
}
/// @notice Mint a badge to a specific address
/// @param _signature The signature authorizing the mint
/// @param _minter The address to mint the badge to
/// @param _badgeId The badge ID to mint
/// @dev Admin only method
function mint(bytes memory _signature, address _minter, uint256 _badgeId) public onlyOwner {
_mintBadgeTo(_signature, _minter, _badgeId);
}
/// @notice Internal method for badge minting
/// @param _signature The signature authorizing the mint
/// @param _minter The address to mint the badge to
/// @param _badgeId The badge ID to mint
function _mintBadgeTo(bytes memory _signature, address _minter, uint256 _badgeId) internal {
if (_badgeId > BADGE_SHINTO) revert INVALID_BADGE_ID();
_consumeMint(_signature, _minter, _badgeId);
uint256 tokenId = totalSupply() + 1;
badges[tokenId] = _badgeId;
_mint(_minter, tokenId);
emit BadgeCreated(tokenId, _minter, _badgeId);
}
/// @notice Sets movement for the calling wallet
/// @param _movementId The movement ID to set
function setMovement(uint256 _movementId) public {
_setMovement(_msgSender(), _movementId);
}
/// @notice Sets movement for a specific address
/// @param _user The address to set the movement for
/// @param _movementId The movement ID to set
/// @dev Owner-only method
function setMovement(address _user, uint256 _movementId) public onlyOwner {
_setMovement(_user, _movementId);
}
/// @notice Internal method for setting movement
/// @param _user The address to set the movement for
/// @param _movementId The movement ID to set
function _setMovement(address _user, uint256 _movementId) internal {
if (_movementId > MOVEMENT_BOOSTED) revert INVALID_MOVEMENT_ID();
movements[_user] = _movementId;
emit MovementSet(_user, _movementId);
}
/// @notice Retrieve a token ID given their owner and Badge ID
/// @param _user The address of the badge owner
/// @param _badgeId The badge ID
/// @return tokenId The token ID
function getTokenId(address _user, uint256 _badgeId) public view returns (uint256) {
return userBadges[_user][_badgeId];
}
/// @notice Retrieve boolean balance for each badge
/// @param _owner The addresses to check
/// @return balances The badges atomic balances
function badgeBalances(address _owner) public view returns (bool[] memory) {
bool[] memory balances = new bool[](8);
balances[0] = 0 != getTokenId(_owner, BADGE_RAVERS);
balances[1] = 0 != getTokenId(_owner, BADGE_ROBOTS);
balances[2] = 0 != getTokenId(_owner, BADGE_BOUNCERS);
balances[3] = 0 != getTokenId(_owner, BADGE_MASTERS);
balances[4] = 0 != getTokenId(_owner, BADGE_MONKS);
balances[5] = 0 != getTokenId(_owner, BADGE_DRUMMERS);
balances[6] = 0 != getTokenId(_owner, BADGE_ANDROIDS);
balances[7] = 0 != getTokenId(_owner, BADGE_SHINTO);
return balances;
}
/// @notice v2
/// @notice Retrieve the base URI
function baseURI() public view returns (string memory) {
return _baseURIExtended;
}
/// @notice Upgraded badgeBalances using tokenOfOwnerByIndex
/// @param _owner The addresses to check
/// @return balances The badges atomic balances
function badgeBalancesV2(address _owner) public view returns (bool[] memory balances) {
uint256 balance = balanceOf(_owner);
uint256[] memory tokenIds = new uint256[](balance);
for (uint256 i = 0; i < balance; i++) {
tokenIds[i] = tokenOfOwnerByIndex(_owner, i);
}
balances = new bool[](8);
for (uint256 i = 0; i < balance; i++) {
uint256 badgeId = badges[tokenIds[i]];
balances[badgeId] = true;
}
return balances;
}
/// @notice Return the total badge supply by badgeId
/// @return balances The amount of each badge id
function totalBadgeSupply() public view returns (uint256[] memory balances) {
uint256 totalSupply = totalSupply();
balances = new uint256[](8);
for (uint256 i = 1; i <= totalSupply; i++) {
uint256 badgeId = badges[i];
balances[badgeId]++;
}
return balances;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract Pausable is Context {
bool private _paused;
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
/**
* @dev The operation failed because the contract is paused.
*/
error EnforcedPause();
/**
* @dev The operation failed because the contract is not paused.
*/
error ExpectedPause();
/**
* @dev Initializes the contract in unpaused state.
*/
constructor() {
_paused = false;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
if (paused()) {
revert EnforcedPause();
}
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
if (!paused()) {
revert ExpectedPause();
}
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}{
"remappings": [
"@openzeppelin/contracts-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable/",
"@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/",
"solady/=node_modules/solady/",
"forge-std/=node_modules/forge-std/",
"ds-test/=node_modules/ds-test/src/",
"p256-verifier/=node_modules/p256-verifier/",
"murky/=node_modules/murky/src/",
"solidity-stringutils/=node_modules/solidity-stringutils/",
"@taiko/blacklist/=node_modules/taiko-mono/packages/supplementary-contracts/contracts/blacklist/",
"openzeppelin-foundry-upgrades/=node_modules/openzeppelin-foundry-upgrades/src/",
"taiko-mono/=node_modules/taiko-mono/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "cancun",
"viaIR": false,
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"name":"ADDRESS_BLACKLISTED","type":"error"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[],"name":"BADGE_LOCKED","type":"error"},{"inputs":[],"name":"BADGE_LOCKED_SEASON_2","type":"error"},{"inputs":[{"internalType":"uint256","name":"seasonId","type":"uint256"},{"internalType":"uint256","name":"seasonEnd","type":"uint256"},{"internalType":"uint256","name":"ts","type":"uint256"}],"name":"BADGE_STILL_LOCKED","type":"error"},{"inputs":[{"internalType":"address","name":"implementation","type":"address"}],"name":"ERC1967InvalidImplementation","type":"error"},{"inputs":[],"name":"ERC1967NonPayable","type":"error"},{"inputs":[],"name":"ERC721EnumerableForbiddenBatchMint","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"owner","type":"address"}],"name":"ERC721IncorrectOwner","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ERC721InsufficientApproval","type":"error"},{"inputs":[{"internalType":"address","name":"approver","type":"address"}],"name":"ERC721InvalidApprover","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"ERC721InvalidOperator","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"ERC721InvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"ERC721InvalidReceiver","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"ERC721InvalidSender","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ERC721NonexistentToken","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"ERC721OutOfBoundsIndex","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"INVALID_BADGE_ID","type":"error"},{"inputs":[],"name":"INVALID_INPUT","type":"error"},{"inputs":[],"name":"INVALID_MOVEMENT_ID","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"MINTER_NOT_WHITELISTED","type":"error"},{"inputs":[],"name":"MINTS_EXCEEDED","type":"error"},{"inputs":[],"name":"NOT_IMPLEMENTED","type":"error"},{"inputs":[],"name":"NOT_OWNER","type":"error"},{"inputs":[],"name":"NO_ACTIVE_SEASON","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[],"name":"ONLY_MINT_SIGNER","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"RECRUITMENT_ALREADY_COMPLETED","type":"error"},{"inputs":[],"name":"RECRUITMENT_LOCK_DURATION_NOT_SET","type":"error"},{"inputs":[],"name":"RECRUITMENT_NOT_FOUND","type":"error"},{"inputs":[],"name":"UUPSUnauthorizedCallContext","type":"error"},{"inputs":[{"internalType":"bytes32","name":"slot","type":"bytes32"}],"name":"UUPSUnsupportedProxiableUUID","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_tokenId","type":"uint256"},{"indexed":false,"internalType":"address","name":"_minter","type":"address"},{"indexed":false,"internalType":"uint256","name":"_badgeId","type":"uint256"}],"name":"BadgeCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_blacklist","type":"address"}],"name":"BlacklistUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_minter","type":"address"},{"indexed":false,"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"MintConsumed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_mintSigner","type":"address"}],"name":"MintSignerUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_user","type":"address"},{"indexed":false,"internalType":"uint256","name":"_movementId","type":"uint256"}],"name":"MovementSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"_uri","type":"string"}],"name":"UriSet","type":"event"},{"inputs":[],"name":"BADGE_ANDROIDS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BADGE_BOUNCERS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BADGE_DRUMMERS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BADGE_MASTERS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BADGE_MONKS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BADGE_RAVERS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BADGE_ROBOTS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BADGE_SHINTO","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MOVEMENT_BASED","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MOVEMENT_BOOSTED","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MOVEMENT_NEUTRAL","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UPGRADE_INTERFACE_VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"badgeBalances","outputs":[{"internalType":"bool[]","name":"","type":"bool[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"badgeBalancesV2","outputs":[{"internalType":"bool[]","name":"balances","type":"bool[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"badges","outputs":[{"internalType":"uint256","name":"_badgeId","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"blacklist","outputs":[{"internalType":"contract IMinimalBlacklist","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"_signature","type":"bytes"},{"internalType":"address","name":"_minter","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"canMint","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentSeasonEndTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentSeasonId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_minter","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"getHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint256","name":"_badgeId","type":"uint256"}],"name":"getTokenId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"string","name":"_rootURI","type":"string"},{"internalType":"address","name":"_mintSigner","type":"address"},{"internalType":"contract IMinimalBlacklist","name":"_blacklistAddress","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_mintSigner","type":"address"},{"internalType":"contract IMinimalBlacklist","name":"_blacklist","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"isLocked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastKnownSeason","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"_signature","type":"bytes"},{"internalType":"uint256","name":"_badgeId","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"_signature","type":"bytes"},{"internalType":"address","name":"_minter","type":"address"},{"internalType":"uint256","name":"_badgeId","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"mintSigner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"signatureHash","type":"bytes32"}],"name":"minted","outputs":[{"internalType":"bool","name":"hasMinted","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"movementBadgeHash","type":"bytes32"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"movementBadges","outputs":[{"internalType":"uint256","name":"movementBadge","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"movements","outputs":[{"internalType":"uint256","name":"_movement","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"proxiableUUID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"recruitmentContract","outputs":[{"internalType":"contract BadgeRecruitment","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"recruitmentContractV2","outputs":[{"internalType":"contract BadgeRecruitmentV2","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"recruitmentLockDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_badgeId","type":"uint256"},{"internalType":"uint256","name":"_cycleId","type":"uint256"}],"name":"resetMigration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"season2EndTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"season3EndTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"season","type":"uint256"}],"name":"seasonEndTimestamps","outputs":[{"internalType":"uint256","name":"endTimestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_movementId","type":"uint256"}],"name":"setMovement","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint256","name":"_movementId","type":"uint256"}],"name":"setMovement","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_recruitmentContract","type":"address"}],"name":"setRecruitmentContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_recruitmentContractV2","type":"address"}],"name":"setRecruitmentContractV2","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_duration","type":"uint256"}],"name":"setRecruitmentLockDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_ts","type":"uint256"}],"name":"setSeason2EndTimestamp","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_ts","type":"uint256"}],"name":"setSeason3EndTimestamp","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_season","type":"uint256"},{"internalType":"uint256","name":"_timestamp","type":"uint256"}],"name":"setSeasonEndTimestamp","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_uri","type":"string"}],"name":"setUri","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"startRecruitment","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_badgeId","type":"uint256"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"startRecruitment","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalBadgeSupply","outputs":[{"internalType":"uint256[]","name":"balances","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"unlockTimestamps","outputs":[{"internalType":"uint256","name":"unlockTimestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IMinimalBlacklist","name":"_blacklist","type":"address"}],"name":"updateBlacklist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_mintSigner","type":"address"}],"name":"updateMintSigner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeToAndCall","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint256","name":"_badgeId","type":"uint256"}],"name":"userBadges","outputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"}]Contract Creation Code
60a06040523060805234801562000014575f80fd5b506200001f62000025565b620000d9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff1615620000765760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b0390811614620000d65780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b608051613c4c620001005f395f81816121f60152818161221f015261235e0152613c4c5ff3fe608060405260043610610457575f3560e01c80638da5cb5b11610241578063c76422ff11610134578063e985e9c5116100b3578063f571049b11610078578063f571049b14610cc4578063f6aacfb114610ce3578063f7da8ce314610d02578063f896d3a014610d21578063ff2c26ec14610d40575f80fd5b8063e985e9c514610c2a578063ed05582b14610c49578063f17af48d14610c68578063f23ff9d914610c86578063f2fde38b14610ca5575f80fd5b8063d944061e116100f9578063d944061e14610b8f578063df91cc2314610ba2578063e1aff31214610bd8578063e30c397814610bf7578063e84be85214610c0b575f80fd5b8063c76422ff14610b05578063c87b56dd14610b30578063c8860d9814610b4f578063ca0259c014610b6e578063d5def7c214610b8f575f80fd5b8063a4b5fa56116101c0578063b58c439e11610185578063b58c439e14610a6a578063b88d4fde14610a89578063b8f88d6414610aa8578063bacb6d4e14610ac7578063c0c53b8b14610ae6575f80fd5b8063a4b5fa56146109f3578063a7fb03ab14610a12578063ad3cb1cc14610a26578063b378b44814610a56578063b4e7409f146109a1575f80fd5b806398e36d8b1161020657806398e36d8b1461097657806398ef238a146109a15780639b642de1146109b55780639cc281641461055d578063a22cb465146109d4575f80fd5b80638da5cb5b146108e55780638e228b64146108f95780639224747d14610918578063950290c81461094357806395d89b4114610962575f80fd5b806342842e0e116103595780636352211e116102d857806373e9c1f91161029d57806373e9c1f91461085b57806375b9c0151461087a57806379ba50971461088e5780637b85f161146108a25780638ccc5f80146108b7575f80fd5b80636352211e146107ca5780636c0360eb146107e9578063700b1acd146107fd57806370a0823114610828578063715018a614610847575f80fd5b806352d1902d1161031e57806352d1902d1461072d57806354fd4d5014610741578063599fdaa21461076b5780635c6f8ca31461078a5780635e75a6c3146107b6575f80fd5b806342842e0e146106b357806345d46748146106d25780634808b109146106e75780634f1ef286146106fb5780634f6ccce71461070e575f80fd5b80631fcfe19c116103e55780632f19062f116103aa5780632f19062f146106225780632f745c59146106415780633136964c1461066057806332c8f5041461067f578063378b570414610694575f80fd5b80631fcfe19c1461059157806323604864146105b057806323b872dd146105c5578063275b629e146105e457806329f99b9f14610603575f80fd5b8063081812fc1161042b578063081812fc146104f3578063095ea7b31461052a5780630e0c8b0a146105495780630f5f04bf1461055d57806318160ddd14610571575f80fd5b80622576121461045b57806301ffc9a71461047c57806303cfb2fa146104b057806306fdde03146104d2575b5f80fd5b348015610466575f80fd5b5061047a6104753660046133e4565b610d5f565b005b348015610487575f80fd5b5061049b61049636600461343a565b610d6e565b60405190151581526020015b60405180910390f35b3480156104bb575f80fd5b506104c4610d98565b6040519081526020016104a7565b3480156104dd575f80fd5b506104e6610db6565b6040516104a791906134a2565b3480156104fe575f80fd5b5061051261050d3660046134b4565b610e57565b6040516001600160a01b0390911681526020016104a7565b348015610535575f80fd5b5061047a6105443660046134df565b610e6b565b348015610554575f80fd5b506104c4600681565b348015610568575f80fd5b506104c4600181565b34801561057c575f80fd5b505f80516020613bf7833981519152546104c4565b34801561059c575f80fd5b5061047a6105ab366004613509565b610e76565b3480156105bb575f80fd5b506104c460675481565b3480156105d0575f80fd5b5061047a6105df36600461355e565b610e8e565b3480156105ef575f80fd5b5061047a6105fe36600461358b565b610f1c565b34801561060e575f80fd5b506104c461061d3660046134df565b610f44565b34801561062d575f80fd5b5061047a61063c3660046134b4565b610f6b565b34801561064c575f80fd5b506104c461065b3660046134df565b610f78565b34801561066b575f80fd5b506104c461067a36600461358b565b610fe9565b34801561068a575f80fd5b506104c460625481565b34801561069f575f80fd5b5061047a6106ae3660046135ab565b61100b565b3480156106be575f80fd5b5061047a6106cd36600461355e565b611068565b3480156106dd575f80fd5b506104c460665481565b3480156106f2575f80fd5b506104c4600781565b61047a6107093660046135c6565b611082565b348015610719575f80fd5b506104c46107283660046134b4565b61109d565b348015610738575f80fd5b506104c461110f565b34801561074c575f80fd5b50604080518082019091526002815261563960f01b60208201526104e6565b348015610776575f80fd5b50606554610512906001600160a01b031681565b348015610795575f80fd5b506107a96107a43660046135ab565b61112a565b6040516104a79190613612565b3480156107c1575f80fd5b506104c46112de565b3480156107d5575f80fd5b506105126107e43660046134b4565b611330565b3480156107f4575f80fd5b506104e661133a565b348015610808575f80fd5b506104c46108173660046135ab565b60346020525f908152604090205481565b348015610833575f80fd5b506104c46108423660046135ab565b6113ca565b348015610852575f80fd5b5061047a611422565b348015610866575f80fd5b5061047a610875366004613657565b611435565b348015610885575f80fd5b506104c4600581565b348015610899575f80fd5b5061047a6114f3565b3480156108ad575f80fd5b506104c460695481565b3480156108c2575f80fd5b5061049b6108d13660046134b4565b60016020525f908152604090205460ff1681565b3480156108f0575f80fd5b50610512611538565b348015610904575f80fd5b5061047a6109133660046134b4565b61156c565b348015610923575f80fd5b506104c46109323660046134b4565b60646020525f908152604090205481565b34801561094e575f80fd5b5061047a61095d3660046134b4565b611579565b34801561096d575f80fd5b506104e6611592565b348015610981575f80fd5b506104c46109903660046134b4565b60336020525f908152604090205481565b3480156109ac575f80fd5b506104c4600281565b3480156109c0575f80fd5b5061047a6109cf366004613680565b6115d0565b3480156109df575f80fd5b5061047a6109ee3660046136c6565b611614565b3480156109fe575f80fd5b50600254610512906001600160a01b031681565b348015610a1d575f80fd5b506104c4600481565b348015610a31575f80fd5b506104e6604051806040016040528060058152602001640352e302e360dc1b81525081565b348015610a61575f80fd5b506104c4600381565b348015610a75575f80fd5b5061049b610a84366004613509565b61161f565b348015610a94575f80fd5b5061047a610aa33660046136fd565b6116e6565b348015610ab3575f80fd5b5061047a610ac2366004613764565b6116fd565b348015610ad2575f80fd5b5061047a610ae13660046134b4565b61186b565b348015610af1575f80fd5b5061047a610b003660046137d4565b611878565b348015610b10575f80fd5b506104c4610b1f3660046134b4565b60686020525f908152604090205481565b348015610b3b575f80fd5b506104e6610b4a3660046134b4565b611988565b348015610b5a575f80fd5b50606354610512906001600160a01b031681565b348015610b79575f80fd5b50610b82611a00565b6040516104a7919061381c565b348015610b9a575f80fd5b506104c45f81565b348015610bad575f80fd5b506104c4610bbc3660046134df565b603560209081525f928352604080842090915290825290205481565b348015610be3575f80fd5b5061047a610bf23660046134b4565b611a9a565b348015610c02575f80fd5b50610512611aa7565b348015610c16575f80fd5b5061047a610c253660046135ab565b611acf565b348015610c35575f80fd5b5061049b610c44366004613853565b611af9565b348015610c54575f80fd5b506104c4610c633660046134df565b611b45565b348015610c73575f80fd5b505f54610512906001600160a01b031681565b348015610c91575f80fd5b5061047a610ca036600461358b565b611b9e565b348015610cb0575f80fd5b5061047a610cbf3660046135ab565b611cf3565b348015610ccf575f80fd5b5061047a610cde3660046135ab565b611d78565b348015610cee575f80fd5b5061049b610cfd3660046134b4565b611dcd565b348015610d0d575f80fd5b506107a9610d1c3660046135ab565b611e0b565b348015610d2c575f80fd5b5061047a610d3b3660046135ab565b611f28565b348015610d4b575f80fd5b5061047a610d5a3660046134df565b611f52565b610d6a823383611f64565b5050565b5f6001600160e01b0319821663780e9d6360e01b1480610d925750610d928261201d565b92915050565b5f80610da26112de565b5f9081526068602052604090205492915050565b5f80516020613bb78339815191528054606091908190610dd59061387f565b80601f0160208091040260200160405190810160405280929190818152602001828054610e019061387f565b8015610e4c5780601f10610e2357610100808354040283529160200191610e4c565b820191905f5260205f20905b815481529060010190602001808311610e2f57829003601f168201915b505050505091505090565b5f610e618261206c565b50610d92826120a3565b610d6a8282336120dc565b610e7e6120e9565b610e89838383611f64565b505050565b6001600160a01b038216610ebc57604051633250574960e11b81525f60048201526024015b60405180910390fd5b5f610ec883833361211b565b9050836001600160a01b0316816001600160a01b031614610f16576040516364283d7b60e01b81526001600160a01b0380861660048301526024820184905282166044820152606401610eb3565b50505050565b610f246120e9565b5f828152606860205260409020819055606954821115610d6a5750606955565b6001600160a01b03919091165f908152603560209081526040808320938352929052205490565b610f753382612172565b50565b5f5f80516020613b97833981519152610f90846113ca565b8310610fc15760405163295f44f760e21b81526001600160a01b038516600482015260248101849052604401610eb3565b6001600160a01b0384165f908152602091825260408082208583529092522054905092915050565b6036602052815f5260405f208160028110611002575f80fd5b01549150829050565b6110136120e9565b600280546001600160a01b0319166001600160a01b0383169081179091556040519081527f75cdf6842d867a09260947ac775619527788d913a79fc3b41da1815fa377af65906020015b60405180910390a150565b610e8983838360405180602001604052805f8152506116e6565b61108a6121eb565b6110938261228f565b610d6a8282612297565b5f5f80516020613b978339815191526110c15f80516020613bf78339815191525490565b83106110e95760405163295f44f760e21b81525f600482015260248101849052604401610eb3565b8060020183815481106110fe576110fe6138b7565b905f5260205f200154915050919050565b5f611118612353565b505f80516020613bd783398151915290565b60408051600880825261012082019092526060915f9190602082016101008036833701905050905061115c835f610f44565b5f1415815f81518110611171576111716138b7565b9115156020928302919091019091015261118c836001610f44565b5f1415816001815181106111a2576111a26138b7565b911515602092830291909101909101526111bd836002610f44565b5f1415816002815181106111d3576111d36138b7565b911515602092830291909101909101526111ee836003610f44565b5f141581600381518110611204576112046138b7565b9115156020928302919091019091015261121f836004610f44565b5f141581600481518110611235576112356138b7565b91151560209283029190910190910152611250836005610f44565b5f141581600581518110611266576112666138b7565b91151560209283029190910190910152611281836006610f44565b5f141581600681518110611297576112976138b7565b911515602092830291909101909101526112b2836007610f44565b5f1415816007815181106112c8576112c86138b7565b9115156020928302919091019091015292915050565b5f805b6069548111611316575f8181526068602052604090205442101561130457919050565b8061130e816138df565b9150506112e1565b5060405163c6f8523360e01b815260040160405180910390fd5b5f610d928261206c565b6060603280546113499061387f565b80601f01602080910402602001604051908101604052809291908181526020018280546113759061387f565b80156113c05780601f10611397576101008083540402835291602001916113c0565b820191905f5260205f20905b8154815290600101906020018083116113a357829003601f168201915b5050505050905090565b5f5f80516020613bb78339815191526001600160a01b038316611402576040516322718ad960e21b81525f6004820152602401610eb3565b6001600160a01b039092165f908152600390920160205250604090205490565b61142a6120e9565b6114335f61239c565b565b3361143f84611330565b6001600160a01b031614611466576040516338ebc58960e11b815260040160405180910390fd5b6065546001600160a01b0316637671afc0336040516001600160e01b031960e084901b1681526001600160a01b0390911660048201526024810186905260448101859052606481018490526084015f604051808303815f87803b1580156114cb575f80fd5b505af11580156114dd573d5f803e3d5ffd5b5050505f93845250506064602052506040812055565b33806114fd611aa7565b6001600160a01b03161461152f5760405163118cdaa760e01b81526001600160a01b0382166004820152602401610eb3565b610f758161239c565b5f807f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c1993005b546001600160a01b031692915050565b6115746120e9565b606755565b6040516343f6e4ab60e01b815260040160405180910390fd5b7f80bb2b638cc20bc4d0a60d66940f3ab4a00c1d7b313497ca82fb0b4ab007930180546060915f80516020613bb783398151915291610dd59061387f565b6115d86120e9565b60326115e4828261393b565b507fd09d05534067f74dbc9e6af794edbd29e904d7698e428d0bc1378e45889acdd78160405161105d91906134a2565b610d6a3383836123d4565b60025460405163fe575a8760e01b81526001600160a01b0384811660048301525f92169063fe575a8790602401602060405180830381865afa158015611667573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061168b91906139fa565b156116a957604051637874e36360e01b815260040160405180910390fd5b83516020808601919091205f9081526001909152604090205460ff16156116d157505f6116df565b6116dc848484612483565b90505b9392505050565b6116f1848484610e8e565b610f16848484846124b8565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b03165f811580156117415750825b90505f826001600160401b0316600114801561175c5750303b155b90508115801561176a575080155b156117885760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156117b257845460ff60401b1916600160401b1785555b61180260405180604001604052806013815260200172547261696c626c617a6572732042616467657360681b815250604051806040016040528060038152602001622a212160e91b8152506125de565b603261180e898261393b565b5061181a8988886125f0565b831561186057845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050505050565b6118736120e9565b606255565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b03165f811580156118bc5750825b90505f826001600160401b031660011480156118d75750303b155b9050811580156118e5575080155b156119035760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561192d57845460ff60401b1916600160401b1785555b6119388888886125f0565b831561197e57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b5050505050505050565b60605f60345f61199785611330565b6001600160a01b0316815260208082019290925260409081015f908120548682526033909352205490915060326119cd83612644565b6119d683612644565b6040516020016119e893929190613a30565b60405160208183030381529060405292505050919050565b60605f611a185f80516020613bf78339815191525490565b6040805160088082526101208201909252919250602082016101008036833701905050915060015b818111611a95575f818152603360205260409020548351849082908110611a6957611a696138b7565b602002602001018051809190611a7e906138df565b905250819050611a8d816138df565b915050611a40565b505090565b611aa26120e9565b606655565b5f807f237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c0061155c565b611ad76120e9565b606380546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b039182165f9081527f80bb2b638cc20bc4d0a60d66940f3ab4a00c1d7b313497ca82fb0b4ab00793056020908152604080832093909416825291909152205460ff1690565b604080516001600160a01b03841660208201529081018290525f9060600160408051601f198184030181528282528051602091820120908301520160405160208183030381529060405280519060200120905092915050565b5f818152606460205260408120548291611bb6610d98565b90505f82118015611bc657508042105b8015611bd157508142105b15611c0857611bde6112de565b6040516324fa1fa360e21b8152600481019190915260248101829052426044820152606401610eb3565b6062545f03611c2a576040516321d9ef2160e21b815260040160405180910390fd5b33611c3485611330565b6001600160a01b031614611c5b576040516338ebc58960e11b815260040160405180910390fd5b5f611c64610d98565b5f8681526064602052604090208190556065549091506001600160a01b031663a6471ecf336040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260248101899052604481018890526064015f604051808303815f87803b158015611cd5575f80fd5b505af1158015611ce7573d5f803e3d5ffd5b50505050505050505050565b611cfb6120e9565b7f237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c0080546001600160a01b0319166001600160a01b0383169081178255611d3f611538565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a35050565b611d806120e9565b5f80546001600160a01b0319166001600160a01b0383169081179091556040519081527f81c142d9a4b33dfaba82444370b6b077fc2cb507c30c41a7c967f695ed72651f9060200161105d565b5f80611dd7610d98565b5f8481526064602052604090205490915015801590611df557508042105b15611e035750600192915050565b505f92915050565b60605f611e17836113ca565b90505f816001600160401b03811115611e3257611e32613348565b604051908082528060200260200182016040528015611e5b578160200160208202803683370190505b5090505f5b82811015611e9757611e728582610f78565b828281518110611e8457611e846138b7565b6020908102919091010152600101611e60565b5060408051600880825261012082019092529060208201610100803683370190505092505f5b82811015611f20575f60335f848481518110611edb57611edb6138b7565b602002602001015181526020019081526020015f205490506001858281518110611f0757611f076138b7565b9115156020928302919091019091015250600101611ebd565b505050919050565b611f306120e9565b606580546001600160a01b0319166001600160a01b0392909216919091179055565b611f5a6120e9565b610d6a8282612172565b6007811115611f8657604051635557cd3560e11b815260040160405180910390fd5b611f918383836126d3565b5f611fa75f80516020613bf78339815191525490565b611fb2906001613ad3565b5f8181526033602052604090208390559050611fce8382612764565b604080518281526001600160a01b03851660208201529081018390527f73ec18fe90e8b3de52d7e88ff3108708ed0e9714e5327b038c182b64cedaae709060600160405180910390a150505050565b5f6001600160e01b031982166380ac58cd60e01b148061204d57506001600160e01b03198216635b5e139f60e01b145b80610d9257506301ffc9a760e01b6001600160e01b0319831614610d92565b5f80612077836127c5565b90506001600160a01b038116610d9257604051637e27328960e01b815260048101849052602401610eb3565b5f9081527f80bb2b638cc20bc4d0a60d66940f3ab4a00c1d7b313497ca82fb0b4ab007930460205260409020546001600160a01b031690565b610e8983838360016127fe565b336120f2611538565b6001600160a01b0316146114335760405163118cdaa760e01b8152336004820152602401610eb3565b5f82815260646020526040812054839082612134610d98565b90505f8211801561214457508042105b801561214f57508142105b1561215c57611bde6112de565b612167878787612911565b979650505050505050565b600281111561219457604051632c4d11b760e11b815260040160405180910390fd5b6001600160a01b0382165f81815260346020908152604091829020849055815192835282018390527fc8f3ae17becdf8c098ef081092d0420324d1318397bd7ac8e90d900c56a50a76910160405180910390a15050565b306001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016148061227157507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166122655f80516020613bd7833981519152546001600160a01b031690565b6001600160a01b031614155b156114335760405163703e46dd60e11b815260040160405180910390fd5b610f756120e9565b816001600160a01b03166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa9250505080156122f1575060408051601f3d908101601f191682019092526122ee91810190613ae6565b60015b61231957604051634c9c8ce360e01b81526001600160a01b0383166004820152602401610eb3565b5f80516020613bd7833981519152811461234957604051632a87526960e21b815260048101829052602401610eb3565b610e8983836129b4565b306001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146114335760405163703e46dd60e11b815260040160405180910390fd5b7f237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c0080546001600160a01b0319168155610d6a82612a09565b5f80516020613bb78339815191526001600160a01b03831661241457604051630b61174360e31b81526001600160a01b0384166004820152602401610eb3565b6001600160a01b038481165f818152600584016020908152604080832094881680845294825291829020805460ff191687151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a350505050565b5f8061248f8484611b45565b90505f61249c8287612a79565b50505f546001600160a01b039081169116149695505050505050565b6001600160a01b0383163b15610f1657604051630a85bd0160e11b81526001600160a01b0384169063150b7a02906124fa903390889087908790600401613afd565b6020604051808303815f875af1925050508015612534575060408051601f3d908101601f1916820190925261253191810190613b39565b60015b61259b573d808015612561576040519150601f19603f3d011682016040523d82523d5f602084013e612566565b606091505b5080515f0361259357604051633250574960e11b81526001600160a01b0385166004820152602401610eb3565b805181602001fd5b6001600160e01b03198116630a85bd0160e11b146125d757604051633250574960e11b81526001600160a01b0385166004820152602401610eb3565b5050505050565b6125e6612ac2565b610d6a8282612b0b565b61260e6001600160a01b03841615612608578361239c565b3361239c565b612616612b3b565b5f80546001600160a01b039384166001600160a01b0319918216179091556002805492909316911617905550565b60605f61265083612b43565b60010190505f816001600160401b0381111561266e5761266e613348565b6040519080825280601f01601f191660200182016040528015612698576020820181803683370190505b5090508181016020015b5f19016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a85049450846126a257509392505050565b6126de83838361161f565b6126fb57604051639ea134e960e01b815260040160405180910390fd5b82516020808501919091205f9081526001808352604091829020805460ff1916909117905580516001600160a01b03851681529182018390527ff71d0afb6ca23454b34db8bf7baf45f79e8c2f41e5499375ea2289312b2d317b910160405180910390a1505050565b6001600160a01b03821661278d57604051633250574960e11b81525f6004820152602401610eb3565b5f61279983835f61211b565b90506001600160a01b03811615610e89576040516339e3563760e11b81525f6004820152602401610eb3565b5f9081527f80bb2b638cc20bc4d0a60d66940f3ab4a00c1d7b313497ca82fb0b4ab007930260205260409020546001600160a01b031690565b5f80516020613bb7833981519152818061282057506001600160a01b03831615155b156128e1575f61282f8561206c565b90506001600160a01b0384161580159061285b5750836001600160a01b0316816001600160a01b031614155b801561286e575061286c8185611af9565b155b156128975760405163a9fbf51f60e01b81526001600160a01b0385166004820152602401610eb3565b82156128df5784866001600160a01b0316826001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45b505b5f93845260040160205250506040902080546001600160a01b0319166001600160a01b0392909216919091179055565b6002545f906001600160a01b031663fe575a87336040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa158015612967573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061298b91906139fa565b156129a957604051637874e36360e01b815260040160405180910390fd5b6116dc848484612c1a565b6129bd82612c83565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a2805115612a0157610e898282612ce6565b610d6a612d58565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b5f805f8351604103612ab0576020840151604085015160608601515f1a612aa288828585612d77565b955095509550505050612abb565b505081515f91506002905b9250925092565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff1661143357604051631afcd79f60e31b815260040160405180910390fd5b612b13612ac2565b5f80516020613bb783398151915280612b2c848261393b565b5060018101610f16838261393b565b611433612ac2565b5f8072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b8310612b815772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6d04ee2d6d415b85acef81000000008310612bad576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc100008310612bcb57662386f26fc10000830492506010015b6305f5e1008310612be3576305f5e100830492506008015b6127108310612bf757612710830492506004015b60648310612c09576064830492506002015b600a8310610d925760010192915050565b5f8060355f612c28866127c5565b6001600160a01b03908116825260208083019390935260409182015f908120888252603385528382208054835290855283822095909555908816815260358352818120935481529290915290208390556116dc848484612e3f565b806001600160a01b03163b5f03612cb857604051634c9c8ce360e01b81526001600160a01b0382166004820152602401610eb3565b5f80516020613bd783398151915280546001600160a01b0319166001600160a01b0392909216919091179055565b60605f80846001600160a01b031684604051612d029190613b54565b5f60405180830381855af49150503d805f8114612d3a576040519150601f19603f3d011682016040523d82523d5f602084013e612d3f565b606091505b5091509150612d4f858383612f35565b95945050505050565b34156114335760405163b398979f60e01b815260040160405180910390fd5b5f80807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841115612db057505f91506003905082612e35565b604080515f808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa158015612e01573d5f803e3d5ffd5b5050604051601f1901519150506001600160a01b038116612e2c57505f925060019150829050612e35565b92505f91508190505b9450945094915050565b5f80612e4c858585612f91565b90506001600160a01b038116612ed357612ece845f80516020613bf783398151915280545f8381527f645e039705490088daad89bae25049a34f4a9072d398537b1ab2425f24cbed0360205260408120829055600182018355919091527fa42f15e5d656f8155fd7419d740a6073999f19cd6e061449ce4a257150545bf20155565b612ef6565b846001600160a01b0316816001600160a01b031614612ef657612ef68185613093565b6001600160a01b038516612f1257612f0d84613132565b6116dc565b846001600160a01b0316816001600160a01b0316146116dc576116dc85856131ff565b606082612f4a57612f4582613257565b6116df565b8151158015612f6157506001600160a01b0384163b155b15612f8a57604051639996b31560e01b81526001600160a01b0385166004820152602401610eb3565b50806116df565b5f5f80516020613bb783398151915281612faa856127c5565b90506001600160a01b03841615612fc657612fc6818587613280565b6001600160a01b0381161561300257612fe15f865f806127fe565b6001600160a01b0381165f908152600383016020526040902080545f190190555b6001600160a01b03861615613032576001600160a01b0386165f9081526003830160205260409020805460010190555b5f85815260028301602052604080822080546001600160a01b0319166001600160a01b038a811691821790925591518893918516917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a495945050505050565b5f80516020613b978339815191525f6130ab846113ca565b5f8481526001840160205260409020549091508082146130fe576001600160a01b0385165f9081526020848152604080832085845282528083205484845281842081905583526001860190915290208190555b505f92835260018201602090815260408085208590556001600160a01b039095168452918252838320908352905290812055565b5f80516020613bf7833981519152545f80516020613b97833981519152905f9061315e90600190613b6f565b5f848152600384016020526040812054600285018054939450909284908110613189576131896138b7565b905f5260205f2001549050808460020183815481106131aa576131aa6138b7565b5f918252602080832090910192909255828152600386019091526040808220849055868252812055600284018054806131e5576131e5613b82565b600190038181905f5260205f20015f905590555050505050565b5f80516020613b978339815191525f6001613219856113ca565b6132239190613b6f565b6001600160a01b039094165f9081526020838152604080832087845282528083208690559482526001909301909252502055565b8051156132675780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b61328b8383836132e4565b610e89576001600160a01b0383166132b957604051637e27328960e01b815260048101829052602401610eb3565b60405163177e802f60e01b81526001600160a01b038316600482015260248101829052604401610eb3565b5f6001600160a01b038316158015906116dc5750826001600160a01b0316846001600160a01b0316148061331d575061331d8484611af9565b806116dc5750826001600160a01b0316613336836120a3565b6001600160a01b031614949350505050565b634e487b7160e01b5f52604160045260245ffd5b5f82601f83011261336b575f80fd5b81356001600160401b038082111561338557613385613348565b604051601f8301601f19908116603f011681019082821181831017156133ad576133ad613348565b816040528381528660208588010111156133c5575f80fd5b836020870160208301375f602085830101528094505050505092915050565b5f80604083850312156133f5575f80fd5b82356001600160401b0381111561340a575f80fd5b6134168582860161335c565b95602094909401359450505050565b6001600160e01b031981168114610f75575f80fd5b5f6020828403121561344a575f80fd5b81356116df81613425565b5f5b8381101561346f578181015183820152602001613457565b50505f910152565b5f815180845261348e816020860160208601613455565b601f01601f19169290920160200192915050565b602081525f6116df6020830184613477565b5f602082840312156134c4575f80fd5b5035919050565b6001600160a01b0381168114610f75575f80fd5b5f80604083850312156134f0575f80fd5b82356134fb816134cb565b946020939093013593505050565b5f805f6060848603121561351b575f80fd5b83356001600160401b03811115613530575f80fd5b61353c8682870161335c565b935050602084013561354d816134cb565b929592945050506040919091013590565b5f805f60608486031215613570575f80fd5b833561357b816134cb565b9250602084013561354d816134cb565b5f806040838503121561359c575f80fd5b50508035926020909101359150565b5f602082840312156135bb575f80fd5b81356116df816134cb565b5f80604083850312156135d7575f80fd5b82356135e2816134cb565b915060208301356001600160401b038111156135fc575f80fd5b6136088582860161335c565b9150509250929050565b602080825282518282018190525f9190848201906040850190845b8181101561364b57835115158352928401929184019160010161362d565b50909695505050505050565b5f805f60608486031215613669575f80fd5b505081359360208301359350604090920135919050565b5f60208284031215613690575f80fd5b81356001600160401b038111156136a5575f80fd5b6136b18482850161335c565b949350505050565b8015158114610f75575f80fd5b5f80604083850312156136d7575f80fd5b82356136e2816134cb565b915060208301356136f2816136b9565b809150509250929050565b5f805f8060808587031215613710575f80fd5b843561371b816134cb565b9350602085013561372b816134cb565b92506040850135915060608501356001600160401b0381111561374c575f80fd5b6137588782880161335c565b91505092959194509250565b5f805f8060808587031215613777575f80fd5b8435613782816134cb565b935060208501356001600160401b0381111561379c575f80fd5b6137a88782880161335c565b93505060408501356137b9816134cb565b915060608501356137c9816134cb565b939692955090935050565b5f805f606084860312156137e6575f80fd5b83356137f1816134cb565b92506020840135613801816134cb565b91506040840135613811816134cb565b809150509250925092565b602080825282518282018190525f9190848201906040850190845b8181101561364b57835183529284019291840191600101613837565b5f8060408385031215613864575f80fd5b823561386f816134cb565b915060208301356136f2816134cb565b600181811c9082168061389357607f821691505b6020821081036138b157634e487b7160e01b5f52602260045260245ffd5b50919050565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b5f600182016138f0576138f06138cb565b5060010190565b601f821115610e8957805f5260205f20601f840160051c8101602085101561391c5750805b601f840160051c820191505b818110156125d7575f8155600101613928565b81516001600160401b0381111561395457613954613348565b61396881613962845461387f565b846138f7565b602080601f83116001811461399b575f84156139845750858301515b5f19600386901b1c1916600185901b1785556139f2565b5f85815260208120601f198616915b828110156139c9578886015182559484019460019091019084016139aa565b50858210156139e657878501515f19600388901b60f8161c191681555b505060018460011b0185555b505050505050565b5f60208284031215613a0a575f80fd5b81516116df816136b9565b5f8151613a26818560208601613455565b9290920192915050565b5f808554613a3d8161387f565b60018281168015613a555760018114613a6a57613a96565b60ff1984168752821515830287019450613a96565b895f526020805f205f5b85811015613a8d5781548a820152908401908201613a74565b50505082870194505b50505050602f60f81b81528451613ab4816001840160208901613455565b612167613acd600183850101602f60f81b815260010190565b86613a15565b80820180821115610d9257610d926138cb565b5f60208284031215613af6575f80fd5b5051919050565b6001600160a01b03858116825284166020820152604081018390526080606082018190525f90613b2f90830184613477565b9695505050505050565b5f60208284031215613b49575f80fd5b81516116df81613425565b5f8251613b65818460208701613455565b9190910192915050565b81810381811115610d9257610d926138cb565b634e487b7160e01b5f52603160045260245ffdfe645e039705490088daad89bae25049a34f4a9072d398537b1ab2425f24cbed0080bb2b638cc20bc4d0a60d66940f3ab4a00c1d7b313497ca82fb0b4ab0079300360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc645e039705490088daad89bae25049a34f4a9072d398537b1ab2425f24cbed02a26469706673582212204290aefb985a586d2012a37f0d172e458ea1d8680ea38579ef923e931a124e8a64736f6c63430008180033
Deployed Bytecode
0x608060405260043610610457575f3560e01c80638da5cb5b11610241578063c76422ff11610134578063e985e9c5116100b3578063f571049b11610078578063f571049b14610cc4578063f6aacfb114610ce3578063f7da8ce314610d02578063f896d3a014610d21578063ff2c26ec14610d40575f80fd5b8063e985e9c514610c2a578063ed05582b14610c49578063f17af48d14610c68578063f23ff9d914610c86578063f2fde38b14610ca5575f80fd5b8063d944061e116100f9578063d944061e14610b8f578063df91cc2314610ba2578063e1aff31214610bd8578063e30c397814610bf7578063e84be85214610c0b575f80fd5b8063c76422ff14610b05578063c87b56dd14610b30578063c8860d9814610b4f578063ca0259c014610b6e578063d5def7c214610b8f575f80fd5b8063a4b5fa56116101c0578063b58c439e11610185578063b58c439e14610a6a578063b88d4fde14610a89578063b8f88d6414610aa8578063bacb6d4e14610ac7578063c0c53b8b14610ae6575f80fd5b8063a4b5fa56146109f3578063a7fb03ab14610a12578063ad3cb1cc14610a26578063b378b44814610a56578063b4e7409f146109a1575f80fd5b806398e36d8b1161020657806398e36d8b1461097657806398ef238a146109a15780639b642de1146109b55780639cc281641461055d578063a22cb465146109d4575f80fd5b80638da5cb5b146108e55780638e228b64146108f95780639224747d14610918578063950290c81461094357806395d89b4114610962575f80fd5b806342842e0e116103595780636352211e116102d857806373e9c1f91161029d57806373e9c1f91461085b57806375b9c0151461087a57806379ba50971461088e5780637b85f161146108a25780638ccc5f80146108b7575f80fd5b80636352211e146107ca5780636c0360eb146107e9578063700b1acd146107fd57806370a0823114610828578063715018a614610847575f80fd5b806352d1902d1161031e57806352d1902d1461072d57806354fd4d5014610741578063599fdaa21461076b5780635c6f8ca31461078a5780635e75a6c3146107b6575f80fd5b806342842e0e146106b357806345d46748146106d25780634808b109146106e75780634f1ef286146106fb5780634f6ccce71461070e575f80fd5b80631fcfe19c116103e55780632f19062f116103aa5780632f19062f146106225780632f745c59146106415780633136964c1461066057806332c8f5041461067f578063378b570414610694575f80fd5b80631fcfe19c1461059157806323604864146105b057806323b872dd146105c5578063275b629e146105e457806329f99b9f14610603575f80fd5b8063081812fc1161042b578063081812fc146104f3578063095ea7b31461052a5780630e0c8b0a146105495780630f5f04bf1461055d57806318160ddd14610571575f80fd5b80622576121461045b57806301ffc9a71461047c57806303cfb2fa146104b057806306fdde03146104d2575b5f80fd5b348015610466575f80fd5b5061047a6104753660046133e4565b610d5f565b005b348015610487575f80fd5b5061049b61049636600461343a565b610d6e565b60405190151581526020015b60405180910390f35b3480156104bb575f80fd5b506104c4610d98565b6040519081526020016104a7565b3480156104dd575f80fd5b506104e6610db6565b6040516104a791906134a2565b3480156104fe575f80fd5b5061051261050d3660046134b4565b610e57565b6040516001600160a01b0390911681526020016104a7565b348015610535575f80fd5b5061047a6105443660046134df565b610e6b565b348015610554575f80fd5b506104c4600681565b348015610568575f80fd5b506104c4600181565b34801561057c575f80fd5b505f80516020613bf7833981519152546104c4565b34801561059c575f80fd5b5061047a6105ab366004613509565b610e76565b3480156105bb575f80fd5b506104c460675481565b3480156105d0575f80fd5b5061047a6105df36600461355e565b610e8e565b3480156105ef575f80fd5b5061047a6105fe36600461358b565b610f1c565b34801561060e575f80fd5b506104c461061d3660046134df565b610f44565b34801561062d575f80fd5b5061047a61063c3660046134b4565b610f6b565b34801561064c575f80fd5b506104c461065b3660046134df565b610f78565b34801561066b575f80fd5b506104c461067a36600461358b565b610fe9565b34801561068a575f80fd5b506104c460625481565b34801561069f575f80fd5b5061047a6106ae3660046135ab565b61100b565b3480156106be575f80fd5b5061047a6106cd36600461355e565b611068565b3480156106dd575f80fd5b506104c460665481565b3480156106f2575f80fd5b506104c4600781565b61047a6107093660046135c6565b611082565b348015610719575f80fd5b506104c46107283660046134b4565b61109d565b348015610738575f80fd5b506104c461110f565b34801561074c575f80fd5b50604080518082019091526002815261563960f01b60208201526104e6565b348015610776575f80fd5b50606554610512906001600160a01b031681565b348015610795575f80fd5b506107a96107a43660046135ab565b61112a565b6040516104a79190613612565b3480156107c1575f80fd5b506104c46112de565b3480156107d5575f80fd5b506105126107e43660046134b4565b611330565b3480156107f4575f80fd5b506104e661133a565b348015610808575f80fd5b506104c46108173660046135ab565b60346020525f908152604090205481565b348015610833575f80fd5b506104c46108423660046135ab565b6113ca565b348015610852575f80fd5b5061047a611422565b348015610866575f80fd5b5061047a610875366004613657565b611435565b348015610885575f80fd5b506104c4600581565b348015610899575f80fd5b5061047a6114f3565b3480156108ad575f80fd5b506104c460695481565b3480156108c2575f80fd5b5061049b6108d13660046134b4565b60016020525f908152604090205460ff1681565b3480156108f0575f80fd5b50610512611538565b348015610904575f80fd5b5061047a6109133660046134b4565b61156c565b348015610923575f80fd5b506104c46109323660046134b4565b60646020525f908152604090205481565b34801561094e575f80fd5b5061047a61095d3660046134b4565b611579565b34801561096d575f80fd5b506104e6611592565b348015610981575f80fd5b506104c46109903660046134b4565b60336020525f908152604090205481565b3480156109ac575f80fd5b506104c4600281565b3480156109c0575f80fd5b5061047a6109cf366004613680565b6115d0565b3480156109df575f80fd5b5061047a6109ee3660046136c6565b611614565b3480156109fe575f80fd5b50600254610512906001600160a01b031681565b348015610a1d575f80fd5b506104c4600481565b348015610a31575f80fd5b506104e6604051806040016040528060058152602001640352e302e360dc1b81525081565b348015610a61575f80fd5b506104c4600381565b348015610a75575f80fd5b5061049b610a84366004613509565b61161f565b348015610a94575f80fd5b5061047a610aa33660046136fd565b6116e6565b348015610ab3575f80fd5b5061047a610ac2366004613764565b6116fd565b348015610ad2575f80fd5b5061047a610ae13660046134b4565b61186b565b348015610af1575f80fd5b5061047a610b003660046137d4565b611878565b348015610b10575f80fd5b506104c4610b1f3660046134b4565b60686020525f908152604090205481565b348015610b3b575f80fd5b506104e6610b4a3660046134b4565b611988565b348015610b5a575f80fd5b50606354610512906001600160a01b031681565b348015610b79575f80fd5b50610b82611a00565b6040516104a7919061381c565b348015610b9a575f80fd5b506104c45f81565b348015610bad575f80fd5b506104c4610bbc3660046134df565b603560209081525f928352604080842090915290825290205481565b348015610be3575f80fd5b5061047a610bf23660046134b4565b611a9a565b348015610c02575f80fd5b50610512611aa7565b348015610c16575f80fd5b5061047a610c253660046135ab565b611acf565b348015610c35575f80fd5b5061049b610c44366004613853565b611af9565b348015610c54575f80fd5b506104c4610c633660046134df565b611b45565b348015610c73575f80fd5b505f54610512906001600160a01b031681565b348015610c91575f80fd5b5061047a610ca036600461358b565b611b9e565b348015610cb0575f80fd5b5061047a610cbf3660046135ab565b611cf3565b348015610ccf575f80fd5b5061047a610cde3660046135ab565b611d78565b348015610cee575f80fd5b5061049b610cfd3660046134b4565b611dcd565b348015610d0d575f80fd5b506107a9610d1c3660046135ab565b611e0b565b348015610d2c575f80fd5b5061047a610d3b3660046135ab565b611f28565b348015610d4b575f80fd5b5061047a610d5a3660046134df565b611f52565b610d6a823383611f64565b5050565b5f6001600160e01b0319821663780e9d6360e01b1480610d925750610d928261201d565b92915050565b5f80610da26112de565b5f9081526068602052604090205492915050565b5f80516020613bb78339815191528054606091908190610dd59061387f565b80601f0160208091040260200160405190810160405280929190818152602001828054610e019061387f565b8015610e4c5780601f10610e2357610100808354040283529160200191610e4c565b820191905f5260205f20905b815481529060010190602001808311610e2f57829003601f168201915b505050505091505090565b5f610e618261206c565b50610d92826120a3565b610d6a8282336120dc565b610e7e6120e9565b610e89838383611f64565b505050565b6001600160a01b038216610ebc57604051633250574960e11b81525f60048201526024015b60405180910390fd5b5f610ec883833361211b565b9050836001600160a01b0316816001600160a01b031614610f16576040516364283d7b60e01b81526001600160a01b0380861660048301526024820184905282166044820152606401610eb3565b50505050565b610f246120e9565b5f828152606860205260409020819055606954821115610d6a5750606955565b6001600160a01b03919091165f908152603560209081526040808320938352929052205490565b610f753382612172565b50565b5f5f80516020613b97833981519152610f90846113ca565b8310610fc15760405163295f44f760e21b81526001600160a01b038516600482015260248101849052604401610eb3565b6001600160a01b0384165f908152602091825260408082208583529092522054905092915050565b6036602052815f5260405f208160028110611002575f80fd5b01549150829050565b6110136120e9565b600280546001600160a01b0319166001600160a01b0383169081179091556040519081527f75cdf6842d867a09260947ac775619527788d913a79fc3b41da1815fa377af65906020015b60405180910390a150565b610e8983838360405180602001604052805f8152506116e6565b61108a6121eb565b6110938261228f565b610d6a8282612297565b5f5f80516020613b978339815191526110c15f80516020613bf78339815191525490565b83106110e95760405163295f44f760e21b81525f600482015260248101849052604401610eb3565b8060020183815481106110fe576110fe6138b7565b905f5260205f200154915050919050565b5f611118612353565b505f80516020613bd783398151915290565b60408051600880825261012082019092526060915f9190602082016101008036833701905050905061115c835f610f44565b5f1415815f81518110611171576111716138b7565b9115156020928302919091019091015261118c836001610f44565b5f1415816001815181106111a2576111a26138b7565b911515602092830291909101909101526111bd836002610f44565b5f1415816002815181106111d3576111d36138b7565b911515602092830291909101909101526111ee836003610f44565b5f141581600381518110611204576112046138b7565b9115156020928302919091019091015261121f836004610f44565b5f141581600481518110611235576112356138b7565b91151560209283029190910190910152611250836005610f44565b5f141581600581518110611266576112666138b7565b91151560209283029190910190910152611281836006610f44565b5f141581600681518110611297576112976138b7565b911515602092830291909101909101526112b2836007610f44565b5f1415816007815181106112c8576112c86138b7565b9115156020928302919091019091015292915050565b5f805b6069548111611316575f8181526068602052604090205442101561130457919050565b8061130e816138df565b9150506112e1565b5060405163c6f8523360e01b815260040160405180910390fd5b5f610d928261206c565b6060603280546113499061387f565b80601f01602080910402602001604051908101604052809291908181526020018280546113759061387f565b80156113c05780601f10611397576101008083540402835291602001916113c0565b820191905f5260205f20905b8154815290600101906020018083116113a357829003601f168201915b5050505050905090565b5f5f80516020613bb78339815191526001600160a01b038316611402576040516322718ad960e21b81525f6004820152602401610eb3565b6001600160a01b039092165f908152600390920160205250604090205490565b61142a6120e9565b6114335f61239c565b565b3361143f84611330565b6001600160a01b031614611466576040516338ebc58960e11b815260040160405180910390fd5b6065546001600160a01b0316637671afc0336040516001600160e01b031960e084901b1681526001600160a01b0390911660048201526024810186905260448101859052606481018490526084015f604051808303815f87803b1580156114cb575f80fd5b505af11580156114dd573d5f803e3d5ffd5b5050505f93845250506064602052506040812055565b33806114fd611aa7565b6001600160a01b03161461152f5760405163118cdaa760e01b81526001600160a01b0382166004820152602401610eb3565b610f758161239c565b5f807f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c1993005b546001600160a01b031692915050565b6115746120e9565b606755565b6040516343f6e4ab60e01b815260040160405180910390fd5b7f80bb2b638cc20bc4d0a60d66940f3ab4a00c1d7b313497ca82fb0b4ab007930180546060915f80516020613bb783398151915291610dd59061387f565b6115d86120e9565b60326115e4828261393b565b507fd09d05534067f74dbc9e6af794edbd29e904d7698e428d0bc1378e45889acdd78160405161105d91906134a2565b610d6a3383836123d4565b60025460405163fe575a8760e01b81526001600160a01b0384811660048301525f92169063fe575a8790602401602060405180830381865afa158015611667573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061168b91906139fa565b156116a957604051637874e36360e01b815260040160405180910390fd5b83516020808601919091205f9081526001909152604090205460ff16156116d157505f6116df565b6116dc848484612483565b90505b9392505050565b6116f1848484610e8e565b610f16848484846124b8565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b03165f811580156117415750825b90505f826001600160401b0316600114801561175c5750303b155b90508115801561176a575080155b156117885760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156117b257845460ff60401b1916600160401b1785555b61180260405180604001604052806013815260200172547261696c626c617a6572732042616467657360681b815250604051806040016040528060038152602001622a212160e91b8152506125de565b603261180e898261393b565b5061181a8988886125f0565b831561186057845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050505050565b6118736120e9565b606255565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b03165f811580156118bc5750825b90505f826001600160401b031660011480156118d75750303b155b9050811580156118e5575080155b156119035760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561192d57845460ff60401b1916600160401b1785555b6119388888886125f0565b831561197e57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b5050505050505050565b60605f60345f61199785611330565b6001600160a01b0316815260208082019290925260409081015f908120548682526033909352205490915060326119cd83612644565b6119d683612644565b6040516020016119e893929190613a30565b60405160208183030381529060405292505050919050565b60605f611a185f80516020613bf78339815191525490565b6040805160088082526101208201909252919250602082016101008036833701905050915060015b818111611a95575f818152603360205260409020548351849082908110611a6957611a696138b7565b602002602001018051809190611a7e906138df565b905250819050611a8d816138df565b915050611a40565b505090565b611aa26120e9565b606655565b5f807f237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c0061155c565b611ad76120e9565b606380546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b039182165f9081527f80bb2b638cc20bc4d0a60d66940f3ab4a00c1d7b313497ca82fb0b4ab00793056020908152604080832093909416825291909152205460ff1690565b604080516001600160a01b03841660208201529081018290525f9060600160408051601f198184030181528282528051602091820120908301520160405160208183030381529060405280519060200120905092915050565b5f818152606460205260408120548291611bb6610d98565b90505f82118015611bc657508042105b8015611bd157508142105b15611c0857611bde6112de565b6040516324fa1fa360e21b8152600481019190915260248101829052426044820152606401610eb3565b6062545f03611c2a576040516321d9ef2160e21b815260040160405180910390fd5b33611c3485611330565b6001600160a01b031614611c5b576040516338ebc58960e11b815260040160405180910390fd5b5f611c64610d98565b5f8681526064602052604090208190556065549091506001600160a01b031663a6471ecf336040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260248101899052604481018890526064015f604051808303815f87803b158015611cd5575f80fd5b505af1158015611ce7573d5f803e3d5ffd5b50505050505050505050565b611cfb6120e9565b7f237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c0080546001600160a01b0319166001600160a01b0383169081178255611d3f611538565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a35050565b611d806120e9565b5f80546001600160a01b0319166001600160a01b0383169081179091556040519081527f81c142d9a4b33dfaba82444370b6b077fc2cb507c30c41a7c967f695ed72651f9060200161105d565b5f80611dd7610d98565b5f8481526064602052604090205490915015801590611df557508042105b15611e035750600192915050565b505f92915050565b60605f611e17836113ca565b90505f816001600160401b03811115611e3257611e32613348565b604051908082528060200260200182016040528015611e5b578160200160208202803683370190505b5090505f5b82811015611e9757611e728582610f78565b828281518110611e8457611e846138b7565b6020908102919091010152600101611e60565b5060408051600880825261012082019092529060208201610100803683370190505092505f5b82811015611f20575f60335f848481518110611edb57611edb6138b7565b602002602001015181526020019081526020015f205490506001858281518110611f0757611f076138b7565b9115156020928302919091019091015250600101611ebd565b505050919050565b611f306120e9565b606580546001600160a01b0319166001600160a01b0392909216919091179055565b611f5a6120e9565b610d6a8282612172565b6007811115611f8657604051635557cd3560e11b815260040160405180910390fd5b611f918383836126d3565b5f611fa75f80516020613bf78339815191525490565b611fb2906001613ad3565b5f8181526033602052604090208390559050611fce8382612764565b604080518281526001600160a01b03851660208201529081018390527f73ec18fe90e8b3de52d7e88ff3108708ed0e9714e5327b038c182b64cedaae709060600160405180910390a150505050565b5f6001600160e01b031982166380ac58cd60e01b148061204d57506001600160e01b03198216635b5e139f60e01b145b80610d9257506301ffc9a760e01b6001600160e01b0319831614610d92565b5f80612077836127c5565b90506001600160a01b038116610d9257604051637e27328960e01b815260048101849052602401610eb3565b5f9081527f80bb2b638cc20bc4d0a60d66940f3ab4a00c1d7b313497ca82fb0b4ab007930460205260409020546001600160a01b031690565b610e8983838360016127fe565b336120f2611538565b6001600160a01b0316146114335760405163118cdaa760e01b8152336004820152602401610eb3565b5f82815260646020526040812054839082612134610d98565b90505f8211801561214457508042105b801561214f57508142105b1561215c57611bde6112de565b612167878787612911565b979650505050505050565b600281111561219457604051632c4d11b760e11b815260040160405180910390fd5b6001600160a01b0382165f81815260346020908152604091829020849055815192835282018390527fc8f3ae17becdf8c098ef081092d0420324d1318397bd7ac8e90d900c56a50a76910160405180910390a15050565b306001600160a01b037f000000000000000000000000b73489029c217979b0c6bd0af3a450b56cc01a1316148061227157507f000000000000000000000000b73489029c217979b0c6bd0af3a450b56cc01a136001600160a01b03166122655f80516020613bd7833981519152546001600160a01b031690565b6001600160a01b031614155b156114335760405163703e46dd60e11b815260040160405180910390fd5b610f756120e9565b816001600160a01b03166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa9250505080156122f1575060408051601f3d908101601f191682019092526122ee91810190613ae6565b60015b61231957604051634c9c8ce360e01b81526001600160a01b0383166004820152602401610eb3565b5f80516020613bd7833981519152811461234957604051632a87526960e21b815260048101829052602401610eb3565b610e8983836129b4565b306001600160a01b037f000000000000000000000000b73489029c217979b0c6bd0af3a450b56cc01a1316146114335760405163703e46dd60e11b815260040160405180910390fd5b7f237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c0080546001600160a01b0319168155610d6a82612a09565b5f80516020613bb78339815191526001600160a01b03831661241457604051630b61174360e31b81526001600160a01b0384166004820152602401610eb3565b6001600160a01b038481165f818152600584016020908152604080832094881680845294825291829020805460ff191687151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a350505050565b5f8061248f8484611b45565b90505f61249c8287612a79565b50505f546001600160a01b039081169116149695505050505050565b6001600160a01b0383163b15610f1657604051630a85bd0160e11b81526001600160a01b0384169063150b7a02906124fa903390889087908790600401613afd565b6020604051808303815f875af1925050508015612534575060408051601f3d908101601f1916820190925261253191810190613b39565b60015b61259b573d808015612561576040519150601f19603f3d011682016040523d82523d5f602084013e612566565b606091505b5080515f0361259357604051633250574960e11b81526001600160a01b0385166004820152602401610eb3565b805181602001fd5b6001600160e01b03198116630a85bd0160e11b146125d757604051633250574960e11b81526001600160a01b0385166004820152602401610eb3565b5050505050565b6125e6612ac2565b610d6a8282612b0b565b61260e6001600160a01b03841615612608578361239c565b3361239c565b612616612b3b565b5f80546001600160a01b039384166001600160a01b0319918216179091556002805492909316911617905550565b60605f61265083612b43565b60010190505f816001600160401b0381111561266e5761266e613348565b6040519080825280601f01601f191660200182016040528015612698576020820181803683370190505b5090508181016020015b5f19016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a85049450846126a257509392505050565b6126de83838361161f565b6126fb57604051639ea134e960e01b815260040160405180910390fd5b82516020808501919091205f9081526001808352604091829020805460ff1916909117905580516001600160a01b03851681529182018390527ff71d0afb6ca23454b34db8bf7baf45f79e8c2f41e5499375ea2289312b2d317b910160405180910390a1505050565b6001600160a01b03821661278d57604051633250574960e11b81525f6004820152602401610eb3565b5f61279983835f61211b565b90506001600160a01b03811615610e89576040516339e3563760e11b81525f6004820152602401610eb3565b5f9081527f80bb2b638cc20bc4d0a60d66940f3ab4a00c1d7b313497ca82fb0b4ab007930260205260409020546001600160a01b031690565b5f80516020613bb7833981519152818061282057506001600160a01b03831615155b156128e1575f61282f8561206c565b90506001600160a01b0384161580159061285b5750836001600160a01b0316816001600160a01b031614155b801561286e575061286c8185611af9565b155b156128975760405163a9fbf51f60e01b81526001600160a01b0385166004820152602401610eb3565b82156128df5784866001600160a01b0316826001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45b505b5f93845260040160205250506040902080546001600160a01b0319166001600160a01b0392909216919091179055565b6002545f906001600160a01b031663fe575a87336040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa158015612967573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061298b91906139fa565b156129a957604051637874e36360e01b815260040160405180910390fd5b6116dc848484612c1a565b6129bd82612c83565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a2805115612a0157610e898282612ce6565b610d6a612d58565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b5f805f8351604103612ab0576020840151604085015160608601515f1a612aa288828585612d77565b955095509550505050612abb565b505081515f91506002905b9250925092565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff1661143357604051631afcd79f60e31b815260040160405180910390fd5b612b13612ac2565b5f80516020613bb783398151915280612b2c848261393b565b5060018101610f16838261393b565b611433612ac2565b5f8072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b8310612b815772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6d04ee2d6d415b85acef81000000008310612bad576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc100008310612bcb57662386f26fc10000830492506010015b6305f5e1008310612be3576305f5e100830492506008015b6127108310612bf757612710830492506004015b60648310612c09576064830492506002015b600a8310610d925760010192915050565b5f8060355f612c28866127c5565b6001600160a01b03908116825260208083019390935260409182015f908120888252603385528382208054835290855283822095909555908816815260358352818120935481529290915290208390556116dc848484612e3f565b806001600160a01b03163b5f03612cb857604051634c9c8ce360e01b81526001600160a01b0382166004820152602401610eb3565b5f80516020613bd783398151915280546001600160a01b0319166001600160a01b0392909216919091179055565b60605f80846001600160a01b031684604051612d029190613b54565b5f60405180830381855af49150503d805f8114612d3a576040519150601f19603f3d011682016040523d82523d5f602084013e612d3f565b606091505b5091509150612d4f858383612f35565b95945050505050565b34156114335760405163b398979f60e01b815260040160405180910390fd5b5f80807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841115612db057505f91506003905082612e35565b604080515f808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa158015612e01573d5f803e3d5ffd5b5050604051601f1901519150506001600160a01b038116612e2c57505f925060019150829050612e35565b92505f91508190505b9450945094915050565b5f80612e4c858585612f91565b90506001600160a01b038116612ed357612ece845f80516020613bf783398151915280545f8381527f645e039705490088daad89bae25049a34f4a9072d398537b1ab2425f24cbed0360205260408120829055600182018355919091527fa42f15e5d656f8155fd7419d740a6073999f19cd6e061449ce4a257150545bf20155565b612ef6565b846001600160a01b0316816001600160a01b031614612ef657612ef68185613093565b6001600160a01b038516612f1257612f0d84613132565b6116dc565b846001600160a01b0316816001600160a01b0316146116dc576116dc85856131ff565b606082612f4a57612f4582613257565b6116df565b8151158015612f6157506001600160a01b0384163b155b15612f8a57604051639996b31560e01b81526001600160a01b0385166004820152602401610eb3565b50806116df565b5f5f80516020613bb783398151915281612faa856127c5565b90506001600160a01b03841615612fc657612fc6818587613280565b6001600160a01b0381161561300257612fe15f865f806127fe565b6001600160a01b0381165f908152600383016020526040902080545f190190555b6001600160a01b03861615613032576001600160a01b0386165f9081526003830160205260409020805460010190555b5f85815260028301602052604080822080546001600160a01b0319166001600160a01b038a811691821790925591518893918516917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a495945050505050565b5f80516020613b978339815191525f6130ab846113ca565b5f8481526001840160205260409020549091508082146130fe576001600160a01b0385165f9081526020848152604080832085845282528083205484845281842081905583526001860190915290208190555b505f92835260018201602090815260408085208590556001600160a01b039095168452918252838320908352905290812055565b5f80516020613bf7833981519152545f80516020613b97833981519152905f9061315e90600190613b6f565b5f848152600384016020526040812054600285018054939450909284908110613189576131896138b7565b905f5260205f2001549050808460020183815481106131aa576131aa6138b7565b5f918252602080832090910192909255828152600386019091526040808220849055868252812055600284018054806131e5576131e5613b82565b600190038181905f5260205f20015f905590555050505050565b5f80516020613b978339815191525f6001613219856113ca565b6132239190613b6f565b6001600160a01b039094165f9081526020838152604080832087845282528083208690559482526001909301909252502055565b8051156132675780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b61328b8383836132e4565b610e89576001600160a01b0383166132b957604051637e27328960e01b815260048101829052602401610eb3565b60405163177e802f60e01b81526001600160a01b038316600482015260248101829052604401610eb3565b5f6001600160a01b038316158015906116dc5750826001600160a01b0316846001600160a01b0316148061331d575061331d8484611af9565b806116dc5750826001600160a01b0316613336836120a3565b6001600160a01b031614949350505050565b634e487b7160e01b5f52604160045260245ffd5b5f82601f83011261336b575f80fd5b81356001600160401b038082111561338557613385613348565b604051601f8301601f19908116603f011681019082821181831017156133ad576133ad613348565b816040528381528660208588010111156133c5575f80fd5b836020870160208301375f602085830101528094505050505092915050565b5f80604083850312156133f5575f80fd5b82356001600160401b0381111561340a575f80fd5b6134168582860161335c565b95602094909401359450505050565b6001600160e01b031981168114610f75575f80fd5b5f6020828403121561344a575f80fd5b81356116df81613425565b5f5b8381101561346f578181015183820152602001613457565b50505f910152565b5f815180845261348e816020860160208601613455565b601f01601f19169290920160200192915050565b602081525f6116df6020830184613477565b5f602082840312156134c4575f80fd5b5035919050565b6001600160a01b0381168114610f75575f80fd5b5f80604083850312156134f0575f80fd5b82356134fb816134cb565b946020939093013593505050565b5f805f6060848603121561351b575f80fd5b83356001600160401b03811115613530575f80fd5b61353c8682870161335c565b935050602084013561354d816134cb565b929592945050506040919091013590565b5f805f60608486031215613570575f80fd5b833561357b816134cb565b9250602084013561354d816134cb565b5f806040838503121561359c575f80fd5b50508035926020909101359150565b5f602082840312156135bb575f80fd5b81356116df816134cb565b5f80604083850312156135d7575f80fd5b82356135e2816134cb565b915060208301356001600160401b038111156135fc575f80fd5b6136088582860161335c565b9150509250929050565b602080825282518282018190525f9190848201906040850190845b8181101561364b57835115158352928401929184019160010161362d565b50909695505050505050565b5f805f60608486031215613669575f80fd5b505081359360208301359350604090920135919050565b5f60208284031215613690575f80fd5b81356001600160401b038111156136a5575f80fd5b6136b18482850161335c565b949350505050565b8015158114610f75575f80fd5b5f80604083850312156136d7575f80fd5b82356136e2816134cb565b915060208301356136f2816136b9565b809150509250929050565b5f805f8060808587031215613710575f80fd5b843561371b816134cb565b9350602085013561372b816134cb565b92506040850135915060608501356001600160401b0381111561374c575f80fd5b6137588782880161335c565b91505092959194509250565b5f805f8060808587031215613777575f80fd5b8435613782816134cb565b935060208501356001600160401b0381111561379c575f80fd5b6137a88782880161335c565b93505060408501356137b9816134cb565b915060608501356137c9816134cb565b939692955090935050565b5f805f606084860312156137e6575f80fd5b83356137f1816134cb565b92506020840135613801816134cb565b91506040840135613811816134cb565b809150509250925092565b602080825282518282018190525f9190848201906040850190845b8181101561364b57835183529284019291840191600101613837565b5f8060408385031215613864575f80fd5b823561386f816134cb565b915060208301356136f2816134cb565b600181811c9082168061389357607f821691505b6020821081036138b157634e487b7160e01b5f52602260045260245ffd5b50919050565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b5f600182016138f0576138f06138cb565b5060010190565b601f821115610e8957805f5260205f20601f840160051c8101602085101561391c5750805b601f840160051c820191505b818110156125d7575f8155600101613928565b81516001600160401b0381111561395457613954613348565b61396881613962845461387f565b846138f7565b602080601f83116001811461399b575f84156139845750858301515b5f19600386901b1c1916600185901b1785556139f2565b5f85815260208120601f198616915b828110156139c9578886015182559484019460019091019084016139aa565b50858210156139e657878501515f19600388901b60f8161c191681555b505060018460011b0185555b505050505050565b5f60208284031215613a0a575f80fd5b81516116df816136b9565b5f8151613a26818560208601613455565b9290920192915050565b5f808554613a3d8161387f565b60018281168015613a555760018114613a6a57613a96565b60ff1984168752821515830287019450613a96565b895f526020805f205f5b85811015613a8d5781548a820152908401908201613a74565b50505082870194505b50505050602f60f81b81528451613ab4816001840160208901613455565b612167613acd600183850101602f60f81b815260010190565b86613a15565b80820180821115610d9257610d926138cb565b5f60208284031215613af6575f80fd5b5051919050565b6001600160a01b03858116825284166020820152604081018390526080606082018190525f90613b2f90830184613477565b9695505050505050565b5f60208284031215613b49575f80fd5b81516116df81613425565b5f8251613b65818460208701613455565b9190910192915050565b81810381811115610d9257610d926138cb565b634e487b7160e01b5f52603160045260245ffdfe645e039705490088daad89bae25049a34f4a9072d398537b1ab2425f24cbed0080bb2b638cc20bc4d0a60d66940f3ab4a00c1d7b313497ca82fb0b4ab0079300360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc645e039705490088daad89bae25049a34f4a9072d398537b1ab2425f24cbed02a26469706673582212204290aefb985a586d2012a37f0d172e458ea1d8680ea38579ef923e931a124e8a64736f6c63430008180033
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.