Radar by SecureChain AI

Mortal (MORTAL) security report

Radar by SecureChain AI scanned Mortal on BNB Smart Chain using verified source. Findings include conditional initialization takeover risk and medium-severity operational controls affecting transfers and tax handling.

The source does not show an arbitrary mint function after initialization, blacklist logic, or proxy upgrade entry points. Material risks remain: an uninitialized instance can be claimed and fully minted by the first initializer; dividend callbacks can block transfers; and owner-controlled migration determines when pool restrictions and taxes change.

Trust score: 55/100. Source verified: Yes. Chain: bsc. Contract: 0x6b1c03400cb3f91e761c6c15cf0a53ff25e37777.

This report was last modified on 2026-10-08 and contains 4 material findings.

high finding: Uninitialized instance can be claimed and fully minted

The external initialize function is protected only by the one-time initializer guard. If a deployed instance is left uninitialized, any caller can initialize it, become the Ownable owner, choose the token configuration, and receive the entire maxSupply through _mint(msg.sender, maxSupply).

Evidence: FlapTaxTokenV3.initialize(...) is external and uses initializer. It calls __Ownable_init(), which assigns ownership to _msgSender(), and then executes _mint(msg.sender, maxSupply). maxSupply is 1e9 ether. The constructor disables initialization only on the implementation contract, not on a separate clone or proxy storage context.

Recommendation: Initialize every deployed instance atomically with deployment or verify its Initialized event and owner immediately after deployment. Factory or proxy deployments should pass initialization calldata in the same transaction and test that the implementation and instance have separate, correctly locked initialization state.

medium finding: Dividend callback can block ordinary token transfers

When dividendContract is configured, every transfer involving a non-excluded address calls dividendContract.setShare. Any revert from that external call is propagated as DividendShareUpdateFailed, so a broken, incompatible, or deliberately reverting dividend contract can prevent affected transfers from completing.

Evidence: _afterTokenTransfer calls IDividend(dividendContract).setShare(from, balanceOf(from)) and setShare(to, balanceOf(to)) inside try/catch blocks. The catch blocks execute revert DividendShareUpdateFailed(...). dividendContract is assigned during initialize and there is no recovery or replacement function in FlapTaxTokenV3.

Recommendation: Use a non-blocking dividend update pattern, allow authorized replacement or disabling of a failed dividend contract, and test transfers when the callback reverts, consumes excessive gas, or returns unexpected behavior.

medium finding: Owner-controlled migration changes pool restrictions and tax enforcement

The owner controls the transition from BondingCurve to Migrating and then to TaxEnforcedAntiFarmer. While BondingCurve, transfers to or from configured pools revert; while Migrating, transfers are plain and untaxed. The owner can therefore determine when pool access opens and when tax enforcement begins.

Evidence: startMigration() and finalizeMigration() are both onlyOwner. In _transfer, BondingCurve requires !pools[from] && !pools[to], whereas Migrating calls _plainTransfer without tax. finalizeMigration sets the state to TaxEnforcedAntiFarmer and establishes tax expiration values.

Recommendation: Document the owner-controlled launch sequence, use a timelocked or multisignature owner, emit and monitor state transitions, and enforce expected migration timing in deployment and operational procedures.

medium finding: Tax liquidation failure transfers the contract balance to the tax processor

If the external tax processor call fails, the fallback path sends the token contract's entire remaining token balance to taxProcessor. This can include tokens sent directly to the token contract in addition to the intended accrued tax balance, creating an irreversible loss path for mistakenly transferred tokens.

Evidence: In _processTax, a failed ITaxProcessor(taxProcessor).processTaxTokens(taxAmount) call is caught. The contract then reads balanceOf(address(this)) and executes _plainTransfer(address(this), taxProcessor, remainingBalance) when the balance is nonzero.

Recommendation: Track accrued tax separately from unsolicited token transfers, limit fallback transfers to the intended tax amount, and use an explicit recovery mechanism for unrelated tokens. Review the configured tax processor and its failure behavior before launch.