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