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.
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
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()
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
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
functiontransfer(addressto,uint256amount)publicvirtualoverridereturns(bool){addressowner=_msgSender();_transfer(owner,to,amount);returntrue;}function_transfer(addressfrom,addressto,uint256amount)internaloverride{// Update dividend tracking BEFORE balance changesif(from!=address(0)&&!isContract(from)){_updateUserDividendTracking(from);}if(to!=address(0)&&!isContract(to)){_updateUserDividendTracking(to);}_update(from,to,amount);}function_update(addressfrom,addressto,uint256amount)private{if(from==address(0))revertZeroMoonAddress();if(to==address(0))revertZeroMoonAddress();if(amount==0)revertZeroMoonAmount();boolisExempt=_isExcludedFromFee[from]||_isExcludedFromFee[to];if(to==address(this)){super._transfer(from,to,amount);_handleRefund(from,amount);emitTransferFeeExempt(from,to,amount,ExemptionReason.REFUND);}elseif(isExempt){super._transfer(from,to,amount);emitTransferFeeExempt(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
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)
functionclaimDividends()externalnonReentrant{addressuser=msg.sender;// Exclude contracts from dividend claimingif(isContract(user))return;uint256userBalance=balanceOf(user);uint256currentDividendPerShare=magnifiedDividendPerShare;uint256lastUserDividendPerShare=lastDividendPerShare[user];// Calculate and accumulate dividends if user has balanceif(userBalance>0&¤tDividendPerShare>lastUserDividendPerShare){uint256dividendDifference=currentDividendPerShare-lastUserDividendPerShare;uint256newDividends=(userBalance*dividendDifference)/MAGNITUDE;if(newDividends>0){accumulatedDividends[user]+=newDividends;}}// ALWAYS update lastDividendPerShare to keep tracking synchronizedlastDividendPerShare[user]=currentDividendPerShare;// Transfer accumulated dividends to useruint256totalAccumulated=accumulatedDividends[user];if(totalAccumulated>0){accumulatedDividends[user]=0;super._transfer(address(this),user,totalAccumulated);emitDividendWithdrawn(user,totalAccumulated);}}
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)
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
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
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
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
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
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
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)
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
functioncalculateNativeForZETH(uint256zETHAmount)publicviewreturns(uint256){if(zETHAmount==0)return0;// Match the minimum refund check in _handleRefundif(zETHAmount<1ether)return0;uint256_totalBurned=totalBurned;// Calculate fees (same as _handleRefund)uint256devFeezETH=Math.mulDiv(zETHAmount,REFUND_DEV_FEE_BPS,10000);uint256reflectionFeezETH=Math.mulDiv(zETHAmount,REFUND_REFLECTION_FEE_BPS,10000);uint256burnFeezETH=(_totalBurned<BURNING_LIMIT)?Math.mulDiv(zETHAmount,75,100000):0;uint256reserveFeezETH=(_totalBurned<BURNING_LIMIT)?Math.mulDiv(zETHAmount,75,100000):Math.mulDiv(zETHAmount,150,100000);uint256zETHForUserRefund;unchecked{zETHForUserRefund=zETHAmount-devFeezETH-reflectionFeezETH-burnFeezETH-reserveFeezETH;}uint256contractBalance=balanceOf(address(this));uint256currentCirculatingSupply=(TOTAL_SUPPLY-_totalBurned)-contractBalance+zETHAmount;if(currentCirculatingSupply==0)return0;uint256effectiveBacking=(address(this).balance*EFFECTIVE_BACKING_NUMERATOR)/EFFECTIVE_BACKING_DENOMINATOR;uint256nativeToUser=Math.mulDiv(zETHForUserRefund,effectiveBacking,currentCirculatingSupply);returnnativeToUser;}
Function: getTotalDividendsDistributed()
Returns the total amount of zETH tokens that have been distributed as dividends since contract inception. Cumulative total that only increases.
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
Public wrapper for circulating supply calculation. Returns total supply minus unsold tokens in contract (equivalent to totalSupply() - contract balance).
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