Skip to main content
PulseScanner.io

Address

0xc8d6fabbb6ba4d9231504e705ebfdf42fa479aa9
Current Holdings
$0.00
TXs sent
not counted
First Active
2026-03-03
block 25,931,485
Last Active
128 days ago
block 26,522,534
Funded By
not identified

Net worth historyi

No net-worth snapshots recorded yet
partial matchHotspotsolc 0.8.21+commit.d9974bedruntime partial · creation partial
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/**
 * HOTSPOT (Hotspot)
 * A fixed single-supply ERC-20 token deployed on PulseChain.
 *
 * Features:
 *  - Fixed total supply of 1 Hotspot (minted once to deployer)
 *  - Owner is always exempt from trading restrictions
 *  - Airdrop: send tokens to multiple wallets in one transaction
 *  - Ownership transfer and renounce supported
 */

abstract contract Ownable {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    event OwnershipRenounced(address indexed previousOwner);

    error NotOwner();
    error ZeroAddress();

    constructor(address initialOwner) {
        if (initialOwner == address(0)) revert ZeroAddress();
        _owner = initialOwner;
        emit OwnershipTransferred(address(0), initialOwner);
    }

    modifier onlyOwner() {
        if (msg.sender != _owner) revert NotOwner();
        _;
    }

    function owner() public view returns (address) {
        return _owner;
    }

    function transferOwnership(address newOwner) external onlyOwner {
        if (newOwner == address(0)) revert ZeroAddress();
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }

    function renounceOwnership() external onlyOwner {
        emit OwnershipRenounced(_owner);
        _owner = address(0);
    }
}

contract Hotspot is Ownable {

    // ── Metadata ──────────────────────────────────────────────────────────────
    string  public constant name     = "Hotspot";
    string  public constant symbol   = "Hotspot";
    uint8   public constant decimals = 18;

    // ── Supply ────────────────────────────────────────────────────────────────
    uint256 public constant TOTAL_SUPPLY = 1 * 10 ** uint256(decimals);

    // ── State ─────────────────────────────────────────────────────────────────
    bool public tradingEnabled;

    mapping(address => uint256)                     private _balances;
    mapping(address => mapping(address => uint256)) private _allowances;

    // ── Events ────────────────────────────────────────────────────────────────
    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);
    event TradingStarted(uint256 timestamp);
    event TradingStopped(uint256 timestamp);
    event AirdropSent(address indexed sender, uint256 recipientCount, uint256 totalAmount);

    // ── Errors ────────────────────────────────────────────────────────────────
    error TradingNotEnabled();
    error InsufficientBalance();
    error InsufficientAllowance();
    error TransferToZeroAddress();
    error ApproveToZeroAddress();
    error TradingAlreadyInThisState();
    error AirdropArrayMismatch();
    error AirdropEmptyList();
    error AirdropTooManyRecipients();
    error AirdropZeroAmount();
    error AirdropInsufficientBalance();

    // ── Constructor ───────────────────────────────────────────────────────────
    constructor() Ownable(msg.sender) {
        _balances[msg.sender] = TOTAL_SUPPLY;
        emit Transfer(address(0), msg.sender, TOTAL_SUPPLY);
        // Trading starts DISABLED — owner must call startTrading() to open it
    }

    // ── Trading Control ───────────────────────────────────────────────────────

    /// @notice Enable trading for all holders. Only owner.
    function startTrading() external onlyOwner {
        if (tradingEnabled) revert TradingAlreadyInThisState();
        tradingEnabled = true;
        emit TradingStarted(block.timestamp);
    }

    /// @notice Disable trading for all non-owner holders. Only owner.
    function stopTrading() external onlyOwner {
        if (!tradingEnabled) revert TradingAlreadyInThisState();
        tradingEnabled = false;
        emit TradingStopped(block.timestamp);
    }

    // ── Airdrop ───────────────────────────────────────────────────────────────

    /**
     * @notice Send the same fixed amount of Hotspot to multiple recipients.
     * @dev    Caller must hold enough tokens. Max 500 recipients per call.
     *         Bypasses trading gate — airdrop always works regardless of
     *         tradingEnabled state, so you can airdrop before launch.
     * @param recipients Array of wallet addresses to receive tokens.
     * @param amount     Amount of Hotspot (in wei units) each recipient receives.
     */
    function airdropEqual(address[] calldata recipients, uint256 amount) external {
        if (recipients.length == 0)   revert AirdropEmptyList();
        if (recipients.length > 500)  revert AirdropTooManyRecipients();
        if (amount == 0)              revert AirdropZeroAmount();

        uint256 totalNeeded = amount * recipients.length;
        if (_balances[msg.sender] < totalNeeded) revert AirdropInsufficientBalance();

        unchecked {
            _balances[msg.sender] -= totalNeeded;
        }

        for (uint256 i = 0; i < recipients.length; ) {
            address to = recipients[i];
            if (to == address(0)) revert TransferToZeroAddress();
            unchecked {
                _balances[to] += amount;
                ++i;
            }
            emit Transfer(msg.sender, to, amount);
        }

        emit AirdropSent(msg.sender, recipients.length, totalNeeded);
    }

    /**
     * @notice Send different amounts of Hotspot to multiple recipients.
     * @dev    recipients[i] receives amounts[i]. Arrays must be same length.
     *         Max 500 recipients per call.
     * @param recipients Array of wallet addresses.
     * @param amounts    Corresponding array of Hotspot amounts (in wei units).
     */
    function airdropCustom(address[] calldata recipients, uint256[] calldata amounts) external {
        if (recipients.length == 0)                    revert AirdropEmptyList();
        if (recipients.length != amounts.length)       revert AirdropArrayMismatch();
        if (recipients.length > 500)                   revert AirdropTooManyRecipients();

        // Pre-calculate total to validate balance upfront (safe, avoids partial sends)
        uint256 totalNeeded = 0;
        for (uint256 i = 0; i < amounts.length; ) {
            if (amounts[i] == 0) revert AirdropZeroAmount();
            unchecked {
                totalNeeded += amounts[i];
                ++i;
            }
        }

        if (_balances[msg.sender] < totalNeeded) revert AirdropInsufficientBalance();

        unchecked {
            _balances[msg.sender] -= totalNeeded;
        }

        for (uint256 i = 0; i < recipients.length; ) {
            address to = recipients[i];
            if (to == address(0)) revert TransferToZeroAddress();
            unchecked {
                _balances[to] += amounts[i];
                ++i;
            }
            emit Transfer(msg.sender, to, amounts[i]);
        }

        emit AirdropSent(msg.sender, recipients.length, totalNeeded);
    }

    // ── ERC-20 Standard ───────────────────────────────────────────────────────

    function totalSupply() external pure returns (uint256) {
        return TOTAL_SUPPLY;
    }

    function balanceOf(address account) external view returns (uint256) {
        return _balances[account];
    }

    function allowance(address tokenOwner, address spender) external view returns (uint256) {
        return _allowances[tokenOwner][spender];
    }

    function approve(address spender, uint256 amount) external returns (bool) {
        if (spender == address(0)) revert ApproveToZeroAddress();
        _allowances[msg.sender][spender] = amount;
        emit Approval(msg.sender, spender, amount);
        return true;
    }

    function transfer(address to, uint256 amount) external returns (bool) {
        _transfer(msg.sender, to, amount);
        return true;
    }

    function transferFrom(address from, address to, uint256 amount) external returns (bool) {
        uint256 currentAllowance = _allowances[from][msg.sender];
        if (currentAllowance != type(uint256).max) {
            if (currentAllowance < amount) revert InsufficientAllowance();
            unchecked { _allowances[from][msg.sender] = currentAllowance - amount; }
        }
        _transfer(from, to, amount);
        return true;
    }

    // ── Internal ──────────────────────────────────────────────────────────────

    function _transfer(address from, address to, uint256 amount) internal {
        if (to == address(0)) revert TransferToZeroAddress();

        // Owner is always exempt from the trading gate
        if (!tradingEnabled && from != owner() && to != owner()) {
            revert TradingNotEnabled();
        }

        uint256 fromBalance = _balances[from];
        if (fromBalance < amount) revert InsufficientBalance();

        unchecked {
            _balances[from] = fromBalance - amount;
            _balances[to]  += amount;
        }

        emit Transfer(from, to, amount);
    }
}