Source Code
Latest 6 from a total of 6 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Validate Quest F... | 9683770 | 503 days ago | IN | 0 FRAX | 0.00002472 | ||||
| Add Limited Ques... | 9683644 | 503 days ago | IN | 0 FRAX | 0.00000529 | ||||
| Add Limited Ques... | 9683632 | 503 days ago | IN | 0 FRAX | 0.00000529 | ||||
| Add Limited Ques... | 9683622 | 503 days ago | IN | 0 FRAX | 0.00000731 | ||||
| Update Base Ques... | 9683599 | 503 days ago | IN | 0 FRAX | 0.00000304 | ||||
| Manage Flox Cont... | 9675464 | 503 days ago | IN | 0 FRAX | 0.00000064 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
Contract Name:
FNSQuestValidator
Compiler Version
v0.8.23+commit.f704f362
Optimization Enabled:
Yes with 100000 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/**
* ====================================================================
* | ______ _______ |
* | / _____________ __ __ / ____(_____ ____ _____ ________ |
* | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ |
* | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ |
* | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ |
* | |
* ====================================================================
* ====================== FNSQuestValidator ===========================
* ====================================================================
* Frax Finance: https://github.com/FraxFinance
*/
import { QuestTracker } from "../QuestTracker.sol";
import { IQuestTrackerEnums } from "../IQuestTrackerEnums.sol";
import { IFNSQuestValidatorErrors } from "./IFNSQuestValidatorErrors.sol";
import { IFNSQuestValidatorEvents } from "./IFNSQuestValidatorEvents.sol";
import { QuestValidatorAccessControl } from "./QuestValidatorAccessControl.sol";
import { IFNS } from "./interfaces/IFNS.sol";
/**
* @title FNSQuestValidator
* @author Frax Finance
* @notice The FNSQuestValidator contract is used to power the quest validation system for Quest track of the Flox
* ecosystem.
*/
contract FNSQuestValidator is IFNSQuestValidatorErrors, IFNSQuestValidatorEvents, QuestValidatorAccessControl {
/// @notice QuestTracker smart contract
QuestTracker public questTracker;
/// @notice FNSNameWrapper smart contract
IFNS public fns;
/**
* @notice Used to track the tokens that have already completed a quest.
* @dev tokenId ID of the token
* @dev alreadyUsed Whether the token has already been used to complete a quest
*/
mapping(uint256 tokenId => bool alreadyUsed) public usedTokens;
/**
* @notice Used to track the limited quests associated with specific domain lengths.
* @dev domainLength Length of the domain
* @dev questId ID of the limited quest associated with the specified domain length
*/
mapping(uint256 domainLength => uint256 questId) private _lengthToQuestId;
/// @notice ID of the base quest.
uint8 public baseQuestId;
/// @notice Used to keep track of the longest domain length still associated with a dedicated limited quest.
uint8 public longestLimitedQuestLength;
/**
* @notice Used to initialize the smart contract.
* @param _questTracker Address of the QuestTracker smart contract
* @param _fns Address of the FNSNameWrapper smart contract
*/
constructor(address _questTracker, address _fns) {
questTracker = QuestTracker(_questTracker);
fns = IFNS(_fns);
}
/**
* @notice Used to get the ID of the limited quest associated with the specified domain length.
* @dev If the domain length is greater than the longestLimitedQuestLength, the longestLimitedQuestLength will be
* truncated to the longest supported limited quest domain length in order to return the correct quest ID.
* @dev Passing a domain length lower than the shortest supported limited quest domain length will return the 0
* value.
* @param domainLength Length of the domain
* @return ID of the limited quest associated with the specified domain length
*/
function lengthToQuestId(uint256 domainLength) public view returns (uint256) {
domainLength = domainLength >= longestLimitedQuestLength ? longestLimitedQuestLength : domainLength;
return _lengthToQuestId[domainLength];
}
/**
* @notice Used to validate a quest for oneself.
* @dev This function can only be called by the owner of the token.
* @dev This function will check if the token is elegible to complete the base quest and the limited quest
* associated with the length of the token's domain.
* @dev Attempting to use the same token to complete the quest multiple times will result in a reverted call.
* @param tokenId ID of the token used to complete the quest
*/
function selfValidateQuest(uint256 tokenId) external {
_validateQuest(tokenId, msg.sender);
}
/**
* @notice Used to validate multiple quests for oneself.
* @dev This function can only be called by the owner of the tokens.
* @dev This function will check if the tokens are elegible to complete the base quest and the limited quest
* associated with the length of the tokens' domains.
* @dev Attempting to use the same token to complete the quest multiple times will result in a reverted call.
* @param tokenIds Array of token IDs used to complete the quests
*/
function bulkSelfValidateQuests(uint256[] memory tokenIds) external {
for (uint256 i; i < tokenIds.length; ) {
_validateQuest(tokenIds[i], msg.sender);
unchecked {
++i;
}
}
}
/**
* @notice Used to validate a quest for a user.
* @dev This function can only be called by a Flox contributor.
* @dev This function will check if the token is elegible to complete the base quest and the limited quest
* associated with the length of the token's domain.
* @dev Attempting to use the same token to complete the quest multiple times will result in a reverted call.
* @dev If a token ID is used to validate a quest for a user, the user has to own it.
* @param tokenId ID of the token used to complete the quest
* @param user Address of the user to validate the quest for
*/
function validateQuestForUser(uint256 tokenId, address user) external {
_onlyFloxContributor();
_validateQuest(tokenId, user);
}
/**
* @notice Used to validate multiple quests for a user.
* @dev This function can only be called by a Flox contributor.
* @dev This function will check if the tokens are elegible to complete the base quest and the limited quest
* associated with the length of the tokens' domains.
* @dev Attempting to use the same token to complete the quest multiple times will result in a reverted call.
* @dev If a token ID is used to validate a quest for a user, the user has to own it.
* @param tokenIds ID of the tokens used to complete the quests
* @param user Address of the user to validate the quests for
*/
function validateQuestsForSingleUser(uint256[] memory tokenIds, address user) external {
_onlyFloxContributor();
for (uint256 i; i < tokenIds.length; ) {
_validateQuest(tokenIds[i], user);
unchecked {
++i;
}
}
}
/**
* @notice Used to validate multiple quests for multiple users.
* @dev This function can only be called by a Flox contributor.
* @dev This function will check if the tokens are elegible to complete the base quest and the limited quest
* associated with the length of the tokens' domains.
* @dev Attempting to use the same token to complete the quest multiple times will result in a reverted call.
* @dev This function will revert if the arrays are of different lengths.
* @dev Token IDs correspond to the users in the same index will be used to validate the quests.
* @dev If a token ID is used to validate a quest for a user, the user has to own it.
* @param tokenIds Array of token IDs used to complete the quests
* @param users Array of addresses of the users to validate the quests for
*/
function validateQuestsForMulitpleUsers(uint256[] memory tokenIds, address[] memory users) external {
_onlyFloxContributor();
for (uint256 i; i < tokenIds.length; ) {
_validateQuest(tokenIds[i], users[i]);
unchecked {
++i;
}
}
}
/**
* @notice Used to add a limited quest associated with a specific domain length.
* @dev This function can only be called by a Flox contributor.
* @param domainLength Length of the domain associated with the limited quest
* @param questId ID of the limited quest
*/
function addLimitedQuest(uint256 domainLength, uint256 questId) external {
_onlyFloxContributor();
_lengthToQuestId[domainLength] = questId;
if (domainLength > longestLimitedQuestLength) {
longestLimitedQuestLength = uint8(domainLength);
}
emit LimitedQuestUpdate(domainLength, questId);
}
/**
* @notice Used to update the base quest ID.
* @dev This function can only be called by a Flox contributor.
* @param _baseQuestId ID of the base quest
*/
function updateBaseQuestId(uint8 _baseQuestId) external {
_onlyFloxContributor();
baseQuestId = _baseQuestId;
emit BaseQuestUpdate(_baseQuestId);
}
/**
* @notice Used to validate an FNS quest.
* @dev This function will check if the token is elegible to complete the base quest and the limited quest
* associated with the length of the token's domain.
* @dev Attempting to use the same token to complete the quest multiple times will result in a reverted call.
* @dev Attempting to validate a quest using a token not owned by the user will result in a reverted call.
* @dev If the user has already completed the quests, the tokens will be marked as used and the function will
* proceed without any further action.
* @param tokenId ID of the token used to complete the quest
* @param user Address of the user to validate the quest for
*/
function _validateQuest(uint256 tokenId, address user) internal {
if (usedTokens[tokenId]) revert TokenAlreadyUsed();
if (fns.ownerOf(tokenId) != user) revert InvalidTokenOwner();
bool baseQuestElegibleForCompletion;
bool limitedQuestElegibleForCompletion;
bytes memory domainName = _getTokenDomainName(tokenId);
uint256 domainLength = _getDecodedStringLength(domainName);
baseQuestElegibleForCompletion = _isQuestElegibleForCompletion(user, uint256(baseQuestId), false);
domainLength = domainLength >= longestLimitedQuestLength ? longestLimitedQuestLength : domainLength;
limitedQuestElegibleForCompletion = _isQuestElegibleForCompletion(
user,
lengthToQuestId(domainLength),
true
);
usedTokens[tokenId] = true;
if (baseQuestElegibleForCompletion) {
questTracker.updateUserQuestStatus(user, uint256(baseQuestId), IQuestTrackerEnums.UserQuestStatus.PendingAllocation);
}
if (limitedQuestElegibleForCompletion) {
questTracker.updateUserQuestStatus(
user,
lengthToQuestId(domainLength),
IQuestTrackerEnums.UserQuestStatus.PendingAllocation
);
}
}
/**
* @notice Used to check if a quest is elegible for completion.
* @param user Address of the user to check for the quest completion elegibility
* @param questId ID of the quest to check for completion elegibility
* @param isLimitedQuest Signifies whether the quest is a limited quest or a base quest
* @return Whether the quest is elegible for completion
*/
function _isQuestElegibleForCompletion(address user, uint256 questId, bool isLimitedQuest) internal view returns (bool) {
if (isLimitedQuest) {
return (
questTracker.userQuestStatus(user, questId) == IQuestTrackerEnums.UserQuestStatus.Incomplete &&
questTracker.remainingLimitedQuestCompletions(questId) > 0
);
} else {
return questTracker.userQuestStatus(user, questId) == IQuestTrackerEnums.UserQuestStatus.Incomplete;
}
}
/**
* @notice Used to get the domain name of a token.
* @param tokenId ID ot the token
* @return Domain name of the token encoded in bytes
*/
function _getTokenDomainName(uint256 tokenId) internal view returns (bytes memory) {
bytes32 hexTokenId = bytes32(tokenId);
return fns.names(hexTokenId);
}
/**
* @notice Used to process the encoded domain name and get the length of the domain.
* @dev Since the length of the domain is encoded in the first byte of the domain name, this function will retrieve
* it and convert it's hex encoding to an uint256.
* @param data Data to decode
* @return Length of the decoded domain name
*/
function _getDecodedStringLength(bytes memory data) internal pure returns (uint256) {
uint256 length = uint8(data[0]);
return length;
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/**
* ====================================================================
* | ______ _______ |
* | / _____________ __ __ / ____(_____ ____ _____ ________ |
* | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ |
* | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ |
* | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ |
* | |
* ====================================================================
* ========================= QuestTracker =============================
* ====================================================================
* Frax Finance: https://github.com/FraxFinance
*/
import { QuestTrackerAccessControl } from "./QuestTrackerAccessControl.sol";
/**
* @title QuestTracker
* @author Frax Finance
* @notice The QuestTracker contract is used to track quests and their progress for users.
*/
contract QuestTracker is QuestTrackerAccessControl {
/**
* @notice This struct represents a quest.
*/
struct Quest {
uint256 reward; // The amount of FXTL points awarded for completing the quest
uint256 startBlock; // The start block of the quest - marks the first block that compleion conditions apply
uint256 endBlock; // The end block of the quest - marks the last block that completion conditions apply
uint256 experiencePoints; // The amount of experience points earned for completing the quest
string title; // The title of the quest
string description; // The URI to the quest description
QuestStatus status; // The status of the quest (Pending, Upcoming, Active, Expired)
QuestType questType; // The type of the quest (Single, Recursive, Limited)
uint128 maxCompletions; // The maximum number of users that can complete the quest
uint64 recursiveCooldown; // The cooldown period between completions of the recursive quest
bool selfServeCompletion; // Whether the quest can be completed by the user or not. If true, it needs QuestValidator
address questValidator; // The address of the contract that validates the quest completion
uint256[50] __gap; // Reserved storage gap to prevent storage collisions
}
/**
* @notice This struct represents the status of a user's progress on a quest.
*/
struct UserData {
uint256 numberOfCompletedQuests; // The number of quests completed by the user
uint256 lifetimePoints; // The total amount of FXTL points earned by the user
uint256 experienceEarned; // The total amount of experience points earned by the user
uint256[50] __gap; // Reserved storage gap to prevent storage collisions
}
/**
* @notice This struct represents the status of a user's progress on a recursive quest.
*/
struct RecursiveQuestCompletions {
uint128 totalCompletions; // The total number of times the user has completed the quest
uint128 unallocatedCompletions; // The number of completions that have not yet been allocated
uint128 lastCompletionTimestamp; // The timestamp of the last completion of the quest
uint256[50] __gap; // Reserved storage gap to prevent storage collisions
}
/**
* @notice This mapping is used to keep track of the number of times a quest has been completed.
* @dev questId ID of the quest
* @dev timesCompleted Number of times the quest has been completed
*/
mapping(uint256 questId => uint128 timesCompleted) public limitedQuestCompletions;
/**
* @notice This mapping is used to keep track of all of the quests.
* @dev The quests are stored using their unique ID.
* @dev questId ID of the quest
* @dev quest The quest being stored
*/
mapping(uint256 questId => Quest quest) private _quests;
/**
* @notice This mapping is used to keep track of users progress on recursive quests.
* @dev The mapping is used to add the ability to implement a cooldown period between completions, so that the users
* can't spam recursive quests.
* @dev user Address of the user
* @dev questId ID of the quest
* @dev questCompletions The struct containing the user's limited progress on the recursive quest
*/
mapping(address user => mapping(uint256 questId => RecursiveQuestCompletions questCompletions))
private _recursiveQuestCompletions;
/**
* @notice This mapping represents the status of a user's progress on a quest.
* @dev Tracking the users progress through quest allows for allocation of awards upon completion of the quest.
* @dev user Address of the user
* @dev questId ID of the quest
* @dev status Status of the user's progress on the quest
*/
mapping(address user => mapping(uint256 questId => UserQuestStatus status)) public userQuestStatus;
/**
* @notice This mapping is used to keep track of the users' quest progress.
* @dev user Address of the user
* @dev data The UserData struct containing the user's quest progress
*/
mapping(address user => UserData userData) private _userQuestProgress;
/**
* @notice This mapping is used to keep track of the quests completed by the user.
* @dev This is an unordered list and serves the purpose of tracking the user's progress for off-chain processing.
* @dev user Address of the user
* @dev completedQuestId ID of the quest completed by the user
*/
mapping(address user => mapping(uint256 questIndex => uint256 completedQuestId)) public completedQuestIds;
/// ID of the next quest to be added as well as the total number of quests.
uint256 public nextQuestId;
/// Flag to determine if the contract has been initialized.
bool public initialized;
/**
* @notice Initialize the QuestTracker and set the owner.
* @param owner Address of the owner of the contract
*/
function initialize(address owner) public override {
if (initialized) revert AlreadyInitialized();
super.initialize(owner);
initialized = true;
}
/**
* @notice Used to retrieve the number of times the quest can still be completed.
* @param questId ID of the quest to check for the remaining compleations
* @return remainingCompletions The number of times the quest can still be completed
*/
function remainingLimitedQuestCompletions(uint256 questId) public view returns (uint256 remainingCompletions) {
if (_quests[questId].questType == QuestType.Limited) {
remainingCompletions = _quests[questId].maxCompletions - limitedQuestCompletions[questId];
} else {
remainingCompletions = 2 ** 256 - 1;
}
}
/**
* @notice Used to retrieve the desired quest.
* @param questId ID of the quest to retrieve
* @return quest The quest being retrieved
*/
function quests(uint256 questId) public view returns (Quest memory quest) {
return _quests[questId];
}
/**
* @notice Used to retrieve the progress statistics of a user.
* @param user Address of the user to retrieve the progress statistics for
* @return userData The UserData struct containing the user's quests progress
*/
function userQuestProgress(address user) public view returns (UserData memory userData) {
return _userQuestProgress[user];
}
/**
* @notice Used to retrieve the information about the user's progress on a specific recursive quest.
* @dev If the quest ID used is for a non-recursive quest, the function will return a struct with all fields set to 0.
* @param user Address of the user to retrieve the progress statistics for
* @param questId ID of the quest to retrieve the progress statistics for
* @return questCompletions The RecursiveQuestCompletions struct containing the user's progress on the recursive quest
*/
function recursiveQuestCompletions(
address user,
uint256 questId
) public view returns (RecursiveQuestCompletions memory questCompletions) {
return _recursiveQuestCompletions[user][questId];
}
/**
* @notice Used to add a new quest to the tracker.
* @dev This can only be called by the Flox contributors.
* @param reward Amount of FXTL received upon completion of the quest
* @param startBlock Block number of the block at which the quest starts
* @param endBlock Block number of the block at which the quest ends
* @param experiencePoints Amount of experience points earned for completing the quest
* @param title The title of the quest
* @param description The URI to the quest description
* @param questType The type of the quest (Single, Recursive, Limited), defaults to Single
* @param maxCompletions The maximum number of users that can complete the quest
* @param recursiveCooldown The cooldown period between completions of the recursive quest
* @param selfServeCompletion Whether the quest can be completed by the user or not. If true, it needs QuestValidator
* @param questValidator The address of the contract that validates the quest completion
*/
function addQuest(
uint256 reward,
uint256 startBlock,
uint256 endBlock,
uint256 experiencePoints,
string memory title,
string memory description,
QuestType questType,
uint128 maxCompletions,
uint64 recursiveCooldown,
bool selfServeCompletion,
address questValidator
) external {
_onlyFloxContributor();
if (startBlock > endBlock) revert InvalidBlockRange();
QuestStatus status;
if (block.number < startBlock) {
status = QuestStatus.Upcoming;
} else if (block.number < endBlock) {
status = QuestStatus.Active;
} else {
status = QuestStatus.Expired;
}
questType = questType == QuestType.Unset ? QuestType.Single : questType;
maxCompletions = questType == QuestType.Limited ? maxCompletions : 0;
recursiveCooldown = questType == QuestType.Recursive ? recursiveCooldown : 0;
questValidator = selfServeCompletion ? questValidator : address(0);
if (selfServeCompletion && questValidator == address(0)) revert InvalidQuestValidator();
uint256[50] memory dummy;
_quests[nextQuestId] = Quest({
reward: reward,
startBlock: startBlock,
endBlock: endBlock,
experiencePoints: experiencePoints,
title: title,
description: description,
status: status,
questType: questType,
maxCompletions: maxCompletions,
recursiveCooldown: recursiveCooldown,
selfServeCompletion: selfServeCompletion,
questValidator: questValidator,
__gap: dummy
});
nextQuestId++;
emit QuestAdded(nextQuestId - 1, reward, startBlock, endBlock);
emit QuestValidatorUpdated(nextQuestId - 1, selfServeCompletion, questValidator);
}
/**
* @notice Used to update an existing quest.
* @dev Passing an empty value for any of the parameters will result in the value not being updated.
* @dev This can only be called by the Flox contributors.
* @param questId ID of the quest to update
* @param updatedReward New amount of FXTL received upon completion of the quest
* @param updatedStartBlock New block number at which the quest starts
* @param updatedEndBlock New block number at which the quest ends
* @param updatedExperiencePoints New amount of experience points earned for completing the quest
* @param updatedTitle New title of the quest
* @param updatedDescription New URI to the quest description
* @param updatedQuestType New type of the quest (Single, Recursive, Limited)
* @param updatedMaxCompletions New maximum number of users that can complete the quest
*/
function updateQuest(
uint256 questId,
uint256 updatedReward,
uint256 updatedStartBlock,
uint256 updatedEndBlock,
uint256 updatedExperiencePoints,
string memory updatedTitle,
string memory updatedDescription,
QuestType updatedQuestType,
uint128 updatedMaxCompletions,
uint64 updatedRecursiveCooldown
) external {
_onlyFloxContributor();
if (questId >= nextQuestId) revert QuestDoesNotExist();
Quest memory quest = _quests[questId];
updatedReward = updatedReward == 0 ? quest.reward : updatedReward;
updatedStartBlock = updatedStartBlock == 0 ? quest.startBlock : updatedStartBlock;
updatedEndBlock = updatedEndBlock == 0 ? quest.endBlock : updatedEndBlock;
updatedExperiencePoints = updatedExperiencePoints == 0 ? quest.experiencePoints : updatedExperiencePoints;
updatedTitle = bytes(updatedTitle).length == 0 ? quest.title : updatedTitle;
updatedDescription = bytes(updatedDescription).length == 0 ? quest.description : updatedDescription;
updatedQuestType = updatedQuestType == QuestType.Unset ? quest.questType : updatedQuestType;
if (updatedQuestType != QuestType.Limited) {
updatedMaxCompletions = 0;
} else if (updatedMaxCompletions == uint128(0)) {
updatedMaxCompletions = quest.maxCompletions;
}
if (updatedQuestType != QuestType.Recursive) {
updatedRecursiveCooldown = 0;
} else if (updatedRecursiveCooldown == uint64(0)) {
updatedRecursiveCooldown = quest.recursiveCooldown;
}
if (updatedStartBlock > updatedEndBlock) revert InvalidBlockRange();
QuestStatus updatedStatus;
if (block.number < updatedStartBlock) {
updatedStatus = QuestStatus.Upcoming;
} else if (block.number < updatedEndBlock) {
updatedStatus = QuestStatus.Active;
} else {
updatedStatus = QuestStatus.Expired;
}
uint256[50] memory dummy;
_quests[questId] = Quest({
reward: updatedReward,
startBlock: updatedStartBlock,
endBlock: updatedEndBlock,
experiencePoints: updatedExperiencePoints,
title: updatedTitle,
description: updatedDescription,
status: updatedStatus,
questType: updatedQuestType,
maxCompletions: updatedMaxCompletions,
recursiveCooldown: updatedRecursiveCooldown,
selfServeCompletion: quest.selfServeCompletion,
questValidator: quest.questValidator,
__gap: dummy
});
emit QuestUpdated(questId, updatedReward, updatedStartBlock, updatedEndBlock, updatedStatus);
}
/**
* @notice Used to update the quest validator settings for a quest.
* @param questId ID of the quest to update the validator for
* @param allowSelfServeCompletion Whether the quest can be completed by the user or not. If true, it needs
* QuestValidator
* @param questValidator Address of the quest validator smart contract for the quest
*/
function manageQuestValidator(uint256 questId, bool allowSelfServeCompletion, address questValidator) external {
_onlyFloxContributor();
if (questId >= nextQuestId) revert QuestDoesNotExist();
Quest storage quest = _quests[questId];
if (quest.selfServeCompletion == allowSelfServeCompletion && quest.questValidator == questValidator) {
revert SameValidatorSettings();
}
if (allowSelfServeCompletion && questValidator == address(0)) revert InvalidQuestValidator();
quest.selfServeCompletion = allowSelfServeCompletion;
if (!allowSelfServeCompletion) {
quest.questValidator = address(0);
} else {
quest.questValidator = questValidator;
}
emit QuestValidatorUpdated(questId, quest.selfServeCompletion, quest.questValidator);
}
/**
* @notice Used to update the status of a user's quest.
* @dev This can only be called by the contributors of the quest of Flox contributors.
* @dev Setting a `Recursive` quest as `Allocated` will only mark one completed instance as allocated. To mark
* multiple instances as allocated, the `Allocated` status must be set multiple times.
* @param user Address of the user receiving the quest status update
* @param questId ID of the quest being updated
* @param status Status of the user's quest progress after the update
*/
function updateUserQuestStatus(address user, uint256 questId, UserQuestStatus status) external {
_onlyContributor(questId);
if (questId >= nextQuestId) revert QuestDoesNotExist();
if (status <= userQuestStatus[user][questId] && _quests[questId].questType != QuestType.Recursive) {
revert InvalidUserQuestStatusUpdate(userQuestStatus[user][questId], status);
}
_updateUserQuestProgress(user, questId, status);
emit UserQuestStatusUpdated(user, questId, status);
}
/**
* @notice Used to update the status of a user's quest progress for multiple quests and various progress statuses.
* @dev Each status will be applied to the corresponding quest ID.
* @dev This can only be called by the Flox contributors.
* @dev Setting a `Recursive` quest as `Allocated` will only mark one completed instance as allocated. To mark
* multiple instances as allocated, the `Allocated` status must be set multiple times.
* @param user Address of the user receiving the quest status updates
* @param questIds IDs of the quests being updated
* @param statuses Statuses of the user's quest progress after the update
*/
function bulkUpdateSingleUserQuestStatuses(
address user,
uint256[] memory questIds,
UserQuestStatus[] memory statuses
) external {
_onlyFloxContributor();
if (questIds.length != statuses.length) revert ArrayLengthMismatch();
for (uint256 i; i < questIds.length; ) {
if (questIds[i] >= nextQuestId) revert QuestDoesNotExist();
if (statuses[i] <= userQuestStatus[user][questIds[i]]) {
revert InvalidUserQuestStatusUpdate(userQuestStatus[user][questIds[i]], statuses[i]);
}
_updateUserQuestProgress(user, questIds[i], statuses[i]);
emit UserQuestStatusUpdated(user, questIds[i], statuses[i]);
unchecked {
++i;
}
}
}
/**
* @notice Used to update the status of multiple users' progresses in a single quest.
* @dev This can only be called by the contributors of the quest or Flox contributors.
* @param users Addresses of the users receiving the quest status updates
* @param questId ID of the quest being updated
* @param status New status assigned to the users' progress on the quest
*/
function bulkUpdateMultipleUsersQuestStatus(
address[] memory users,
uint256 questId,
UserQuestStatus status
) external {
_onlyContributor(questId);
if (questId >= nextQuestId) revert QuestDoesNotExist();
for (uint256 i; i < users.length; ) {
if (status <= userQuestStatus[users[i]][questId]) {
revert InvalidUserQuestStatusUpdate(userQuestStatus[users[i]][questId], status);
}
_updateUserQuestProgress(users[i], questId, status);
emit UserQuestStatusUpdated(users[i], questId, status);
unchecked {
++i;
}
}
}
/**
* @notice Used to update the status of multiple users' progresses in multiple quests.
* @dev The status at each index will be applied to the corresponding user and quest ID.
* @dev This can only be called by the Flox contributors.
* @dev Setting a `Recursive` quest as `Allocated` will only mark one completed instance as allocated. To mark
* multiple instances as allocated, the `Allocated` status must be set multiple times.
* @param users Addresses of the ussers receiving the quest status updates
* @param questIds IDs of the quests being updated
* @param statuses Quest progress statuses of the users after the update
*/
function bulkUpdateMultipleUsersQuestsStatuses(
address[] memory users,
uint256[] memory questIds,
UserQuestStatus[] memory statuses
) external {
_onlyFloxContributor();
if (users.length != questIds.length) revert ArrayLengthMismatch();
if (questIds.length != statuses.length) revert ArrayLengthMismatch();
for (uint256 i; i < users.length; ) {
if (questIds[i] >= nextQuestId) revert QuestDoesNotExist();
if (statuses[i] <= userQuestStatus[users[i]][questIds[i]]) {
revert InvalidUserQuestStatusUpdate(userQuestStatus[users[i]][questIds[i]], statuses[i]);
}
_updateUserQuestProgress(users[i], questIds[i], statuses[i]);
emit UserQuestStatusUpdated(users[i], questIds[i], statuses[i]);
unchecked {
++i;
}
}
}
/**
* @notice Used to update the status of a user's progress on a quest.
* @dev Once the quest progress status transitions from `Incomplete` to anything else, the experience points for it
* are allocated to the user.
* @dev Once the quest progress status transitions to `Allocated`, the reward earned for completing the quest is
* credited to the user's data.
* @dev `Recursive` quests can only be marked as `Allocated` if there are unallocated completions.
* @param user Address of the user receiving the quest status update
* @param questId ID of the quest being updated
* @param status New status assigned to the user's progress on the quest
*/
function _updateUserQuestProgress(address user, uint256 questId, UserQuestStatus status) internal {
QuestType questType = _quests[questId].questType;
if (questType == QuestType.Limited) {
if (
limitedQuestCompletions[questId] >= _quests[questId].maxCompletions &&
userQuestStatus[user][questId] != UserQuestStatus.PendingAllocation
) {
revert MaximumNumberOfCompletionsReached();
}
}
if (questType == QuestType.Recursive) {
uint64 cooldown = _quests[questId].recursiveCooldown;
if (
_recursiveQuestCompletions[user][questId].lastCompletionTimestamp + uint256(cooldown) > block.timestamp
) {
revert CooldownStillActive();
}
if (
status == UserQuestStatus.Allocated &&
_recursiveQuestCompletions[user][questId].unallocatedCompletions == 0
) {
revert NoRecursiveQuestPendingAllocations();
}
}
if (
userQuestStatus[user][questId] == UserQuestStatus.Incomplete ||
(questType == QuestType.Recursive && status == UserQuestStatus.PendingAllocation)
) {
completedQuestIds[user][_userQuestProgress[user].numberOfCompletedQuests] = questId;
_userQuestProgress[user].numberOfCompletedQuests++;
_userQuestProgress[user].experienceEarned += _quests[questId].experiencePoints;
if (questType == QuestType.Recursive) {
_recursiveQuestCompletions[user][questId].totalCompletions++;
_recursiveQuestCompletions[user][questId].unallocatedCompletions++;
_recursiveQuestCompletions[user][questId].lastCompletionTimestamp = uint128(block.timestamp);
} else if (questType == QuestType.Limited) {
limitedQuestCompletions[questId]++;
}
}
if (status == UserQuestStatus.Allocated) {
_userQuestProgress[user].lifetimePoints += _quests[questId].reward;
if (questType == QuestType.Recursive) {
_recursiveQuestCompletions[user][questId].unallocatedCompletions--;
}
}
userQuestStatus[user][questId] = status;
}
/// Storage gap to prevent storage collisions.
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/**
* ====================================================================
* | ______ _______ |
* | / _____________ __ __ / ____(_____ ____ _____ ________ |
* | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ |
* | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ |
* | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ |
* | |
* ====================================================================
* ===================== IQuestTrackerEnums ===========================
* ====================================================================
* Frax Finance: https://github.com/FraxFinance
*/
/**
* @title IQuestTrackerEnums
* @author Frax Finance
* @notice A collection of enums used by the Flox Quest tracker system.
*/
contract IQuestTrackerEnums {
/**
* @notice This enum represents the global status of a quest.
*/
enum QuestStatus {
Pending, // 0; Quest has not yet been added or fully defined
Upcoming, // 1; Quest is defined but not yet active (the starting block is in the future)
Active, // 2; Quest is currently active and can be completed by the users in order to earn rewards
Expired // 3; Quest is no longer active and can no longer be completed by the users
}
/**
* @notice This enum represents the type of a quest.
* @dev The Unset quest type is used to power the ability to update the quest type.
*/
enum QuestType {
Unset, // 0; Quest type has not yet been set
Single, // 1; Quest can only be completed once
Recursive, // 2; Quest can be completed multiple times
Limited // 3; Quest can be completed by a limited number of users
}
/**
* @notice This enum represents the status of a user's progress on a quest.
*/
enum UserQuestStatus {
Incomplete, // 0; User has not yet completed the quest
PendingAllocation, // 1; User has completed the quest but the reward has not yet been allocated
Allocated // 2; User has completed the quest and the reward has been allocated
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/**
* ====================================================================
* | ______ _______ |
* | / _____________ __ __ / ____(_____ ____ _____ ________ |
* | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ |
* | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ |
* | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ |
* | |
* ====================================================================
* =================== IFNSQuestValidatorErrors =======================
* ====================================================================
* Frax Finance: https://github.com/FraxFinance
*/
/**
* @title IFNSQuestValidatorErrors
* @author Frax Finance
* @notice The IFNSQuestValidatorErrors interface is used to provide the errors used by the FNSQuestValidator smart
* contract.
*/
interface IFNSQuestValidatorErrors {
/// @notice Error emitted when trying to validate a quest with a token that is not owned by the user.
error InvalidTokenOwner();
/// @notice Error emitted when the token has already been used to complete a quest.
error TokenAlreadyUsed();
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/**
* ====================================================================
* | ______ _______ |
* | / _____________ __ __ / ____(_____ ____ _____ ________ |
* | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ |
* | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ |
* | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ |
* | |
* ====================================================================
* =================== IFNSQuestValidatorEvents =======================
* ====================================================================
* Frax Finance: https://github.com/FraxFinance
*/
/**
* @title IFNSQuestValidatorEvents
* @author Frax Finance
* @notice The IFNSQuestValidatorEvents interface is used to provide the events used by the FNSQuestValidator smart
* contract.
*/
interface IFNSQuestValidatorEvents {
/**
* @notice Emitted when a base quest is updated.
* @param baseQuestId ID of the base quest
*/
event BaseQuestUpdate(uint8 baseQuestId);
/**
* @notice Emitted when a limited quest is updated.
* @param domainLength Length of the domain
* @param questId ID of the limited quest associated with the specified domain length
*/
event LimitedQuestUpdate(uint256 domainLength, uint256 questId);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/**
* ====================================================================
* | ______ _______ |
* | / _____________ __ __ / ____(_____ ____ _____ ________ |
* | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ |
* | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ |
* | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ |
* | |
* ====================================================================
* ================= QuestValidatorAccessControl ======================
* ====================================================================
* Frax Finance: https://github.com/FraxFinance
*/
import { QuestValidatorAccessControlEvents } from "./QuestValidatorAccessControlEvents.sol";
import { QuestValidatorAccessControlErrors } from "./QuestValidatorAccessControlErrors.sol";
/**
* @title QuestValidatorAccessControl
* @author Frax Finance
* @notice The QuestValidatorAccessControl contract is used to power the access control of the quest validator for the
* Quest track of the Flox ecosystem.
*/
contract QuestValidatorAccessControl is QuestValidatorAccessControlEvents, QuestValidatorAccessControlErrors {
/// Address of the owner of the contract.
address public owner;
/// Address of the nominated owner of the contract.
address public nominatedOwner;
/**
* @notice Used to track Flox contributors.
* @dev contributor Address of the contributor
* @dev isContributor Status of the contributor
*/
mapping(address contributor => bool isContributor) public floxContributors;
/**
* @notice Used to initialize the smart contract and set the owner.
* @dev Address of the owner of the contract will be set to the deployer.
*/
constructor() {
owner = msg.sender;
}
/**
* @notice Used to restrict function execution to calls initiated by the owner.
*/
modifier onlyOwner() {
if (msg.sender != owner) revert NotOwner();
_;
}
/**
* @notice Nominate a new owner for the contract.
* @dev Only the current owner can nominate a new owner.
* @param _owner Address of the new owner
*/
function nominateNewOwner(address _owner) external onlyOwner {
nominatedOwner = _owner;
emit OwnerNominated(_owner);
}
/**
* @notice Accept the ownership of the contract.
* @dev Only the nominated owner can accept the ownership.
*/
function acceptOwnership() external {
if (msg.sender != nominatedOwner) revert NotNominatedOwner();
address oldOwner = owner;
owner = nominatedOwner;
nominatedOwner = address(0);
emit OwnerChanged(oldOwner, owner);
}
/**
* @notice Manage Flox contributors.
* @dev Flox contributor is allowed to manage all quests as well as their progress status for every user.
* @param _contributor Address of the contributor to manage
* @param _isContributor Status to assign the contributor. `false` to remove, `true` to add.
*/
function manageFloxContributors(address _contributor, bool _isContributor) external onlyOwner {
if (floxContributors[_contributor] == _isContributor) revert SameContributorStatus();
floxContributors[_contributor] = _isContributor;
emit FloxContributorUpdate(_contributor, _isContributor);
}
/**
* @notice Used to restrict function execution to calls initiated by a Flox contributor.
*/
function _onlyFloxContributor() internal view {
if (!floxContributors[msg.sender]) revert NotFloxContributor();
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
interface IFNS {
function names(bytes32) external view returns (bytes memory);
function ownerOf(uint256 id) external view returns (address owner);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/**
* ====================================================================
* | ______ _______ |
* | / _____________ __ __ / ____(_____ ____ _____ ________ |
* | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ |
* | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ |
* | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ |
* | |
* ====================================================================
* =================== QuestTrackerAccessControl ======================
* ====================================================================
* Frax Finance: https://github.com/FraxFinance
*/
import { IQuestTrackerEvents } from "./IQuestTrackerEvents.sol";
/**
* @title QuestTrackerAccessControl
* @author Frax Finance
* @notice The QuestTrackerAccessControl contract is used to power the access control of the QuestTracker smart contract.
*/
contract QuestTrackerAccessControl is IQuestTrackerEvents {
/// Address of the owner of the contract.
address public owner;
/// Address of the nominated owner of the contract.
address public nominatedOwner;
/**
* @notice Used to track Flox contributors.
* @dev contributor Address of the contributor
* @dev isContributor Status of the contributor
*/
mapping(address contributor => bool isContributor) public floxContributors;
/**
* @notice Used to track contributors for specific quests.
* @dev The quest contributors are used to manage the statuses of specific quests.
* @dev questId ID of the quest
* @dev questContibutor Address of the quest contributor
* @dev isContributor Status of the quest contributor
*/
mapping(uint256 questId => mapping(address questContibutor => bool isContributor)) public questContributors;
/**
* @notice Used to initialize the smart contract and set the owner.
* @param _owner Address of the owner of the contract
*/
function initialize(address _owner) public virtual {
if (owner != address(0)) revert AlreadyInitialized();
owner = _owner;
}
/**
* @notice Used to restrict function execution to calls initiated by the owner.
*/
modifier onlyOwner() {
if (msg.sender != owner) revert NotOwner();
_;
}
/**
* @notice Nominate a new owner for the contract.
* @dev Only the current owner can nominate a new owner.
* @param _owner Address of the new owner
*/
function nominateNewOwner(address _owner) external onlyOwner {
nominatedOwner = _owner;
emit OwnerNominated(_owner);
}
/**
* @notice Accept the ownership of the contract.
* @dev Only the nominated owner can accept the ownership.
*/
function acceptOwnership() external {
if (msg.sender != nominatedOwner) revert NotNominatedOwner();
address oldOwner = owner;
owner = nominatedOwner;
nominatedOwner = address(0);
emit OwnerChanged(oldOwner, owner);
}
/**
* @notice Manage Flox contributors.
* @dev Flox contributor is allowed to manage all quests as well as their progress status for every user.
* @param _contributor Address of the contributor to manage
* @param _isContributor Status to assign the contributor. `false` to remove, `true` to add.
*/
function manageFloxContributors(address _contributor, bool _isContributor) external onlyOwner {
if (floxContributors[_contributor] == _isContributor) revert SameContributorStatus();
floxContributors[_contributor] = _isContributor;
emit FloxContributorUpdate(_contributor, _isContributor);
}
/**
* @notice Update the specific contributor for a quest.
* @dev Only the owner can call this function.
* @dev We allow multiple contributors for a single quest, so that we support the possibility of automatic quest
* status updates as well as dedicated validators of quest completion to work in synergy.
* @param _questId ID of the quest we are configuring the specific contibutor for.
* @param _contributor Address of the quest contributor being managed
* @param _isContributor Status to assign the contributor. `false` to remove, `true` to add.
*/
function updateQuestContributor(uint256 _questId, address _contributor, bool _isContributor) external onlyOwner {
if (questContributors[_questId][_contributor] == _isContributor) revert SameContributorStatus();
questContributors[_questId][_contributor] = _isContributor;
emit QuestContributorUpdate(_questId, _contributor, _isContributor);
}
/**
* @notice Used to restrict function execution to calls initiated by a Quest contributor.
* @param _questId ID of the quest to check the quest contributor status for
*/
function _onlyQuestContributor(uint256 _questId) internal view {
if (!questContributors[_questId][msg.sender]) revert NotQuestContributor();
}
/**
* @notice Used to restrict function execution to calls initiated by a Flox contributor.
*/
function _onlyFloxContributor() internal view {
if (!floxContributors[msg.sender]) revert NotFloxContributor();
}
/**
* @notice Used to restrict function execution to calls initiated by a Flox or Quest contributor.
* @param _questId ID of the quest to check the quest contributor status for
*/
function _onlyContributor(uint256 _questId) internal view {
if (!floxContributors[msg.sender] && !questContributors[_questId][msg.sender]) {
revert NotFloxOrQuestContributor();
}
}
/// Storage gap to prevent storage collisions.
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/**
* ====================================================================
* | ______ _______ |
* | / _____________ __ __ / ____(_____ ____ _____ ________ |
* | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ |
* | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ |
* | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ |
* | |
* ====================================================================
* ============== QuestValidatorAccessControlEvents ===================
* ====================================================================
* Frax Finance: https://github.com/FraxFinance
*/
/**
* @title QuestValidatorAccessControlEvents
* @author Frax Finance
* @notice The QuestValidatorAccessControlEvents contract is used to power the quest tracking system for the Flox ecosystem.
*/
contract QuestValidatorAccessControlEvents {
/**
* @notice Emitted when the contributor status of an address is updated.
* @param contributor Adress of the contributor
* @param isContributor Contributor status; `true` if the address is a contributor, `false` otherwise
*/
event FloxContributorUpdate(address contributor, bool isContributor);
/**
* @notice Emitted when a new address is nominated as the owner of the contract.
* @param newOwner Address of the account nominated to be the new owner
*/
event OwnerNominated(address newOwner);
/**
* @notice Emitted when the ownership of the contract is transferred.
* @param oldOwner Address of the previous owner of the smart contract
* @param newOwner Address of the new owner of the smart contract
*/
event OwnerChanged(address oldOwner, address newOwner);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/**
* ====================================================================
* | ______ _______ |
* | / _____________ __ __ / ____(_____ ____ _____ ________ |
* | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ |
* | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ |
* | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ |
* | |
* ====================================================================
* ============== QuestValidatorAccessControlErrors ===================
* ====================================================================
* Frax Finance: https://github.com/FraxFinance
*/
/**
* @title QuestValidatorAccessControlErrors
* @author Frax Finance
* @notice The QuestValidatorAccessControlErrors contract is used to provide the errors used by the quest validators of
* the Quest track of the Flox ecosystem.
*/
contract QuestValidatorAccessControlErrors {
/// @notice Error emitted when the caller is not a Flox contributor.
error NotFloxContributor();
/// @notice Error emitted when the caller is not the nominated owner.
error NotNominatedOwner();
/// @notice Error emitted when the caller is not the owner.
error NotOwner();
/// @notice Error emitted when the attempted contibutor status change is the same as the current status.
error SameContributorStatus();
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/**
* ====================================================================
* | ______ _______ |
* | / _____________ __ __ / ____(_____ ____ _____ ________ |
* | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ |
* | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ |
* | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ |
* | |
* ====================================================================
* ======================== IQuestTrackerEvents =======================
* ====================================================================
* Frax Finance: https://github.com/FraxFinance
*/
import { IQuestTrackerErrors } from "./IQuestTrackerErrors.sol";
/**
* @title IQuestTrackerEvents
* @author Frax Finance
* @notice A collection of events used by the Flox Quest tracker system.
*/
contract IQuestTrackerEvents is IQuestTrackerErrors {
/**
* @notice Emitted when the Flox contributor status is updated.
* @param contributor Address of the cintributor being updated
* @param isContributor New status assigned to the contributor
*/
event FloxContributorUpdate(address contributor, bool isContributor);
/**
* @notice Emitted when the ownership of the contract is transferred.
* @param oldOwner Address of the previous owner
* @param newOwner Address of the new owner
*/
event OwnerChanged(address oldOwner, address newOwner);
/**
* @notice Emitted when a new owner is nominated.
* @param newOwner Address of the account nominated to be the new owner
*/
event OwnerNominated(address newOwner);
/**
* @notice Emitted when a user's progress on a quest is updated.
* @param user Address of the user receiving the quest status update
* @param questId ID of the quest being updated
* @param status New status assigned to the user's progress on the quest
*/
event UserQuestStatusUpdated(address indexed user, uint256 indexed questId, UserQuestStatus status);
/**
* @notice Emitted when a new quest is added.
* @param questId ID of the quest being added
* @param reward Amount of FXTL received upon completion of the quest
* @param startBlock Block at which the quest starts
* @param endBlock Block at which the quest ends
*/
event QuestAdded(uint256 indexed questId, uint256 reward, uint256 startBlock, uint256 endBlock);
/**
* @notice Emitted when the quest contributor status is updated.
* @param questId ID of the quest receiving the contributor update
* @param contributor Address of the contributor being updated
* @param isContributor New status assigned to the contributor
*/
event QuestContributorUpdate(uint256 questId, address contributor, bool isContributor);
/**
* @notice Emitted when a quest is updated.
* @param questId ID of the quest being updated
* @param reward Amount of FXRTL received upon completion of the quest
* @param startBlock Block number at which the quest starts
* @param endBlock BlockNumber at which the quest starts
* @param status Status of the quest after the update
*/
event QuestUpdated(
uint256 indexed questId,
uint256 reward,
uint256 startBlock,
uint256 endBlock,
QuestStatus status
);
/**
* @notice Emitted when the validator settings of a quest are updated.
* @param questId ID of the quest that had validator settings updated
* @param allowsSelfValidation Whether the quest allows self-validation
* @param validator Address of the validator smart contract for the quest
*/
event QuestValidatorUpdated(uint256 indexed questId, bool allowsSelfValidation, address indexed validator);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/**
* ====================================================================
* | ______ _______ |
* | / _____________ __ __ / ____(_____ ____ _____ ________ |
* | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ |
* | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ |
* | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ |
* | |
* ====================================================================
* ======================== IQuestTrackerErrors =======================
* ====================================================================
* Frax Finance: https://github.com/FraxFinance
*/
import { IQuestTrackerEnums } from "./IQuestTrackerEnums.sol";
/**
* @title IQuestTrackerErrors
* @author Frax Finance
* @notice A collection of events used by the Flox Quest tracker system.
*/
contract IQuestTrackerErrors is IQuestTrackerEnums {
/// Returned if the smart contract is already initialized
error AlreadyInitialized();
/// Returned if the length of the arrays passed to a function do not match.
error ArrayLengthMismatch();
/// Returned if the cooldown period of the Recursive quest is still active.
error CooldownStillActive();
/// Returned if the start block is greater than the end block.
error InvalidBlockRange();
/// Returned if the quest validator address is set to the zero address if the self validation is permitted.
error InvalidQuestValidator();
/**
* @notice Returned if the user quest status update is invalid.
* @param currentStatus The current status of the user's progress on the quest
* @param attemptedStatus The status attempted to be assigned to the user's progress on the quest
*/
error InvalidUserQuestStatusUpdate(UserQuestStatus currentStatus, UserQuestStatus attemptedStatus);
/// Signifies that the Limited quest has been completed the maximum number of times.
error MaximumNumberOfCompletionsReached();
/// Signifies that the recursive quest has no pending allocations.
error NoRecursiveQuestPendingAllocations();
/// Signifies that the caller is not a Flox contributor.
error NotFloxContributor();
/// Signifies tht the caller is neiter a Flox or Quest contributor.
error NotFloxOrQuestContributor();
/// Signifies that the caller is not the nominated owner.
error NotNominatedOwner();
/// Signifies that the caller is not the owner.
error NotOwner();
/// Signifies that the caller is not the quest contributor.
error NotQuestContributor();
/// Returned if the quest being accessed does not exist.
error QuestDoesNotExist();
/// Signifies that the attempted status change is the same as the preexisting status.
error SameContributorStatus();
/// Signifies that the attempted quest validator settings updates are the same as the preexisting settings.
error SameValidatorSettings();
}{
"remappings": [
"frax-std/=lib/frax-standard-solidity/src/",
"@eth-optimism/=lib/optimism/packages/",
"lib/optimism/packages/contracts-bedrock:src/=lib/optimism/packages/contracts-bedrock/src/",
"src/=src/",
"@openzeppelin-4/=node_modules/@openzeppelin-4/",
"@openzeppelin-5/=node_modules/@openzeppelin-5/",
"@openzeppelin/=node_modules/@openzeppelin/",
"@rari-capital/=node_modules/@rari-capital/",
"clones-with-immutable-args/=lib/optimism/packages/contracts-bedrock/lib/clones-with-immutable-args/src/",
"ds-test/=lib/frax-standard-solidity/lib/forge-std/lib/ds-test/src/",
"forge-std/=lib/frax-standard-solidity/lib/forge-std/src/",
"frax-standard-solidity/=lib/frax-standard-solidity/src/",
"kontrol-cheatcodes/=lib/optimism/packages/contracts-bedrock/lib/kontrol-cheatcodes/src/",
"lib-keccak/=lib/optimism/packages/contracts-bedrock/lib/lib-keccak/contracts/",
"openzeppelin-contracts-upgradeable/=lib/optimism/packages/contracts-bedrock/lib/openzeppelin-contracts-upgradeable/",
"openzeppelin-contracts/=lib/optimism/packages/contracts-bedrock/lib/openzeppelin-contracts/",
"optimism/=lib/optimism/",
"safe-contracts/=lib/optimism/packages/contracts-bedrock/lib/safe-contracts/contracts/",
"solady/=lib/optimism/packages/contracts-bedrock/lib/solady/",
"solidity-bytes-utils/=lib/frax-standard-solidity/lib/solidity-bytes-utils/",
"solmate/=lib/optimism/packages/contracts-bedrock/lib/solmate/src/"
],
"optimizer": {
"enabled": true,
"runs": 100000
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "paris",
"viaIR": false,
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_questTracker","type":"address"},{"internalType":"address","name":"_fns","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"InvalidTokenOwner","type":"error"},{"inputs":[],"name":"NotFloxContributor","type":"error"},{"inputs":[],"name":"NotNominatedOwner","type":"error"},{"inputs":[],"name":"NotOwner","type":"error"},{"inputs":[],"name":"SameContributorStatus","type":"error"},{"inputs":[],"name":"TokenAlreadyUsed","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"baseQuestId","type":"uint8"}],"name":"BaseQuestUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"contributor","type":"address"},{"indexed":false,"internalType":"bool","name":"isContributor","type":"bool"}],"name":"FloxContributorUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"domainLength","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"questId","type":"uint256"}],"name":"LimitedQuestUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerNominated","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"domainLength","type":"uint256"},{"internalType":"uint256","name":"questId","type":"uint256"}],"name":"addLimitedQuest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"baseQuestId","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"bulkSelfValidateQuests","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"contributor","type":"address"}],"name":"floxContributors","outputs":[{"internalType":"bool","name":"isContributor","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fns","outputs":[{"internalType":"contract IFNS","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"domainLength","type":"uint256"}],"name":"lengthToQuestId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"longestLimitedQuestLength","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_contributor","type":"address"},{"internalType":"bool","name":"_isContributor","type":"bool"}],"name":"manageFloxContributors","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"nominateNewOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"nominatedOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"questTracker","outputs":[{"internalType":"contract QuestTracker","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"selfValidateQuest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"_baseQuestId","type":"uint8"}],"name":"updateBaseQuestId","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"usedTokens","outputs":[{"internalType":"bool","name":"alreadyUsed","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"user","type":"address"}],"name":"validateQuestForUser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"address[]","name":"users","type":"address[]"}],"name":"validateQuestsForMulitpleUsers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"address","name":"user","type":"address"}],"name":"validateQuestsForSingleUser","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
608060405234801561001057600080fd5b506040516114ec3803806114ec83398101604081905261002f91610087565b600080546001600160a01b03199081163317909155600380546001600160a01b03948516908316179055600480549290931691161790556100ba565b80516001600160a01b038116811461008257600080fd5b919050565b6000806040838503121561009a57600080fd5b6100a38361006b565b91506100b16020840161006b565b90509250929050565b611423806100c96000396000f3fe608060405234801561001057600080fd5b50600436106101515760003560e01c806379ba5097116100cd578063b382e4c511610081578063f501127a11610066578063f501127a14610324578063fc403b7a14610337578063fe5979fb1461034a57600080fd5b8063b382e4c5146102f1578063ee7db1331461031157600080fd5b8063889338d0116100b2578063889338d0146102ab5780638da5cb5b146102be578063af1bee0b146102de57600080fd5b806379ba509714610290578063888d911f1461029857600080fd5b80634fd9d1e0116101245780636024a641116101095780636024a6411461023e5780636b26aafc14610262578063782f67981461028357600080fd5b80634fd9d1e0146101fb57806353a47bb71461021e57600080fd5b80630f7ee879146101565780631627540c1461018e57806328f30c51146101a35780632f2f8c36146101e8575b600080fd5b610179610164366004610eac565b60056020526000908152604090205460ff1681565b60405190151581526020015b60405180910390f35b6101a161019c366004610ee7565b61035d565b005b6003546101c39073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610185565b6101a16101f6366004611015565b610428565b610179610209366004610ee7565b60026020526000908152604090205460ff1681565b6001546101c39073ffffffffffffffffffffffffffffffffffffffff1681565b60075461025090610100900460ff1681565b60405160ff9091168152602001610185565b610275610270366004610eac565b61046c565b604051908152602001610185565b6007546102509060ff1681565b6101a16104a8565b6101a16102a6366004611067565b610584565b6101a16102b936600461112b565b6105dc565b6000546101c39073ffffffffffffffffffffffffffffffffffffffff1681565b6101a16102ec366004611168565b610617565b6004546101c39073ffffffffffffffffffffffffffffffffffffffff1681565b6101a161031f366004610eac565b61067f565b6101a161033236600461118b565b61068c565b6101a16103453660046111b0565b61069e565b6101a16103583660046111e3565b6107e5565b60005473ffffffffffffffffffffffffffffffffffffffff1633146103ae576040517f30cd747100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040519081527f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce22906020015b60405180910390a150565b610430610878565b60005b82518110156104675761045f83828151811061045157610451611205565b6020026020010151836108c3565b600101610433565b505050565b600754600090610100900460ff168210156104875781610493565b600754610100900460ff165b60009081526006602052604090205492915050565b60015473ffffffffffffffffffffffffffffffffffffffff1633146104f9576040517fb1f6da5000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080546001805473ffffffffffffffffffffffffffffffffffffffff8082167fffffffffffffffffffffffff0000000000000000000000000000000000000000808616821790965594909116909155604080519190921680825260208201939093527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c910161041d565b61058c610878565b60005b8251811015610467576105d48382815181106105ad576105ad611205565b60200260200101518383815181106105c7576105c7611205565b60200260200101516108c3565b60010161058f565b60005b81518110156106135761060b8282815181106105fd576105fd611205565b6020026020010151336108c3565b6001016105df565b5050565b61061f610878565b600780547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff83169081179091556040519081527f031379e7d376148a539f4a337e00ec2654764c77feb4e1b281c0a21b9d21b3669060200161041d565b61068981336108c3565b50565b610694610878565b61061382826108c3565b60005473ffffffffffffffffffffffffffffffffffffffff1633146106ef576040517f30cd747100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff821660009081526002602052604090205481151560ff909116151503610756576040517fd31fc68300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff821660008181526002602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00168515159081179091558251938452908301527f1b6e6916bd76fb0e62ff02acf466a38852d02426c13ba96e69803693ac627d5891015b60405180910390a15050565b6107ed610878565b6000828152600660205260409020819055600754610100900460ff1682111561084257600780547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff1661010060ff8516021790555b60408051838152602081018390527f5c9eca55d6b1f6aece7086203e2ef8884f05d1fcad00cbdbdb16affbd39a518f91016107d9565b3360009081526002602052604090205460ff166108c1576040517f6b697ed100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b60008281526005602052604090205460ff161561090c576040517f883209e000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600480546040517f6352211e00000000000000000000000000000000000000000000000000000000815291820184905273ffffffffffffffffffffffffffffffffffffffff83811692911690636352211e90602401602060405180830381865afa15801561097e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109a29190611234565b73ffffffffffffffffffffffffffffffffffffffff16146109ef576040517f2a7c6b6e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008060006109fd85610bb8565b90506000610a0a82610c77565b600754909150610a2090869060ff166000610c9c565b600754909450610100900460ff16811015610a3b5780610a47565b600754610100900460ff165b9050610a5d85610a568361046c565b6001610c9c565b600087815260056020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905592508315610b2f576003546007546040517f36d1467a00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff909216916336d1467a91610afc91899160ff1690600190600401611280565b600060405180830381600087803b158015610b1657600080fd5b505af1158015610b2a573d6000803e3d6000fd5b505050505b8215610bb05760035473ffffffffffffffffffffffffffffffffffffffff166336d1467a86610b5d8461046c565b60016040518463ffffffff1660e01b8152600401610b7d93929190611280565b600060405180830381600087803b158015610b9757600080fd5b505af1158015610bab573d6000803e3d6000fd5b505050505b505050505050565b600480546040517f20c38e2b000000000000000000000000000000000000000000000000000000008152918201839052606091839173ffffffffffffffffffffffffffffffffffffffff16906320c38e2b90602401600060405180830381865afa158015610c2a573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052610c7091908101906112e9565b9392505050565b60008082600081518110610c8d57610c8d611205565b016020015160f81c9392505050565b60008115610df6576003546040517fe02140f900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff868116600483015260248201869052600092169063e02140f990604401602060405180830381865afa158015610d1c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d4091906113b3565b6002811115610d5157610d51611251565b148015610def57506003546040517f3dd030420000000000000000000000000000000000000000000000000000000081526004810185905260009173ffffffffffffffffffffffffffffffffffffffff1690633dd0304290602401602060405180830381865afa158015610dc9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ded91906113d4565b115b9050610c70565b6003546040517fe02140f900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff868116600483015260248201869052600092169063e02140f990604401602060405180830381865afa158015610e6e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e9291906113b3565b6002811115610ea357610ea3611251565b14949350505050565b600060208284031215610ebe57600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff8116811461068957600080fd5b600060208284031215610ef957600080fd5b8135610c7081610ec5565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715610f7a57610f7a610f04565b604052919050565b600067ffffffffffffffff821115610f9c57610f9c610f04565b5060051b60200190565b600082601f830112610fb757600080fd5b81356020610fcc610fc783610f82565b610f33565b8083825260208201915060208460051b870101935086841115610fee57600080fd5b602086015b8481101561100a5780358352918301918301610ff3565b509695505050505050565b6000806040838503121561102857600080fd5b823567ffffffffffffffff81111561103f57600080fd5b61104b85828601610fa6565b925050602083013561105c81610ec5565b809150509250929050565b6000806040838503121561107a57600080fd5b823567ffffffffffffffff8082111561109257600080fd5b61109e86838701610fa6565b93506020915081850135818111156110b557600080fd5b85019050601f810186136110c857600080fd5b80356110d6610fc782610f82565b81815260059190911b820183019083810190888311156110f557600080fd5b928401925b8284101561111c57833561110d81610ec5565b825292840192908401906110fa565b80955050505050509250929050565b60006020828403121561113d57600080fd5b813567ffffffffffffffff81111561115457600080fd5b61116084828501610fa6565b949350505050565b60006020828403121561117a57600080fd5b813560ff81168114610c7057600080fd5b6000806040838503121561119e57600080fd5b82359150602083013561105c81610ec5565b600080604083850312156111c357600080fd5b82356111ce81610ec5565b91506020830135801515811461105c57600080fd5b600080604083850312156111f657600080fd5b50508035926020909101359150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60006020828403121561124657600080fd5b8151610c7081610ec5565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b73ffffffffffffffffffffffffffffffffffffffff841681526020810183905260608101600383106112db577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b826040830152949350505050565b600060208083850312156112fc57600080fd5b825167ffffffffffffffff8082111561131457600080fd5b818501915085601f83011261132857600080fd5b81518181111561133a5761133a610f04565b61136a847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601610f33565b9150808252868482850101111561138057600080fd5b60005b8181101561139e578381018501518382018601528401611383565b50600090820190930192909252509392505050565b6000602082840312156113c557600080fd5b815160038110610c7057600080fd5b6000602082840312156113e657600080fd5b505191905056fea2646970667358221220816c1a85841541e8cff556f4fdb35ad3f5ebcc8588d4f3243adb8a2b2f9b66d964736f6c634300081700330000000000000000000000006e174ebf5377246d1a0c77734c79a28318f1f205000000000000000000000000b989e514980dc837fd554f1f85673b0091cf25f3
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101515760003560e01c806379ba5097116100cd578063b382e4c511610081578063f501127a11610066578063f501127a14610324578063fc403b7a14610337578063fe5979fb1461034a57600080fd5b8063b382e4c5146102f1578063ee7db1331461031157600080fd5b8063889338d0116100b2578063889338d0146102ab5780638da5cb5b146102be578063af1bee0b146102de57600080fd5b806379ba509714610290578063888d911f1461029857600080fd5b80634fd9d1e0116101245780636024a641116101095780636024a6411461023e5780636b26aafc14610262578063782f67981461028357600080fd5b80634fd9d1e0146101fb57806353a47bb71461021e57600080fd5b80630f7ee879146101565780631627540c1461018e57806328f30c51146101a35780632f2f8c36146101e8575b600080fd5b610179610164366004610eac565b60056020526000908152604090205460ff1681565b60405190151581526020015b60405180910390f35b6101a161019c366004610ee7565b61035d565b005b6003546101c39073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610185565b6101a16101f6366004611015565b610428565b610179610209366004610ee7565b60026020526000908152604090205460ff1681565b6001546101c39073ffffffffffffffffffffffffffffffffffffffff1681565b60075461025090610100900460ff1681565b60405160ff9091168152602001610185565b610275610270366004610eac565b61046c565b604051908152602001610185565b6007546102509060ff1681565b6101a16104a8565b6101a16102a6366004611067565b610584565b6101a16102b936600461112b565b6105dc565b6000546101c39073ffffffffffffffffffffffffffffffffffffffff1681565b6101a16102ec366004611168565b610617565b6004546101c39073ffffffffffffffffffffffffffffffffffffffff1681565b6101a161031f366004610eac565b61067f565b6101a161033236600461118b565b61068c565b6101a16103453660046111b0565b61069e565b6101a16103583660046111e3565b6107e5565b60005473ffffffffffffffffffffffffffffffffffffffff1633146103ae576040517f30cd747100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040519081527f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce22906020015b60405180910390a150565b610430610878565b60005b82518110156104675761045f83828151811061045157610451611205565b6020026020010151836108c3565b600101610433565b505050565b600754600090610100900460ff168210156104875781610493565b600754610100900460ff165b60009081526006602052604090205492915050565b60015473ffffffffffffffffffffffffffffffffffffffff1633146104f9576040517fb1f6da5000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080546001805473ffffffffffffffffffffffffffffffffffffffff8082167fffffffffffffffffffffffff0000000000000000000000000000000000000000808616821790965594909116909155604080519190921680825260208201939093527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c910161041d565b61058c610878565b60005b8251811015610467576105d48382815181106105ad576105ad611205565b60200260200101518383815181106105c7576105c7611205565b60200260200101516108c3565b60010161058f565b60005b81518110156106135761060b8282815181106105fd576105fd611205565b6020026020010151336108c3565b6001016105df565b5050565b61061f610878565b600780547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff83169081179091556040519081527f031379e7d376148a539f4a337e00ec2654764c77feb4e1b281c0a21b9d21b3669060200161041d565b61068981336108c3565b50565b610694610878565b61061382826108c3565b60005473ffffffffffffffffffffffffffffffffffffffff1633146106ef576040517f30cd747100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff821660009081526002602052604090205481151560ff909116151503610756576040517fd31fc68300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff821660008181526002602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00168515159081179091558251938452908301527f1b6e6916bd76fb0e62ff02acf466a38852d02426c13ba96e69803693ac627d5891015b60405180910390a15050565b6107ed610878565b6000828152600660205260409020819055600754610100900460ff1682111561084257600780547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff1661010060ff8516021790555b60408051838152602081018390527f5c9eca55d6b1f6aece7086203e2ef8884f05d1fcad00cbdbdb16affbd39a518f91016107d9565b3360009081526002602052604090205460ff166108c1576040517f6b697ed100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b60008281526005602052604090205460ff161561090c576040517f883209e000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600480546040517f6352211e00000000000000000000000000000000000000000000000000000000815291820184905273ffffffffffffffffffffffffffffffffffffffff83811692911690636352211e90602401602060405180830381865afa15801561097e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109a29190611234565b73ffffffffffffffffffffffffffffffffffffffff16146109ef576040517f2a7c6b6e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008060006109fd85610bb8565b90506000610a0a82610c77565b600754909150610a2090869060ff166000610c9c565b600754909450610100900460ff16811015610a3b5780610a47565b600754610100900460ff165b9050610a5d85610a568361046c565b6001610c9c565b600087815260056020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905592508315610b2f576003546007546040517f36d1467a00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff909216916336d1467a91610afc91899160ff1690600190600401611280565b600060405180830381600087803b158015610b1657600080fd5b505af1158015610b2a573d6000803e3d6000fd5b505050505b8215610bb05760035473ffffffffffffffffffffffffffffffffffffffff166336d1467a86610b5d8461046c565b60016040518463ffffffff1660e01b8152600401610b7d93929190611280565b600060405180830381600087803b158015610b9757600080fd5b505af1158015610bab573d6000803e3d6000fd5b505050505b505050505050565b600480546040517f20c38e2b000000000000000000000000000000000000000000000000000000008152918201839052606091839173ffffffffffffffffffffffffffffffffffffffff16906320c38e2b90602401600060405180830381865afa158015610c2a573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052610c7091908101906112e9565b9392505050565b60008082600081518110610c8d57610c8d611205565b016020015160f81c9392505050565b60008115610df6576003546040517fe02140f900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff868116600483015260248201869052600092169063e02140f990604401602060405180830381865afa158015610d1c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d4091906113b3565b6002811115610d5157610d51611251565b148015610def57506003546040517f3dd030420000000000000000000000000000000000000000000000000000000081526004810185905260009173ffffffffffffffffffffffffffffffffffffffff1690633dd0304290602401602060405180830381865afa158015610dc9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ded91906113d4565b115b9050610c70565b6003546040517fe02140f900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff868116600483015260248201869052600092169063e02140f990604401602060405180830381865afa158015610e6e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e9291906113b3565b6002811115610ea357610ea3611251565b14949350505050565b600060208284031215610ebe57600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff8116811461068957600080fd5b600060208284031215610ef957600080fd5b8135610c7081610ec5565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715610f7a57610f7a610f04565b604052919050565b600067ffffffffffffffff821115610f9c57610f9c610f04565b5060051b60200190565b600082601f830112610fb757600080fd5b81356020610fcc610fc783610f82565b610f33565b8083825260208201915060208460051b870101935086841115610fee57600080fd5b602086015b8481101561100a5780358352918301918301610ff3565b509695505050505050565b6000806040838503121561102857600080fd5b823567ffffffffffffffff81111561103f57600080fd5b61104b85828601610fa6565b925050602083013561105c81610ec5565b809150509250929050565b6000806040838503121561107a57600080fd5b823567ffffffffffffffff8082111561109257600080fd5b61109e86838701610fa6565b93506020915081850135818111156110b557600080fd5b85019050601f810186136110c857600080fd5b80356110d6610fc782610f82565b81815260059190911b820183019083810190888311156110f557600080fd5b928401925b8284101561111c57833561110d81610ec5565b825292840192908401906110fa565b80955050505050509250929050565b60006020828403121561113d57600080fd5b813567ffffffffffffffff81111561115457600080fd5b61116084828501610fa6565b949350505050565b60006020828403121561117a57600080fd5b813560ff81168114610c7057600080fd5b6000806040838503121561119e57600080fd5b82359150602083013561105c81610ec5565b600080604083850312156111c357600080fd5b82356111ce81610ec5565b91506020830135801515811461105c57600080fd5b600080604083850312156111f657600080fd5b50508035926020909101359150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60006020828403121561124657600080fd5b8151610c7081610ec5565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b73ffffffffffffffffffffffffffffffffffffffff841681526020810183905260608101600383106112db577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b826040830152949350505050565b600060208083850312156112fc57600080fd5b825167ffffffffffffffff8082111561131457600080fd5b818501915085601f83011261132857600080fd5b81518181111561133a5761133a610f04565b61136a847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601610f33565b9150808252868482850101111561138057600080fd5b60005b8181101561139e578381018501518382018601528401611383565b50600090820190930192909252509392505050565b6000602082840312156113c557600080fd5b815160038110610c7057600080fd5b6000602082840312156113e657600080fd5b505191905056fea2646970667358221220816c1a85841541e8cff556f4fdb35ad3f5ebcc8588d4f3243adb8a2b2f9b66d964736f6c63430008170033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000006e174ebf5377246d1a0c77734c79a28318f1f205000000000000000000000000b989e514980dc837fd554f1f85673b0091cf25f3
-----Decoded View---------------
Arg [0] : _questTracker (address): 0x6e174EBf5377246d1A0C77734C79a28318f1F205
Arg [1] : _fns (address): 0xb989e514980dC837Fd554f1F85673B0091cf25F3
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 0000000000000000000000006e174ebf5377246d1a0c77734c79a28318f1f205
Arg [1] : 000000000000000000000000b989e514980dc837fd554f1f85673b0091cf25f3
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in FRAX
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.