Swaps update reserves and the fee basis before a mint or burn operation settles protocol LP tokens.
The fixture estimates 22 protocol LP after its first swap, settles 22 on mint, then settles another 11 on burn. These are raw LP units.

A swap can increase a pool's estimated protocol fee while the treasury still holds zero newly minted LP tokens. A dashboard that reports those two values under one revenue label can give its operator the wrong answer about what is available to claim.

The White-label DEX Launch Kit uses a V2-style protocol fee calculation based on growth in the square root of the reserves' product. Its current LaunchPair settles that fee during liquidity mint or burn operations. Swaps change reserves without immediately minting protocol LP. The source and its existing fee fixture were inspected on 29 October 2026.

This lab builds an integer Python model of that sequence. You will run a swap, observe pending LP, add liquidity, run another swap and burn some LP. The complete example includes tests for repeated settlement and disabled fees. Download the runnable accounting lab.

Start with a known pool

Use Python 3.13.3, the version used to verify this archive. No packages, wallet or RPC endpoint are required.

python3 protocol_lp.py
python3 -m unittest -v

The initial synthetic pool contains one million raw units of each token and one million raw LP units. Its swap fee is 30 basis points. The demonstration begins after pool initialization, so initial liquidity locking is outside the model.

The first swap sends in 100,000 token-0 units and receives 90,661 token-1 units. Reserves become 1,100,000 and 909,339. The calculator reports 22 pending protocol LP units while the treasury balance remains zero.

That distinction gives a finance or operations screen two useful facts. One value estimates what the next qualifying liquidity event would mint from the current state. The other describes LP already held by the recipient. Neither field is a cash receipt in a chosen reporting currency.

Calculate the protocol's LP share

The downloadable function uses integer square roots and division throughout.

def protocol_lp(supply, reserve0, reserve1, k_last, enabled, fee_bps):
    if not enabled or fee_bps == 0 or k_last == 0:
        return 0
    current, previous = isqrt(reserve0 * reserve1), isqrt(k_last)
    if current <= previous:
        return 0
    return supply * (current - previous) // (5 * current + previous)

k_last records the reserves' product at the previous eligible liquidity checkpoint. The current root uses the pool's saved reserves before the new deposit or withdrawal changes them. If growth is positive, the numerator multiplies that growth by existing LP supply. The denominator includes five current roots plus the previous root.

For the first swap in the fixture, the current integer root is 1,000,136 and the prior root is 1,000,000. With supply of 1,000,000, integer division produces 22 LP units. Copying a percentage of trading volume into the treasury balance would not reproduce this result.

The calculation returns zero when the protocol recipient is disabled, the pool fee is zero or there is no prior baseline. The current product also requires a nonzero swap fee to enable protocol minting. That extra condition matters because reserve growth can have causes other than a fee-bearing swap. The tests include synthetic reserve growth in a zero-fee pool and still expect zero protocol LP.

Settle before calculating the new LP position

Adding liquidity can change both supply and reserves. The protocol fee must be accounted for before calculating how many LP units the depositor receives. Here is the full mint method from the teaching model.

def mint(self, amount0, amount1):
    if any(type(v) is not int or v <= 0 for v in (amount0, amount1)):
        raise ValueError("positive integer deposits required")
    new_supply = self.supply + self.pending_lp()
    lp = min(amount0 * new_supply // self.reserve0,
             amount1 * new_supply // self.reserve1)
    if lp == 0:
        raise ValueError("zero minted LP")
    settled = self._settle()
    self.supply += lp
    self.reserve0 += amount0
    self.reserve1 += amount1
    self._checkpoint()
    return {"user_lp": lp, "protocol_lp": settled}

The code first computes the supply including pending protocol LP. It uses that supply to determine the depositor's LP amount. Only after validating that result does it mutate state. This preserves the original pool state when an invalid illustrative deposit fails.

In the fixture, the deposit adds 1,100,000 token-0 units and 909,339 token-1 units. The protocol receives 22 LP units, the depositor receives 1,000,022, and total supply becomes 2,000,044. The new reserves' product becomes 4,001,091,600,000 and is recorded as the next baseline.

Immediately checking pending protocol LP now returns zero. A second proportional deposit with no intervening growth must not charge the first growth again. The test suite exercises that sequence explicitly. A stale baseline can otherwise make successive liquidity events appear to create repeated protocol income from the same reserve change.

A swap changes reserves and pending protocol LP; a mint or burn event settles LP and resets the K baseline.
The fixture estimates 22 protocol LP after its first swap, settles 22 on mint, then settles another 11 on burn. These are raw LP units.

Follow the burn path too

The second swap sends in 50,000 token-0 units and returns 40,296 token-1 units. Burning 100,000 LP units then settles another 11 protocol LP units. The treasury has 33 raw LP units after both settlements.

def test_mint_then_burn_fixture(self):
    p = Pool()
    p.swap0(100_000)
    self.assertEqual(p.mint(1_100_000, 909_339), {"user_lp": 1_000_022, "protocol_lp": 22})
    self.assertEqual(p.supply, 2_000_044)
    self.assertEqual(p.k_last, 4_001_091_600_000)
    self.assertEqual(p.swap0(50_000), 40_296)
    self.assertEqual(p.burn(100_000)["protocol_lp"], 11)
    self.assertEqual(p.treasury_lp, 33)
    self.assertEqual(p.k_last, p.reserve0 * p.reserve1)

Those fixed expectations match the arithmetic sequence recorded in the product repository's protocol fee test. The downloadable test executes the new Python model. It does not execute Solidity, and the product's Foundry suite was not rerun for this article.

The burn method includes the newly minted protocol LP in the supply denominator before calculating the withdrawal. Leaving it out would allocate slightly too much of the pool to the withdrawing user. After the withdrawal, the model updates the reserve baseline again.

The exercise tracks one aggregate treasury balance and a simplified user burn. It omits individual LP ownership, ERC-20 transfer behavior, minimum locked liquidity and contract reentrancy protections. These omissions keep the accounting sequence inspectable. They also mean the program cannot be used to execute or authorize a real withdrawal.

Keep estimates separate from claimable assets

A product integration needs to read the pool state consistently before showing this estimate. Mixing reserves from one block with supply or kLast from another can describe a state that never existed. The offline model has one in-memory state, so it cannot test RPC snapshot consistency or chain reorganizations.

The kit's RevenueSplitter separately tracks received token balances and amounts already released to configured payees. In the trading product, the intended protocol receipts are LP tokens. Splitting received LP and selling or redeeming that LP are different operations. A report should retain the token units at each step and explain any subsequent asset valuation.

This article stops before splitter accounting. A future lab can test cumulative payee entitlement and rounding dust without conflating those balances with pending pool fees. The current plan also separates reward budgets, which are funded by the project, from exchange fee accounting.

Make the numbers part of the buyer demo

Ten named tests passed from a fresh extraction of the downloadable archive. They cover the 22-plus-11 sequence, disabled protocol fees and the zero-fee pool condition. A large-integer case repeats twenty swaps, then settles once and confirms that pending LP returns to zero. Invalid inputs are checked for unintended state changes.

For a team evaluating a White-label DEX Launch Kit, these cases make the revenue discussion concrete. Ask to see pending LP before a liquidity event, the recipient's balance after that event, and a second event with no fresh growth. Then ask how the interface values the received LP and how the configured recipients claim their shares.

The current kit's recorded demonstration uses a local chain and test tokens. The team operating a deployment supplies liquidity and reward budgets, and target-network acceptance remains project-specific. Bring an expected accounting sequence to the walkthrough so the displayed revenue fields can be checked against actual token balances before trading begins.