> ## Documentation Index
> Fetch the complete documentation index at: https://docs.raydium.io/llms.txt
> Use this file to discover all available pages before exploring further.

# 2026-08-27 — LaunchLab: platform holds the withdraw-withheld authority from mint creation

> InitializeWithToken2022 now writes PlatformConfig.transfer_fee_extension_auth into the new base mint's withdraw_withheld_authority instead of the launch authority PDA, and MigrateToCpswap only reassigns that authority when the PDA still holds it. No account layout, instruction, or error change.

<Info>
  This entry covers an upcoming LaunchLab program update. It was verified against the local release branch before deployment. Confirm the deployed program before relying on pre-graduation withheld-fee withdrawal.
</Info>

A Token-2022 base mint created by `InitializeWithToken2022` can carry a `TransferFeeConfig`. That extension has two authorities: `transfer_fee_config_authority`, which changes the fee rate and maximum fee, and `withdraw_withheld_authority`, which withdraws the fees Token-2022 withholds on token accounts.

Until this release the program put the launch `authority` PDA on both of them at mint creation and handed both to `PlatformConfig.transfer_fee_extension_auth` at graduation. The launchpad program has no withdraw-withheld or harvest instruction, so for the whole bonding-curve phase nobody could withdraw the withheld balance — the only key that could was a PDA with no instruction to sign with it.

This release writes `transfer_fee_extension_auth` directly into `withdraw_withheld_authority` at mint creation, so the platform can sweep withheld fees from the first trade. `MigrateToCpswap` gains a guard so it only reassigns that authority when the PDA is still the one holding it, which keeps graduation working for mints from both generations.

Nothing else changed. No account grew or shrank, no instruction gained or lost an account, no argument changed, and no error code moved.

## TL;DR for integrators

* **Withheld transfer fees are withdrawable during the launch.** On a new Token-2022 launch whose platform has `transfer_fee_extension_auth` set, that key is the mint's `withdraw_withheld_authority` from creation. Call Token-2022's own `WithdrawWithheldTokensFromAccounts` / `WithdrawWithheldTokensFromMint` directly; LaunchLab has no instruction for this and never did.
* **`transfer_fee_config_authority` timing is unchanged.** It is still the launch `authority` PDA until graduation, and still moves to `transfer_fee_extension_auth` in `MigrateToCpswap`. The fee rate cannot be changed mid-launch.
* **Set `transfer_fee_extension_auth` before launching.** When it is `Pubkey::default()` at mint creation, the withdraw authority falls back to the `authority` PDA — the old behavior. Setting the field afterwards still recovers the authority at graduation, but the withheld balance is stranded until then.
* **Do not rotate the field mid-launch.** The two authorities are read at two different moments, so a value that changes in between leaves them on different keys. Details in [Rotation splits the two authorities](#rotation-splits-the-two-authorities).
* **`MigrateToCpswap` is unchanged for callers.** Same accounts, same arguments, same errors. The new guard is internal; without it, migration of a new-generation mint would revert inside Token-2022 because the PDA can no longer sign away an authority it does not hold.
* **Existing launches are unaffected.** Mints already on-chain keep the PDA on both authorities and still get both handed over at graduation.
* **No IDL refresh is required.** No layouts, accounts, arguments, or error codes changed.

## Who holds what, and when

| Authority                       | Holder at mint creation                                                                   | Holder after `MigrateToCpswap`                                                            |
| ------------------------------- | ----------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| `withdraw_withheld_authority`   | `transfer_fee_extension_auth`; the `authority` PDA when that field is `Pubkey::default()` | Unchanged when it is already the platform key. Reassigned only if the PDA still holds it. |
| `transfer_fee_config_authority` | The `authority` PDA, always                                                               | `transfer_fee_extension_auth`, when that field is non-default                             |

Both handovers still sit behind the same outer condition they always did: the base mint must carry `TransferFeeConfig` **and** `transfer_fee_extension_auth` must be non-default. A platform that never sets the field keeps both authorities on the `authority` PDA forever — the fee rate can never be changed and the withheld balance can never be withdrawn.

## Rotation splits the two authorities

`transfer_fee_extension_auth` is read twice per launch, at two moments that can be days apart:

1. At `InitializeWithToken2022`, for `withdraw_withheld_authority`.
2. At `MigrateToCpswap`, for `transfer_fee_config_authority` — and for `withdraw_withheld_authority` only when the PDA still holds it.

Rotate the field through `UpdatePlatformConfig` in between and the two authorities land on different keys: the withdraw side keeps the key that was configured at creation, because the PDA no longer holds it and migration skips that handover, while the fee-config side goes to the new key. Neither call fails; the split is silent. Rotate between launches, or reconcile the keys afterwards through Token-2022 directly with the old key signing.

## Why the guard is required, not just defensive

Token-2022's `SetAuthority` requires the *current* authority to sign. Under the new creation rule the launch `authority` PDA is not the withdraw-withheld authority on a new mint, so the unconditional CPI the program used to make would fail — and because it sits inside `MigrateToCpswap`, that failure would have blocked graduation for every Token-2022 launch with a transfer fee on a platform that had configured the field. The two changes only work as a pair.

## What did not change

* **Transfer-fee math and accounting.** `MAX_TRANSFER_FEE_RATE` is still `500` (5%, denominator `10_000`), the fee is still priced into the curve the same way, and the withheld balance still accrues on the token accounts Token-2022 puts it on.
* **`MigrateToCpswap`'s interface.** Same account list, same `remaining_accounts` indices, same two token programs, no arguments.
* **`InitializeWithToken2022`'s interface.** Same accounts, same `TransferFeeExtensionParams`, same `NoSupportExtension` rejection for anything outside `MetadataPointer` and `TransferFeeConfig`.
* **Quote-side Token-2022 handling.** Unchanged from [2026-08-24](/reference/changelog/2026-08-24-launchlab-token2022-quote-mint).
* **Error codes.** `6000`–`6023` are unchanged; this release adds none.
* **Account layouts and the IDL.** Unchanged. `PlatformConfig.transfer_fee_extension_auth` keeps its offset and its type; only when the program reads it changed.

## Pages updated

* `products/launchlab/platform-config` — new "Token-2022 transfer-fee authorities" section covering both authorities, both handover points, and the rotation trap; layout comment and pitfalls list corrected.
* `products/launchlab/instructions` — `MigrateToCpswap` gains a "Token-2022 transfer-fee authority handover" block; `Initialize` postconditions record the new mint-creation authorities.
* `products/launchlab/accounts` — authority-PDA role list and the base-mint section note which transfer-fee authorities the PDA actually holds.
* `reference/token-2022-support` — LaunchLab base-mint entry states who holds the transfer-fee authorities and when.
