Radar by SecureChain.ai

牛来 (NiuLai) security report

Radar by SecureChain.ai AI-scanned the verified NiuLai contract on BSC using source review and GoPlus evidence. No confirmed honeypot, minting, blacklist, proxy, or selfdestruct behavior was identified. Material findings concern the unlimited tax-processor allowance, dividend-contract transfer dependency, conditional migration lock after ownership renunciation, and unverified initialization tax rates. Slither was not run.

The token was AI-scanned using the verified Solidity source and supplied GoPlus signals. GoPlus reports the token as non-mintable, non-proxy, non-blacklisted, and not a detected honeypot. Material risks remain around trusted external contracts and a conditional permanent trading lock if ownership was renounced before migration completed.

Trust score: 68/100. Source verified: Yes. Chain: bsc. Contract: 0xebed2809cc850240741b8de78f572bc7b91f7777.

This report was last modified on 2026-09-20 and contains 4 material findings.

high finding: Configured tax processor receives unlimited token allowance and controls tax disposition

After the first tax liquidation, the token grants the configured taxProcessor an effectively unlimited allowance over the token contract balance. The processor can then pull tax tokens using transferFrom, and the contract also sends its remaining token balance directly to taxProcessor when processing fails. The taxProcessor is selected during initialization and has no setter or on-chain validation in this contract.

Evidence: _processTax() calls _approve(address(this), taxProcessor, type(uint256).max) whenever the allowance is insufficient. On processor failure, it calls _plainTransfer(address(this), taxProcessor, remainingBalance). initialize() accepts params.taxProcessor and stores it permanently. The supplied ABI exposes taxProcessor() but no function to replace or revoke it. The actual processor implementation and initialization parameters were not supplied.

Recommendation: Verify the deployed taxProcessor bytecode, ownership, fee configuration, and recipient addresses. Use immutable, audited processor code with bounded approvals or explicit allowance revocation, and add governance or emergency controls for processor replacement and stuck-tax handling.

medium finding: Dividend contract can block ordinary token transfers

Every transfer involving a non-excluded holder can call the externally configured dividendContract.setShare(). If either external call reverts, _afterTokenTransfer() reverts the entire token transfer. A faulty, paused, malicious, or incompatible dividend contract can therefore create a transfer denial of service for affected accounts.

Evidence: _afterTokenTransfer() calls IDividend(dividendContract).setShare(from, balanceOf(from)) and setShare(to, balanceOf(to)) without a non-reverting fallback. Each failure is caught and immediately rethrown as DividendShareUpdateFailed. dividendContract is assigned during initialize() and cannot be changed by any exposed function. GoPlus reports external_call as 0, but that signal does not establish that the configured dividend contract is safe or non-blocking.

Recommendation: Use a validated dividend implementation, add a fail-open or bounded-failure mode for share updates, and provide a controlled way to disable or replace the dividend integration if it becomes unavailable. Confirm the deployed dividendContract behavior before relying on transfers.

medium finding: Ownership renunciation can permanently lock pool trading before migration

While poolState is BondingCurve, all transfers to or from configured pool addresses revert. Only the owner can call startMigration() and finalizeMigration(). If ownership was renounced while the contract remained in BondingCurve, the configured DEX pool could become permanently unusable. GoPlus reports the current owner as the zero address, but the current pool state was not supplied.

Evidence: _transfer() requires !pools[from] && !pools[to] in PoolState.BondingCurve. startMigration() and finalizeMigration() are both onlyOwner. GoPlus reports owner_address as 0x0000000000000000000000000000000000000000 and transfer_pausable as 0. The evidence does not include state() or getPoolStateData(), so the lock condition is conditional rather than confirmed.

Recommendation: Verify the live state() and poolState values. If migration has completed, document that status. For future deployments, complete and verify migration before renouncing ownership, or add an owner-independent migration finalization path.

medium finding: Initialization-selected tax rates can make pool transfers revert

Buy and sell tax rates are supplied as uint16 values during one-time initialization without an explicit maximum below 10000 basis points. If a configured rate exceeds 10000, _taxedTransfer() computes amount - tax and reverts whenever the calculated tax is greater than the transfer amount, potentially disabling buys or sells for the lifetime of the deployment.

Evidence: initialize() stores params.buyTax and params.sellTax directly in poolState. _getTaxWithPoolState() calculates amount * rate / 10000, while _taxedTransfer() executes uint256 remainingAmount = amount - tax. The supplied GoPlus buy_tax, sell_tax, and transfer_tax fields are blank, so the deployed rates were not confirmed from the evidence.

Recommendation: Read buyTaxRate(), sellTaxRate(), and getPoolStateData() on-chain and confirm both rates are below 10000 basis points, preferably with an application-specific cap. Treat unusually high configured rates as a trading-risk condition.