RecoverNested is the only instruction that signs for a parent ATA's PDA, so it's the only path for a user to unwind tokens from a nested ATA. Its transfer_checked CPI is built with five accounts: source, mint, destination, authority, and the token program. That's fine for legacy SPL Token and for Token-2022 mints without hooks. It's not fine for mints with TransferHook set — Token-2022 tries to CPI into the hook program during the transfer, needs the hook program plus the ExtraAccountMetaList PDA plus whatever hook-specific accounts resolve from it, and RecoverNested never forwards any of them. The CPI errors out with MissingAccount, the outer transaction rolls back, and the tokens sit in the nested ATA with no instruction in the program that can extract them.
Previously filed as GHSA-cj9j-wvf7-3w99 (private advisory); re-filing here as a regular issue per @joncinque's guidance. Should be fixable in the Pinocchio migration via spl_token_2022::onchain::invoke_transfer_checked, which already handles the hook + fee cases correctly.
Runnable PoC (4 tests against mainnet-dumped BPF): https://gist.github.com/tschifra/ba737cfb139372af7fe7abe9b099be86
RecoverNestedis the only instruction that signs for a parent ATA's PDA, so it's the only path for a user to unwind tokens from a nested ATA. Itstransfer_checkedCPI is built with five accounts: source, mint, destination, authority, and the token program. That's fine for legacy SPL Token and for Token-2022 mints without hooks. It's not fine for mints withTransferHookset — Token-2022 tries to CPI into the hook program during the transfer, needs the hook program plus theExtraAccountMetaListPDA plus whatever hook-specific accounts resolve from it, andRecoverNestednever forwards any of them. The CPI errors out withMissingAccount, the outer transaction rolls back, and the tokens sit in the nested ATA with no instruction in the program that can extract them.Previously filed as GHSA-cj9j-wvf7-3w99 (private advisory); re-filing here as a regular issue per @joncinque's guidance. Should be fixable in the Pinocchio migration via
spl_token_2022::onchain::invoke_transfer_checked, which already handles the hook + fee cases correctly.Runnable PoC (4 tests against mainnet-dumped BPF): https://gist.github.com/tschifra/ba737cfb139372af7fe7abe9b099be86