Radar by SecureChain AI

Datacenter Token (Datacenter) security report

Radar by SecureChain AI scanned Datacenter Token on BNB Smart Chain. The review found conditional initialization takeover risk, callback-based transfer blocking, owner-controlled migration, and tax-processor configuration risks.

The source does not expose an ongoing public mint function or blacklist and pause controls. However, an uninitialized deployment can be taken over through initialize(), which mints the maximum supply to the caller. Transfers can also be blocked if the configured dividend contract reverts, and buy or sell operations can revert when configured tax rates exceed 100%. Slither was not run.

Trust score: 48/100. Source verified: Yes. Chain: bsc. Contract: 0xb088071eac4992c89f28e80f44f89a9b32107777.

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

high finding: Permissionless initialization can enable deployment takeover

initialize() is externally callable and protected only by the one-time initializer guard. If a proxy or clone is deployed without atomically invoking initialize(), any caller can initialize it, become the owner through __Ownable_init(), select the pool and external processors, and receive the entire maxSupply through _mint(msg.sender, maxSupply).

Evidence: FlapTaxTokenV3.initialize(InitParams memory params) is external and uses initializer, while it calls __Ownable_init() and _mint(msg.sender, maxSupply). maxSupply is the constant 1e9 ether. The constructor disables initialization only on the implementation contract, not on an uninitialized proxy or clone. GoPlus reports owner_address as 0xe2ce6ab80874fa9fa2aae65d277dd6b8e65c9de0, indicating the supplied instance has an owner, but deployment atomicity for the proxy or clone is not established by the supplied evidence.

Recommendation: Deploy proxies or clones with initialization calldata in the same transaction, verify the initialized owner and configuration immediately, and reject or monitor any deployment that remains uninitialized. If the supplied address is a direct implementation rather than an initialized proxy or clone, confirm its deployment model before relying on the owner signal.

medium finding: Dividend callback failure can block token transfers

Every transfer involving a non-exempt address invokes dividendContract.setShare() in _afterTokenTransfer(). If either callback reverts, the token transfer reverts with DividendShareUpdateFailed. Consequently, a misconfigured, unavailable, or intentionally reverting dividend contract can prevent ordinary transfers and can also block buys and sells involving a pool.

Evidence: _afterTokenTransfer() calls IDividend(dividendContract).setShare(from, balanceOf(from)) and setShare(to, balanceOf(to)) inside try/catch blocks, but each catch executes revert DividendShareUpdateFailed(...). Only the token contract, zero address, dead address, dividend contract, and configured pools are skipped; ordinary recipients and senders are not skipped.

Recommendation: Use a trusted, permanently available dividend implementation, validate it during initialization, and consider isolating dividend accounting from ERC-20 transfer success. A failure-tolerant queue or non-blocking callback design would reduce the risk of a complete transfer outage.

medium finding: Tax rates above 100% can make pool transfers revert

The initializer accepts uint16 buyTax and sellTax values without enforcing a maximum of 10000 basis points. When a taxed transfer occurs, _taxedTransfer calculates remainingAmount = amount - tax. Any tax above the transfer amount causes a Solidity arithmetic underflow and reverts, preventing affected buys or sells while that tax state is active.

Evidence: initialize() stores params.buyTax and params.sellTax directly in poolState without a bounds check. _getTaxWithPoolState() computes (amount * currentBuyTaxRate) / 10000 or (amount * currentSellTaxRate) / 10000, and _taxedTransfer() then executes uint256 remainingAmount = amount - tax. Rates greater than 10000 can therefore produce tax > amount.

Recommendation: Require buyTax and sellTax to be <= 10000 during initialization and validate the deployed values before migration. If rates above 100% are intentionally supported, define explicit behavior rather than relying on arithmetic underflow.

medium finding: Owner-controlled migration state can restrict or alter transfer behavior

The owner alone controls startMigration() and finalizeMigration(). While the token remains in BondingCurve, transfers to or from every configured pool revert. The owner also determines when the contract enters the tax-enforced states, which activate the configured buy and sell taxes and anti-farmer restrictions.

Evidence: startMigration() and finalizeMigration() both use onlyOwner. In _transfer(), PoolState.BondingCurve requires !pools[from] && !pools[to], reverting transfers involving configured pools. finalizeMigration() moves the state to TaxEnforcedAntiFarmer and activates tax timing based on block.timestamp.

Recommendation: Document the owner-controlled lifecycle, use a timelock or multisignature for migration operations, publish the configured pool addresses and state-transition criteria, and add operational monitoring for unexpected state changes.

medium finding: Failed tax processing can transfer the full token-contract balance to the processor

When TaxProcessor.processTaxTokens() reverts, the catch branch transfers balanceOf(address(this)) to taxProcessor rather than limiting the transfer to the current taxAmount. This can include any additional token balance held by the contract, and the destination is fixed during initialization.

Evidence: In _processTax(), a failed ITaxProcessor(taxProcessor).processTaxTokens(taxAmount) call enters the catch block, reads uint256 remainingBalance = balanceOf(address(this)), and calls _plainTransfer(address(this), taxProcessor, remainingBalance). taxProcessor is set from initialize() and has no setter in the verified source.

Recommendation: On failure, retain or isolate unrelated balances and transfer only the intended tax amount after verifying accounting. Validate that taxProcessor is a compatible contract during initialization and provide a controlled recovery mechanism for failed processing.

medium finding: A non-contract or incompatible tax processor can redirect accrued tax balance

taxProcessor is required to be nonzero but is not required to contain code or implement the expected interface. Calls to an incompatible address can fail and trigger the fallback that transfers the contract's entire token balance to that address, creating a material configuration and custody risk.

Evidence: initialize() checks only require(params.taxProcessor != address(0), "taxProcessor required"). _processTax() calls ITaxProcessor(taxProcessor).processTaxTokens(taxAmount) in a try/catch and, on failure, transfers balanceOf(address(this)) to taxProcessor. There is no code-existence or interface validation in the verified source.

Recommendation: Require taxProcessor.code.length > 0, validate the expected interface and configuration before activation, and remove the full-balance fallback. Use explicit accounting for tax amounts and a recoverable failure path.