// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
// Import OpenZeppelin contracts for secure ERC20 token functionality.
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
/// @notice Interface for the Festival Pass NFT contract (simplified)
interface IFestivalPassNFT {
function mintPass(address to, uint256 tier) external;
function getUserTier(address user) external view returns (bool eligible, uint256 tier);
}
/// @title Eternity X (ETX) Smart Contract
/// @notice Combines an ERC20 utility token (with burn/mint/pause features) with a tiered staking rewards system and auxiliary features.
contract EternityX is ERC20, ERC20Burnable, Pausable, Ownable {
// =============================================================
// TOKEN CONFIGURATION
// =============================================================
uint256 public constant TOTAL_SUPPLY = 10_000_000 * 10 ** 18; // Total supply: 10M ETX
// Distribution:
// • 50% Public Sale → minted to ownerWallet
// • 20% Staking Rewards → minted to the contract itself (for reward distribution)
// • 20% Team & Development → minted to ownerWallet
// • 10% Ecosystem Growth → minted to ownerWallet
// =============================================================
// STAKING / REWARDS CONFIGURATION
// =============================================================
// Block frequency estimation on BNB Smart Chain (~2 seconds per block)
uint256 public constant BLOCKS_PER_YEAR = 1_576_800;
// Staking thresholds (token amounts with 18 decimals)
uint256 public constant SILVER_THRESHOLD = 10_000 * 10 ** 18; // ≥10,000 ETX qualifies for Silver tier
uint256 public constant GOLD_THRESHOLD = 50_000 * 10 ** 18; // ≥50,000 ETX qualifies for Gold tier
// Users staking >0 but less than SILVER_THRESHOLD are Bronze
// Annual reward percentages are converted into per‑block reward rates (scaled by 1e18).
// Calculation for each tier: rewardRate = (annualRatePercentage * 1e18) / BLOCKS_PER_YEAR.
// For example:
// • Gold: 3.2% annual → (0.032 * 1e18) / BLOCKS_PER_YEAR
// • Silver:2.2% annual → (0.022 * 1e18) / BLOCKS_PER_YEAR
// • Bronze:1.2% annual → (0.012 * 1e18) / BLOCKS_PER_YEAR
uint256 public constant GOLD_REWARD_RATE = 32000000000000000 / BLOCKS_PER_YEAR; // ≈3.2% annual
uint256 public constant SILVER_REWARD_RATE = 22000000000000000 / BLOCKS_PER_YEAR; // ≈2.2% annual
uint256 public constant BRONZE_REWARD_RATE = 12000000000000000 / BLOCKS_PER_YEAR; // ≈1.2% annual
// =============================================================
// WALLET ADDRESSES
// =============================================================
address public festivalWallet;
address public stakingWallet;
address public liquidityWallet;
address public charityWallet;
address public ownerWallet; // Owner wallet
// NOTE: The ownerWallet is hard-coded below with a placeholder.
// 🔴 Replace 0xYOUR_WALLET_ADDRESS with your actual Ethereum (BNB Smart Chain) address before deployment!
// =============================================================
// STAKING STORAGE
// =============================================================
mapping(address => uint256) public stakedBalance; // How many tokens each user has staked
mapping(address => uint256) public rewards; // Accumulated (but not yet claimed) rewards per user
mapping(address => uint256) public userLastUpdatedBlock; // Last block number when user rewards were updated
mapping(address => uint8) public userTier; // 0: None, 1: Bronze, 2: Silver, 3: Gold
uint256 public totalStaked; // Total tokens staked in the contract
// =============================================================
// EVENTS
// =============================================================
event Staked(address indexed user, uint256 amount, uint8 tier);
event Unstaked(address indexed user, uint256 amount);
event RewardPaid(address indexed user, uint256 reward);
event TokensBurned(address indexed burner, uint256 amount);
event CallbackTriggered(address indexed caller, string functionName);
event TierUpdated(address indexed user, uint8 newTier);
// =============================================================
// CONSTRUCTOR
// =============================================================
constructor(
address _festivalWallet,
address _stakingWallet,
address _liquidityWallet,
address _charityWallet
) ERC20("Eternity X", "ETX") {
// Set owner wallet with a red dot marker. Replace with your actual wallet address.
ownerWallet = ADDRESS; //
// Set designated wallets (can be updated later via updateWallets)
festivalWallet = _festivalWallet;
stakingWallet = _stakingWallet;
liquidityWallet = _liquidityWallet;
charityWallet = _charityWallet;
// Token Distribution: mint tokens according to the distribution plan.
// 50% for Public Sale → ownerWallet
_mint(ownerWallet, TOTAL_SUPPLY * 50 / 100);
// 20% for Staking Rewards → held by the contract for distribution
_mint(address(this), TOTAL_SUPPLY * 20 / 100);
// 20% for Team & Development → ownerWallet
_mint(ownerWallet, TOTAL_SUPPLY * 20 / 100);
// 10% for Ecosystem Growth → ownerWallet
_mint(ownerWallet, TOTAL_SUPPLY * 10 / 100);
// User-specific staking update will occur when a user stakes for the first time.
}
// =============================================================
// ADMINISTRATIVE FUNCTIONS
// =============================================================
/// @notice Mint function (onlyOwner)
function mint(address to, uint256 amount) external onlyOwner whenNotPaused {
_mint(to, amount);
}
/// @notice Pause token transfers (if needed)
function pause() external onlyOwner {
_pause();
}
/// @notice Unpause token transfers
function unpause() external onlyOwner {
_unpause();
}
/// @dev Override OpenZeppelin hook to enforce pausing on transfers.
function _beforeTokenTransfer(address from, address to, uint256 amount)
internal
override
whenNotPaused
{
super._beforeTokenTransfer(from, to, amount);
}
// =============================================================
// STAKING FUNCTIONS & TIERED REWARDS SYSTEM
// =============================================================
/// @notice Returns the earned rewards for an account.
function earned(address account) public view returns (uint256) {
uint256 blocksPassed = block.number - userLastUpdatedBlock[account];
uint256 rate = getRewardRate(account);
return rewards[account] + (stakedBalance[account] * rate * blocksPassed) / 1e18;
}
/// @notice Internal function to update a user’s accrued rewards.
function updateReward(address account) internal {
if (stakedBalance[account] > 0) {
rewards[account] = earned(account);
}
userLastUpdatedBlock[account] = block.number;
}
/// @notice Determines the reward rate for an account based on its staking tier.
function getRewardRate(address account) public view returns (uint256) {
uint8 tier = userTier[account];
if (tier == 3) {
return GOLD_REWARD_RATE;
} else if (tier == 2) {
return SILVER_REWARD_RATE;
} else if (tier == 1) {
return BRONZE_REWARD_RATE;
} else {
return 0;
}
}
/// @notice Updates the staking tier of a user based on the current staked balance.
function updateUserTier(address account) internal {
uint256 balance = stakedBalance[account];
uint8 newTier;
if (balance >= GOLD_THRESHOLD) {
newTier = 3; // Gold
} else if (balance >= SILVER_THRESHOLD) {
newTier = 2; // Silver
} else if (balance > 0) {
newTier = 1; // Bronze
} else {
newTier = 0;
}
if (newTier != userTier[account]) {
userTier[account] = newTier;
emit TierUpdated(account, newTier);
}
}
/// @notice Stake a specified amount of ETX tokens.
function stake(uint256 amount) external whenNotPaused {
require(amount > 0, "Amount must be greater than 0");
updateReward(msg.sender);
// Transfer tokens from the user to the contract
_transfer(msg.sender, address(this), amount);
stakedBalance[msg.sender] += amount;
totalStaked += amount;
updateUserTier(msg.sender);
emit Staked(msg.sender, amount, userTier[msg.sender]);
}
/// @notice Unstake a specified amount of ETX tokens.
function unstake(uint256 amount) external whenNotPaused {
require(amount > 0 && amount <= stakedBalance[msg.sender], "Invalid unstake amount");
updateReward(msg.sender);
stakedBalance[msg.sender] -= amount;
totalStaked -= amount;
// Transfer tokens back from the contract to the user
_transfer(address(this), msg.sender, amount);
updateUserTier(msg.sender);
emit Unstaked(msg.sender, amount);
}
/// @notice Claim all accrued staking rewards.
function claimReward() external whenNotPaused {
updateReward(msg.sender);
uint256 rewardAmount = rewards[msg.sender];
require(rewardAmount > 0, "No rewards to claim");
rewards[msg.sender] = 0;
_transfer(address(this), msg.sender, rewardAmount);
emit RewardPaid(msg.sender, rewardAmount);
}
// =============================================================
// FESTIVAL PASS NFT INTEGRATION (CALLBACK EXAMPLE)
// =============================================================
/// @notice Call the external Festival Pass NFT contract to mint a ticket.
function claimFestivalPass(address nftContract) external whenNotPaused {
IFestivalPassNFT festivalPass = IFestivalPassNFT(nftContract);
(bool eligible, uint256 tier) = festivalPass.getUserTier(msg.sender);
require(eligible, "Not eligible for festival pass");
festivalPass.mintPass(msg.sender, tier);
emit CallbackTriggered(msg.sender, "claimFestivalPass");
}
// =============================================================
// DEFATIONARY & GOVERNANCE FUNCTIONS
// =============================================================
/// @notice Burn tokens “from festival usage” (only the festival wallet can trigger this).
function burnFromFestival(uint256 amount) external {
require(msg.sender == festivalWallet, "Only festival wallet can burn tokens");
_burn(address(this), amount);
emit TokensBurned(msg.sender, amount);
}
/// @notice Governance function to propose a burn (restricted to the owner).
function proposeBurn(uint256 amount) external onlyOwner {
_burn(address(this), amount);
emit TokensBurned(msg.sender, amount);
}
// =============================================================
// WALLET MANAGEMENT
// =============================================================
/// @notice Update designated wallet addresses (festival, staking, liquidity, charity)
function updateWallets(
address _festivalWallet,
address _stakingWallet,
address _liquidityWallet,
address _charityWallet
) external onlyOwner {
festivalWallet = _festivalWallet;
stakingWallet = _stakingWallet;
liquidityWallet = _liquidityWallet;
charityWallet = _charityWallet;
}
}
Hi, welcome to the community!
So what is wrong?