Description:
Proxy contract enabling upgradeable smart contract patterns. Delegates calls to an implementation contract.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"src/ReputationTracker.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;
import "utility-contracts/TwoStepOwnable.sol";
/**
* @title ReputationTracker
* @notice Tracks reputation points for wallets, updatable only by QuantumEntities.
* Uses SeaDrop's TwoStepOwnable (no OZ Ownable).
*/
contract ReputationTracker is TwoStepOwnable {
// =============================================================
// STORAGE
// =============================================================
address public quantumEntitiesContract;
struct WalletReputation {
uint128 totalHoldTime;
uint32 transferCount;
uint32 originalMints;
uint32 currentTokens;
uint32 lastUpdateTime;
uint32 interactionPoints;
uint32 totalTokensEverHeld;
uint16 longestHoldStreak;
bool isFounder;
}
struct TokenReputation {
uint32 totalTransfers;
uint32 lastTransferTime;
uint32 mintTime;
address currentHolder;
}
mapping(address => WalletReputation) public walletReputation;
mapping(uint256 => TokenReputation) public tokenReputation;
uint256 public constant ORIGINAL_MINT_BONUS = 200;
uint256 public constant DAILY_HOLD_BONUS = 10;
uint256 public constant TRANSFER_PENALTY = 50;
uint256 public constant FOUNDER_MULTIPLIER = 150;
// =============================================================
// EVENTS
// =============================================================
event ReputationUpdated(address indexed wallet, uint256 newScore);
event InteractionRecorded(address indexed wallet, string action, uint32 points);
event FounderStatusGranted(address indexed wallet);
// =============================================================
// MODIFIERS
// =============================================================
modifier onlyQuantumEntities() {
require(msg.sender == quantumEntitiesContract, "Only QuantumEntities contract");
_;
}
// =============================================================
// CONSTRUCTOR
// =============================================================
constructor() {
// quantumEntitiesContract will be set after deployment
}
// =============================================================
// CORE FUNCTIONS
// =============================================================
function updateOnMint(address minter, uint256 tokenId) external onlyQuantumEntities {
WalletReputation storage rep = walletReputation[minter];
rep.originalMints++;
rep.currentTokens++;
rep.totalTokensEverHeld++;
rep.lastUpdateTime = uint32(block.timestamp);
tokenReputation[tokenId] = TokenReputation({
totalTransfers: 0,
lastTransferTime: uint32(block.timestamp),
mintTime: uint32(block.timestamp),
currentHolder: minter
});
emit ReputationUpdated(minter, calculateWalletScore(minter));
}
function updateOnTransfer(address from, address to, uint256 tokenId) external onlyQuantumEntities {
_updateHoldTime(from);
WalletReputation storage fromRep = walletReputation[from];
fromRep.transferCount++;
fromRep.currentTokens--;
fromRep.lastUpdateTime = uint32(block.timestamp);
_updateHoldTime(to);
WalletReputation storage toRep = walletReputation[to];
toRep.currentTokens++;
toRep.totalTokensEverHeld++;
toRep.lastUpdateTime = uint32(block.timestamp);
TokenReputation storage tokenRep = tokenReputation[tokenId];
tokenRep.totalTransfers++;
tokenRep.lastTransferTime = uint32(block.timestamp);
tokenRep.currentHolder = to;
_updateHoldStreak(from);
emit ReputationUpdated(from, calculateWalletScore(from));
emit ReputationUpdated(to, calculateWalletScore(to));
}
function _updateHoldTime(address wallet) internal {
WalletReputation storage rep = walletReputation[wallet];
if (rep.lastUpdateTime > 0 && rep.currentTokens > 0) {
uint256 timeDiff = block.timestamp - rep.lastUpdateTime;
rep.totalHoldTime += uint128(timeDiff);
}
}
function _updateHoldStreak(address wallet) internal {
WalletReputation storage rep = walletReputation[wallet];
if (rep.lastUpdateTime > 0) {
uint256 holdDuration = block.timestamp - rep.lastUpdateTime;
uint16 holdDays = uint16(holdDuration / 86400);
if (holdDays > rep.longestHoldStreak) {
rep.longestHoldStreak = holdDays;
}
}
}
// =============================================================
// REPUTATION CALCULATION
// =============================================================
function calculateWalletScore(address wallet) public view returns (uint256) {
WalletReputation memory rep = walletReputation[wallet];
if (rep.lastUpdateTime == 0) return 0;
uint256 score = 0;
score += rep.originalMints * ORIGINAL_MINT_BONUS;
uint256 totalHoldTime = rep.totalHoldTime;
if (rep.currentTokens > 0 && rep.lastUpdateTime > 0) {
totalHoldTime += block.timestamp - rep.lastUpdateTime;
}
score += (totalHoldTime / 86400) * DAILY_HOLD_BONUS;
if (rep.transferCount > 0) {
uint256 penalty = rep.transferCount * TRANSFER_PENALTY;
uint256 maxPenalty = score / 2;
penalty = penalty > maxPenalty ? maxPenalty : penalty;
score = score > penalty ? score - penalty : score / 2;
}
score += rep.interactionPoints;
score += rep.longestHoldStreak * 5;
score += rep.totalTokensEverHeld * 50;
if (rep.isFounder) {
score = (score * FOUNDER_MULTIPLIER) / 100;
}
return score;
}
function getReputationTier(address wallet) external view returns (string memory) {
uint256 score = calculateWalletScore(wallet);
if (score >= 10000) return "QUANTUM MASTER";
if (score >= 5000) return "ENTITY GUARDIAN";
if (score >= 2500) return "PARTICLE SAGE";
if (score >= 1000) return "VOID WALKER";
if (score >= 500) return "DIGITAL NOMAD";
if (score >= 100) return "QUANTUM NOVICE";
return "OBSERVER";
}
// =============================================================
// INTERACTION SYSTEM
// =============================================================
function recordInteraction(address wallet, string calldata action, uint32 points) external onlyOwner {
WalletReputation storage rep = walletReputation[wallet];
rep.interactionPoints += points;
rep.lastUpdateTime = uint32(block.timestamp);
emit InteractionRecorded(wallet, action, points);
emit ReputationUpdated(wallet, calculateWalletScore(wallet));
}
function batchRecordInteractions(
address[] calldata wallets,
string calldata action,
uint32 points
) external onlyOwner {
for (uint256 i = 0; i < wallets.length; i++) {
WalletReputation storage rep = walletReputation[wallets[i]];
rep.interactionPoints += points;
rep.lastUpdateTime = uint32(block.timestamp);
emit InteractionRecorded(wallets[i], action, points);
emit ReputationUpdated(wallets[i], calculateWalletScore(wallets[i]));
}
}
function grantFounderStatus(address wallet) external onlyOwner {
walletReputation[wallet].isFounder = true;
emit FounderStatusGranted(wallet);
emit ReputationUpdated(wallet, calculateWalletScore(wallet));
}
function batchGrantFounderStatus(address[] calldata wallets) external onlyOwner {
for (uint256 i = 0; i < wallets.length; i++) {
walletReputation[wallets[i]].isFounder = true;
emit FounderStatusGranted(wallets[i]);
emit ReputationUpdated(wallets[i], calculateWalletScore(wallets[i]));
}
}
// =============================================================
// VIEW FUNCTIONS
// =============================================================
function getWalletScore(address wallet) external view returns (uint256) {
return calculateWalletScore(wallet);
}
function getWalletStats(address wallet) external view returns (
uint256 score,
string memory tier,
uint32 originalMints,
uint32 currentTokens,
uint32 transferCount,
uint16 longestHoldStreak,
uint32 totalTokensEverHeld,
bool isFounder
) {
WalletReputation memory rep = walletReputation[wallet];
return (
calculateWalletScore(wallet),
this.getReputationTier(wallet),
rep.originalMints,
rep.currentTokens,
rep.transferCount,
rep.longestHoldStreak,
rep.totalTokensEverHeld,
rep.isFounder
);
}
function getTokenStats(uint256 tokenId) external view returns (
uint32 totalTransfers,
uint32 lastTransferTime,
uint32 mintTime,
address currentHolder,
uint32 ageInDays
) {
TokenReputation memory rep = tokenReputation[tokenId];
return (
rep.totalTransfers,
rep.lastTransferTime,
rep.mintTime,
rep.currentHolder,
rep.mintTime > 0 ? uint32((block.timestamp - rep.mintTime) / 86400) : 0
);
}
function qualifiesForBenefit(address wallet, string calldata benefitType) external view returns (bool) {
uint256 score = calculateWalletScore(wallet);
WalletReputation memory rep = walletReputation[wallet];
bytes32 benefit = keccak256(abi.encodePacked(benefitType));
if (benefit == keccak256("EARLY_ACCESS")) {
return score >= 1000 || rep.isFounder;
}
if (benefit == keccak256("REDUCED_FEES")) {
return score >= 2500;
}
if (benefit == keccak256("GOVERNANCE_VOTE")) {
return score >= 500 && rep.originalMints > 0;
}
if (benefit == keccak256("EXCLUSIVE_DROPS")) {
return score >= 5000 || rep.isFounder;
}
return false;
}
// =============================================================
// ADMIN FUNCTIONS
// =============================================================
function setQuantumEntitiesContract(address _contract) external onlyOwner {
quantumEntitiesContract = _contract;
}
function resetWalletReputation(address wallet) external onlyOwner {
delete walletReputation[wallet];
emit ReputationUpdated(wallet, 0);
}
}"
},
"lib/seadrop/lib/utility-contracts/src/TwoStepOwnable.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;
import {ConstructorInitializable} from "./ConstructorInitializable.sol";
/**
@notice A two-step extension of Ownable, where the new owner must claim ownership of the contract after owner initiates transfer
Owner can cancel the transfer at any point before the new owner claims ownership.
Helpful in guarding against transferring ownership to an address that is unable to act as the Owner.
*/
abstract contract TwoStepOwnable is ConstructorInitializable {
address private _owner;
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);
address internal potentialOwner;
event PotentialOwnerUpdated(address newPotentialAdministrator);
error NewOwnerIsZeroAddress();
error NotNextOwner();
error OnlyOwner();
modifier onlyOwner() {
_checkOwner();
_;
}
constructor() {
_initialize();
}
function _initialize() private onlyConstructor {
_transferOwnership(msg.sender);
}
///@notice Initiate ownership transfer to newPotentialOwner. Note: new owner will have to manually acceptOwnership
///@param newPotentialOwner address of potential new owner
function transferOwnership(address newPotentialOwner)
public
virtual
onlyOwner
{
if (newPotentialOwner == address(0)) {
revert NewOwnerIsZeroAddress();
}
potentialOwner = newPotentialOwner;
emit PotentialOwnerUpdated(newPotentialOwner);
}
///@notice Claim ownership of smart contract, after the current owner has initiated the process with transferOwnership
function acceptOwnership() public virtual {
address _potentialOwner = potentialOwner;
if (msg.sender != _potentialOwner) {
revert NotNextOwner();
}
delete potentialOwner;
emit PotentialOwnerUpdated(address(0));
_transferOwnership(_potentialOwner);
}
///@notice cancel ownership transfer
function cancelOwnershipTransfer() public virtual onlyOwner {
delete potentialOwner;
emit PotentialOwnerUpdated(address(0));
}
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (_owner != msg.sender) {
revert OnlyOwner();
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing 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`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
"
},
"lib/seadrop/lib/utility-contracts/src/ConstructorInitializable.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;
/**
* @author emo.eth
* @notice Abstract smart contract that provides an onlyUninitialized modifier which only allows calling when
* from within a constructor of some sort, whether directly instantiating an inherting contract,
* or when delegatecalling from a proxy
*/
abstract contract ConstructorInitializable {
error AlreadyInitialized();
modifier onlyConstructor() {
if (address(this).code.length != 0) {
revert AlreadyInitialized();
}
_;
}
}
"
}
},
"settings": {
"remappings": [
"@openzeppelin/=lib/openzeppelin-contracts/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/",
"erc721a-seadrop/=lib/seadrop/",
"seadrop/=lib/seadrop/src/",
"forge-std/=lib/forge-std/src/",
"solmate/=lib/seadrop/lib/solmate/src/",
"utility-contracts/=lib/seadrop/lib/utility-contracts/src/",
"ERC721A/=lib/seadrop/lib/ERC721A/contracts/",
"ERC721A-Upgradeable/=lib/seadrop/lib/ERC721A-Upgradeable/contracts/",
"create2-helpers/=lib/seadrop/lib/create2-helpers/",
"create2-scripts/=lib/seadrop/lib/create2-helpers/script/",
"ds-test/=lib/seadrop/lib/ds-test/src/",
"erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
"murky/=lib/seadrop/lib/murky/src/",
"openzeppelin-contracts-upgradeable/=lib/seadrop/lib/openzeppelin-contracts-upgradeable/",
"openzeppelin/=lib/openzeppelin-contracts/contracts/",
"operator-filter-registry/=lib/seadrop/lib/operator-filter-registry/src/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs"
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "london",
"viaIR": true
}
}}
Submitted on: 2025-09-18 12:03:29
Comments
Log in to comment.
No comments yet.