Skip to content

Functions

DISCLAIMER // NFA // DYOR

This analysis is based on observations of the contract behavior. We are not smart contract security experts. This document aims to explain what the contract appears to do based on the code. It should not be considered a comprehensive security audit or financial advice. Always verify critical information independently and consult with blockchain security professionals for important decisions.

⊙ generated by robots | curated by humans

METADATA
Contract Address 0x41b242c36F7dc5f18be21c1a6B7b5e05b2FD6532 (etherscan)
Network Ethereum Mainnet
Analysis Date 2026-02-04

Function Selectors

SELECTOR FUNCTION SIGNATURE CATEGORY
0x1003e2d2 buy() User
0xa50dd39c claimDividends() User
0xa6f2ae3a transfer(address,uint256) User
0x23b872dd transferFrom(address,address,uint256) User
0x09b81cba calculatezETHForNative(uint256) View
0xa3a29eda calculateNativeForZETH(uint256) View
0x70a08231 balanceOf(address) View
0x18160ddd totalSupply() View
0xb5d1f88e pendingDividends(address) View
0x78d573f2 setDevAddress(address) Admin
0x8c66896f excludeFromFee(address,bool) Admin
0x2b14ca56 getTotalDividendsDistributed() View
0x1b42e96e getMagnifiedDividendPerShare() View
0x6de1a5a9 getCirculatingSupplyPublic() View
0x6a627842 getUserDividendInfo(address) View
0x39509351 approve(address,uint256) User
0xa457c2d7 increaseAllowance(address,uint256) User
0xa9059cbb decreaseAllowance(address,uint256) User

Summary

ZeroMoon (zETH) implements a fair dividend distribution mechanism with ETH backing and refund capability. The contract includes 18 externally accessible functions across user operations, administrative controls, and view functions for querying system state.

CATEGORY NUM FUNCTIONS
Total Functions 18
User Functions 7
Admin Functions 2
View Functions 9

User Functions

Function: buy()

Purchases zETH tokens by sending ETH to the contract. This is the primary mechanism for users to acquire tokens. Can be called by sending ETH directly via receive() or explicitly via buy().

ATTRIBUTE VALUE
Selector 0x1003e2d2
Parameters None (uses msg.value)
Access External, Payable
Protection nonReentrant
FLAG OBSERVATION
Dynamic pricing mechanism: uses base price (0.0001 ETH) at launch, then refund price + 0.1% markup
Applies buy fees: 5 BPS dev, 10 BPS reserve, 10 BPS reflection (distributed as dividends)
Buyer is marked as "caught up" to dividend distribution to prevent retroactive earning from own purchase fees
Protected against reentrancy attacks
Minimum purchase of 0.0001 ETH prevents dust attacks
CONDITION REQUIREMENT
Minimum ETH msg.value >= MINIMUM_PURCHASE_NATIVE (0.0001 ETH)
Token availability tokensSold + zETHToPurchase <= TOTAL_SUPPLY (1.25B tokens)
Sufficient contract tokens Contract must have unsold tokens available
Valid buyer msg.sender must be non-zero address
STEP ACTION
1 Calculate zETH tokens based on ETH amount and current price
2 Calculate and deduct buy fees (5% dev, 10% reserve, 10% reflection)
3 Increment tokensSold counter
4 Distribute reflection fee as dividends to all EOA holders
5 Mark buyer's dividend tracking as caught-up to prevent retroactive dividend accrual
6 Transfer net tokens to buyer and dev fee to dev address
7 Emit Buy event with buyer, ETH paid, and zETH received
VARIABLE CHANGE
tokensSold Incremented by zETHToPurchase
magnifiedDividendPerShare Increased by reflection fee distribution
totalDividendsDistributed Increased by reflection fee amount
lastDividendPerShare[buyer] Set to current magnifiedDividendPerShare (for EOA buyers)
User balance Increased by zETHToUser (net tokens after fees)
Dev address balance Increased by dev fee amount
Contract ETH balance Increased by msg.value
CONDITION REVERT MESSAGE
msg.value < 0.0001 ETH InsufficientNative
Insufficient unsold tokens InsufficientBalance
Reentrancy attempt Revert (nonReentrant guard)
Zero address caller ZeroMoonAddress
function buy() external payable {
    _buy(msg.sender, msg.value);
}

// Internal implementation
function _buy(address buyer, uint256 amountNative) private nonReentrant {
    if (amountNative < MINIMUM_PURCHASE_NATIVE) revert InsufficientNative();

    uint256 balanceBefore = address(this).balance - amountNative;
    uint256 zETHToPurchase = _getzETHForNative(amountNative, balanceBefore);

    if (zETHToPurchase == 0) revert InsufficientNative();
    if (tokensSold + zETHToPurchase > TOTAL_SUPPLY) revert InsufficientBalance();

    uint256 devFee = Math.mulDiv(zETHToPurchase, BUY_DEV_FEE_BPS, 10000);
    uint256 reserveFee = Math.mulDiv(zETHToPurchase, BUY_RESERVE_FEE_BPS, 10000);
    uint256 reflectionFee = Math.mulDiv(zETHToPurchase, BUY_REFLECTION_FEE_BPS, 10000);
    uint256 zETHToUser;
    unchecked {
        zETHToUser = zETHToPurchase - devFee - reserveFee - reflectionFee;
    }

    tokensSold = tokensSold + zETHToPurchase;

    _distributeDividends(reflectionFee);

    if (!isContract(buyer)) {
        lastDividendPerShare[buyer] = magnifiedDividendPerShare;
    }

    super._transfer(address(this), devAddress, devFee);
    super._transfer(address(this), buyer, zETHToUser);

    emit Buy(buyer, amountNative, zETHToUser);
}

Function: receive()

Fallback function that automatically triggers buy() when ETH is sent directly to the contract address without calling any specific function. Enables simple "send ETH to buy tokens" functionality.

ATTRIBUTE VALUE
Selector N/A (fallback)
Parameters None
Access External, Payable
Protection nonReentrant (via _buy)
FLAG OBSERVATION
Delegates to _buy() with msg.sender and msg.value
Allows user-friendly token purchases (no need to call explicit function)
Subject to same minimum purchase requirements as buy()
CONDITION REQUIREMENT
ETH value Transaction must include ETH (msg.value > 0)
Same as buy() All conditions from buy() function apply
STEP ACTION
1 Receive incoming ETH payment
2 Call _buy(msg.sender, msg.value)
3 Execute full buy logic (see buy() function)
CONDITION REVERT MESSAGE
Same as buy() See buy() function failure cases
receive() external payable {
    _buy(msg.sender, msg.value);
}

Function: transfer(address to, uint256 amount)

Standard ERC20 transfer function with custom ZeroMoon fee logic. Transfers tokens from sender to recipient with automatic fee deduction. Dividend tracking is updated before transfer execution.

ATTRIBUTE VALUE
Selector 0xa6f2ae3a
Parameters to (recipient), amount (transfer amount)
Access External, Non-payable
Protection nonReentrant (dividend claim), custom fee logic
FLAG OBSERVATION
Detects DEX swaps (liquidity pair transfers) vs regular transfers
DEX swaps are fee-free (0% dev, reflection, reserve)
Regular transfers have 25 BPS total fees (5% dev, 10% reflection, 10% reserve)
Dividend tracking updated BEFORE balance changes prevent timing exploits
Transfers to contract address (0x41b242...) trigger refund mechanism
Fee-exempt addresses bypass fee deduction (owner, dev, contract)
Contracts are auto-excluded from dividend earnings
CONDITION REQUIREMENT
Sender balance balanceOf(msg.sender) >= amount
Valid addresses Neither from nor to is address(0)
Non-zero amount amount > 0
Not during reentrancy If dividend-eligible, dividend tracking must be synchronized
STEP ACTION
1 Update dividend tracking for sender (if EOA)
2 Update dividend tracking for recipient (if EOA)
3 Route to appropriate transfer handler based on recipient
4a (Refund) If to == address(this), call _handleRefund()
4b (Exempt) If sender or recipient is fee-exempt, transfer full amount
4c (Taxed) If regular transfer, apply fees and distribute dividends
VARIABLE CHANGE
Sender balance Decreased by amount
Recipient balance Increased by amount - fees (or full amount if exempt)
magnifiedDividendPerShare Increased if reflection fees distributed
accumulatedDividends[user] Updated for both sender and recipient (if EOA)
lastDividendPerShare[user] Updated to current dividend per share
CONDITION REVERT MESSAGE
Insufficient balance InsufficientBalance
Zero address ZeroMoonAddress
Zero amount ZeroMoonAmount
Refund with zero supply NoTokensInCirculation
Failed ETH transfer (refund) NativeTransferFailed
function transfer(address to, uint256 amount) public virtual override returns (bool) {
    address owner = _msgSender();
    _transfer(owner, to, amount);
    return true;
}

function _transfer(address from, address to, uint256 amount) internal override {
    // Update dividend tracking BEFORE balance changes
    if (from != address(0) && !isContract(from)) {
        _updateUserDividendTracking(from);
    }

    if (to != address(0) && !isContract(to)) {
        _updateUserDividendTracking(to);
    }

    _update(from, to, amount);
}

function _update(address from, address to, uint256 amount) private {
    if (from == address(0)) revert ZeroMoonAddress();
    if (to == address(0)) revert ZeroMoonAddress();
    if (amount == 0) revert ZeroMoonAmount();

    bool isExempt = _isExcludedFromFee[from] || _isExcludedFromFee[to];

    if (to == address(this)) {
        super._transfer(from, to, amount);
        _handleRefund(from, amount);
        emit TransferFeeExempt(from, to, amount, ExemptionReason.REFUND);
    } else if (isExempt) {
        super._transfer(from, to, amount);
        emit TransferFeeExempt(from, to, amount, ExemptionReason.EXCLUDED_ADDRESS);
    } else {
        _handleTaxedTransfer(from, to, amount);
    }
}

Function: transferFrom(address from, address to, uint256 amount)

Standard ERC20 delegated transfer function. Allows spender to transfer tokens on behalf of token holder if sufficient allowance has been granted. Applies same fee logic as transfer().

ATTRIBUTE VALUE
Selector 0x23b872dd
Parameters from (sender), to (recipient), amount (transfer amount)
Access External, Non-payable
Protection Allowance check, dividend tracking
FLAG OBSERVATION
Checks and decrements spender's allowance before transfer
Reverts if spender has insufficient allowance
Applies same fee logic as transfer()
Dividend tracking updated before balance changes
CONDITION REQUIREMENT
Sender balance balanceOf(from) >= amount
Spender allowance allowance(from, msg.sender) >= amount
Valid addresses Neither from nor to is address(0)
Non-zero amount amount > 0
STEP ACTION
1 Verify spender's allowance >= amount
2 Update spender's allowance (unless infinite)
3 Execute transfer with fee logic
CONDITION REVERT MESSAGE
Insufficient allowance ERC20: insufficient allowance
Insufficient balance InsufficientBalance
Same as transfer() See transfer() failure cases
function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
    address spender = _msgSender();
    _spendAllowance(from, spender, amount);
    _transfer(from, to, amount);
    return true;
}

Function: claimDividends()

Claims accumulated and pending dividends for the calling user. Updates dividend tracking, calculates any newly accrued dividends, and transfers total accumulated dividends to user. Only EOA addresses can claim; contracts are automatically excluded.

ATTRIBUTE VALUE
Selector 0xa50dd39c
Parameters None
Access External, Non-payable
Protection nonReentrant
FLAG OBSERVATION
Contracts cannot claim (auto-excluded via isContract() check)
Updates dividend tracking to current global dividend per share before claiming
Calculates newly accrued dividends based on user balance and dividend delta
Adds new dividends to accumulated dividends before transferring
Transfers dividends as zETH tokens from contract balance
Protected against reentrancy attacks
Early return for contracts (no revert, silent exclusion)
CONDITION REQUIREMENT
Valid caller msg.sender must be EOA (not contract)
Some dividends pendingDividends(user) > 0 (optional, but otherwise zero transfer)
Contract solvency Contract balance must have sufficient zETH to distribute
STEP ACTION
1 Return early if caller is identified as contract
2 Get user's current token balance
3 Get current global dividend per share
4 Get user's last recorded dividend per share
5 Calculate dividend delta since last update
6 Calculate new dividends: (userBalance * dividendDelta) / MAGNITUDE
7 Add new dividends to accumulated dividends
8 Update user's last dividend per share to current
9 Transfer accumulated dividends to user
10 Reset accumulated dividends to 0
11 Emit DividendWithdrawn event
VARIABLE CHANGE
lastDividendPerShare[user] Set to current magnifiedDividendPerShare
accumulatedDividends[user] Set to 0
User balance Increased by accumulated dividends amount
Contract balance Decreased by transferred dividends
CONDITION REVERT MESSAGE
Caller is contract Silent return (no error)
No dividends available Silent (zero transfer if balance is 0)
Insufficient contract balance Standard ERC20 transfer failure
Reentrancy attempt Revert (nonReentrant guard)
function claimDividends() external nonReentrant {
    address user = msg.sender;

    // Exclude contracts from dividend claiming
    if (isContract(user)) return;

    uint256 userBalance = balanceOf(user);
    uint256 currentDividendPerShare = magnifiedDividendPerShare;
    uint256 lastUserDividendPerShare = lastDividendPerShare[user];

    // Calculate and accumulate dividends if user has balance
    if (userBalance > 0 && currentDividendPerShare > lastUserDividendPerShare) {
        uint256 dividendDifference = currentDividendPerShare - lastUserDividendPerShare;
        uint256 newDividends = (userBalance * dividendDifference) / MAGNITUDE;

        if (newDividends > 0) {
            accumulatedDividends[user] += newDividends;
        }
    }

    // ALWAYS update lastDividendPerShare to keep tracking synchronized
    lastDividendPerShare[user] = currentDividendPerShare;

    // Transfer accumulated dividends to user
    uint256 totalAccumulated = accumulatedDividends[user];
    if (totalAccumulated > 0) {
        accumulatedDividends[user] = 0;
        super._transfer(address(this), user, totalAccumulated);
        emit DividendWithdrawn(user, totalAccumulated);
    }
}

Function: approve(address spender, uint256 amount)

Standard ERC20 approve function. Grants spender permission to transfer up to amount tokens on behalf of msg.sender. Overwrites previous allowance.

ATTRIBUTE VALUE
Selector 0x39509351
Parameters spender (address to grant approval), amount (maximum allowance)
Access External, Non-payable
Protection None specific (standard ERC20)
FLAG OBSERVATION
Standard ERC20 pattern with event emission
Race condition possible: recommended to use increaseAllowance() / decreaseAllowance()
CONDITION REVERT MESSAGE
Spender is zero address ERC20: approve to the zero address
Approver is zero address ERC20: approve from the zero address
function approve(address spender, uint256 amount) public virtual override returns (bool) {
    address owner = _msgSender();
    _approve(owner, spender, amount);
    return true;
}

Function: increaseAllowance(address spender, uint256 addedValue)

Atomically increases the allowance granted to a spender by adding addedValue to their current allowance. Safer alternative to approve() for increasing allowance without race condition.

ATTRIBUTE VALUE
Selector 0xa457c2d7
Parameters spender (address to increase allowance for), addedValue (amount to add)
Access External, Non-payable
Protection Atomic operation
FLAG OBSERVATION
Adds addedValue to current allowance atomically
Prevents race condition vs standard approve()
function increaseAllowance(address spender, uint256 addedValue) public virtual override returns (bool) {
    address owner = _msgSender();
    _approve(owner, spender, allowance(owner, spender) + addedValue);
    return true;
}

Function: decreaseAllowance(address spender, uint256 subtractedValue)

Atomically decreases the allowance granted to a spender by subtracting subtractedValue from their current allowance. Safer alternative to approve() for decreasing allowance.

ATTRIBUTE VALUE
Selector 0xa9059cbb
Parameters spender (address to decrease allowance for), subtractedValue (amount to subtract)
Access External, Non-payable
Protection Atomic operation, underflow check
FLAG OBSERVATION
Reverts if subtractedValue exceeds current allowance
Prevents accidental negative allowance
CONDITION REVERT MESSAGE
subtractedValue > allowance InsufficientBalance
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual override returns (bool) {
    address owner = _msgSender();
    uint256 currentAllowance = allowance(owner, spender);
    if (currentAllowance < subtractedValue) revert InsufficientBalance();
    unchecked {
        _approve(owner, spender, currentAllowance - subtractedValue);
    }
    return true;
}

Admin Functions

Function: setDevAddress(address _devAddress)

Changes the address that receives development fees. Only callable by contract owner. Automatically updates fee exclusion status—removes exclusion from old dev address and adds to new dev address.

ATTRIBUTE VALUE
Selector 0x78d573f2
Parameters _devAddress (new development fee recipient address)
Access External, Ownable (onlyOwner)
Protection Owner verification
FLAG OBSERVATION
Verifies _devAddress is not zero address
Removes fee exclusion from old dev address
Automatically adds fee exclusion to new dev address
Emits event with old and new addresses
Owner renouncement would prevent future changes
CONDITION REQUIREMENT
Caller is owner msg.sender == owner()
Valid address _devAddress != address(0)
STEP ACTION
1 Verify caller is contract owner
2 Verify _devAddress is not zero address
3 Save old dev address
4 Remove fee exclusion from old dev address
5 Update devAddress state variable
6 Add fee exclusion to new dev address
7 Emit DevAddressChanged event
VARIABLE CHANGE
devAddress Updated to new address
_isExcludedFromFee[oldAddress] Set to false
_isExcludedFromFee[newAddress] Set to true
CONDITION REVERT MESSAGE
Caller not owner Ownable: caller is not the owner
_devAddress == address(0) ZeroMoonAddress
function setDevAddress(address _devAddress) external onlyOwner {
    if (_devAddress == address(0)) revert ZeroMoonAddress();
    address oldDevAddress = devAddress;
    _isExcludedFromFee[devAddress] = false;
    devAddress = _devAddress;
    _isExcludedFromFee[devAddress] = true;
    emit DevAddressChanged(oldDevAddress, _devAddress);
}

Function: excludeFromFee(address account, bool isExcluded)

Grants or revokes fee exclusion status for an account. Fee-excluded addresses bypass all transfer fees on transfers where they are sender or recipient. Only callable by contract owner.

ATTRIBUTE VALUE
Selector 0x8c66896f
Parameters account (address to set exclusion for), isExcluded (true to exclude, false to include)
Access External, Ownable (onlyOwner)
Protection Owner verification
FLAG OBSERVATION
Verifies account is not zero address
Can both grant and revoke exclusion status
Excluded addresses bypass dev, reflection, and reserve fees
Contract owner and dev address are auto-excluded at deployment
Owner renouncement would prevent future exclusion changes
CONDITION REQUIREMENT
Caller is owner msg.sender == owner()
Valid address account != address(0)
STEP ACTION
1 Verify caller is contract owner
2 Verify account is not zero address
3 Update fee exclusion status
4 Emit FeeExclusionSet event
VARIABLE CHANGE
_isExcludedFromFee[account] Set to isExcluded value
CONDITION REVERT MESSAGE
Caller not owner Ownable: caller is not the owner
account == address(0) ZeroMoonAddress
function excludeFromFee(address account, bool isExcluded) external onlyOwner {
    if (account == address(0)) revert ZeroMoonAddress();
    _isExcludedFromFee[account] = isExcluded;
    emit FeeExclusionSet(account, isExcluded);
}

View Functions

Function: balanceOf(address account)

Returns the zETH token balance of an account. Standard ERC20 view function. No parameters modifying behavior.

ATTRIBUTE VALUE
Selector 0x70a08231
Parameters account (address to query)
Access Public, View
Return Token balance (uint256)
function balanceOf(address account) public view override returns (uint256) {
    return super.balanceOf(account);
}

Function: totalSupply()

Returns the current circulating supply of zETH tokens. Accounts for burned tokens—decreases as refunds burn tokens (up to 20% burning limit). Differs from immutable TOTAL_SUPPLY which is fixed at 1.25B tokens.

ATTRIBUTE VALUE
Selector 0x18160ddd
Parameters None
Access Public, View
Return Circulating supply (uint256)
FLAG OBSERVATION
Returns TOTAL_SUPPLY - totalBurned
Decreases as tokens are burned through refunds
Burning stops once totalBurned >= BURNING_LIMIT (20% of supply)
This is the supply used for dividend distribution calculations
function totalSupply() public view override returns (uint256) {
    return TOTAL_SUPPLY - totalBurned;
}

Function: pendingDividends(address user)

Returns the total unclaimed dividends for a user, including both accumulated dividends and newly accrued dividends since last update. Calculates pending amount without claiming.

ATTRIBUTE VALUE
Selector 0xb5d1f88e
Parameters user (address to query)
Access External, View
Return Pending dividends (uint256)
FLAG OBSERVATION
Returns 0 for contract addresses (auto-excluded)
Includes already accumulated dividends + newly accrued
Only accrues dividends if user has token balance
Dividend accrual = (userBalance * dividendDelta) / MAGNITUDE
Calculation uses current global dividend per share
STEP ACTION
1 Return 0 if user is identified as contract
2 Get user's current token balance
3 If balance is 0, return only accumulated dividends
4 Get current global dividend per share
5 Get user's last recorded dividend per share
6 If current > last, calculate dividend delta
7 Calculate new dividends = (balance * delta) / MAGNITUDE
8 Return accumulated + new dividends
function pendingDividends(address user) external view returns (uint256) {

    if (isContract(user)) return 0;

    uint256 userBalance = balanceOf(user);
    if (userBalance == 0) return accumulatedDividends[user];

    uint256 currentDividendPerShare = magnifiedDividendPerShare;
    uint256 lastUserDividendPerShare = lastDividendPerShare[user];

    if (currentDividendPerShare > lastUserDividendPerShare) {
        uint256 dividendDifference = currentDividendPerShare - lastUserDividendPerShare;
        uint256 newDividends = (userBalance * dividendDifference) / MAGNITUDE;
        return accumulatedDividends[user] + newDividends;
    }

    return accumulatedDividends[user];
}

Function: calculatezETHForNative(uint256 nativeAmount)

Calculates the amount of zETH tokens that would be received for a given ETH amount. Used for buy price previews on frontend. Does not include fee deduction.

ATTRIBUTE VALUE
Selector 0x09b81cba
Parameters nativeAmount (ETH amount to query)
Access Public, View
Return zETH tokens that would be received before fees (uint256)
FLAG OBSERVATION
Returns gross tokens before fee deduction
Uses same pricing logic as _buy()
Dynamic pricing: base price at launch, then (refund price * 1.001)
Capped at available unsold tokens in contract
Actual received amount will be ~20-30 BPS lower after fees
STEP ACTION
1 Call internal _getzETHForNative() with contract balance
2 Calculate price per token based on circulating supply and balance
3 Divide ETH amount by token price
4 Cap result at available unsold tokens
5 Return tokens that would be received
function calculatezETHForNative(uint256 nativeAmount) public view returns (uint256) {
    return _getzETHForNative(nativeAmount, address(this).balance);
}

function _getzETHForNative(uint256 nativeAmount, uint256 balanceBefore) private view returns (uint256) {
    if (nativeAmount == 0) return 0;
    uint256 availableToSell = balanceOf(address(this));
    if (availableToSell == 0) return 0;

    uint256 circulating = totalSupply() - availableToSell;

    uint256 pricePerToken;
    if (circulating == 0 || balanceBefore == 0) {
        pricePerToken = BASE_PRICE;
    } else {
        uint256 refundPrice = (balanceBefore * 1e18) / circulating;
        pricePerToken = (refundPrice * 10010) / PRECISION_DIVISOR;
    }

    uint256 tokensToPurchase = (nativeAmount * 1e18) / pricePerToken;
    return Math.min(tokensToPurchase, availableToSell);
}

Function: calculateNativeForZETH(uint256 zETHAmount)

Calculates the amount of ETH that would be received for a given zETH refund amount. Used for refund preview on frontend. Accounts for fees and 99.9% backing ratio.

ATTRIBUTE VALUE
Selector 0xa3a29eda
Parameters zETHAmount (zETH amount to query)
Access Public, View
Return ETH that would be received after fees and backing (uint256)
FLAG OBSERVATION
Accounts for refund fees (5% dev, 5% reflection, 7.5-15% reserve, 7.5% burn if limit not reached)
Uses same backing calculation as _handleRefund() (99.9% effective backing)
Returns 0 for amounts < 1 token (matches minimum refund requirement)
Uses Math.mulDiv for precision-safe calculations
Burn fee doubles to 15% reserve once burning limit reached
Actual ETH received depends on contract balance at claim time
STEP ACTION
1 Return 0 if zETHAmount is 0
2 Return 0 if zETHAmount < 1 ether (below minimum)
3 Calculate refund fees (same as _handleRefund())
4 Calculate net zETH after fee deduction
5 Calculate current circulating supply
6 Calculate effective backing (99.9% of contract balance)
7 Calculate ETH value: (netZETH * effectiveBacking) / circulatingSupply
8 Return ETH amount
CONDITION RETURN VALUE
zETHAmount == 0 0
zETHAmount < 1 ether 0
circulatingSupply == 0 0
function calculateNativeForZETH(uint256 zETHAmount) public view returns (uint256) {
    if (zETHAmount == 0) return 0;

    // Match the minimum refund check in _handleRefund
    if (zETHAmount < 1 ether) return 0;

    uint256 _totalBurned = totalBurned;

    // Calculate fees (same as _handleRefund)
    uint256 devFeezETH = Math.mulDiv(zETHAmount, REFUND_DEV_FEE_BPS, 10000);
    uint256 reflectionFeezETH = Math.mulDiv(zETHAmount, REFUND_REFLECTION_FEE_BPS, 10000);
    uint256 burnFeezETH = (_totalBurned < BURNING_LIMIT) ? Math.mulDiv(zETHAmount, 75, 100000) : 0;
    uint256 reserveFeezETH = (_totalBurned < BURNING_LIMIT) ? Math.mulDiv(zETHAmount, 75, 100000) : Math.mulDiv(zETHAmount, 150, 100000);

    uint256 zETHForUserRefund;
    unchecked {
        zETHForUserRefund = zETHAmount - devFeezETH - reflectionFeezETH - burnFeezETH - reserveFeezETH;
    }

    uint256 contractBalance = balanceOf(address(this));
    uint256 currentCirculatingSupply = (TOTAL_SUPPLY - _totalBurned) - contractBalance + zETHAmount;

    if (currentCirculatingSupply == 0) return 0;

    uint256 effectiveBacking = (address(this).balance * EFFECTIVE_BACKING_NUMERATOR) / EFFECTIVE_BACKING_DENOMINATOR;

    uint256 nativeToUser = Math.mulDiv(zETHForUserRefund, effectiveBacking, currentCirculatingSupply);

    return nativeToUser;
}

Function: getTotalDividendsDistributed()

Returns the total amount of zETH tokens that have been distributed as dividends since contract inception. Cumulative total that only increases.

ATTRIBUTE VALUE
Selector 0x2b14ca56
Parameters None
Access External, View
Return Total dividends distributed (uint256)
function getTotalDividendsDistributed() external view returns (uint256) {
    return totalDividendsDistributed;
}

Function: getMagnifiedDividendPerShare()

Returns the current magnified dividend per share value. This value increases monotonically as reflection fees are distributed. Used internally for dividend calculations with MAGNITUDE factor (2^128) for precision.

ATTRIBUTE VALUE
Selector 0x1b42e96e
Parameters None
Access External, View
Return Magnified dividend per share (uint256)
FLAG OBSERVATION
Value is magnified by 2^128 for precision in fixed-point arithmetic
Increases with each distribution, never decreases
Used to track if a user is "caught up" with dividend distribution
function getMagnifiedDividendPerShare() external view returns (uint256) {
    return magnifiedDividendPerShare;
}

Function: getCirculatingSupplyPublic()

Public wrapper for circulating supply calculation. Returns total supply minus unsold tokens in contract (equivalent to totalSupply() - contract balance).

ATTRIBUTE VALUE
Selector 0x6de1a5a9
Parameters None
Access External, View
Return Circulating supply (uint256)
function getCirculatingSupplyPublic() external view returns (uint256) {
    return getCirculatingSupply();
}

function getCirculatingSupply() private view returns (uint256) {
    uint256 total = totalSupply();
    uint256 contractBalance = balanceOf(address(this));
    return total - contractBalance;
}

Function: getUserDividendInfo(address user)

Returns comprehensive dividend information for a user in a single call. Includes balance, dividend tracking pointers, accumulated dividends, and contract status.

ATTRIBUTE VALUE
Selector 0x6a627842
Parameters user (address to query)
Access External, View
Return Tuple of (balance, lastDividendPerShare, accumulatedDividends, currentDividendPerShare, isContract)
FLAG OBSERVATION
Returns user's current balance
Returns user's last recorded dividend per share
Returns user's accumulated unclaimed dividends
Returns current global dividend per share for comparison
Indicates whether user is identified as contract
To get pending dividends, use pendingDividends() function instead
STEP ACTION
1 Get user's token balance
2 Get user's last recorded dividend per share
3 Get user's accumulated dividends
4 Get current global dividend per share
5 Check if user address is contract
6 Return all values as tuple
function getUserDividendInfo(address user) external view returns (
    uint256 balance,
    uint256 userLastDividendPerShare,
    uint256 userAccumulatedDividends,
    uint256 currentDividendPerShare,
    bool isUserContract
) {
    return (
        balanceOf(user),
        lastDividendPerShare[user],
        accumulatedDividends[user],
        magnifiedDividendPerShare,
        isContract(user)
    );
}

Function Call Flow Diagrams

The following diagram illustrates the primary transaction flows through the ZeroMoon contract:

graph TD
    A["User sends ETH"] --> B{"Recipient?"}
    B -->|Direct to contract<br/>buy()/receive()| C["_buy()"]
    B -->|Transfer to EOA/Contract| D["_transfer()"]

    C --> C1["Calculate zETH<br/>from ETH amount"]
    C1 --> C2["Apply buy fees<br/>Dev: 5 BPS<br/>Reflection: 10 BPS<br/>Reserve: 10 BPS"]
    C2 --> C3["Distribute dividends<br/>to all EOA holders"]
    C3 --> C4["Mark buyer as<br/>caught-up to<br/>dividend distribution"]
    C4 --> C5["Transfer tokens<br/>to buyer"]
    C5 --> C6["Emit Buy event"]

    D --> D1["Update dividend<br/>tracking for<br/>sender & recipient"]
    D1 --> D2{"Transfer<br/>to contract?"}
    D2 -->|Yes| E["_handleRefund()"]
    D2 -->|No| D3{"Exempt<br/>from fees?"}

    D3 -->|Yes| D4["Transfer full<br/>amount<br/>no fees"]
    D3 -->|No| D5["_handleTaxedTransfer()"]

    D5 --> D6{"DEX swap<br/>or<br/>regular?"}
    D6 -->|DEX swap| D7["0% fees<br/>no distribution"]
    D6 -->|Regular| D8["Apply transfer fees<br/>Dev: 5 BPS<br/>Reflection: 10 BPS<br/>Reserve: 10 BPS"]
    D8 --> D9["Distribute dividends<br/>to all EOA holders"]
    D9 --> D10["Transfer net<br/>amount to recipient"]

    E --> E1["Calculate refund<br/>value with 99.9%<br/>backing"]
    E1 --> E2["Apply refund fees<br/>Dev: 5 BPS<br/>Reflection: 5 BPS<br/>Reserve/Burn: 7.5-15 BPS"]
    E2 --> E3{"Burning<br/>limit<br/>reached?"}
    E3 -->|No| E4["Burn tokens<br/>Distribute dividends"]
    E3 -->|Yes| E4["Double reserve fee<br/>Distribute dividends"]
    E4 --> E5["Transfer ETH<br/>to user"]

Fee Summary Table

TRANSACTION TYPE DEV FEE REFLECTION FEE RESERVE FEE BURN FEE TOTAL
Buy 5 BPS 10 BPS 10 BPS 25 BPS
Refund 5 BPS 5 BPS 7.5-15 BPS 0-7.5 BPS 17.5-27.5 BPS
Regular Transfer 5 BPS 10 BPS 10 BPS 25 BPS
DEX Swap 0 BPS 0 BPS 0 BPS 0 BPS

Changelog

DATE AUTHOR NOTES
2026-02-04 Artificial. Generated by robots. Gas: 28 tok
? ? Reviewed, edited, and curated by humans.