89304 bc high wormhole miner volume fee is credited without a zk leaf and can never be spent
Submitted on Aug 18th 2026 at 14:17:20 UTC by @Pyro for Audit Comp | Quantus
Report ID: #89304
Report Type: Blockchain/DLT
Report severity: High
Target: https://github.com/immunefi-team/audit-comp-quantus-chain
Impacts:
Permanent freezing of unclaimed yield
Description
A wormhole address holds no spendable balance. process_exit_bundle never debits the source address when it settles an exit. What makes value at such an address usable is a ZK-tree leaf, and record_transfer is the pallet's only leaf writer.
The exit-credit path pairs the credit with the recorder:
match <T::Currency as Unbalanced<_>>::increase_balance(
exit_account,
*exit_balance,
frame_support::traits::tokens::Precision::Exact,
) { Self::record_transfer(
T::AssetId::default(),
&from_account,
&to_account,
*exit_balance,
);The miner volume fee uses the same event-free primitive and stops at the event:
The author is qp_wormhole::extract_author_from_digest, which returns derive_wormhole_address(preimage). The miner holds the secret behind that address and can satisfy the exit circuit. It never receives a leaf, so there is nothing to prove against.
Nothing repairs the omission later:
increase_balancewrites the balance without emitting aBalancesevent, soWormholeProofRecorderExtensioncannot see it.The settlement is an unsigned bare extrinsic, where that extension does not run at all.
The pallet exposes only
verify_private_batchandverify_public_batch, so no admin path can insert the leaf.Leaves are per
(recipient, transfer_count)and each commits one fixed amount, so the mining-rewards leaf does not cover the fee.
Both failure arms of the same branch add the unpaid fee back to burn_amount. The branch's contract is pay or burn. The success path does neither.
Recommendation
Record the leaf inside the same success arm, exactly as the sibling exit-credit branch does.
Better, factor a helper that does increase_balance then record_transfer, and route every event-free credit in process_exit_bundle through it so no future branch can credit without recording.
Proof of Concept
Add to mod private_batch_proof_tests in pallets/wormhole/src/tests.rs. It reuses the committed proof fixture, so no node build is needed.
Output shape (the credited amount depends on the committed fixture's exit total):
Balance credited, leaf count unchanged. That pair is the whole finding.
Was this helpful?