Guardian's review of Protocol Review for Clanker, published August 2026. The report records 74 findings across 2 review rounds, including 20 high and 13 medium.
- Published
- Review window
- June 29 to July 29, 2026
- Rounds
- Main Review, Remediation Review
- Language
- Solidity
- Chains
- Base
- Sector
- Token launches
- 0 Critical
- 20 High
- 13 Medium
- 20 Low
- 21 Informational
Scope
46 files in scope · 4,713 nSLOC
| File | nSLOC | Lines |
|---|---|---|
src/v5/ClankerV5.sol | 744 | 744 |
src/v5/ClankerCompatibilityRegistry.sol | 616 | 616 |
src/v5/ClankerDeploymentRegistry.sol | 67 | 67 |
src/v5/adapters/UniswapV4Adapter.sol | 276 | 276 |
src/v5/adapters/PancakeInfinityCLAdapter.sol | 117 | 117 |
src/v5/adapters/BaselineMercuryWrapper.sol | 104 | 104 |
src/v5/adapters/AmmAdapterModuleBase.sol | 73 | 73 |
src/v5/adapters/types/AmmAdapterBlobTypes.sol | 28 | 28 |
src/v5/extensions/ClankerUniv4EthDevBuyV5.sol | 80 | 80 |
src/v5/extensions/ExtensionModuleBase.sol | 57 | 57 |
src/v5/extensions/ClankerVaultV5.sol | 63 | 63 |
src/v5/extensions/ClankerAirdropV5.sol | 63 | 63 |
src/v5/extensions/ClankerAirdropV5V2.sol | 63 | 63 |
src/v5/fees/FeeRoutingModuleBase.sol | 73 | 73 |
src/v5/fees/ClankerFeeRouting.sol | 44 | 44 |
src/v5/fees/types/FeeRoutingBlobTypes.sol | 12 | 12 |
src/v5/mechanics/LaunchMechanicModuleBase.sol | 73 | 73 |
src/v5/mechanics/DefaultLaunchMechanic.sol | 65 | 65 |
src/v5/mechanics/types/MechanicBlobTypes.sol | 15 | 15 |
src/v5/token-sources/BaseBTokenSource.sol | 197 | 197 |
src/v5/token-sources/ClankerTokenSource.sol | 136 | 136 |
src/v5/token-sources/TokenSourceModuleBase.sol | 83 | 83 |
src/v5/token-sources/TokenDescriptorRegistry.sol | 75 | 75 |
src/v5/token-sources/types/TokenSourceBlobTypes.sol | 33 | 33 |
src/v5/libraries/LaunchValidation.sol | 134 | 134 |
src/v5/libraries/ClankerV5Constants.sol | 129 | 129 |
src/v5/libraries/SupplyAccounting.sol | 69 | 69 |
src/v5/libraries/BaseBTokenInitCallValidation.sol | 54 | 54 |
src/v5/libraries/ClankerModuleKindLibrary.sol | 53 | 53 |
src/v5/libraries/UniswapV4PoolKeyValidation.sol | 51 | 51 |
src/v5/libraries/UniswapV4LaunchIndexing.sol | 38 | 38 |
src/v5/types/ClankerV5Types.sol | 161 | 161 |
src/v5/interfaces/IClankerCompatibilityRegistry.sol | 234 | 234 |
src/v5/interfaces/IClankerV5.sol | 222 | 222 |
src/v5/interfaces/ITokenDescriptorRegistry.sol | 65 | 65 |
src/v5/interfaces/IClankerV5LaunchFactory.sol | 56 | 56 |
src/v5/interfaces/IClankerAmmAdapter.sol | 41 | 41 |
src/v5/interfaces/IClankerTokenSource.sol | 37 | 37 |
src/v5/interfaces/IClankerDeploymentRegistry.sol | 37 | 37 |
src/v5/interfaces/IPancakeInfinityCLLauncher.sol | 35 | 35 |
src/v5/interfaces/IClankerExtensionV5.sol | 31 | 31 |
src/v5/interfaces/IClankerFeeRouting.sol | 25 | 25 |
src/v5/interfaces/IBaselineMercuryLauncher.sol | 25 | 25 |
src/v5/interfaces/IClankerModule.sol | 24 | 24 |
src/v5/interfaces/IClankerLaunchMechanic.sol | 24 | 24 |
src/v5/interfaces/IClankerUniswapV4MevFinalizer.sol | 11 | 11 |
Findings 74
Main Review
45 findings · June 29 to July 16, 2026-
H-01 High Forced ETH Transfer Bricks All Launches DoS Resolved
Description
deployLaunchfinalizes with a strict zero-balance check:if (address(this).balance != 0) { revert MsgValueMismatch(); }This assumes core holds no ETH beyond the msg.value that extensions consume during the launch. Although there is no receive/fallback mechanism, anyone can force ETH into core with
selfdestruct(core), whichEIP-6780still allows if the attack contract is created and destroyed in the same transaction.From that point every
deployLaunchreverts at this check, sinceaddress(this).balance > 0always holds, as the deployment function only consumesmsg.value = requiredMsgValue.Recommendation
This check already guarantees the user has provided sufficient ETH:
if (msg.value != requiredMsgValue) { revert MsgValueMismatch(); }Therefore, consider refactoring the
if (address(this).balance != 0)check to refund excess ETH by tracking before and after amounts:// at entry, before extensions run uint256 ethBaseline = address(this).balance - msg.value; // ... // at exit uint256 ethBalance = address(this).balance; if (ethBalance < ethBaseline) { revert MsgValueMismatch(); // can't consume more than supplied } uint256 launchEthRemaining = ethBalance - ethBaseline; if (launchEthRemaining != 0) { // Refund unspent launch ETH to creator or relayer (bool refunded,) = payable(msg.sender).call{value: launchEthRemaining}(""); if (!refunded) revert MsgValueMismatch(); }Consider if you want to refund to
msg.senderorenvelope.creator. -
L-01 Low setCore Missing Access Control Access Control Resolved
Description
ClankerDeploymentRegistry.setCoresets the CORE address with no caller authentication:function setCore(address core_) external { if (CORE != address(0) || core_ == address(0)) { revert UnauthorizedCore(); } CORE = core_; }The deploy script constructs the registry with
address(0)then callssetCorein a later transaction. Between those two transactions an attacker can callsetCorewhich a malicious core address, making the deployer'ssetCore(core)revert and forcing redeployment (unless unnoticed).Recommendation
Add access control to setCore (e.g. onlyOwner or a stored deployer address)
-
L-02 Low Permissionless createToken Enables Launch Grief Access Control Resolved
Description
The token launch deployment flow can call
ClankerTokenSource.createTokenorBaseBTokenSource.createToken, which have no access control, anyone can call them directly.The deployed address is CREATE2-deterministic from the constructor args, therefore allows an attacker who knows a victim's pending parameters
tokenAdminand the fulltokenSourceDatato deploy the victim's exact address first via directcreateTokencalls. The victim's laterdeployLaunchthen reverts at theCREATE2deploy step insidecreateToken.Since Base has no public mempool, the attack relies on knowing the victim's parameters before-hand rather than a direct front-run attack by observing the mempool.
Recommendation
Either gate
createTokento the core factory (msg.sender == CORE) or includemsg.senderin the salt. -
H-02 High Hidden Genesis Mint Bypasses Supply Conservation Validation Resolved
Description
BaseBTokenSource.createTokentreatsbalanceOf(source) == config.totalSupplyas its only supply-conservation check and never validates the token's realtotalSupply(). The B-20 factory mints nothing itself, as the entire supply is inputted by caller-controlledmint/batchMintinitCalls. BecauseBaseBTokenInitCallValidationgates selectors (and not arguments), amint(attacker, 5x) initCallpasses validation: the mint selector is allowlisted and the recipient and amount are never checked.A creator adds one extra mint alongside the required one:
initCalls = [ mint(source, TOTAL_SUPPLY), // satisfies balanceOf(source) == totalSupply mint(attacker, 5 * TOTAL_SUPPLY) ]; // hidden, never accountedThe launch reports
totalLaunchSupply == TOTAL_SUPPLYand seeds liquidity as if that is 100% of supply, while the token's realtotalSupply()is 6x, with 5x already sitting in the creator's wallet, invisible to the launch and to v5's supply-conservation invariant. The creator can then dump the hidden supply into the freshly seeded pool.This does not apply to
ClankerTokenSource, since it mints a fixed supply in the token constructor, sobalanceOf(source) == totalSupplyimpliestoken.totalSupply() == totalSupply.Recommendation
In
BaseBTokenSource.createToken, assertIERC20(token).totalSupply() == config.totalSupplyin addition to the existing source-balance check. The balance check enforces that the accounted supply landed with the source, while the total-supply check closes the mint-elsewhere hole, since any extra genesis mint inflatestotalSupply(). Also consider validatingBaseBTokenInitCallValidationmint/batchMint arguments, restricting recipients to the source or disallowing genesis mints outside the accounted supply. -
L-03 Low Caller launchId Enables DoS And Collision DoS Resolved
Description
launchIdis a caller-supplied bytes32 and_reserveLaunchmarks it taken on first use and revertsInvalidLaunchIdon any reuse:function _reserveLaunch(bytes32 launchId, address creator) internal { if (_launchIdTaken[launchId]) { revert InvalidLaunchId(); } _launchIdTaken[launchId] = true; _launchStatuses[launchId] = LaunchLifecycleStatus.Reserved; emit LaunchReserved(launchId, creator, LaunchLifecycleStatus.Reserved); }Because the id is chosen by the caller and keyed globally rather than per creator, two launches can collide on the same value.
This happens maliciously if an attacker knows a victim's intended launchId in advance and submits it first, causing the victim's deployLaunch to revert. It can also happen naturally if launch tooling assigns sequential ids, since independent creators may pick the same value.
On Base there is no public mempool, so any attack depends on the victim's id being known beforehand rather than observed in flight.
Recommendation
Consider keying
keccak256(creator, launchId)instead of the raw launchId, removing both the malicious DoS and the accidental collision regardless of what raw value a creator supplies. -
L-04 Low B-20 Descriptor Cannot Derive Address Configuration Resolved
Description
BaseBTokenSourcerecords the rawconfig.saltas the descriptor'screate2Saltwithcreate2Deterministic == true, but the true B-20 address derives from(variant, source, salt), not the standardkeccak256(0xff, deployer, salt, initCodeHash)scheme. ClankerTokenSource, by contrast, recordskeccak256(tokenAdmin, salt). A consumer that treats thecreate2Saltfield uniformly across sources, expecting standard CREATE2 derivation, cannot reconstruct the B-20 token address and will mispredict it.Recommendation
Either store enough information in the descriptor to reconstruct the B-20 address (variant, source, and salt), or document that B-20 descriptors' create2Salt is the raw factory salt and that deriving the address additionally requires the source address and variant.
-
I-01 Informational Asset Multiplier Not Shown in Capabilities Informational Resolved
Description
The B-20 Asset variant carries an operator-controlled rebase multiplier (updateMultiplier, OPERATOR_ROLE), but
_assetCapabilityFlags()showcases norebase/multiplierbit, so a consumer reading the descriptor gets no signal the token has a multiplier mechanism.The multiplier does seem to affect standard ERC-20
balanceOf() or totalSupply()accounting, so the impact is limited. However, it is still a nonstandard B-20 Asset feature, and omitting it from_assetCapabilityFlags()leaves descriptor consumers without a clear signal that operator-controlled multiplier behavior exists.Recommendation
Consider adding a
TOKEN_CAP_REBASING(multiplier) flag and set it for asset-variant tokens, for completeness of the capability descriptor. -
I-02 Informational Zero totalSupply Diverges Between Token Sources Documentation Resolved
Description
ClankerTokenSourcesilently defaultstotalSupply == 0to the100B default supply, whileBaseBTokenSourcerevertsZeroTotalSupplyon the same input. An integration that passes 0 to either source therefore gets inconsistent behavior: a 100B-supply token from one and a revert from the other. This inconsistency can cause integrations that usetotalSupply = 0as a sentinel for using the default supply to behave differently depending on the selected token source.Recommendation
Consider documenting this behavior or enforce consistent
totalSupply = 0handling for both token sources. -
H-03 High Baseline Launches With Modular Fee Can Be DoS'ed DoS Resolved
Description
The intended v5 fee-routing path is modular fee routing, where launches route fees through a fee module rather than relying only on the legacy/v4-parity path. In the current implementation, this means
ClankerVolumeThresholdFee, which supports both active and passive fee collection. Uniswap/Pancake-style fee flows are active, while Baseline Mercury fee flows are always passive because Mercury sends reserve-token fees directly to the configured fee recipient.Passive fee collection is guarded by
_passiveFeeTokenVenues, but this mapping is keyed only by fee token and not bylaunchIdor venue instance. As a result, the sharedClankerVolumeThresholdFeecan only initialize one passive Baseline fee policy per reserve token. Once WETH, USDC, or another reserve token has been used by any passive Baseline launch, subsequent Baseline launches using modular fee routing and that same reserve token revert during fee policy initialization.This can happen naturally as normal launches reuse common reserve assets, or it can be forced by an attacker. Because launches are permissionless once the adapter and modular fee path are enabled, an attacker can cheaply launch arbitrary tokens once per common reserve token, permanently occupying those reserve-token slots in the shared fee module and DoS'ing later modular-fee Baseline launches for those reserves.
Recommendation
Route Baseline passive fees to launch-specific fee sink contracts that receive Mercury fees for a single launch/reserve and then forward attributed amounts into the shared fee module, or alternatively deploy isolated fee module instances for passive Baseline launches.
-
L-05 Low Fee Token Can Mismatch Venue Assets Validation Resolved
Description
ClankerVolumeThresholdFeelets launchers configure an arbitrary non-zero feeToken without validating that it matches the assets from which the selected venue actually generates fees. The documented configuration isfeeToken = address(0)for active Uniswap v4 and Pancake venues so pool currencies are accepted, andfeeToken = reserveor zero for Baseline so the reserve token is used.However, there is no validation regarding caller provided non-zero
feeToken. If a launcher provides an incorrect non-zerofeeToken, active venues can reject legitimate collected pool fees duringreceiveFees, while Baseline can track a token different from the reserve token Mercury is expected to pay. This breaks fee collection or distribution for the affected launch and may leave actual venue fees unattributed. Additionally, for Baseline venues, a launcher can configure one token as the reserve while setting a different token as the fee token, causingdistributeFeesto attribute and distribute passive balance increases of the incorrectfeeTokenif that token is later transferred to the fee module.Recommendation
Validate
feeTokenagainst venue assets during fee policy initialization, or enforce venue-specific defaults by deriving the accepted fee token set from the initialized position data. -
M-01 Medium VenueId Collision With Different Pancake Fees Logical Error Resolved
Description
Pancake Infinity CL launches create a pool key that includes the configured
lpFee, but the returned ClankervenueIdis derived only fromcurrency0,currency1, andparameters. As a result, two Pancake pools for the same token pair and tick spacing but different fee tiers resolve to the same Clanker venue even though they are distinct AMM pools.V5 supports appending additional protocol-tracked liquidity positions after launch, and those positions are not required to use the same Pancake pool as the original launch. When a token admin adds another Pancake position for the same pair and tick spacing but a different fee tier, both positions are routed through the same
launchId + venueIdfee policy. Their collected LP fees, threshold accounting, pending balances, and fee events are merged as if they came from one venue.Recommendation
Include
poolKey.feein the PancakevenueIdderivation, preferably by hashing the full canonical Pancake pool key. -
H-04 High Adapter Donations Block Liquidity Additions DoS Acknowledged
Description
After a successful launch, any holder of the launch token can transfer a small amount of that token directly to the Pancake or Baseline adapter. Later, when the token admin attempts to add supplemental liquidity through
addLiquidityPosition, core transfers the intendedtokenAmountto the adapter, but the adapter’s existing donated balance interferes with its accounting.For Pancake, the launcher consumes exactly the assigned
tokenAmount, leaving the donated dust in the adapter. The adapter then checks that its launch-token balance is zero and reverts withLauncherDidNotConsumeTokens. For Baseline, the adapter checks before calling Mercury that its total launch-token balance equals the assignedtokenAmount; because the donated amount makes the balancetokenAmount + donation, it reverts withExceedsAssignedTokenAmount.This creates a repeatable griefing vector against supplemental liquidity additions for Pancake venues. Admin asset recovery can clear the adapter balance, but an attacker can donate again after recovery, forcing operational intervention each time the token admin wants to add liquidity. Additionally, initial launch validation does not allow the same adapter to be used more than once in the launch envelope, so a token owner who wants to add another liquidity position through the same venue/adapter after launch must use
addLiquidityPosition.This does not affect Uniswap v4 in the same way as its adapter approves and places only the assigned
tokenAmountwithout requiring the adapter’s pre- or post-call token balance to exactly match that amount.Recommendation
Make adapter accounting delta-based or assigned-amount-based instead of absolute-balance-based, so pre-existing donated balances are ignored or swept separately while the adapter still verifies that only the intended
tokenAmountis consumed for the liquidity operation -
H-05 High Tick Gap Bypasses Launch FDV Guard Validation Partially resolved
Description
The launch valuation guard relies on the adapter’s
quoteLaunchFdvPaired()result to enforce max-FDV limits. In the Uniswap v4 adapter, the quoted FDV is computed from the pool’s initialized starting tick.int24 startingTick = token0IsClanker ? config.poolConfig.tickIfToken0IsClanker : -config.poolConfig.tickIfToken0IsClanker; uint160 sqrtPriceX96 = TickMath.getSqrtPriceAtTick(startingTick); fdvPaired = LaunchValuationMath.fdvPairedFromSqrtPriceX96( context.supplyAllocation.totalLaunchSupply, sqrtPriceX96, token0IsClanker );However, liquidity is placed separately through the locker configuration IClankerV5LaunchFactory(LAUNCH_FACTORY).factoryPlaceV4Liquidity.
Both UniswapV4 and PancakeInfinity adapter quotes price FDV at the init spot. UniswapV4Adapter.quoteLaunchFdvPaired computes it from tickIfToken0IsClanker, and PancakeInfinityCLAdapter.quoteLaunchFdvPaired from
params.sqrtPriceX9(both viaLaunchValuationMath). Meanwhile the liquidity is single-sided and minted in[tickLower, tickUpper], and the only constraint tying it to spot is the locker'stickLower[i] >= startingTickcheck, which prevents liquidity below spot but does not require min(tickLower) == startingTick.Therefore, for the same pool, launches can accept a malicious config such as poolConfig.tickIfToken0IsClanker = -230400; // very low starting tick, lockerConfig.tickLower[0] = -30000; // much higher first liquidity tick, lockerConfig.positionBps[0] = 10_000; // all liquidity here.
The adapter attempts to validate the tick arrays via UniswapV4Adapter._validateConfig, but it does not require the lowest liquidity tick to equal the initialized starting tick. As a result, a creator can initialize the pool at a low starting tick to pass the max-FDV guard, then place all launch liquidity at a much higher tickLower. This creates a liquidity-free gap between the guarded spot price and the first executable liquidity price.
The attached PoCs confirm this, as a launch initialized at startingTick = -230400 passed valuation using an FDV of approximately 9.87 WETH, while all liquidity was placed near tickLower = -30000. A real WETH to token swap skipped the empty gap and filled near -29999, giving an effective entry FDV of approximately 4.98e9 WETH. This demonstrates that buyers can enter at a valuation far above the value checked by the guard.
This defeats the purpose of max-FDV guarded launches. The launch appears compliant because the guard measures the initialized spot price, but users actually trade at the first executable liquidity price.
Recommendation
Compute guarded FDV at the first executable liquidity price instead of the initialized spot. For Uniswap launches, use the minimum tickLower across launch liquidity positions, or require
min(tickLower) == startingTick. For Pancake launches, use the lowest liquidity tick from the configured range, or require the initial sqrtPriceX96 to match the first liquidity tick. -
H-06 High Extension Refund Reentrancy Drains Core Fees Reentrancy Resolved
Description
ClankerV5._executeExtensionAllocations() temporarily authorizes the active extension by setting _activeExtensionModule. This flag is intended to allow only the currently executing extension to call sensitive factory helpers such as factoryTriggerV4Extension().
However, in the failed extension path, _activeExtensionModule remains set while the contract executes a refund to
tokenAdminvia an external call:_activeExtensionModule = extensionModule; try IClankerExtensionV5(extensionModule) .receiveLaunchAllocation{value: extensionMsgValue}( context, envelope.extensionAllocations[i], extensionSupply ) { ... } catch (bytes memory reason) { ... (bool refunded,) = payable(context.tokenAdmin).call{value: extensionMsgValue}(""); if (!refunded) { revert MsgValueMismatch(); } ... } _activeExtensionModule = address(0);Because tokenAdmin is creator-controlled, an attacker can set it to a contract and reenter during the ETH refund. At that point _activeExtensionModule is still set, so the attacker can route through a legitimate registered extension wrapper and reach factoryTriggerV4Extension().
That function is gated only by onlyActiveExtensionModule and then approves an arbitrary token from the core to the supplied extension path:
function factoryTriggerV4Extension( address extension, IClanker.DeploymentConfig calldata deploymentConfig, PoolKey calldata poolKey, address token, uint256 extensionSupply, uint256 extensionIndex ) external payable onlyActiveExtensionModule { IERC20 tokenContract = IERC20(token); SafeERC20.forceApprove(tokenContract, extension, extensionSupply); IClankerExtension(extension).receiveTokens{value: msg.value}( deploymentConfig, poolKey, token, extensionSupply, extensionIndex ); SafeERC20.forceApprove(tokenContract, extension, 0); }This allows the attacker to make ClankerV5 approve and transfer foreign ERC20 balances held by the core. The launch can still finalize because the final supply conservation check only verifies the current launch token balance, not unrelated ERC20 balances.
The allows direct theft of accumulated hook protocol fees held by the core, as the claimTeamFees natspec states "Claims accumulated hook protocol fees held by core. Distinct from stray recovery; sweeps the full token balance as protocol fees."
Any creator can trigger the issue by configuring a failed extension allocation with a non-reverting failure policy and a refund to an attacker-controlled tokenAdmin. The exploit does not require registering a malicious extension, since a legitimate registered wrapper can be used as the reentry proxy.
Recommendation
Clear
_activeExtensionModulebefore performing any external refund or token transfer in the failed extension path:catch (bytes memory reason) { _activeExtensionModule = address(0); //@audit reset here ... (bool refunded,) = payable(context.tokenAdmin).call{value: extensionMsgValue}(""); if (!refunded) { revert MsgValueMismatch(); } ... } -
L-06 Low Public Adapters Spoof Launch Events Access Control Partially resolved
Description
PancakeInfinityCLAdapter.placeLiquidityandBaselineMercuryWrapper.placeLiquidityare externally callable and do not require msg.sender to be ClankerV5. A caller can pre-fund the adapter with tokens, provide arbitraryLaunchContext,LiquidityAllocation, and adapter data, then execute the adapter directly outside the core deploy oraddLiquidityPositionflows.This does not update Clanker’s deployment registry or current liquidity position registry, so core view functions remain unaffected. However, the direct call can still create real external protocol state, such as an unregistered Pancake/Baseline liquidity position or market, and the adapters emit
LiquidityPlaced(context.launchId, ...)using the caller-suppliedlaunchId. As a result, an attacker can emit adapter-level events that appear to reference another user’s launch and may confuse off-chain systems that trust adapter events without reconciling them against ClankerV5 core events or registry state.Uniswap v4 is not affected in the same way because its adapter must call back into ClankerV5 factory helpers guarded by
onlyActiveLiquidityAdapter, which fails outside the core-controlled launch/add-liquidity flow. However, Baseline and Pancake skip those guarded callbacks when the caller supplies a zero fee module.Recommendation
Consider restricting adapter
placeLiquidityentrypoints to the authorized launch factory/core. -
H-07 High Same Pool Liquidity Additions Always Fail Compatibility Partially resolved
Description
ClankerV5.addLiquidityPositionis exposed as a post-launch path for appending supplemental liquidity positions, but it calls the sameplaceLiquidityentry point used during the initial deploy flow on all adapters. The production adapters then execute their pool-creation logic again instead of placing liquidity into an already initialized pool.As a result, same-pool supplemental additions revert across the current production paths. Uniswap v4 tries pool initialization before locker placement, Pancake Infinity CL calls
CL_POOL_MANAGER.initialize(...), and Baseline Mercury callscreatePoolFromInvariant(...); each path rejects an already initialized pool. This prevents token admins from adding a new LP position in the same pool, including normal use cases such as adding liquidity in a different tick range.As a result, supplemental liquidity cannot be added to the original launch pool. Callers may still create additional positions by changing pool-key parameters such as the fee tier or paired token, but that creates a different pool rather than adding liquidity to the launch pool and fragments liqudity.
Recommendation
Do not reuse the same
placeLiquiditypath for supplemental liquidity as that path always attempts to initialize or create the pool. Implement dedicated supplemental-liquidity logic for each AMM that first checks whether the target pool is already initialized, then either adds a new position to the existing pool or initializes the pool before placing the new position. -
H-08 High All Passive Baseline Fees Can Be Stolen Access Control Resolved
Description
ClankerVolumeThresholdFee.receiveFeesForDepositoronly checks that the supplied depositor address is authorized for the launch and venue. It does not require msg.sender to be that depositor or an approved forwarder. Because authorized depositor addresses are public, any caller can pass a valid depositor and make the fee module account an existing token balance to that venue.This allows passive Baseline fees to be stolen atomically. Baseline Mercury
claimPoolFeesis permissionless and transfers accrued reserve fees to the v5 fee module. An attacker can callclaimPoolFeesfor a Baseline market and then immediately callreceiveFeesForDepositorfor their own active venue, causing the newly transferred reserve fees to be accounted to the attacker’s venue. The attacker can then calldistributeFeesfor their venue and receive the passive fees through their configured recipients.Additionally, because active venues with
feeToken == address(0)accept any ERC20, the same missing caller authentication lets an attacker credit arbitrary worthless token balances as fee revenue, prematurely crossing the threshold or pushingcumulativeRevenueneartype(uint256).maxand causing future fee accounting to revert.Recommendation
Require
receiveFeesForDepositorto authenticate msg.sender as the depositor or an approved forwarder, and prevent active fee accounting from consuming passive fee balances belonging to other venues. -
H-09 High FDV Guard Bypass Via Mining Clanker as currency1 Logical Error Resolved
Description
Pancake Infinity CL valuation uses the decoded
sqrtPriceX96fromlaunchParams, or derives it from the user-providedtickLowerwhensqrtPriceX96 == 0. The production launcher then initializes the pool at that same unmirrored price. However, when the Clanker token sorts ascurrency1, the launcher mirrors only the LP tick range by returning(-tickUpper, -tickLower)and does not mirror the initialization price used for valuation.A malicious creator can intentionally mine/grind the token-source salt so the Clanker token sorts after the paired token, making Clanker
currency1. They can then provide launch params that pass valuation at the unmirrored price while liquidity is actually deposited in the mirrored range. For example, valuation can be approved at tick +30000, while the real LP range is [-60000, -30000]; the first executable buy-side liquidity sits near -30000, which is materially more expensive (~400x) than the guarded valuation point.This bypasses valuation-guarded Pancake launches. The launch passes valuation and finalizes as compliant, but executable liquidity sits at an attacker-favorable price far from the approved valuation.
Additionally, this is not limited to valuation-guarded launches: under the default mechanic, Pancake
token1launches initialize the pool at the unmirrored price while minting liquidity in the mirrored range, causing incorrect pool initialization even without any valuation check.Recommendation
Mirror the valuation/init price as well as the liquidity range so both are checked and executed in the same tick space when Clanker is currency1. Also, consider enforcing that the guarded price matches the first executable liquidity boundary.
-
L-07 Low Relayed Launch Extension Refund Mismatch Logical Error Resolved
Description
ClankerV5 supports relayed launches where
msg.senderis an authorized relayer instead of the launch creator. In that path, the relayer supplies the fullmsg.valuerequired by the launch envelope, including extension native budgets.When a non-reverting extension failure occurs, ClankerV5 refunds the failed extension’s
msgValuetotokenAdminrather than the payer. This may be acceptable for direct launches, but in relayed launches it sends the relayer-funded refund to a creator-controlled address while the launch continues.Recommendation
Refund failed extension native value to the actual launch payer, or add an explicit signed refund recipient for relayed launches. Alternatively, document this behavior clearly if this is intended and accepted behavior.
-
M-02 Medium DevBuy ETH Can Be Stranded In UniversalRouter Logical Error Partially resolved
Description
The in-scope
ClankerUniv4EthDevBuyV5extension is a wrapper that forwards dev-buy execution to the legacyClankerUniv4EthDevBuyextension. That legacy extension forwards the full nativeamountIntoUniversalRouterwhen the configured v4 hop uses native ETH.Although the route uses
SWAP_EXACT_IN_SINGLE, Uniswap v4 does not guarantee that the full specified input is consumed. If available liquidity is insufficient, the swap can stop before consuming the fullamountIn.SETTLE_ALLthen settles only the pool’s actual debt, not the full ETH value sent to the router.The extension does not sweep or refund the remaining ETH. As a result, leftover ETH stays on the shared Universal Router and can be taken by any caller through the permissionless
SWEEPcommand. Additionally, ERC20 inputs can remain unused when less than the approved amount is consumed, but those leftovers stay in the extension rather than becoming permissionlessly sweepable from the router.Recommendation
Track actual input consumed and explicitly recover any unspent input after each router swap, using a router
SWEEPfor native ETH and refunding any remaining ERC20 balance from the extension to the intended recipient. -
L-08 Low Passive Fees Can Be Misattributed Logical Error Resolved
Description
ClankerVolumeThresholdFeesnapshots only the passive venue’s_distributableToken(state)during initialization. For Baseline/passive venues this is intended to be the reserve or configuredfeeToken. However,distributeFeeslater acceptsstate.tokenas well, even thoughstate.tokenwas not necessarily snapshotted for passive accounting.An attacker can launch token A with a passive venue whose fee token is B, causing only B to be snapshotted. If another passive venue later uses token A as its reserve or fee token, any uncustodied token A balance in the shared fee module can be distributed through the attacker’s venue because
A == state.tokenpasses_isAcceptedFeeTokenand its last passive balance is zero.This requires the victim passive fee token to equal the attacker’s launched token, so it is unlikely for standard Baseline reserves. Still, the passive accounting invariant is broken: passive venues should only distribute assets that were initialized and snapshotted for that passive venue.
Recommendation
For passive venues, snapshot and account every token that
distributeFeesaccepts including thestate.token, or restrict passive distribution to exactly the configured distributable fee token. -
L-09 Low Uniswap Adapter Underreports Capabilities Compatibility Resolved
Description
UniswapV4Adapterdoes not declareMODULE_CAP_MOVES_TOKENS, while the Pancake and Baseline adapters do. This is inconsistent because Uniswap also participates in ERC20 movement: it approves launch tokens toLAUNCH_FACTORY, which then pulls them into the v4 locker/liquidity path.The missing capability does not appear to affect current execution, since AMM adapters only require
MODULE_CAP_LP_PLACEMENTand do not forbid token movement. However, it weakens the capability model for registry review, admin policy, and any UI/tooling that relies on module capability masks as a risk signal.Recommendation
Add
MODULE_CAP_MOVES_TOKENStoUniswapV4Adapterso its declared capabilities match its token-moving authority.Additionally, consider also reviewing whether
MODULE_CAP_POOL_INITandMODULE_CAP_MEV_CONTROLshould be declared for adapters that initialize pools or arm MEV modules, unless those omissions are intentional policy choices. -
I-03 Informational Baseline Launches Require Zero Extensions Validation Resolved
Description
Clanker v5’s general allocation model allows launch supply to be split between liquidity allocations and extension allocations. That works for AMM paths that can accept a partial launch-supply liquidity allocation, but it does not compose with the current Baseline Mercury path.
Baseline Mercury ultimately calls
createPoolFromInvariant, which requires the suppliedinitialPoolBTokensto equal the token’s livetotalSupply(Reference: https://github.com/0xBaseline/mercury/blob/6bc32cfbf4e7e2b0a09f1de814007af3c419203a/src/components/BFactory.sol#L279-L283). Therefore, any Baseline launch with nonzero extension allocations will provide less than the full token supply to Mercury and revert late during the Baseline handoff.This constraint is not documented in the v5 Baseline integration docs, and there is no Clanker-side validation to fail fast. As a result, otherwise well-formed Baseline launch envelopes can pass Clanker validation and only fail much later at the Mercury call.
Recommendation
Document that Baseline launches require zero extension allocations and add an early Clanker-side validation for Baseline allocations to fail before reaching Mercury
-
L-10 Low BToken Descriptors Can Be False Validation Resolved
Description
BaseBTokenSourceregisters the token descriptor from the caller-provided config aftercreateB20returns. However, B20initCalls are executed before descriptor registration and Clanker only validates their selectors, not their arguments or final effects.A launcher can use allowed
initCallsto change the token’s live name/symbol and to grant or revoke admin roles before Clanker records the descriptor. As a result, the registered descriptor and deployment record can show the original configured metadata andtokenAdmin, while the actual BToken already has different metadata or is controlled by different role holders at launch finalization.Recommendation
After
createB20returns, read back and validate the live BToken metadata and role/admin state before registering the descriptor, or disallowinitCallsthat can mutate descriptor-recorded fields during bootstrap. -
M-03 Medium Admin Rotation Is Not Recorded Logical Error Resolved
Description
Clanker v5 records both
tokenAdminandoriginalTokenAdminat launch finalization.originalTokenAdminis stored as the launch-time original admin, whiletokenAdminis used by v5 for admin-gated lifecycle actions such as supplemental liquidity additions and liquidity migration authorization.However,
tokenAdminis also immutable in the v5 deployment record.ClankerToken.updateAdmincan legitimately rotate the token-level admin, and B20 admin roles can be transferred or revoked, but v5 never updates the recordedtokenAdmin. As a result, admin-gated v5 actions continue to authorize the launch-time recorded address rather than the token’s current admin.This can DoS supplemental liquidity additions and liquidity migrations when the recorded admin is no longer available. More importantly, it can also let the old admin continue performing v5 admin-gated actions after token-level authority has been rotated away, bypassing the intended access-control change.
Recommendation
Add a v5-side admin-rotation flow for deployment records and document token level updates require record update as well.
-
H-10 High Stolen Protocol Fees Due To Arbitrary Recipient Validation Partially resolved
Description
ClankerVolumeThresholdFeelets launchers provideprotocolRecipientdirectly infeeModuleData. During fee policy initialization the module stores that address as-is, and validation only requires it to be nonzero.In the default/v4-parity route, protocol trading fees accrue to the core/factory and are later claimed to the team fee recipient. Contrary to this, a launcher can route the protocol share of modular fee revenue to themselves instead of the Clanker protocol treasury.
This conflicts with the documented economic model where protocol and creator economics come from trading fees after launch. Unless arbitrary protocol-recipient substitution is explicitly intended, modular fee routing lets external launchers bypass Clanker’s trading-fee revenue.
Recommendation
Derive
protocolRecipientfrom protocol-controlled configuration, or validate that the caller-provided value equals the approved Clanker protocol fee recipient.Additionally, if zero protocol fees are not explicitly intended, also enforce protocol-governed minimum values for
initialProtocolNumeratorandpostThresholdProtocolNumeratorto prevent revenue loss. -
L-11 Low BToken Capabilities Are Underreported Compatibility Resolved
Description
BaseBTokenSourcerecords BToken capability flags as supply-capped, mintable, pausable, and transfer-policy controlled. However, B20 tokens also expose mutable metadata and role-gated burn functionality that are not reflected in the descriptor.This can cause indexers, UIs, and future compatibility checks to treat launched BTokens as less mutable or less permissioned than they actually are. The clearest mismatch is metadata mutability: B20 supports name, symbol, contract URI, and asset extra-metadata updates, but
TOKEN_CAP_MUTABLE_METADATAis not set. B20 also supports role-gated burning, includingburn,burnWithMemo, andburnBlocked, whileTOKEN_CAP_BURNis not set.Recommendation
BaseBTokenSourcecapability flags to include metadata and burn capabilities, or document that BToken descriptors intentionally do not advertise those behaviors. -
I-04 Informational Warning Regarding B20 Transfer Policies Trust Assumptions Resolved
Description
B20 tokens launched through Clanker may configure transfer policies during
initCalls, or admin can update policies after launc. These policies can restrict which addresses are allowed to send tokens, receive tokens, or executetransferFrom.A restrictive policy can be configured at launch to allow the Clanker deployment flow and AMM setup while limiting normal user transfers afterward. For example, a malicious allowlisted sender policy can permit protocol setup addresses and pool-side transfers, allowing users to buy tokens, while preventing ordinary users from sending tokens back to the pool to sell. This means a B20 token can be launched through Clanker with honeypot-like transfer behavior from the beginning.
Recommendation
Document this behavior clearly and warn users for B20 policies.
-
I-05 Informational Misleading InvalidLockerRewards Error Usage Error Resolved
Description
UniswapV4Adapter._validateConfigvalidates both reward configuration and liquidity position configuration. However, failures in the position array checks fortickLower,tickUpper, andpositionBpsalso revert withInvalidLockerRewards, which is misleading as these fields define LP position ranges and allocation splits, not reward configuration.Recommendation
Use a separate error such as
InvalidLockerPositionsfor tick range andpositionBpsvalidation failures. -
H-11 High Uniswap Supplemental Liqudity Always Reverts DoS Resolved
Description
Uniswap v4 launches require locker reward configuration in practice because the downstream production locker rejects empty reward recipients and stores reward state for each launched token. During the initial launch,
placeLiquidityfunction in locker records_tokenRewards[token]for the launched token and uses that token-level state to manage the locker’s LP reward accounting.ClankerV5.addLiquidityPositionis intended to support supplemental liquidity for the same launched token, but the Uniswap adapter routes supplemental additions through the sameplaceLiquiditydeployment path. When the same locker is used again, the locker sees that_tokenRewards[token]already exists and reverts withTokenAlreadyHasRewards. As a result, Uniswap supplemental liquidity is permanently unusable for normal production launches using the standard locker.Noting that this is separate from the previous H-07 issue where same pool liquidity additions revert with
PoolAlreadyInitialized. Even if the token admin avoids the original pool and targets a fresh Uniswap pool, the supplemental add still fails because the locker state.Recommendation
Implement dedicated supplemental liquidity flows instead of reusing launch-time
placeLiquidityfunctions. For Uniswap, this likely requires locker-level support for appending positions or a separate supplemental custody path, because the current production locker cannot cleanly support same-token supplemental additions without implementation changes. -
I-06 Informational Supplemental MEV Reinitialization Risk Warning Resolved
Description
Current Uniswap supplemental liquidity additions is already blocked by separate pool-initialization and locker-state issues (H-07, H-11). However, even if those blockers are fixed, same-pool supplemental liquidity may still fail because the v5 supplemental lifecycle always calls
finalizeSupplementalPositionafter placement, and the Uniswap adapter always attempts to initialize the MEV module for the position’s pool.For pools whose MEV module was already initialized during the original launch, this can cause secondary failures or unexpected behavior. One-time MEV modules such as descending-fee or sniper-auction modules revert on a second initialization, while delay-based modules may overwrite the unlock time and re-enable/re-lock the pool. This means a future fix for supplemental liquidity should also account for MEV state, not only pool and locker state.
Recommendation
When implementing dedicated supplemental liquidity flows, distinguish fresh-pool additions from same-pool additions and only initialize MEV for pools that have not already had their MEV module initialized
-
M-04 Medium FDV Guard Does Not Validate Denomination Token Validation Resolved
Description
The protocol admins can set valuation profiles via
setValuationProfilewhich serves as a protocol price safety policy for a launch. This stores theminFdvPairedandmaxFdvPairedvalues, but the FDV denomination used to enforce them is creator-controlled. The min/max values are checked against thefdvPairedvalue if a valuation guard is enabled by the creator. However,PancakeInfinityCLAdapter.quoteLaunchFdvPairedreturns FDV denominated inconfig.pairedToken, which is supplied by the creator and is only checked to be nonzero and different from the launch token.(uint256 fdvPaired, address denominationToken) = IClankerAmmValuationAdapter(allocation.adapter).quoteLaunchFdvPaired( context, allocation, liquidityTokenAmounts[i], allocation.adapterData ); if (policy.maxFdvPaired > 0 && fdvPaired > policy.maxFdvPaired) { revert LaunchFdvAboveMaximum(fdvPaired, policy.maxFdvPaired); }In the adapter, the fdv quote is calculated from the pool price and returned with the following:
fdvPaired = LaunchValuationMath.fdvPairedFromSqrtPriceX96( context.supplyAllocation.totalLaunchSupply, sqrtPriceX96, token0IsClanker ); denominationToken = config.poolConfig.pairedToken;So the creator controls
config.poolConfig.pairedToken, while the policy compares rawfdvPairedagainst rawmaxFdvPairedwithout validating the denomination token. For example, an admin can setmaxFdvPaired = 50e18(i.e., for weth denominated cap), but a user can pair their launch with a 6-decimal token, which will bypass the guard since the same rawmaxFdvPairedvalue is used to represent a much larger amount of the paired token. As a result, a creator can satisfy an admin-governedmaxFdvPairedwhile making the cap represent a far larger amount of the selected paired token than the admin intended.Recommendation
Bind each valuation profile to an expected denomination token and decimal scale, then require the adapter’s returned denominationToken to match before applying minFdvPaired or maxFdvPaired. Alternatively, restrict guarded launches to approved denomination tokens and normalize FDV values before comparison.
struct ValuationProfile { bool enabled; uint256 minFdvPaired; uint256 maxFdvPaired; address expectedDenominationToken; //@audit check against expected denom token uint8 expectedDecimals; //@audit check against expected decimals } -
I-07 Informational Pancake Allows Arbitrary Static LP Fee Validation Resolved
Description
Uniswap pools are forced to use
fee == DYNAMIC_FEE_FLAGthroughUniswapV4PoolKeyValidation, making fees hook-managed capped atMAX_LP_FEE, where creators select a hook contract from a protocol supplied set. The Pancake path instead accepts creator-suppliedparams.lpFeeand creates the pool withhooks = address(0), with no validation on the static fee.Buyers are still able to see this when interacting with the pool, this creates inconsistent behavior between Pancake and Uniswap launches.
Recommendation
Bound
params.lpFeeto an approved range, or force a fee path similar to Uniswap. If arbitrary static Pancake fees are intended, document the asymmetry for users and integrators. -
L-12 Low Airdrop V2 Allows Admin Root Replacement Validation Resolved
Description
ClankerAirdropV5V2forwards the configured airdrop admin and merkle root intoClankerAirdropV2, which custodies the launch’s airdrop allocation. Unlike V1, ClankerAirdropV2 does not reject a zero merkle root during initialization.updateMerkleRootallows the airdrop admin to replace the root, and its time-based overwrite guard is skipped when the current root is zero. As a result, a V2 airdrop can be created withmerkleRoot == 0, custody the full airdrop allocation, block normal claims, and leave the admin able to install a new root at any later time. The admin can then reassign the previous allocation to an addresses of its choosing.Even with a nonzero root, V2 also allows the admin to overwrite the root if no claims have occurred within the configured post-unlock overwrite window. This work as a recovery mechanism, but it weakens recipient guarantees because unclaimed allocations remain mutable after launch until at least one claim occurs.
Recommendation
Restore V1’s zero-root rejection in
ClankerAirdropV2.receiveTokensso V2 airdrops cannot be initialized in an indefinitely mutable zero-root state. Consider using a timelock for root replacement or clearly documenting that the airdrop admin can replace the root until the overwrite conditions are no longer satisfied. -
L-13 Low Passive Mode Accepted For Active Venues Validation Resolved
Description
Uniswap v4 and Pancake Infinity CL currently support active modular fee collection paths. Their hook, locker, or launcher transfers collected fees to the fee module and then calls
receiveFeesor receiveFeesForDepositorto account them.However,
ClankerVolumeThresholdFeedoes not validate this venue constraint and acceptspassiveFeeCollection = truefor Uniswap and Pancake venues as well. Once configured this way, their normal active fee collection calls revert withPassiveVenueDisallowsReceiveFees, making modular fee collection unusable for that launch.Recommendation
Validate
passiveFeeCollectionduring fee policy initialization and reject passive mode for Uniswap v4 and Pancake Infinity CL unless those venues add a passive-compatible fee path. -
M-05 Medium Raw Fee Units Skew Thresholds Logical Error Resolved
Description
ClankerVolumeThresholdFeetracks onecumulativeRevenuevalue per launch venue, but active AMM fees can be received in both pool tokens. Pancake collects and routes bothcurrency0andcurrency1LP fees to the fee module, and Uniswap LP fee routing can also forward both reward tokens. Each received amount is then added directly to the same venue-level revenue counter without recording the token denomination or normalizing decimals.As a result, fees from assets with different decimals or values are mixed as raw token units. For example, in a Pancake pool with an 18-decimal launch token and 6-decimal USDC pair, 1e18 launch-token units and 1e6 USDC units are both added directly to
cumulativeRevenue. The USDC side is therefore effectively treated as dust relative to the launch token side, even though it may represent the economically meaningful fee asset.This makes the volume threshold inaccurate for common mixed-decimal pools and can cause the protocol-share transition to occur too early or too late depending on which token side produces fees. The configured threshold therefore does not reliably represent fee volume or value across the venue.
Additionally, even when both assets use 18 decimals, the same raw-unit accounting remains incorrect: for example, 1e18 WETH fee units and 1e18 launch-token fee units are treated as equal revenue even though their economic values can differ by orders of magnitude.
Recommendation
Track threshold revenue in a single normalized denomination, or maintain separate per-token thresholds/accounting so fees from different assets are not summed as raw units.
-
M-06 Medium Lost LP Fees When v4-parity With Pancake Configuration Resolved
Description
Pancake Infinity CL allocations can currently be launched with the v4-parity fee-routing path, even though that path does not configure a runtime fee module for Pancake LP fee collection. In this path,
FeePolicyLibrary.decodeFeePolicyreturns no runtime fee module, soPancakeInfinityCLAdapternever callsfactoryConfigurePancakeFeeModulefor the minted Pancake LP NFT.This leaves the Pancake launcher holding the LP NFT without a
_feeModuleRoutingentry for itstokenId. Pancake LP fees will accrue on the position, but the documented collection path,collectLpFeesToFeeModule, reverts withFeeModuleNotConfigured. Therefore, Pancake launches using the allowed v4-parity path lose access to their LP fees through the intended v5 fee-routing mechanism.Recommendation
Enforce adapter-aware fee-routing compatibility so Pancake launches either configure a runtime fee module before liquidity placement or use a dedicated no-module Pancake fee collection path with explicit fee recipients.
-
I-08 Informational Baseline Fee Sink Remains Mutable Warning Resolved
Description
For modular-fee Baseline launches,
BaselineMercuryWrapperrewrites the MercuryfeeRecipientto the selected v5 fee module, but the separate Mercury creator field remains launcher-supplied. Because Mercury allows the recorded creator to later callsetFeeRecipient, the initial v5 fee-module sink can be changed after launch.This can redirect future fees that would have flowed into the v5 fee module, including amounts that the module would have split as protocol fees and beneficiary fees. This does not directly affect other launches or existing balances, and may be acceptable if launch creators are expected to control their own Baseline fee economics. However, integrators should not assume that a Baseline launch’s recorded v5 fee module remains the immutable recipient of future Mercury creator fees.
Recommendation
Document this behavior or constrain the Mercury creator/fee-recipient authority for Baseline launches where immutable v5 fee-module routing is required
-
I-09 Informational Mercury Requires Full Supply At Launch Informational Resolved
Description
Baseline Mercury’s market creation model requires the launch token allocation to be supplied when the market is initialized, and the Clanker integration forwards the full assigned token supply to Mercury during launch. While v5 docs and tests imply supplemental liquidity is intended and supported for Baseline, Mercury itself does not expose an existing-market liquidity addition mechanism like the other AMM venues.
Specifically,
v5-liquidity-ownership-and-management.mdsays Baseline can “Append positions (single-sided token path),” andtest_addLiquidityPosition_baselineAdapter_appendsPositionasserts Baseline supplemental liquidity works through a mock launcher. In production, this expectation does not match Mercury’s market model, so Baseline should be treated as a day-one allocation venue.Recommendation
Update the docs/tests to mark Baseline Mercury as day-one allocation only and cannot support supplemental liquidity unless the downstream protocol implements it.
-
M-07 Medium Supplemental Pools Can Break Fees And Swaps Validation Resolved
Description
Uniswap v4 modular fee policy is stored per
(launchId, venueId), and all Uniswap v4 positions use the sameVENUE_ID_UNISWAP_V4. A fixedfeeTokenpolicy is valid for the original pool becauseClankerVolumeThresholdFeeaccepts both the launch token and the configured fee token. For example, a WETH-paired launch withfeeToken = WETHcan account launch-token fees and WETH fees.However, when the token admin later adds a supplemental Uniswap v4 pool with a different paired token, such as USDC, the new pool is still bound to the same launch-wide Uniswap fee policy. The second
initializeFeePolicycall is a no-op because the venue state is already initialized, so the supplemental pool’s USDC fee asset is never validated against or added to the accepted fee-token set.After USDC fees accrue, a later swap on the supplemental pool triggers the hook fee-claim path. The hook forwards USDC to
receiveFees(launchId, VENUE_ID_UNISWAP_V4, USDC, ...), but the fee module rejects USDC because it is neither the launch token nor the configured WETH fee token. The rejection bubbles up through the hook and reverts the swap, so trading on the supplemental pool can stop once incompatible fees are pending.Noting that the supplemental Uniswap liquidity is currently blocked by separate pool/locker issues, but this becomes reachable once those blockers are fixed.
Recommendation
Validate supplemental pool fee assets against the already-initialized venue fee policy before binding hooks/lockers to the fee module. This prevents incompatible paired-token pools from being added through the supplemental path when the existing policy uses a fixed
feeToken.If supplemental pools with different paired assets are intended to be supported, scope fee policy by a pool/position identifier instead of only (launchId, venueId), or document that creators must use
feeToken = address(0)when they want one Uniswap fee policy to accept multiple paired assets. -
H-12 High Unchecked lockerData Allows Malicious Fee Module Validation Resolved
Description
UniswapV4Adapter.placeLiquidityvalidates fee-module locker data only whencontext.feeModule.module != address(0). Whencontext.feeModule.module == address(0), launcher-providedlockerDatais forwarded to the downstream locker as-is, without checking its schema or contents.A launcher can therefore select the no-module/v4-parity fee path while still providing
SCHEMA_LOCKER_FEE_MODULE_V1-shapedlockerData. Downstream locker decodes that data independently and, if the embeddedfeeModuleis nonzero, stores it as the token’s fee route. Later LP-fee collection transfers fees to that embedded module and callsreceiveFees.This bypasses v5 fee-module validation entirely and allows an arbitrary, unregistered contract to become the LP-fee sink for the Uniswap position. The launch appears to have no runtime fee module at the v5 level, but locker-collected fees are routed through attacker-controlled module.
Recommendation
Reject
SCHEMA_LOCKER_FEE_MODULE_V1locker data whenevercontext.feeModule.module == address(0), and keep requiring embedded fee-module locker data to match the current v5 fee-module context when a module is configured -
I-10 Informational Supplemental Liquidity Re-binds Revoked Fee Module Informational Partially resolved
Description
deployLaunchvalidates the concrete runtime fee module, butaddLiquidityPositionrebuildsLaunchContextfrom the immutable deployment record andvalidateSupplementalLiquidityPathonly revalidates the token source, launch mechanic, fee-routing module, and added adapter path. If governance later revokes the recorded fee module, a token admin can still add a supplemental position which re-binds and authorizescontext.feeModule.modulefor the new pool without checking that the fee module remains enabled and unrevoked.Already-deployed positions should continue using the fee module they were launched with, even if that module is later revoked, because disabling existing fee routes can break live deployments. However, whether a newly added supplemental position under the same
launchIdshould be treated as existing usage or as a new module binding should be an explicit protocol decision.If revocation is intended to block only new launches, the current behavior is acceptable; if it is intended to block all new future fee-module bindings, supplemental liquidity should revalidate the recorded fee module before placement.
Recommendation
Define revocation semantics for supplemental liquidity. If revoked modules should not be used for new supplemental fee routes, revalidate
record.feeModuleduringaddLiquidityPositionbefore adapter execution. -
I-11 Informational Pancake Default Params Ignore Decimals Informational Resolved
Description
Pancake launches may omit
launchParams, in which case the launcher falls back to protocol defaults. These defaults use fixed raw tick/price based only on token ordering. They are not adjusted for paired-token decimals, so using assets like USDC can initialize the pool at an unexpected human-normalized price.Recommendation
Document that empty Pancake
launchParamsare raw defaults, or require explicit launch parameters when using arbitrary paired tokens. -
M-08 Medium Pancake Residuals Bypass Consumption Check Rounding Partially resolved
Description
PancakeInfinityCLClankerLauncherprefunds the PancakeCL_POSITION_MANAGERwith the full assignedtokenAmount, computes integer CL liquidity from that amount, and then records the originaltokenAmountas placed liquidity. The surrounding adapter/launcher checks show the intended invariant is full consumption: if the assigned launch tokens are not consumed, the flow should revert.However, the post-mint balance checks only inspect the launcher/adapter balances. They do not account for the residual ERC20 balances left on
CL_POSITION_MANAGER. For normal/default Pancake ranges this appears to leave no meaningful remainder, but with extreme custom tick ranges, floor-rounded liquidity can require materially less than the pre-funded amount. The launch can finalize while v5 records the full allocation as locked LP even though a large remainder remains as raw ERC20 balance onCL_POSITION_MANAGER.Because Pancake’s position manager exposes a public
SWEEPaction, any caller can sweep the residual launch tokens. This can make a finalized launch appear fully collateralized by LP while part of the allocation was never actually deposited into the position.Recommendation
Sweep residual launch tokens from
CL_POSITION_MANAGERafter minting. If the protocol strictly requires the full allocation to be added as liquidity, revert when any residual remains; however, this may DoS legitimate ranges due to dust-level rounding residue. Otherwise, enforce a bounded dust threshold and revert only when the swept residual exceeds it. -
H-13 High
collectRewardsWithoutUnlockLets Fees Stolen Access Control Partially resolvedDescription
ClankerLpLockerFeeConversion.collectRewardsWithoutUnlockis public even though it is intended for the hook-controlled path while the pool is already unlocked. An attacker can directly callPoolManager.unlock, then execute a TAKE action first on PositionManager viaPositionManager.modifyLiquiditiesWithoutUnlockand transfer fee tokens to themselves. This creates negative delta debt against the shared PositionManager account.Then, before the unlock ends, the attacker executes
collectRewardsWithoutUnlock(token). The locker then collects pending LP fees with a zero-liquidity decrease. Since the locker owns the LP NFT, the PositionManager ownership check passes, and the collected LP fees credit the same PositionManager delta account. Those credits net against the attacker-created debt, so the unlock settles while the attacker keeps the taken tokens.As a result, pending LP fees can be stolen before normal fee collection via
collectRewardsWithoutUnlockroute.Recommendation
Restrict
collectRewardsWithoutUnlockto only trusted hooks or another approved collection context.
Remediation Review
29 findings · July 25 to 29, 2026-
H-01 High Supplemental Liquidity Can Still Be DoS'ed DoS Acknowledged
Description
As part of the previous Pancake residual-token fix,
_mintSingleSidedLiquiditywas updated to include a PancakeSWEEPaction after minting. This is safe during the initial launch because the launch token is unique and cannot be pre-donated before deployment, which is also reflected by the code comment: "Shared PM sweeps are safe for launch tokens (unique per deployment)".However, initial liquidity and supplemental liquidity are now separate flows, but both paths use the same
_mintSingleSidedLiquidityhelper. After launch, the token is live and anyone can transfer even 1 wei of the launch token directly to the shared PancakeCL_POSITION_MANAGER. This causes unconditional SWEEP to pull donated dust back to the launcher, causing the launcher’s exact balance-delta check to fail. As a result, anyone can still DoS the supplemental addition similar to the previous donation issue on main review but with a different path.Recommendation
Consider using separate minting logic for supplemental liquidity like performing delta based accounting, or make the sweep launch-only.
If supplemental minting also intended to have residual handling, account only for the current call’s residual rather than sweeping arbitrary pre-existing balances.
-
H-02 High Launches With ETH Buy Can Easily DoS'ed DoS Acknowledged
Description
The previous ETH dev-buy remediation added router sweep/refund handling for partial exact-input swaps, but also introduced a strict pre-swap check that reverts with
UnexpectedRouterEthBalancewhenever the shared Universal Router already has any native ETH balance.The check is too strict because anyone can leave ETH dust on
Universal Routerthrough its payableexecutefunction without performing any action, for example by calling it with empty commands and inputs. Once the router has even 1 wei of native ETH, every launch using native ETH dev-buy reverts withUnexpectedRouterEthBalance, causing a permissionless DoS of that launch path.Recommendation
Remove the global router pre-balance requirement and track/refund only the ETH delta created by the current dev-buy operation, without depending on the router’s unrelated pre-existing balance
-
I-01 Informational Note Regarding Uniswap Supplemental Liquidity Informational Acknowledged
Description
Uniswap v4 supplemental liquidity cannot be added while the launch MEV module is operational.
UniswapV4Adapterrequires a nonzero MEV module for v4 launches, and after launch finalization the hook enables that module for the pool. During this period,ClankerHookV2._beforeAddLiquidityrejects liquidity additions withMevModuleEnabled.Noting that this does not appear to permanently break supplemental liquidity. On the current fork path, same-pool Uniswap supplemental liquidity succeeds after
MAX_MEV_MODULE_DELAY, which is 2 minutes, or earlier if the configured MEV module disables itself through swap flow. The behavior is therefore should be treated as an operational/documentation concern.Recommendation
Document that Uniswap v4 supplemental liquidity can only be added after the MEV protection window has ended, or explicitly surface this condition in the supplemental liquidity flow
-
H-03 High Broken Supplemental Liquidity Due To
venueIdDoS AcknowledgedDescription
PancakeInfinityCLClankerLauncheruses different venue id derivation between launch-time and supplemental liquidity placement. During initial placement, the launcher records the venue id withPancakeVenueIdLib.venueId(poolKey), which hashes the full Pancake pool key includingcurrency0,currency1,hooks,poolManager,fee, andparameters.However, during
placeInfinityCLSupplementalLiquidity, after validating that the supplemental request targets the same pool key, it returns a venue id derived only fromcurrency0,currency1, andparameters.PancakeInfinityCLAdapter.placeSupplementalLiquiditythen compares that returned venue id against the recorded launch position’s venue id and reverts withSupplementalLiquidityPoolMismatch.As a result, Pancake same-pool supplemental liquidity is unusable. This is not an intentionally rejected new-pool case: the supplemental call targets the same pool, but fails because the launcher returns a non-canonical venue id. Token admins cannot append additional Pancake liquidity to their launch pool through the intended v5 supplemental liquidity flow.
Additionally, this is separate from the prior Pancake supplemental-liquidity issue (remediations H-01) as they have different root causes: that one is caused by sweep/balance accounting, while this one is caused by inconsistent
venueIdderivation, so fixing one does not resolve the other failure path.Recommendation
Use the same canonical
PancakeVenueIdLib.venueId(poolKey)derivation in both initial and supplemental Pancake liquidity paths. -
I-02 Informational Refunds Require Payable Callers Documentation Acknowledged
Description
Refunds during deployment or failed extension paths are returned to the
msg.sender. However, if the caller is a contract (for example during relayed launches) without a payablereceiveorfallback, these refund transfers revert and the launch fails.Recommendation
Document that callers need to be able accept native tokens for refunds.
-
I-03 Informational Document Dev-Buy Recipient Also Receives Refunds Documentation Acknowledged
Description
ClankerUniv4EthDevBuyrefunds unspent dev-buy input todevBuyData.recipientfor both native ETH and ERC20 inputs. This is reasonable because the legacy extension is called by core, somsg.senderis not the actual payer; refunding tomsg.senderwould send funds back to core and require additional steps to reach the actual payer.Recommendation
Document that devBuyData.recipient receives both bought tokens and any unspent ETH or ERC20 input refunded after router execution. If separate refund routing is desired, add an explicit refund-recipient parameter.
Additionally, document that refund recipient needs to be able to accept native tokens for refunds.
-
I-04 Informational Registry Admin Update Event Is Not Emitted Events Acknowledged
Description
Both
ClankerV5andClankerDeploymentRegistrydeclareDeploymentTokenAdminUpdated, but only core emits it.ClankerDeploymentRegistry.updateTokenAdminupdatesrecord.tokenAdminwithout emitting the registry event.Recommendation
Either emit the event from the registry as well, or remove the registry event declaration.
-
L-01 Low Mechanic Context Uses Raw Launch Id Compatibility Acknowledged
Description
Core now derives the canonical launch id as
keccak256(creator, envelope.launchId)and uses it inLaunchContext.launchId, registry records, events, fees, and position tracking. However, the default and valuation-guarded launch mechanics still encode the rawenvelope.launchIdinsidelaunchMechanicContext.Noting that current production modules do not use that embedded id for state. Future consumers decoding mechanic context may treat the raw id as canonical and treat it incorrectly
Recommendation
Encode the canonical launch id in mechanic context too, or document the embedded value as the raw/client launch id.
-
L-02 Low Revoked Fee Module Blocks Supplemental Adds DoS Acknowledged
Description
addLiquidityPositionrevalidates the launch’s recorded runtime fee module before allowing supplemental liquidity. If that module is later revoked or disabled, the launch can keep using its already-bound fee route, but new supplemental liquidity reverts because rebinding the revoked module is prevented.Ideally, a launch that originally used fee module
Ashould not be able to bindAagain afterAis revoked, but it should have a way to use a governance-approved replacement fee moduleBfor future liquidity additions.However, that path is not supported at the moment, so the fix also blocks all future supplemental liquidity for launches that used the revoked module.
Recommendation
Evaluate whether this behavior is intended and document that revoking a fee module disables future supplemental liquidity for launches using that module.
If continued liquidity additions are required after revocation, add an explicit validated fee-module replacement path. However, this may increase the complexity and introduce accounting concerns.
-
L-03 Low Missing
onlyLaunchFactoryWhen Adding Liqudity Access Control AcknowledgedDescription
PancakeInfinityCLAdapter.placeLiquiditywas restricted toonlyLaunchFactory, but the newly addedplaceSupplementalLiquidityremains public and trusts caller-supplied context/target position data. This allows direct callers to bypass core validation and mint untracked supplemental Pancake positions, including against an existing launch pool if they supply the launch token themselves.Noting that the same boundary exists one layer lower:
PancakeInfinityCLClankerLauncher.placeInfinityCLLiquidityandplaceInfinityCLSupplementalLiquidityare also public, so callers can bypass the adapter entirely and mint unmanaged launcher-custodied NFTs if they supply the tokens.Also, similar issues exist in the Baseline paths. However, Baseline supplemental liquidity is not supported and reverts regardless of the caller, and
launchWithTokenAllocationrequires the full token supply, so direct calls are unlikely to succeed in practice.Recommendation
Restrict Pancake supplemental placement in adapter to the launch factory as well. Additionally, consider restricting launcher functionalities to the adapter as well.
-
I-05 Informational Supplemental CL Ranges Must Match Price Documentation Acknowledged
Description
Supplemental liquidity paths supply only the launch token, so the added tick range must be single-sided relative to the current pool price. If the live price is inside the range, the position requires both assets and may revert or fail exact-consumption checks.
This is expected CL behavior, but should be documented for token admins/integrators.
Noting that the same constraint exists at launch time, but the launch paths perform stricter range/starting-price validation than supplemental additions.
Recommendation
Document the provided range must be valid compared to the live price/slot0 values, and consider adding fail-fast validation where the live price is available.
-
H-04 High Min FDV Bypass Via 1 BPS Allocation Logical Error Acknowledged
Description
The H-05 remediation moved Uniswap valuation away from the initialized spot and toward executable liquidity, which resolves the previous max-FDV gap where liquidity could sit above the guarded price. However, the new quote logic represents all Uniswap ranges with a single value: the highest nonzero
tickLower. While this remediation works for a maximum-FDV cap, it is not sufficient for a minimum-FDV floor.A creator can place 9,999 bps of launch liquidity at a low tick that would fail the governed minimum FDV, then add a 1 bps decoy position at a higher tick that satisfies the floor.
quoteLaunchFdvPairedreports only the high-tick position, soLaunchValuationValidationaccepts the launch even though almost all executable supply is available at the prohibited low valuation.A buyer can then acquire from the low range while the deployment appears to have passed the minimum-FDV guard. Later buyers or integrations relying on the guard are exposed to a manipulated launch where the creator/early buyer captured supply under the prohibited floor.
Recommendation
Validate every actually funded range boundary instead of selecting one representative high tick. Enforce the minimum against the lowest executable funded boundary and the maximum against the highest.
-
H-05 High Hook-Only Fee Collection Still Allows Stolen Fee Access Control Acknowledged
Description
H-13 was remediated by restricting
collectRewardsWithoutUnlockso only the token’s registered pool hook can call it. This blocks the direct public-call variant where an attacker opensPoolManager.unlock, preloads debt against the sharedPositionManagerdelta account, and then calls the locker directly. However, additional testing surfaced that a similar debt-netting attack path is still possible through a trusted hook.An attacker can open a
PoolManager.unlock, callPositionManager.modifyLiquiditiesWithoutUnlock(TAKE)to create debt on the sharedPositionManageraccount, and then perform a normal swap on the victim Clanker pool within the same unlock. DuringbeforeSwap, the trusted Clanker hook callscollectRewardsWithoutUnlock, so the hook-only caller check passes. The locker then collects LP fees through the same sharedPositionManagerdelta account, and those fee credits net against the attacker’s preloaded debt.As a result, pending LP fees can still be stolen even though the locker is only called by the registered hook. Fee recipients receive only the remaining net collection, while the attacker keeps the tokens taken before the hook-triggered collection.
Recommendation
Require zero
PositionManagertransient currency deltas for both pool currencies before unlocked fee collection. If either delta is nonzero, consider skipping hook-triggered collection instead of reverting, since reverting could DoS legitimate composed transactions with preexisting deltas.Note that this may delay fee realization in those composed transactions, but fees can still be collected later through the locked collection path.
-
H-06 High Pancake Launches Revert On Rounding Dust Compatibility Acknowledged
Description
M-08 was fixed by enforcing a full-consumption invariant after sweeping residual launch tokens, which prevents launch tokens from remaining outside the recorded LP position. However, the non-tolerant full-consumption check also makes normal Pancake CL rounding incompatible with many realistic launch ranges.
Pancake Infinity CL launch liquidity is minted by converting the requested launch-token amount into a floor-rounded liquidity value, then settling the actual token amount required for that liquidity. Because the conversion is effectively
amount -> liquidity -> amount, the settled amount can be slightly lower than the original requested amount for otherwise valid tick ranges.With the default 100B token supply, a $10k-$100k FDV launch corresponds to roughly $0.0000001-$0.000001 per Clanker token. At an assumed $2,000/WETH, that maps to approximately
[-237240, -214140]raw WETH/token ticks. With USDC as the paired token, assuming $1/USDC and 6 decimals, the same human price band maps to approximately[-437520, -414480]ticks.While the launches at default range is successful, realistic FDV-aware ranges such as $10k-$50k, $10k-$100k, $100k-$200k, $100k-$1M, $1M-$10M, and $10M-$100M leave residual dust across WETH-paired and USDC-paired launches, for both Clanker
token0andtoken1ordering. These launches revert withLaunchTokensNotFullyConsumedeven though the configured ranges are economically realistic and the leftover amount is only a rounding artifact.Recommendation
Sweep residual launch tokens and allow a tightly bounded dust tolerance instead of requiring exact full-token consumption for every valid Pancake range, and consider refunding the tolerated dust. Revert only when the residual exceeds that tolerance.
-
I-06 Informational Baseline Deploy Docs Use Old Address Documentation Acknowledged
Description
BaselineMercuryLauncherDeployConfigwas updated with a newPREDICTED_ADDRESSand INIT_CODE_HASHfor the currentBaselineMercuryClankerLauncherbytecode. However,baseline-mercury-launcher-deploy.mdstill documents the previous allowlist address and init-code hashRecommendation
Update the markdown deployment guide to match
BaselineMercuryLauncherDeployConfig -
M-01 Medium Crosschain FDV Uses Local Supply Logical Error Acknowledged
Description
SyncedTokenSourcereports only the current chain’sLOCAL_GENESIS_ALLOCATIONastotalLaunchSupply, while the deployedClankerTokenCrosschainalso has an immutableGLOBAL_SUPPLYand controller-gated bridge minting. Core then uses this localtotalLaunchSupplyfor launch supply accounting and valuation checks.As a result, valuation-guarded launches can approve a crosschain launch based only on the local tranche rather than the full global token supply. For example, a launch with 30% of supply minted on the current chain and 70% allocated to other chains will be valued using only the 30% local tranche, even though the remaining supply can later bridge into the same market. This can make a launch appear to satisfy max-FDV limits while the actual global FDV is materially higher.
Recommendation
For SyncedTokenSource, compute valuation againstGLOBAL_SUPPLY, or disallow valuation-guarded launch paths unless the policy explicitly defines the limit as local-chain FDV only. -
L-04 Low Descriptor Records Only Local Supply Logical Error Acknowledged
Description
SyncedTokenSourceregisters the token descriptor withtotalLaunchSupplyset to the current chain’sLOCAL_GENESIS_ALLOCATION. However, the deployedClankerTokenCrosschainalso exposes an immutable GLOBAL_SUPPLY and a full chain allocation manifest, which are not recorded in the descriptor.This can mislead indexers, UIs, or integrations that rely on
TokenDescriptorRegistryas the normalized token metadata source. For crosschain tokens, the descriptor’stotalLaunchSupplyis only the local launch tranche, not the global supply basis of the token.Recommendation
Extend crosschain descriptors to include global supply and controller metadata, or clearly document that
totalLaunchSupplyis chain-local forSyncedTokenSourceand consumers must readGLOBAL_SUPPLYfrom the token. -
I-07 Informational Warning Regarding Multi Chains Warning Acknowledged
Description
Current documented deployments are mainly on chains such as Base, Arbitrum, and Unichain, where classic public mempool front-running assumptions are weaker or do not directly apply. However, the crosschain test files in the latest commit include chain id 534352, which corresponds to Scroll, and Scroll exposes pending transaction / mempool-style behavior.
If protocol is expected to be deployed on chains with public mempools, deterministic launch parameters may let attackers front-run a pending launch and directly initialize the target Uniswap v4 or Pancake pool before the official launch transaction executes. This can cause the later launch to revert with an already-initialized pool.
Noting that this is only a deployment-environment warning and can be discarded if those chain ids are present only for testing.
Recommendation
Be aware of this situation. If chains with mempools will be used, consider private RPCs to prevent frontrunning/deterministic address DoS attacks
-
I-08 Informational Unbound Bridge LaunchId Validation Acknowledged
Description
CrosschainSupplyController.bridgeOutaccepts a caller-providedlaunchId, andbridgeInonly authenticates the exact message through the transport adapter; neither side verifies that thelaunchIdbelongs to the bridged token. This can produce real burn/mint transfers with misleading launch ids in bridge events/messages.No production
ICrosschainTransportAdapterimplementation is included in the reviewed codebase and is OOS. Only the interface and mock adapter are present, so this may be part of unfinished crosschain integration work.Recommendation
Bind token => launchId in the controller or require adapters to enforce it when they are production ready.
-
M-02 Medium One Token-Restricted Beneficiary Blocks Fees Logical Error Acknowledged
Description
ClankerVolumeThresholdFeestores the creator-selected beneficiary list during fee policy initialization and later distributes each venue’s fees through a single push-baseddistributeFeescall. The function transfers the protocol share and then pushes each beneficiary share withSafeERC20.safeTransfer.If the fee token rejects transfer to any configured beneficiary, the entire distribution reverts. This rolls back the protocol payment, all other beneficiary payments, and the pending-fee accounting updates.
This can happen with B20 transfer policies or externally blacklisted fee assets, and permanently lock pending and future fees for the affected launch, venue, and token.
Recommendation
Use pull-based per-recipient accounting or otherwise isolate failed payouts so one restricted beneficiary cannot block distribution for the protocol and all other recipients. Alternatively, consider adding an authorized beneficiary-rotation path for permanently unpayable recipients.
-
M-03 Medium Pancake Defaults Depend On Token Order Logical Error Acknowledged
Description
PancakeInfinityLaunchParamsbuilds default launch parameters from_configuredDefaults(token0IsClanker)and then passes the result through_orient. For token1-Clanker launches,_configuredDefaults(false)already selects the positive canonical range[60, 6000], but_orient(false)mirrors it again into the negative pool-side range[-6000, -60].As a result, default Pancake launches start at different normalized prices depending only on token address ordering. When Clanker is
token0, the pool starts attickLower = -6000, so the launch price is pairedToken / Clanker at tick -6000. When Clanker istoken1, the final pool-side range becomes[-6000, -60]and the pool starts at tickUpper = -60; because pool price is then Clanker / pairedToken, the normalized pairedToken / Clanker price is the inverse, equivalent to tick +60.This means two otherwise identical launches using empty/default Pancake params can differ by 6060 ticks, roughly 1.83x, solely due to token sorting. The valuation quote uses the same order-dependent defaults, so it reports the divergent FDV instead of correcting it. This can change early buyer execution and make valuation-guard outcomes depend on token address order.
Recommendation
Define Pancake defaults in one canonical pairedToken / Clanker price frame, then apply token-order orientation exactly once for pool placement and valuation.
-
M-04 Medium Baseline Uses Local Supply As Full Supply Compatibility Acknowledged
Description
Baseline Mercury placement requires the full token supply to be deposited at launch.
SyncedTokenSourceonly mints and hands core the chain-local genesis tranche, but at deployment time that tranche equals the token’s live localtotalSupply, so the Baseline full-supply check passes.After the corresponding crosschain deployments and bridge mints occur, additional legitimate supply can arrive on the initial chain. Mercury and Clanker still record the launch against the smaller local tranche, creating a discrepancy between the recorded full-supply market and the token’s actual crosschain supply.
Recommendation
Consider rejecting Synced/crosschain token sources for Baseline. Alternatively, if Baseline supports a crosschain-specific initialization path, use that path explicitly so the market is initialized against global supply rather than deployment-time local supply.
-
M-05 Medium Zero Protocol Numerators Disable Fees Logical Error Acknowledged
Description
ClankerVolumeThresholdFeelets creators setinitialProtocolNumeratorandpostThresholdProtocolNumerator, and validation only enforces an upper bound. The protocol recipient is governance-controlled, but the protocol share can still be set to zero.A creator can configure both numerators as zero and route all future venue fees to creator-selected beneficiaries. They can also set
volumeThresholdto 0 as well.This makes protocol trading-fee revenue optional for modular-fee launches. If Clanker is expected to earn protocol fees from modular fee routes, this allows creators to opt out permanently.
Recommendation
Enforce governance-approved fee profiles or nonzero minimum protocol numerators for both threshold states.
-
I-09 Informational Passive Sinks Assume Standard ERC20 Informational Acknowledged
Description
Passive sinks flush/recover their full token balance in one transfer. Nonstandard reserve tokens with caps, blacklists, transfer fees, or balance-dependent restrictions can make those calls revert or account unexpected amounts.
Recommendation
Document standard ERC20 reserve requirements.
-
L-05 Low Synced Pool Squatting Across Chains Frontrunning Acknowledged
Description
Synced tokens have deterministic same-address deployments across chains. Once one chain is launched, the token address and pool parameters may be public before the same launch is completed on another chain.
During this gap, an attacker can call public
initializePoolOpenon the destination chain with the future token address and expected Uniswap pool parameters. The later v5 launch then deploys the token but reverts when the adapter tries to initialize the already-created pool.Noting that this is different from mempool-dependent front-running: deployment on the first chain can reveal the token address and pool parameters, giving anyone enough information to squat the destination-chain pool during the multi-chain deployment gap
Recommendation
Document the sequencing risk and use protected/private execution for synced deployments where this matters.
-
I-10 Informational Misleasding Comment In Fee Module Informational Acknowledged
Description
The ClankerVolumeThresholdFee has this code comment: "Halves hook protocol share after a cumulative revenue threshold is reached."
However, the current behavior is not halving the protocol share as both pre and post threshold numerators are launcher provided.
Recommendation
Update the comment. If the intended behavior is indeed halving the fees, enforce it in the implementation.
-
L-06 Low Reauthorization Revives Stale Forwarders Logical Error Acknowledged
Description
ClankerVolumeThresholdFeetracks whether a depositor is authorized independently from the generation used to validate its delegated fee forwarders.When the launch factory or module owner revokes a depositor through
authorizeFeeDepositor(..., false), the function only clears_authorizedDepositors. It does not advance_feeForwarderGenerationor otherwise invalidate forwarders previously authorized by that depositor.While the depositor remains revoked, both the depositor and its forwarders are rejected because
receiveFeesForDepositorfirst checks_authorizedDepositors. However, if the same depositor is subsequently reauthorized, every forwarder whose stored generation still matches the unchanged current generation automatically becomes authorized again.Recommendation
If this is not explicitly intended, consider advancing the depositor’s forwarder generation whenever it transitions from authorized to unauthorized, requiring forwarders to be explicitly authorized again.
-
L-07 Low Getter Hides Paired-Token Pending Fees Unexpected Behavior Acknowledged
Description
venueState.pendingBalancereports pending fees only for the threshold-denomination token. When a venue collects both launch-token and paired-token fees, paired-token fees may be pending and distributable even though the getter reports zero.No other view function exposes
_pendingByTokenfor an arbitrary token, so keepers and interfaces relying onvenueStatemay overlook paired-token fees and leave them undistributed.Recommendation
Consider adding a view function returning the pending balance for a specified
(launchId, venueId, token). -
H-07 High Shared Hook Enables Cross-Pool Fee Theft Logical Error Acknowledged
Description
Hook protocol fees are stored as ERC-6909 claims keyed only by the shared hook address and currency. At the beginning of a swap,
_hookFeeClaimburns the hook’s entire balance for the current paired currency and credits it to the launch associated with whichever pool swaps next, without tracking which pool generated the fees.An attacker can create a pool using the same hook and paired currency, wait for a confirmed victim swap to generate fees, and execute a small regular swap through the attacker pool. The victim’s pending fees are credited to the attacker launch and can be distributed to attacker-controlled beneficiaries, making the theft permissionless and repeatable.
Additionally, this does not require an active attack and occurs naturally, as each swap assigns the previous swap’s pending protocol fees to whichever shared-currency pool swaps next.
Note: This issue was identified in the out-of-scope legacy
ClankerHookV2contract while testing the in-scope V5 integration; the contract was not audited as part of this engagement, and this finding should not be interpreted as a comprehensive review or security assurance of that contract.Recommendation
Track accrued hook claims per
PoolIdand forward only the amount generated by the pool currently being claimed.
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.
