Token Audit Modules

Every Token Audit runs up to 11 independent verification modules. Each module produces a status (pass / fail / warn / n/a), a risk score, and a set of specific findings called invariants. All 11 modules are always shown in the results.


Module Overview

Module 1
Live Simulation
Coming Soon
Module 2
Ownership & Access
Active
Module 3
Liquidity & Pools
Active
Module 4
Supply & Mint
Active
Module 5
Transfer Integrity
Active
Module 6
Pausability
Active
Module 7
Approve Integrity
Active
Module 8
Permit
Active
Module 9
Reentrancy
Active
Module 10
Arithmetic & Fees
Coming Soon
Module 11
Event Integrity
Coming Soon

n/a is not a pass. Modules where the checked feature is absent return n/a - the feature simply isn't in this contract. Modules that are not yet active show a Coming Soon badge.


1. Live Simulation Coming Soon

Forks the blockchain at the current block, deploys the contract in an isolated environment, and executes a real buy and sell. Confirms whether sells are actually possible and measures the effective tax on both sides.

This module will provide the most direct honeypot signal available - a confirmed failed sell is definitive evidence that a token is a honeypot, regardless of what the source code analysis shows.


2. Ownership & Access

Checks who controls the contract and what they can do with that control.

What it checks:
- Is the owner address an EOA (single key) or a contract (multisig / timelock)?
- Is ownership renounced (set to address(0))?
- What capabilities does the owner have - can they mint, pause, blacklist, upgrade, or drain?
- Is there a timelock on privileged functions?
- Are there hidden shadow-owner addresses hardcoded into the contract?

Blast radius measures the maximum damage the current owner can cause in a single transaction - from none (renounced) through low, medium, high, to critical.

Key findings:

Finding Severity Description
Unguarded ownership transferCriticalAnyone can take ownership - no access control
EOA owner with high capabilityHighSingle key controls mint, pause, blacklist, upgrade, or drain
Hidden ownerCriticalHardcoded address found - possible shadow control
No timelockHighPrivileged functions take effect immediately with no delay
Broken renounceHighrenounceOwnership() does not actually set owner to zero
Owner unresolvableHighowner() reverts or returns an unresolvable address - ownership state is opaque
Renounce missingMediumrenounceOwnership() is absent - owner cannot voluntarily give up control. Advisory; no score impact.
Roles not separatedAdvisoryAccessControl roles not well-separated - same role controls multiple risk-tier capabilities. No score impact.

3. Liquidity & Pools

Discovers all liquidity pools for the token across known DEXes and checks whether liquidity is locked and for how long.

What it checks:
- Pool discovery across PancakeSwap V2/V3, Uniswap V2/V3, and other major DEXes on each chain
- Total pool TVL in USD
- Whether LP tokens are locked in a recognised locker (PinkLock, UniCrypt, TeamFinance, Mudra, or burned)
- Lock expiry date and percentage of pool supply locked

Note: LP lock detection is available for BSC and Ethereum. V3 NFT positions are tracked via the NonfungiblePositionManager.

Key findings:

FindingSeverityDescription
No pool foundLowNo pool found via CREATE2 discovery on any known factory
LP unlockedHighLP tokens not held by any recognised locker - pool can be drained at any time
Lock expiredHighLP lock has expired - liquidity can be withdrawn immediately
Lock expiring soonMediumLP lock expires within 30 days
Partial lockMediumLock covers less than 80% of pool supply
Low TVLMedium / HighPool TVL below $1,000 or $100 - trivially manipulable
Unknown factory poolHighPool deployed by an unrecognised factory

4. Supply & Mint

Checks whether the token supply is fixed or can be inflated, and whether the deployer holds a disproportionate share.

What it checks:
- Is there a mint() function, and who can call it?
- Is there a mint cap, and is it mutable?
- Are there non-standard functions that write to totalSupply?
- Does totalSupply match the sum of all balances?
- What percentage of supply does the deployer currently hold?
- Is the burn mechanism real (tokens removed from supply) or fake (tokens sent to zero but still counted)?

Key findings:

FindingSeverityDescription
Hidden mintCriticalNon-standard function can inflate supply
No mint capCriticalSupply can be inflated without limit
Supply inconsistencyCriticaltotalSupply and sum of balances do not match
Fake burnHighTokens sent to zero address are not removed from total supply
Deployer holds 100%HighNo distribution has occurred - full rug risk
Active inflationCriticalCurrent supply exceeds deploy-time supply - inflation confirmed on-chain
Mutable mint capHighMint cap exists but is mutable - owner can raise the cap at any time
Transfer supply changeCritical_transfer() call graph adds to totalSupply - inflationary transfer path
Deployer holds 80%MediumDeployer holds 80% or more of supply - highly concentrated; manipulation risk

5. Transfer Integrity

Checks whether tokens are transferred correctly - that the sender's balance decreases by exactly the amount the recipient receives, and no funds leak to unauthorised addresses.

What it checks:
- Does every debit have a matching credit?
- Does transferFrom() respect the approved allowance?
- Are there more than one recipient address in the _transfer() path?
- Can the owner set fees, and is there an upper bound?
- Is the Transfer event emitted correctly?

Tokens with fee setter functions also get a Live Monitoring flag, indicating that the tax rate should be watched for changes.

Key findings:

FindingSeverityDescription
Conservation failureCriticalTokens disappear - balance decreases without a matching credit
Unauthorized recipientHighMore than one address receives tokens on every transfer
transferFrom() overdrainCriticalMore tokens can be pulled than the approved allowance
Unbounded fee setterCriticalOwner can set transfer fees to 100% with no cap
High-bound fee setterHighFee cap exists but is above 25%
Low-bound fee setterMediumFee cap exists and is at or below 25% - fees are capped but still settable by owner
Event missingMediumTransfer event not emitted - breaks tracking and compliance tools
Assembly in transferHighDangerous inline assembly in the _transfer() call graph
Phantom balanceOfCriticalbalanceOf/transfer mapping identity mismatch - phantom balance where reads and writes differ

6. Pausability

Checks whether the contract has a pause mechanism, who controls it, and whether it can be abused.

Only runs if the contract has a pause() function - returns n/a otherwise

What it checks:
- Is pause() access-controlled, or can anyone call it?
- Is the pauser a single EOA key?
- Does unpause() exist? Without it, the contract can be permanently frozen.
- Is the pause applied symmetrically - does it block both buys and sells, or only sells?
- Has the contract been paused before, and how many times?
- Is the contract currently paused?

Any contract that reaches this module automatically receives a Live Monitoring flag, regardless of findings - a pausable contract warrants ongoing monitoring even if the current configuration looks safe.

Key findings:

FindingSeverityDescription
Unguarded pauseCriticalAnyone can freeze all transfers
No unpauseCriticalContract can be permanently frozen with no recovery
Currently pausedCriticalAll transfers are frozen right now
Asymmetric pauseCriticalPause blocks sells but not buys - classic honeypot technique
EOA pauserHighSingle key can freeze all transfers instantly
Abusive pause historyCriticalContract has been paused more than 3 times
Hidden pauserHighPauser address variable is private - not readable on-chain
Conditional pauseCriticalConditional pause that can selectively block transfers for specific addresses
Mint not pausedHighmint() has no whenNotPaused modifier - deployer can mint while all transfers are frozen
Unpause restrictedMediumpause() is access-controlled but unpause() auth pattern was not detected - verify manually
Paused multiple timesHighContract has been paused multiple times - recurring pause usage
Paused onceMediumContract has been paused once - may be a legitimate emergency stop; monitor

7. Approve Integrity

Checks whether the approve() function correctly and only updates the allowance mapping - and nothing else.

Runs on all tokens

What it checks:
- Does approve() write to any state beyond the allowance mapping?
- Are there dangerous inline operations or external calls in the approve() call path?
- Does the amount parameter actually flow through to the allowance write?
- Is the Approval event emitted?

Key findings:

FindingSeverityDescription
Extra state writeCriticalapprove() writes to extra state - possible sell gate
External call in approveCriticalNon-view external call can block or redirect allowance writes
Amount not flowingCriticalThe amount parameter does not reach the allowance - silent-zero or event spoofing
Assembly in approveHighDangerous inline operations in the approve path
Event missingHighApproval event never emitted
Forbidden opcodeCriticalForbidden opcode (CALL, DELEGATECALL, CREATE2, SELFDESTRUCT) found in the approve() bytecode path
Double emitMediumApproval event emitted more than once - double-emit anomaly

8. Permit (Gasless Approvals)

Checks the EIP-2612 permit() implementation - the function that allows off-chain signatures to set allowances without a gas transaction.

Only runs if the contract has a permit() function - returns n/a otherwise

What it checks:
- Is the nonce incremented after each use? Without this, the same signature can be replayed unlimited times.
- Is the deadline enforced?
- Is chainId included in the domain separator? Without it, signatures can be replayed across chains.
- Is the ecrecover() result verified against the owner address?
- Do permit() and approve() write to the same allowance slot?

Key findings:

FindingSeverityDescription
Nonce not incrementedCriticalSame signature can be replayed unlimited times
Deadline not enforcedCriticalPermit signatures never expire
Chain ID missingCriticalSignatures valid across all chains
ecrecover() not checkedCriticalAnyone can forge a permit for any address
Different allowance slotCriticalPermits grant allowances invisible to allowance()
Preloaded permitCriticalDeployer may have pre-granted unlimited allowance in the constructor
Deadline wrong orderHighDeadline comparison uses wrong operator - expired signatures may be accepted
Domain separator mutableHighDOMAIN_SEPARATOR is reassigned after construction - may be changeable by owner
Chain ID hardcodedHighchainId is hardcoded - permit signatures valid across chains if contract is deployed at the same address
Chain ID mutableHighchainId assignment is mutable - owner can change the domain separator
Zero address not rejectedHighecrecover() zero-address result is not rejected - invalid signatures may match address(0) approvals
Phishing risk signalMediumpermit() selector detected in bytecode but source is unavailable - correctness cannot be verified

9. Reentrancy

Checks whether the contract is vulnerable to reentrancy attacks - where an external call during a transfer allows an attacker to re-enter the contract before state is updated.

Runs on all tokens

What it checks:
- Does the contract make external calls before updating balances (violating Checks-Effects-Interactions)?
- Are reentrancy guards present on external-calling functions?
- Is there a cross-function reentrancy path between _transfer() and mint()?

Key findings:

FindingSeverityDescription
CEI violationCriticalExternal call before balance write - classic reentrancy path
No reentrancy guardHighExternal-calling function has no protection
ERC-677 callback earlyCriticalCallback fires before balance update
Cross-function mint reentrancyHighTransfer has external calls but mint is unguarded
Read-only reentrancyMediumExternal calls in transfer path without guard - read-only reentrancy risk allowing price oracle manipulation
ETH call without guardHigh.call{value:} pattern without reentrancy guard - ETH transfer can re-enter
Legacy ETH transferLow.transfer() or .send() legacy ETH pattern - gas-limited but still reentrancy-adjacent
ApproveAndCall dispatcherHighExternal payable function grants allowance then forwards arbitrary .call{value:}(data) to a caller-controlled address without nonReentrant - any function selector can be encoded in data, including privileged functions on the token itself
OFT delegate hijackCriticalContract exposes both an approveAndCall-style dispatcher and public lzReceive/setDelegate - attacker can replace the LayerZero delegate then forge messages to mint arbitrary supply

10. Arithmetic & Fees Coming Soon

Will check fee calculation logic and arithmetic safety - unbounded fee accumulation, rounding errors that favour the contract, and overflow/underflow patterns in fee math.


11. Event Integrity Coming Soon

Will verify that the contract emits correct ERC-20 events on every state change - Transfer on balance writes, Approval on allowance writes - in the right order and with the correct parameters.


Honeypot Risk Score

In addition to the 11 modules, the audit runs a dedicated honeypot pattern analysis across the full contract code. This produces a separate honeypot_risk_score (0-100+) based on the weighted sum of dangerous patterns found.

ScoreClassification
0-24Clean
25-49Suspicious
50-79High Risk
≥ 80Honeypot

Key findings:

FindingSeverityDescription
Controlled launch modeCriticalLaunch mode gate prevents transfers until deployer enables trading
External call in transferCriticalstaticcall/delegatecall/call found inside a transfer function
Transfer kill switchCriticalKill switch or emergency stop that permanently disables all transfers
SHA256 privilege checkCriticalsha256(abi.encodePacked(msg.sender)) used as an auth gate - obfuscated access control
Upgradeable proxy - EOA adminCriticalTransparent proxy with EOA admin - contract logic can be replaced by a single key
UUPS proxy - EOA ownerCriticalUUPS upgradeable proxy with EOA owner - upgrade authority held by a single key
Assembly in transferHighInline assembly block inside a transfer function (not a known safe storage pattern)
Transfer admin onlyHighTransfer restricted to admin/owner only - regular users cannot sell
Max uint balance manipulationHightype(uint256).max written to a balance/allowance mapping - underflow blacklist pattern
Custom transfer entry pointHighNon-standard public transfer-like function - hidden sell path that routes tokens differently
Layered transfer delegationHighMultiple layers of transfer delegation with opaque logic
Missing standard functionsHighStandard ERC-20 functions (transfer, approve, allowance) are missing
UUPS proxy - unknown authHighUUPS proxy with unidentified upgrade authority
Excessive branchingMediumExcessive conditional branches in transfer function - complex sell restriction logic
Obfuscated variable namesMediumVariable names appear deliberately obfuscated
Unexpected events in transferMediumNon-standard events emitted inside transfer functions
BlacklistMediumBlacklist mechanism present - targeted addresses can be blocked from transferring
UUPS proxy - contract ownerMediumUUPS proxy owned by a contract - upgrade authority exists but is not a plain EOA
Proxy - generic contract adminMediumTransparent proxy admin is a generic contract (not a timelock or multisig)

Behavioral Signals

Two additional signals feed into the aggregate verdict from ChainAware's behavioral database - independently of the code analysis:

Signal Description
behavioral_fraud_score Fraud probability (0.0-1.0) for the deployer and feeder wallets, derived from 20M+ wallet profiles
behavioral_is_honeypot Whether the behavioral scanner classified this contract as a honeypot based on on-chain trading patterns

A contract can pass all static analysis modules but still carry a non-zero behavioral fraud score if the deployer wallet has a history of fraudulent deployments or the trading pattern matches known honeypot behaviour.


Further Reading


See also: Verdicts | Token Audit Overview