PeakeCoin Matic Contract - Added Features

Completed PeakeCoin Matic Contract... I really don't have much to say.
made in Remix IDE
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
// PeakeCoin Token with Multiple Minters (Upgradeable)
contract PeakeCoin is Initializable, ERC20Upgradeable, OwnableUpgradeable {
mapping(address => bool) public isMinter;
event MinterAdded(address indexed minter);
event MinterRemoved(address indexed minter);
bool public paused;
uint256 public cooldownBlocks;
mapping(address => uint256) public lastActionBlock;
event Paused();
event Unpaused();
event CooldownBlocksChanged(uint256 newCooldown);
uint256 public mintBurnFee;
address public profitRecipient;
event FeeChanged(uint256 newFee);
event ProfitRecipientChanged(address newRecipient);
uint256 public maxTotalSupply;
mapping(address => bool) public isFeeExempt;
event FeeExemptionSet(address indexed account, bool isExempt);
event Recovered(address token, uint256 amount);
// --- Staking Extensibility ---
bool public stakingEnabled; // Use only this variable for staking enabled/disabled
uint256 public minStakeTime; // Minimum time to stake before rewards
uint256 public unstakeDelay; // Time after unstaking before tokens are liquid
uint256 public stakingRewardRate; // Placeholder for reward rate (per year, per block, etc.)
struct StakeInfo {
uint256 amount;
uint256 stakedAt;
uint256 unstakeRequestedAt;
bool isUnstaking;
}
mapping(address => StakeInfo) public stakes;
event StakingEnabled(bool enabled);
event Staked(address indexed user, uint256 amount);
event UnstakeRequested(address indexed user, uint256 amount);
event Unstaked(address indexed user, uint256 amount);
event RewardsClaimed(address indexed user, uint256 amount);
event StakingParamsSet(uint256 minStakeTime, uint256 unstakeDelay, uint256 rewardRate);
function initialize(address initialOwner) public initializer {
__ERC20_init("PeakeCoin", "PEK");
__Ownable_init(initialOwner);
paused = false;
cooldownBlocks = 10;
mintBurnFee = 0.01 ether;
maxTotalSupply = 1000000000 ether; // Example: 1 billion tokens max
}
// Set fee exemption for an address
function setFeeExemption(address account, bool exempt) external onlyOwner {
isFeeExempt[account] = exempt;
emit FeeExemptionSet(account, exempt);
}
// Rescue function to recover ERC20 tokens or Ether sent to contract
function recoverERC20(address tokenAddress, uint256 amount) external onlyOwner {
require(tokenAddress != address(0), "Invalid token address");
require(amount > 0, "Amount must be greater than zero");
emit Recovered(tokenAddress, amount);
IERC20Upgradeable(tokenAddress).transfer(owner(), amount);
}
function recoverETH(uint256 amount) external onlyOwner {
require(amount > 0, "Amount must be greater than zero");
emit Recovered(address(0), amount);
payable(owner()).transfer(amount);
}
// Only minters can mint/burn
modifier onlyMinter() {
require(isMinter[msg.sender], "Not a minter");
_;
}
modifier notPaused() {
require(!paused, "Contract is paused");
_;
}
modifier cooldownPassed() {
require(block.number > lastActionBlock[msg.sender] + cooldownBlocks, "Cooldown not passed");
_;
lastActionBlock[msg.sender] = block.number;
}
modifier paysFee() {
if (!isFeeExempt[msg.sender]) {
require(msg.value >= mintBurnFee, "Insufficient fee sent");
if (profitRecipient != address(0)) {
payable(profitRecipient).transfer(msg.value);
} else {
payable(owner()).transfer(msg.value);
}
}
_;
}
// Owner can add a bridge/minter
function addMinter(address minter) external onlyOwner {
isMinter[minter] = true;
emit MinterAdded(minter);
}
// Owner can remove a bridge/minter
function removeMinter(address minter) external onlyOwner {
isMinter[minter] = false;
emit MinterRemoved(minter);
}
function pause() external onlyOwner {
paused = true;
emit Paused();
}
function unpause() external onlyOwner {
paused = false;
emit Unpaused();
}
function setCooldownBlocks(uint256 blocks_) external onlyOwner {
cooldownBlocks = blocks_;
emit CooldownBlocksChanged(blocks_);
}
function setMintBurnFee(uint256 fee) external onlyOwner {
mintBurnFee = fee;
emit FeeChanged(fee);
}
function setProfitRecipient(address recipient) external onlyOwner {
profitRecipient = recipient;
emit ProfitRecipientChanged(recipient);
}
event MintWithMemo(address indexed to, uint256 amount, string memo);
event BurnWithMemo(address indexed from, uint256 amount, string memo);
// Mapping for future supported external tokens/contracts
mapping(bytes32 => address) public externalContracts; // e.g., chainId => contract address
event ExternalContractSet(bytes32 indexed chainId, address contractAddress);
// Event for cross-chain transfers
event CrossChainTransfer(address indexed from, address indexed to, uint256 amount, string targetChain, string txReference, string memo);
// Owner can update maxTotalSupply to match Hive or other chains
function setMaxTotalSupply(uint256 newMax) external onlyOwner {
require(newMax >= totalSupply(), "New max less than current supply");
maxTotalSupply = newMax;
}
// Owner can register external contracts for future expansion
function setExternalContract(bytes32 chainId, address contractAddress) external onlyOwner {
require(contractAddress != address(0), "Invalid contract address");
require(chainId != bytes32(0), "Invalid chainId");
// Prevent overwriting unless explicitly allowed (optional: add a mechanism for removal)
require(externalContracts[chainId] == address(0), "Contract already set for this chainId");
externalContracts[chainId] = contractAddress;
emit ExternalContractSet(chainId, contractAddress);
}
// Minter can mint to any address
function mint(address to, uint256 amount, string calldata memo, string calldata targetChain, string calldata txReference) external payable onlyMinter notPaused cooldownPassed paysFee {
require(totalSupply() + amount <= maxTotalSupply, "Mint exceeds cap");
_mint(to, amount);
emit MintWithMemo(to, amount, memo);
emit CrossChainTransfer(address(0), to, amount, targetChain, txReference, memo);
}
// Minter can burn from any address (for bridge redemptions)
function burn(address from, uint256 amount, string calldata memo, string calldata targetChain, string calldata txReference) external payable onlyMinter notPaused cooldownPassed paysFee {
_burn(from, amount);
emit BurnWithMemo(from, amount, memo);
emit CrossChainTransfer(from, address(0), amount, targetChain, txReference, memo);
} // --- Staking Rewards Logic ---
// Placeholder for future reward logic
/*
function _calculateRewards(address user) internal view returns (uint256) {
// Future implementation
return 0;
}
function claimRewards() external {
// Future implementation
revert("Staking rewards not enabled yet");
}
*/
function stake(uint256 amount) external {
require(stakingEnabled, "Staking not enabled");
require(amount > 0, "Cannot stake 0");
StakeInfo storage info = stakes[msg.sender];
_transfer(msg.sender, address(this), amount);
info.amount += amount;
// Note: block.timestamp is used for staking timing. This is acceptable for this use case,
// but not suitable for critical randomness or high-value time-based security.
info.stakedAt = block.timestamp;
info.isUnstaking = false;
emit Staked(msg.sender, amount);
}
function requestUnstake(uint256 amount) external {
require(stakingEnabled, "Staking not enabled");
StakeInfo storage info = stakes[msg.sender];
require(info.amount >= amount && amount > 0, "Insufficient staked");
require(!info.isUnstaking, "Already unstaking");
info.amount -= amount;
// Note: block.timestamp is used for staking timing. This is acceptable for this use case,
// but not suitable for critical randomness or high-value time-based security.
info.unstakeRequestedAt = block.timestamp;
info.isUnstaking = true;
emit UnstakeRequested(msg.sender, amount);
}
// Complete the unstaking process after the delay
function _completeUnstake(address user) internal {
StakeInfo storage info = stakes[user];
require(info.isUnstaking, "Not unstaking");
/*
Note: block.timestamp is used for staking timing. This is acceptable for this use case,
but not suitable for critical randomness or high-value time-based security.
*/
require(block.timestamp >= info.unstakeRequestedAt + unstakeDelay, "Unstake delay not passed");
uint256 amount = info.amount;
info.amount = 0;
info.isUnstaking = false;
_transfer(address(this), user, amount);
emit Unstaked(user, amount);
}
// Owner can enable or disable staking
function setStakingEnabled(bool enabled) external onlyOwner {
stakingEnabled = enabled;
emit StakingEnabled(enabled);
}
// Owner can set staking parameters
function setStakingParams(uint256 minTime, uint256 delay, uint256 rewardRate) external onlyOwner {
minStakeTime = minTime;
unstakeDelay = delay;
stakingRewardRate = rewardRate;
emit StakingParamsSet(minTime, delay, rewardRate);
}
// Override _beforeTokenTransfer to handle automatic unstaking
function _beforeTokenTransfer(address from, address /*to*/, uint256 /*amount*/) internal virtual {
if (from != address(0)) {
StakeInfo storage info = stakes[from];
require(!info.isUnstaking || block.timestamp >= info.unstakeRequestedAt + unstakeDelay, "Cannot transfer while unstaking");
}
}
}
0
0
0.000
0 comments