Guardian's review of Protocol Review for Bigcoin, published October 2025. The report records 20 findings across 3 review rounds, including 1 critical and 4 high.
- Published
- Review window
- September 29 to October 22, 2025
- Rounds
- Main Review, Remediation Review, Remediation Review 2
- Language
- Solidity
- Sector
- Gaming and prediction
- 1 Critical
- 4 High
- 3 Medium
- 5 Low
- 7 Informational
Scope
12 files in scope · 1,701 nSLOC
| File | nSLOC | Lines |
|---|---|---|
src/MainV2.sol | 738 | 1247 |
src/Types.sol | 63 | 87 |
src/minepad/IMinepad.sol | 3 | 5 |
src/minepad/IMinepadFactory.sol | 3 | 5 |
src/libraries/Errors.sol | 44 | 46 |
src/libraries/Events.sol | 47 | 77 |
src/minepad/uniswap-v3/LiquidityLocker.sol | 120 | 201 |
src/minepad/uniswap-v3/LiquidityLockerFactory.sol | 39 | 81 |
src/minepad/uniswap-v3/MinepadToken.sol | 241 | 356 |
src/minepad/uniswap-v3/MinepadTokenFactory.sol | 211 | 375 |
src/minepad/uniswap-v3/TickMath.sol | 168 | 205 |
src/minepad/uniswap-v3/Types.sol | 24 | 30 |
Findings 20
Main Review
13 findings · September 29 to October 8, 2025-
C-01 Critical Sniping Risk Via Liquidity Cap Finalization Logical Error Resolved
Description
The allocated hashrate mechanism in Minepad is designed to give all participants enough time to gradually obtain token allocations and discourage sniping. However, because mining can also be finalized once the liquidity cap is satisfied, a sniping attack remains possible.
Attack scenario:
- An attacker allows the creator to deploy the token.
- They allocate some hash rate.
- They donate Bigcoin such that the
balanceOfis greater than the cap. - They then trigger finalization.
This would allow them to receive the entire miners’ supply of Minepad tokens in exchange for the Bigcoin that went into LP. They could later recover those funds by continuing to mine. So instead of waiting for Bigcoin rewards to accrue before Minepad tokens can finalize, they can do it instantly. And if the attacker is also the creator, they don’t even lose their donated Bigcoin, since they would get it back after vesting.
This behavior undermines the anti-sniping intent of the system and introduces a loophole.
Recommendation
Consider tracking the bigcoin rewards sent to minepad token instead of relying on
balanceOfalone. -
H-01 High supplyForMiners Instead Of supplyForLPs Logical Error Resolved
Description
During finalization, if
sqrtPriceX96Start != sqrtPriceX96, a swap is attempted to reach the desired price. If there is zero liquidity in the pool, this swap occurs without pulling tokens from the Minepad token. However, if anyone has already added liquidity to the pool directly, the Minepad token handles this case by rebalancing throughextraForLpRebalance.The problem is that this rebalancing amount is subtracted from
supplyForMinersinstead ofsupplyForLPs. Since the swap pullsextraForLpRebalanceworth of Minepad tokens and these tokens accrue to LPs, they should be subtracted fromsupplyForLPs. Otherwise, it dilutes or even eliminates the allocation to miners.In the worst case, if
extraForLpRebalance >= supplyForMiners, miners would receive no allocation at all, and all rewards would go to users who provided liquidity directly on Uniswap v3.Recommendation
Consider reducing
extraLPForRebalancefrom the LP balance instead of the miners’ balance. However, there could still be an underflow problem if direct liquidity addition on Uniswap v3 is too large. We understand that this scenario is not profitable, but ideally, there should not be any edge case like that. Please give us some time to come back with a finalized solution. -
H-02 High No Retry Path On Migration Failiure Logical Error Resolved
Description
In _finalizeMining(), the contract first sets miningCompleted = true, calls MAIN.deallocateHashrate(), and removes the LP guard. If the subsequent finalization steps fail, for example if _calculatePrice returns an invalid price or the pool cannot be set exactly at the target sqrtPriceX96, the function emits MigrationFailed and returns early. At that point, the Minepad is marked as finalized (miningCompleted = true), but no LP is minted or locked and no excess amounts paid out. This leaves the Bigcoin and MinepadToken balances intended to seed liquidity stranded in the contract. The creator receives neither LP nor the excess Bigcoin or MinepadToken payouts, and the contract has no mechanism to retry migration or recover the stuck assets.
Recommendation
Defer setting miningCompleted to true until after a successful LP mint, or introduce a retryFinalize mechanism. As a fallback, consider adding a recovery path to sweep stranded Bigcoin or MinepadToken if migration cannot be completed.
-
H-03 High removeLPGuardLq Reverts DoS Acknowledged
Description
The removeLPGuardLiquidity function removes the LP guard, collects any accrued fees, and burns the position. The issue is that when calling collect, amount0Max and amount1Max are both set to tokensOwed0 and tokensOwed1. If both values are zero, the collect call to UNISWAP_POSITION_MANAGER will revert, since it requires at least one of the amounts to be greater than zero. As a result, the _finalizeMining function, which calls removeLPGuardLiquidity, will also revert blocking the migration process entirely. This situation can occur if the current price and ticks produce a withdrawal of zero and there are no fees to collect, and can also be exploited by manipulating conditions to effectively prevent finalization.
Recommendation
Modify the collect logic in the removeLPGuardLiquidity function to only perform the collect call when either tokensOwed0 or tokensOwed1 is greater than zero.
-
M-01 Medium Incorrect Address Used In _canBuyMiner Logical Error Resolved
Description
The canBuyMiner function allows anyone to check whether a facility at a given facilityAddress can buy a miner at a specified location using a miner index and coordinates. The problem is that the _isInvalidCoordinates function, which is called within _canBuyMiner, always uses msg.sender as the facilityAddress. As a result, when a user calls canBuyMiner for a facilityAddress that is not the caller, the coordinate validation is performed against the wrong address. This leads to incorrect results, as the function does not evaluate the intended facility’s validity but instead checks coordinates against the caller.
Recommendation
Modify _isInvalidCoordinates to use a provided facilityAddress argument rather than msg.sender to ensure coordinate validation is performed against the correct facility.
-
M-02 Medium deployMinepad Misuse Validation Resolved
Description
The
createMinepadfunction requires users to approve theMinepadTokenFactoryfor at least thedeploymentFeein Bigcoin before it can be called. This fee is transferred indeployMinepadto theMinepadTokenFactoryowner address.A potential issue arises because
deployMinepaditself is publicly callable and allows specifying an arbitrarycreatoraddress. If a user has granted the factory the maximum approval or has a lingering approval, an attacker can repeatedly calldeployMinepadwith that user’s address as thecreator, draining the user’s entire approved Bigcoin balance. The resulting Minepads would be unusable, since they are not deployed through the intendedcreateMinepadflow, but the user’s funds would already be consumed.Recommendation
Restrict deployMinepad so it can only be called from the MainV2 contract, preventing attackers from directly exploiting user approvals.
-
L-01 Low minepadCompleted Uses Wrong Address Logical Error Resolved
Description
The minepadCompleted view function is intended to take a minepad address as input and check whether it is still active by reading from the minepadDetails mapping. However, the function currently references msg.sender instead of the provided minepad address. As a result, it does not return the correct status of the target minepad and instead always resolves to the default boolean value. This breaks the intended functionality of the function and prevents users or integrators from correctly querying a minepad’s completion state.
Recommendation
Update the function to reference the minepad address passed as input rather than msg.sender when looking up minepadDetails.
-
L-02 Low syncMinepad Can Trigger Before Allocation Frontrunning Resolved
Description
Currently, anyone can call
hashrateUpdatedthroughsyncMinepad. Although this scenario is unlikely, if a user has not yet allocated hashrate and someone callssyncMinepad, it would start the clock forminingEndTimefrom that sync call itself:miningStartTime = block.timestamp; miningEndTime += timeShift;This goes against the protection logic Bigcoin is trying to enforce.
Recommendation
Consider allowing
syncMinepadonly if mining has already started, or otherwise handle this edge case to avoid unintended early mining timers. -
I-01 Informational Remainder Left In Minepad Contract Warning Acknowledged
Description
Since
distributerounds down when calculating miner rewards:uint256 totalRewards = FixedPointMathLib.mulDiv( facilityShares, amountForMiners, totalShares ); // @audit precision lossa small portion of
amountForMiners(dust) will remain in the contract.Recommendation
Be aware of this behavior. No fix is required. Rounding down is correct direction.
-
I-02 Informational Referral Fee Not Applied With Minepad Warning Resolved
Description
For normal claims, Bigcoin takes a referral fee or credits it to the referrer. This is not applied to claims made through Minepad. As a result, Bigcoin generated by hashrate allocated to Minepad does not include a referral fee.
Recommendation
Review this behavior. If this was not intended, consider fixing it.
-
I-03 Informational Predictable Address Enables Pool Griefing DoS Acknowledged
Description
The MinepadToken constructor reverts with PoolAlreadyCreated() if a Uniswap V3 pool for (MinepadToken, BIGCOIN, feeTier) already exists. Because the Minepad token address is deterministically derived via CREATE2 from the factory’s salt loop and deployment parameters, an attacker can precompute the future token address and front-run deployment. By calling Uniswap V3 createPool for that pair before the constructor executes, the attacker can create a pool against the future address. Uniswap V3 permits permissionless pool creation for arbitrary addresses (even if no code is deployed yet), so this succeeds. When the creator later calls createMinepad, the constructor detects the preexisting pool and reverts, blocking deployment.
Recommendation
Be aware of this griefing vector that can block deployments; affected creators can typically resolve it by retrying once the factory’s salt advances, yielding a new MinepadToken address.
-
I-04 Informational _mint Function Reverts Past Cap Informational Acknowledged
Description
The _mint function in the MainV2 contract enforces a supply cap by reverting if amtBurned + supply + amount exceeds MAX_SUPPLY + halvingOvershoot. Once this limit is reached, all functions that call _mint will revert.
Recommendation
If this behaviour is intended, ensure it does not unintentionally block any critical functionality that it's not supposed to.
-
I-05 Informational Inconsistent TickMath Version Informational Acknowledged
Description
The TickMath library specifies pragma solidity ^0.8.19, while the Uniswap V3 TickMath library uses pragma solidity >=0.5.0 <0.8.0. Although this currently has no observable impact, it could introduce subtle issues if any parts of the codebase rely on underflow or overflow behaviour that was possible in pre-0.8 Solidity versions.
Recommendation
Consider migrating to the Uniswap V4 TickMath implementation to ensure compatibility with Solidity 0.8’s built-in arithmetic safety and prevent unintended behaviour in edge-case calculations.
Remediation Review
5 findings · October 17 to 18, 2025-
H-01 High Hardcoded Swap Direction Can Block Migration DoS Resolved
Description
The _migrateLiquidity function attempts to move the pool price to match the target before minting the position. However, the internal swap direction is hardcoded to zeroForOne = false. This creates a scenario where, if a user performs a swap between minepad creation and migration, they can remove the Minepad tokens seeded by the LP guard and push the pool price above the target. Once this happens, the migration’s price adjustment logic will always fail to bring the price back down, resulting in a blocked migration and preventing LP creation.
Recommendation
Consider dynamically determining the swap direction in _adjustPoolPrice based on the current pool price relative to the target to prevent migration blockage.
-
L-01 Low Overwritten _extraForLpRebalance Value Logical Error Resolved
Description
The uniswapV3SwapCallback function assigns _extraForLpRebalance to amount0Delta, representing the additional tokens used for LP rebalancing during migration. This value is later deducted from supplyForLP in _migrateLiquidity. However, _extraForLpRebalance is overwritten each time uniswapV3SwapCallback is invoked, rather than accumulated. Because this callback can be triggered multiple times, such as during migration retries, only the most recent amount0Delta is reflected, leading to an inaccurate LP supply deduction.
Recommendation
Accumulate _extraForLpRebalance across all swap callback calls rather than overwriting it, ensuring the total extra tokens used for LP rebalancing are accurately reflected during migration.
-
L-02 Low LP Rebalance Not Deducted Logical Error Resolved
Description
The withdrawForManualMigration function allows the protocol owner to manually withdraw liquidity and BIGCOIN from the contract to create a new LP if the migration process fails. However, unlike the successful migration path, this function does not deduct _extraForLpRebalance from supplyForLP. As a result, the missing deduction is effectively taken from supplyForMiners.
Recommendation
Ensure _extraForLpRebalance is deducted from supplyForLP during manual migrations to maintain consistent internal accounting.
-
L-03 Low Retry Migration Enables Sandwich Attack Access Control Resolved
Description
The Retry Migration function currently allows anyone to trigger a retry after the first migration failure. Since the Minepad token swap is predetermined, this opens an attack vector for predictable sandwiching, where an attacker can manipulate token prices or extract value from the transaction.
Although the likelihood of a retry migration occurring is relatively low, and the post-migration check
sqrtPriceX96Start = sqrtPriceX96ensures that the intended Minepad state is achieved, the open access still poses a risk. Attackers can potentially sandwich the excess Bigcoin or Minepad tokens that should have been burned or distributed to miners, leading to unintended value leakage.
Recommendation
Consider implementing access control for the Retry Migration function.
-
I-01 Informational Guarded Liquidity Constrained Configuration Acknowledged
Description
The current Bigcoin implementation restricts guarded liquidity to a single tick spacing using the following logic:
_constrainedTickLower = MIN_CONSTRAINED_TICK_LOWER; _constrainedTickLower = (_constrainedTickLower / _tickSpacing) * _tickSpacing; _constrainedTickUpper = _constrainedTickLower + _tickSpacing;Because liquidity is only allocated within one tick, it becomes possible for an attacker or trader to remove all guarded liquidity (1 minepad) with a single swap before finalize with minimal costs. This ensures that any swaps performed before finalization do not consume the entire guarded liquidity in one step, improving liquidity resilience and reducing manipulation risk.
Recommendation
Consier allocating liquidity across the full range, from
MIN_CONSTRAINED_TICK_LOWERtoMAX_CONSTRAINED_TICK_LOWER, similar to a v2-style wide-range position.
Remediation Review 2
2 findings · October 22, 2025-
M-01 Medium Inverted Amount In _migrateLiquidity Logical Error Resolved
Description
The _migrateLiquidity function sets zeroForOne to true when sqrtPriceX96Start > sqrtPriceX96, and false otherwise. While this directional logic is correct, the amount selection is inverted. The function uses bigcoinBalance when zeroForOne is true, and minepadToken when false. This is incorrect as the zeroForOne swap should use the token0 amount, which in this case corresponds to the Minepad token. As a result, the swap executes with the wrong amountSpecified.
Recommendation
Ensure the amount aligns with the correct token side for the determined direction in _migrateLiquidity, using the Minepad token when zeroForOne is true and Bigcoin when false.
-
I-01 Informational Unbounded Creator Token Allocation Informational Resolved
Description
The Minepad token contract allows the creator to optionally specify a creator token allocation when initializing a new Minepad. However, there is currently no maximum limit imposed on this allocation. As a result, a creator could assign an excessively large portion of the total supply to themselves.
Recommendation
Consider setting a maximum cap on the creator token allocation to prevent excessive self-allocation.
No findings match.
Put your code through the same review.
This review started with a conversation about scope. Tell us what you are building and we will plan yours with you.
