Assessment reports>Memecoin Launcher>High findings>Fee-recipient reentrancy
Category: Coding Mistakes

Fee-recipient reentrancy

High Severity
High Impact
Medium Likelihood

Description

After a successful token deployment to a liquidity pool, the functions quoteToken, swapTokenForETH, and swapETHForToken prevent swapping or quoting the token with the contract with the following check:

if (token.dexPair != address(0)) revert TokenAlreadyDeployed();

During the deployment of the token to a liquidity pool, the fee recipient is paid with the _safeTransferETH function before the variable token.dexPair is set:

function _deployTokenLiquidity(address _address) internal {
        //...

        IERC20(token.token).approve(address(router), tokenAmount);

        (uint256 tokenAddedAmount, uint256 ethAddedAmount, uint256 liquidity) = router.addLiquidityETH{ value: quotedEthAmount }(
            token.token,
            false,
            quotedTokenAmount,
            quotedTokenAmount,
            quotedEthAmount,
            address(this),
            block.timestamp + 10
        );

        if (liquidity == 0) revert LiquidityDeployFailed();

        if (tokenAddedAmount < tokenAmount) {
            _safeTransferToken(token.token, $.config.feeRecipient, tokenAmount - tokenAddedAmount);
        }
        if (ethAddedAmount < ethAmount) {
            _safeTransferETH($.config.feeRecipient, ethAmount - ethAddedAmount);
        }

        token.dexPair = router.pairFor(address(weth), token.token, false);
        token.bondingCurve = BondingCurve({ vTokenReserves: 0, vETHReserves: 0, rTokenReserves: 0, rETHReserves: 0 });

        emit TokenLiquidityDeploy(token.token, token.dexPair);
    }

Note that this ETH transfer happens before the dexPair is set and bondingCurve is zeroed, which means that if the fee recipient reenters the contract during the transfer, they will still be able to trade on the invalid bonding curve. If they sell tokens at this time, the tokens will become stuck in the contract, and the ETH sent out will be double-counted, making the contract insolvent.

Impact

Since the fee recipient receives all the fees, it does not cost them anything to attempt this attack.

So, by transactionally deploying a new token and then buying enough of it with ETH such that it immediately is deployed to the DEX, and then doing this reentrancy attack, the fee recipient can freely exfiltrate as much ETH from the native balance of the contract as they like.

Recommendations

The contract should implement the checks-effects-interactions pattern and set the variables token.dexPair and token.bondingCurve before transferring ETH to the feeRecipient.

Remediation

This issue has been acknowledged by PondFun, and a fix was implemented in commit b0721f46.

Zellic © 2025Back to top ↑