89306 bc high reward leaves commit floor amount 1e10 with no remainder carry
Submitted on Aug 18th 2026 at 14:20:53 UTC by @Pyro for Audit Comp | Quantus
Report ID: #89306
Report Type: Blockchain/DLT
Report severity: High
Target: https://github.com/immunefi-team/audit-comp-quantus-chain
Impacts:
Permanent freezing of unclaimed yield
Description
mint_reward mints the raw reward and hands the same raw amount to the proof recorder:
match T::Currency::mint_into(miner, reward) {
Ok(_) => {
T::ProofRecorder::record_transfer_proof(
None, // Native token
mint_account,
miner.clone(),
reward,
);hash_leaf commits only whole quanta:
AMOUNT_SCALE_DOWN_FACTOR is 10,000,000,000 planck. The sub-quantum remainder never enters the Poseidon preimage, so no circuit can witness it, and process_exit_bundle reconstructs summed_output_amount * SCALE_DOWN_FACTOR. Entitlement stops at whole quanta while the balance is the raw amount.
There is no accumulator. on_finalize makes three independent mint_reward calls and each opens its own leaf at the next transfer_count, with its own floor. The nullifier binds to (secret, transfer_count), so a leaf spends once and a later leaf cannot absorb an earlier remainder.
The runtime already names this hazard and enforces the invariant elsewhere. VestingPayoutQuantum is defined as pallet_wormhole::SCALE_DOWN_FACTOR, with the comment that “a sub-quantum transfer would be committed as a zero-value leaf, stranding funds paid to keyless beneficiaries”. Pallet-vesting honours it:
Vesting pays whole quanta and keeps the remainder in the pot for the next claim. Mining-rewards mints the remainder into an address that can never claim it.
The same chokepoint accepts unaligned credits from the event scanner, so a third-party transfer sized for a wormhole recipient loses its remainder the same way. record_transfer_proof rejects only a zero amount. There is no alignment check and no usable minimum.
The shortfall is permanent and recurs on every block. At the 12,000 ms target that is 7,200 block rewards a day, each stranding its own remainder at an address that can never claim it, while total issuance rises by the full raw amount and the unclaimable part consumes the 21,000,000 QUAN supply budget. The shortfall grows as a share of the reward as emission decays.
Example:
Recommendation
Apply the rule pallet-vesting already applies. Pay the miner one credit, quantized down to whole leaf quanta, and roll the remainder into the next block's bucket.
LeafQuantum is a new pallet constant, wired in the runtime to pallet_wormhole::SCALE_DOWN_FACTOR, exactly as VestingPayoutQuantum already is.
Add the same guard at the shared chokepoint so no caller can bypass it. In record_transfer_proof, reject a native credit to a wormhole-form recipient that is not a multiple of AMOUNT_SCALE_DOWN_FACTOR, or that is below the two-quantum minimum the circuit fee relation needs for a nonzero output.
Proof of Concept
Two parts. The first proves the remainder is absent from the commitment, the second proves it is minted anyway and never carried.
Was this helpful?