Core Contract

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0; // Specify the Solidity compiler version

import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; // Import standard ERC20 implementation
import "@openzeppelin/contracts/access/AccessControl.sol"; // Import AccessControl for role-based permissions
import "@openzeppelin/contracts/utils/Pausable.sol"; // Import Pausable for emergency stop mechanism

contract UnaxiGold is ERC20, AccessControl, Pausable {
    // Define role identifiers using keccak256 hashes
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");
    bytes32 public constant SALE_MANAGER_ROLE = keccak256("SALE_MANAGER_ROLE");

    // Track total circulating supply manually
    uint256 private _circulatingSupply;

    // Mappings for locked token amounts and their release block numbers
    mapping(address => uint256) private _lockedTokens;
    mapping(address => uint256) private _releaseBlock;

    // Event declarations for transparency and external tracking
    event TokensLocked(address indexed account, uint256 amount, uint256 releaseBlock);
    event TokensReleased(address indexed account, uint256 amount);
    event TokensSold(address indexed buyer, uint256 amount, uint256 price);
    event TokensBought(address indexed seller, uint256 amount, uint256 price);

    // Contract constructor, sets token name and symbol, and assigns roles to deployer
    constructor() ERC20("UnaxiGold", "UXG") {
        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
        _grantRole(MINTER_ROLE, msg.sender);
        _grantRole(BURNER_ROLE, msg.sender);
        _grantRole(SALE_MANAGER_ROLE, msg.sender);
    }

    // Modifier to restrict function access to admin only
    modifier onlyAdmin() {
        require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "Not admin");
        _;
    }

    // Modifier to restrict function access to minters
    modifier onlyMinter() {
        require(hasRole(MINTER_ROLE, msg.sender), "Not minter");
        _;
    }

    // Modifier to restrict function access to burners
    modifier onlyBurner() {
        require(hasRole(BURNER_ROLE, msg.sender), "Not burner");
        _;
    }

    // Modifier to restrict function access to sale managers
    modifier onlySaleManager() {
        require(hasRole(SALE_MANAGER_ROLE, msg.sender), "Not sale manager");
        _;
    }

    // Admin-only function to pause all transfers and sensitive actions
    function pause() public onlyAdmin {
        _pause();
    }

    // Admin-only function to resume operations
    function unpause() public onlyAdmin {
        _unpause();
    }

    // Minter-only function to mint new tokens to a specific address
    function mint(address to, uint256 amount) public onlyMinter whenNotPaused {
        _mint(to, amount);
        _circulatingSupply += amount;
    }

    // Burner-only function to destroy tokens from caller's balance
    function burn(uint256 amount) public onlyBurner whenNotPaused {
        require(balanceOf(msg.sender) >= amount, "Insufficient balance");
        require(getUnlockedBalance(msg.sender) >= amount, "Insufficient unlocked");
        _burn(msg.sender, amount);
        _circulatingSupply -= amount;
    }

    // Override ERC20 transfer to restrict based on locked balance
    function transfer(address recipient, uint256 amount) public override whenNotPaused returns (bool) {
        require(getUnlockedBalance(msg.sender) >= amount, "Locked balance");
        return super.transfer(recipient, amount);
    }

    // Override ERC20 transferFrom with locked balance check
    function transferFrom(address sender, address recipient, uint256 amount) public override whenNotPaused returns (bool) {
        require(getUnlockedBalance(sender) >= amount, "Locked balance");
        return super.transferFrom(sender, recipient, amount);
    }

    // Sale manager-only function to sell tokens (mint to buyer)
    function sellToPublic(address buyer, uint256 amount, uint256 price) public onlySaleManager whenNotPaused {
        require(amount > 0, "Amount zero");
        _mint(buyer, amount);
        _circulatingSupply += amount;
        emit TokensSold(buyer, amount, price);
    }

    // Sale manager-only function to buy tokens (burn from seller)
    function buyFromPublic(address seller, uint256 amount, uint256 price) public onlySaleManager whenNotPaused {
        require(amount > 0, "Amount zero");
        require(balanceOf(seller) >= amount, "Insufficient balance");
        require(getUnlockedBalance(seller) >= amount, "Locked tokens");
        _burn(seller, amount);
        _circulatingSupply -= amount;
        emit TokensBought(seller, amount, price);
    }

    // Admin-only function to lock tokens of a specific account until a future block
    function lockTokens(address account, uint256 amount, uint256 releaseBlock) public onlyAdmin whenNotPaused {
        require(amount > 0, "Amount zero");
        require(balanceOf(account) >= amount, "Not enough tokens");
        require(releaseBlock > block.number, "Release in future");
        require(_lockedTokens[account] == 0, "Already locked");

        _lockedTokens[account] = amount;
        _releaseBlock[account] = releaseBlock;
        emit TokensLocked(account, amount, releaseBlock);
    }

    // Admin-only batch version of lockTokens for multiple accounts
    function batchLockTokens(address[] calldata accounts, uint256[] calldata amounts, uint256 releaseBlock) public onlyAdmin whenNotPaused {
        require(accounts.length == amounts.length, "Mismatched arrays");
        require(releaseBlock > block.number, "Release in future");

        for (uint256 i = 0; i < accounts.length; i++) {
            require(amounts[i] > 0, "Amount zero");
            require(balanceOf(accounts[i]) >= amounts[i], "Insufficient");
            require(_lockedTokens[accounts[i]] == 0, "Already locked");

            _lockedTokens[accounts[i]] = amounts[i];
            _releaseBlock[accounts[i]] = releaseBlock;
            emit TokensLocked(accounts[i], amounts[i], releaseBlock);
        }
    }

    // Function to release locked tokens manually after release block has passed
    function releaseTokens(address account) public whenNotPaused {
        require(_lockedTokens[account] > 0, "No locked tokens");
        require(block.number >= _releaseBlock[account], "Not yet");

        uint256 amount = _lockedTokens[account];
        _lockedTokens[account] = 0;
        _releaseBlock[account] = 0;
        emit TokensReleased(account, amount);
    }

    // View function to check how many tokens are locked for an account
    function getLockedTokens(address account) public view returns (uint256) {
        return _lockedTokens[account];
    }

    // View function to check the release block of locked tokens for an account
    function getReleaseBlock(address account) public view returns (uint256) {
        return _releaseBlock[account];
    }

    // View function to get the available (unlocked) balance of an account
    function getUnlockedBalance(address account) public view returns (uint256) {
        return balanceOf(account) - _lockedTokens[account];
    }

    // View function to get current circulating supply
    function circulatingSupply() public view returns (uint256) {
        return _circulatingSupply;
    }

    // Admin-only override to grant roles
    function grantRole(bytes32 role, address account) public override onlyAdmin {
        _grantRole(role, account);
    }

    // Admin-only override to revoke roles
    function revokeRole(bytes32 role, address account) public override onlyAdmin {
        _revokeRole(role, account);
    }
}

Last updated