89307 bc medium sub existential deposit block fees are minted before the account endowing reward
Submitted on Aug 18th 2026 at 14:22:54 UTC by @Pyro for Audit Comp | Quantus
Report ID: #89307
Report Type: Blockchain/DLT
Report severity: Medium
Target: https://github.com/immunefi-team/audit-comp-quantus-chain
Impacts:
Griefing (e.g. no profit motive for an attacker, but damage to the users or the protocol)
Description
on_finalize mints collected fees to the miner before the base reward:
// Send fees to miner if any
Self::mint_reward(miner.as_ref(), tx_fees);
// Send block rewards to miner
Self::mint_reward(miner.as_ref(), miner_reward);mint_into uses Precision::Exact, so a credit leaving a nonexistent account below the 1,000,000,000 planck existential deposit fails with BelowMinimum. The error arm redirects the fee to the treasury and records a wormhole transfer proof crediting the treasury.
The base reward on the next line then creates the same account, but the fee is already gone. This contradicts the runtime's stated rule that 100 percent of transaction fees go to the block miner.
The diversion is permanent. CollectedFees is cleared by the take() at the top of the hook, the redirect succeeds, and no retry path ever returns the amount to the miner it was owed to.
An attacker can trigger it on demand. A post-dispatch Pays::No extrinsic collapses the charged fee to the tip alone, so pallet_ranked_collective::cleanup_poll with a sub-existential-deposit tip sets the block's fee total below the threshold at will. Doing that on the block a new miner first wins denies that miner its fee revenue, at a cost to the attacker of the tip. Every miner joining the network is a fresh target, since each new rewards_inner_hash derives an author address with no prior balance.
Recommendation
Aggregate both miner-directed amounts into one credit, checked once against the existential deposit. The single-credit form recommended in H-02 already does this and fixes both defects together.
Proof of Concept
Add to pallets/mining-rewards/src/tests.rs. TestMiner(7) is not endowed by the mock genesis, so it is a fresh author address.
Output:
Was this helpful?