Holly Crab (CRAB) security report
Radar AI scanned the verified Holly Crab CRAB contract on BNB Smart Chain. Material findings involve external callback failures, tax processing authority, and initialization-time tax limits.
The verified source does not expose public minting, blacklist controls, proxy upgrade functions, or owner-controlled fee setters after initialization. However, configured external callbacks can block transfers, the tax processor receives an effectively unlimited token allowance, and initialization permits tax rates above 100%, which could make pool transfers revert if misconfigured.
Trust score: 62/100. Source verified: Yes. Chain: bsc. Contract: 0x331c99a4c23f4d814d3e22aabcb8926a42ea7777.
This report was last modified on 2026-11-04 and contains 5 material findings.
medium finding: Dividend callback failure can block token transfers
Every non-excluded transfer invokes the configured dividend contract through setShare. If that external call reverts, the token deliberately reverts the entire transfer, creating a conditional transfer-denial path for buys, sells, or wallet transfers.
Evidence: In _afterTokenTransfer, IDividend(dividendContract).setShare(...) is wrapped in try/catch, but the catch executes revert DividendShareUpdateFailed(from, reason). The same behavior applies to the recipient when the recipient is not excluded. _transfer always reaches this hook after moving balances.
Recommendation: Do not make ordinary ERC-20 transfers depend on a reverting external dividend callback. Consider recording failed share updates for later retry, using a non-blocking callback design, and validating the dividend contract before initialization.
medium finding: Tax processor receives an unlimited allowance over accrued tax tokens
The token grants the configured taxProcessor a maximum ERC-20 allowance. A compromised, malicious, or incorrectly configured processor could use that allowance to pull the token contract's accrued tax balance, beyond the amount intended for one liquidation.
Evidence: In _processTax, the contract calls _approve(address(this), taxProcessor, type(uint256).max) whenever the current allowance is below taxAmount. The processor is then called with processTaxTokens(taxAmount), but the token contract does not constrain subsequent use of the maximum allowance to that amount.
Recommendation: Approve only the exact taxAmount for each liquidation, or use a tightly controlled processor with allowance reset and explicit authorization checks. Add tests proving that a processor cannot transfer more than the current liquidation amount.
medium finding: Initialization permits tax rates above 100 percent
The initializer accepts uint16 buy and sell tax rates but does not enforce a maximum of 10,000 basis points. A faulty or abusive initialization can therefore configure a tax greater than the transferred amount, causing arithmetic underflow in taxed transfers and effectively blocking pool interactions.
Evidence: initialize stores params.buyTax and params.sellTax directly in poolState. _getTaxWithPoolState computes amount * rate / 10000, while _taxedTransfer executes uint256 remainingAmount = amount - tax. Solidity 0.8 reverts when tax exceeds amount.
Recommendation: Require both tax parameters to be at most 10,000 basis points during initialization, and add invariant tests covering buys and sells at the upper boundary and above-bound rejection.
medium finding: Owner-controlled migration sequencing can conditionally restrict pool transfers
While the pool state is BondingCurve, all transfers to or from addresses marked as pools revert. The owner controls the transitions into Migrating and then tax-enforced states, so incorrect or delayed migration sequencing can keep pool interactions unavailable until the state changes.
Evidence: _transfer requires !pools[from] && !pools[to] in PoolState.BondingCurve. startMigration and finalizeMigration are both external onlyOwner functions, and no permissionless recovery transition exists.
Recommendation: Use an immutable or time-bounded migration process, validate the intended pool and migration authority at deployment, and provide an independently verifiable recovery path for an incorrectly sequenced state.
medium finding: Tax liquidation depends on an unvalidated external processor
Tax liquidation makes an external call to taxProcessor during a token transfer. If processing fails, the fallback sends the entire token contract balance to taxProcessor rather than only the current taxAmount, increasing the amount exposed to that external dependency.
Evidence: In _processTax, a failed processTaxTokens call enters the catch block, reads balanceOf(address(this)), and calls _plainTransfer(address(this), taxProcessor, remainingBalance). The taxProcessor address is supplied during initialize and has no setter or validation of its implementation behavior.
Recommendation: Restrict the fallback to the current taxAmount, verify the processor interface and deployment before initialization, and avoid transferring unrelated accumulated balances on an external-call failure.