Rigloop (RIGLOOP) security report
Radar by SecureChain AI scanned Rigloop’s verified BNB Smart Chain source. Findings include migration-state control, unbounded initialization tax rates, and callback-dependent transfers.
The contract does not expose post-initialization minting, blacklist controls, or owner-controlled tax setters. However, the initializer accepts unrestricted buy and sell tax values, the owner controls migration state transitions that govern pool transfers, and a configured dividend contract can make transfers revert if its callback fails. These are material configuration and operational risks requiring mitigation.
Trust score: 55/100. Source verified: Yes. Chain: bsc. Contract: 0x37c5bd19b10db534b2208597597030d1b3027777.
This report was last modified on 2026-10-17 and contains 5 material findings.
medium finding: Unbounded initialization tax rates can make pool transfers revert
The initializer accepts buyTax and sellTax as uint16 values without requiring either value to be at most 10000 basis points. Once tax enforcement is active, _taxedTransfer computes remainingAmount = amount - tax. A configured rate above 10000 basis points can make tax exceed amount and cause Solidity arithmetic to revert, blocking affected pool buys or sells.
Evidence: initialize() assigns params.buyTax and params.sellTax directly to poolState.buyTaxRate and poolState.sellTaxRate. _getTaxWithPoolState() calculates (amount * rate) / 10000, and _taxedTransfer() performs uint256 remainingAmount = amount - tax. The supplied GoPlus record leaves buy_tax, sell_tax, and transfer_tax blank, so the deployed effective rates were not established by the supplied evidence.
Recommendation: Enforce buyTax <= 10000 and sellTax <= 10000 during initialization, preferably with a lower documented maximum. Add deployment-time validation and tests covering pool transfers at the maximum permitted rate.
medium finding: Owner-controlled migration state can gate or alter pool transfers
The owner can call startMigration() and finalizeMigration(), controlling transitions from BondingCurve to Migrating and then to TaxEnforcedAntiFarmer. In BondingCurve, every transfer involving a configured pool reverts. In Migrating, transfers are allowed without tax. This gives the owner discretion over when pool trading is enabled and when the tax regime begins, creating a material launch and operational control risk.
Evidence: startMigration() and finalizeMigration() are both onlyOwner. In _transfer(), PoolState.BondingCurve requires !pools[from] && !pools[to], while PoolState.Migrating calls _plainTransfer() without tax. The pool addresses are supplied during initialize() and mainPool is set from params.pools[0].
Recommendation: Use an externally verifiable launch schedule or timelock for migration actions, constrain valid state transitions, emit and document expected transition windows, and consider removing discretionary owner control after launch.
medium finding: Dividend callback failure can block token transfers
When dividendContract is nonzero, _afterTokenTransfer() calls setShare() for eligible sender and recipient addresses. If either external call reverts, the token transfer reverts through DividendShareUpdateFailed. A malfunctioning, incompatible, or intentionally reverting dividend contract can therefore create a transfer denial-of-service for transfers involving non-exempt addresses.
Evidence: _afterTokenTransfer() invokes IDividend(dividendContract).setShare(from, balanceOf(from)) and setShare(to, balanceOf(to)) inside try/catch blocks, then explicitly reverts with DividendShareUpdateFailed on failure. dividendContract is assigned during initialize() and there is no setter in the verified contract to replace or disable it after initialization.
Recommendation: Avoid making core transfers depend on an external dividend callback. Queue share updates asynchronously, tolerate callback failure, or provide a governed emergency mechanism to disable or replace the dividend integration.
medium finding: Tax processing depends on a privileged external processor
During liquidation, the token grants taxProcessor an effectively unlimited allowance and calls processTaxTokens(). If that call fails, the contract transfers its entire remaining token balance to taxProcessor. The configured processor therefore controls handling of accrued tax tokens and can receive those tokens directly on failure, subject to the deployment configuration and processor implementation.
Evidence: _processTax() approves taxProcessor for type(uint256).max when needed. In the catch branch, it reads balanceOf(address(this)) and calls _plainTransfer(address(this), taxProcessor, remainingBalance). taxProcessor is supplied during initialize() and is not changeable through the verified token ABI.
Recommendation: Use a narrowly scoped allowance, transfer only the intended taxAmount, validate the processor address and implementation, and define a safer recovery path that does not redirect the entire contract token balance on processing failure.
medium finding: Initialization is a high-impact trust boundary
The externally callable initializer sets the owner, tax processor, dividend contract, pool addresses, tax rates, router, and supply recipient in one call. It also mints the full maxSupply to msg.sender. If a clone or proxy is deployed without atomic initialization, the first caller can select these parameters, become owner, and receive the initial supply.
Evidence: initialize() uses the initializer modifier, sets ownership through __Ownable_init(), accepts external addresses and rates, and executes _mint(msg.sender, maxSupply). The implementation constructor calls _disableInitializers(), but the supplied evidence does not establish the initialization sequence of any separate clone or proxy.
Recommendation: Atomically initialize each instance during deployment, verify initialization events and parameter values, and use a factory that prevents uninitialized instances from being exposed. Treat the initializer as privileged deployment logic.