MEME NASDAQ (MDAQ) security report
MEME NASDAQ (MDAQ) was AI-scanned from supplied Solidity source, ABI, and GoPlus data. Slither was skipped. This automated review does not guarantee safety.
The contract was AI-scanned using the supplied verified source, ABI, GoPlus evidence, and an independent source review. No immediately proven arbitrary-user theft path was established from the available evidence, but several high and medium control risks were identified. Slither analysis was skipped. The actual deployment state, owner, tax rates, processor implementations, and proxy initialization status should be verified on-chain before relying on the token.
Trust score: 32/100. Source verified: Yes. Chain: bsc. Contract: 0x63082387b0753f8c45adc4af5d3469fd711d7777.
This report was last modified on 2026-09-29 and contains 6 material findings.
high finding: Tax processor receives effectively unlimited allowance over accumulated token tax
The token approves taxProcessor for type(uint256).max whenever its allowance is below the current tax amount. The configured processor can therefore call transferFrom on the token contract and move the contract's entire token balance, not merely the amount currently being processed. This creates a powerful trusted-processor asset-drain capability.
Evidence: In _processTax(), the contract executes _approve(address(this), taxProcessor, type(uint256).max), then calls ITaxProcessor(taxProcessor).processTaxTokens(taxAmount). No allowance reduction or transfer amount restriction is applied after processing.
Recommendation: Use an exact allowance for taxAmount, or use forceApprove followed by allowance revocation. Restrict the processor implementation, validate its code and configuration, and add a bounded accounting mechanism so the processor cannot withdraw unrelated token balances.
high finding: Owner can keep the token in a state that blocks pool trading
The owner alone controls startMigration() and finalizeMigration(). While the token remains in BondingCurve state, every transfer involving a configured pool reverts. The owner can therefore prevent buys and sells by not completing migration. This is a centralized trading-lock capability.
Evidence: startMigration() and finalizeMigration() are both onlyOwner. In _transfer(), PoolState.BondingCurve requires !pools[from] && !pools[to], reverting with "Transfers to/from pools are restricted in BondingCurve state".
Recommendation: Use an immutable or time-bound migration schedule, validate state transitions against an external launch condition, and disclose the owner-controlled trading dependency. Monitor ownership and migration events.
high finding: Uninitialized proxy or clone could be claimed by an arbitrary caller
The implementation disables initialization in its constructor, but initialize() is externally callable through a proxy or clone and mints the entire maxSupply to msg.sender. If the deployed token address is an uninitialized proxy or clone, the first caller could configure malicious pools, taxProcessor, dividendContract, and receive the entire supply. The supplied deployment evidence is insufficient to confirm initialization status.
Evidence: initialize() uses initializer but has no caller restriction and executes _mint(msg.sender, maxSupply). GoPlus reports owner_address as empty, holder_count as 0, and is_open_source as 0; these signals are not conclusive but warrant verification of the target's storage and proxy state.
Recommendation: Verify that the deployed address is initialized, identify its implementation and proxy administrator, and initialize atomically at deployment. Revoke or transfer ownership to a controlled multisig after initialization.
medium finding: Configured tax rates can make buys or sells revert
Initialization accepts uint16 buy and sell tax rates without enforcing a maximum of 10000 basis points. If either rate exceeds 10000 while taxes are active, amount - tax underflows and pool transfers revert. Even rates near 10000 can make trading economically impractical.
Evidence: initialize() stores params.buyTax and params.sellTax directly in poolState. _taxedTransfer() calculates uint256 remainingAmount = amount - tax without a rate-bound check. Tax is calculated as amount * rate / 10000.
Recommendation: Require buyTax and sellTax to be <= 10000 during initialization, expose effective rates clearly, and test every migration and tax-expiration state against the deployed configuration.
medium finding: Dividend callback can lock ordinary token transfers
When dividendContract is nonzero, _afterTokenTransfer() calls setShare() for non-pool sender and recipient addresses. Any revert from that external contract is converted into DividendShareUpdateFailed and reverts the entire token transfer. A broken, malicious, or incompatible dividend contract can therefore block normal wallet-to-wallet transfers and potentially other operations.
Evidence: _afterTokenTransfer() executes IDividend(dividendContract).setShare(...) inside try/catch and explicitly reverts on failure. dividendContract is supplied during initialize() and is not changeable through the exposed token ABI.
Recommendation: Avoid making core ERC20 transfers dependent on an external callback, or use a non-reverting best-effort update with a recovery queue. Validate the dividend contract before initialization and provide an emergency disable mechanism.
medium finding: Tax liquidation failure redirects all held tax tokens to the processor
If processTaxTokens() reverts, the catch branch transfers the token contract's entire remaining balance to taxProcessor. This is broader than the current taxAmount and can transfer unrelated tokens that were sent to or accumulated in the contract.
Evidence: In _processTax(), the catch block reads balanceOf(address(this)) and calls _plainTransfer(address(this), taxProcessor, remainingBalance) whenever the balance is nonzero.
Recommendation: On processing failure, retain balances for retry or transfer only the current taxAmount. Add explicit accounting for tax balances and require a controlled recovery procedure for failed liquidations.