Description
OrderSwapInterface::transfer_tao (pallets/subtensor/src/staking/order_swap.rs:116-119) calls <T as Config>::Currency::transfer(from, to, amount, Preservation::Expendable) directly. With Preservation::Expendable and the runtime configured as type DustRemoval = () (runtime/src/lib.rs:523), if the transfer would leave from with a positive residual balance strictly less than the existential deposit (ED = 500 RAO, runtime/src/lib.rs:510-512), the balances pallet reaps from, burns the dust, and decrements balances::TotalIssuance by that dust.
The sibling Pallet::transfer_tao in pallets/subtensor/src/coinbase/tao.rs:70-80 is designed exactly to avoid this drift: it delegates to transfer_allow_death_update_ti (tao.rs:38-64), which snapshots balances::TotalIssuance before and after the transfer and decrements subtensor::TotalIssuance by any observed delta so the two issuance counters stay locked. The OrderSwapInterface implementation bypasses this wrapper entirely and re-implements the unsafe direct-currency path.
Net effect: every limit-orders fee transfer that happens to land the signer's residual balance below ED silently widens the gap balances::TI < subtensor::TI by the dust amount (up to 499 RAO per occurrence). This is exactly the class of divergence that migrate_fix_total_issuance_evm_fees (pallets/subtensor/src/migrations/migrate_fix_total_issuance_evm_fees.rs) was added to repair for the prior EVM-fee burn bug — the same root cause (a Credit / dust burn that decrements balances::TI without a matching subtensor::TI decrement) is being re-introduced here.
subtensor::TotalIssuance is consensus-affecting:
block_emission.rs:32-34 reads Self::get_total_issuance() (= TotalIssuance::<T>::get()) and uses it to compute the per-block emission curve. A persistently inflated subtensor TI suppresses emission (the log2 curve treats the chain as more mature than it really is).
mint_tao (tao.rs:252-266) clamps against <T as Config>::Currency::total_issuance() (balances TI) for the 21M cap. With drift, the two halves of the same accounting question (get_block_emission and mint_tao's cap) get inconsistent answers, and a future migration similar to migrate_fix_total_issuance_evm_fees will be needed to reconcile.
Recommendation
Either:
- Route the trait impl through the existing safe wrapper: replace the body with
Pallet::<T>::transfer_tao(from, to, amount) (tao.rs:70-80), which already calls transfer_allow_death_update_ti; or
- Inline the same before/after
balances::total_issuance() snapshot + subtensor::TotalIssuance::saturating_sub(burned) reconciliation that transfer_allow_death_update_ti performs.
Either fix removes the structural class of bug (any future direct-currency-transfer path needs the same wrapper).
Description
OrderSwapInterface::transfer_tao(pallets/subtensor/src/staking/order_swap.rs:116-119) calls<T as Config>::Currency::transfer(from, to, amount, Preservation::Expendable)directly. WithPreservation::Expendableand the runtime configured astype DustRemoval = ()(runtime/src/lib.rs:523), if the transfer would leavefromwith a positive residual balance strictly less than the existential deposit (ED = 500 RAO,runtime/src/lib.rs:510-512), the balances pallet reapsfrom, burns the dust, and decrementsbalances::TotalIssuanceby that dust.The sibling
Pallet::transfer_taoinpallets/subtensor/src/coinbase/tao.rs:70-80is designed exactly to avoid this drift: it delegates totransfer_allow_death_update_ti(tao.rs:38-64), which snapshotsbalances::TotalIssuancebefore and after the transfer and decrementssubtensor::TotalIssuanceby any observed delta so the two issuance counters stay locked. TheOrderSwapInterfaceimplementation bypasses this wrapper entirely and re-implements the unsafe direct-currency path.Net effect: every limit-orders fee transfer that happens to land the signer's residual balance below ED silently widens the gap
balances::TI < subtensor::TIby the dust amount (up to 499 RAO per occurrence). This is exactly the class of divergence thatmigrate_fix_total_issuance_evm_fees(pallets/subtensor/src/migrations/migrate_fix_total_issuance_evm_fees.rs) was added to repair for the prior EVM-fee burn bug — the same root cause (aCredit/ dust burn that decrements balances::TI without a matching subtensor::TI decrement) is being re-introduced here.subtensor::TotalIssuanceis consensus-affecting:block_emission.rs:32-34readsSelf::get_total_issuance()(=TotalIssuance::<T>::get()) and uses it to compute the per-block emission curve. A persistently inflated subtensor TI suppresses emission (the log2 curve treats the chain as more mature than it really is).mint_tao(tao.rs:252-266) clamps against<T as Config>::Currency::total_issuance()(balances TI) for the 21M cap. With drift, the two halves of the same accounting question (get_block_emissionandmint_tao's cap) get inconsistent answers, and a future migration similar tomigrate_fix_total_issuance_evm_feeswill be needed to reconcile.Recommendation
Either:
Pallet::<T>::transfer_tao(from, to, amount)(tao.rs:70-80), which already callstransfer_allow_death_update_ti; orbalances::total_issuance()snapshot +subtensor::TotalIssuance::saturating_sub(burned)reconciliation thattransfer_allow_death_update_tiperforms.Either fix removes the structural class of bug (any future direct-currency-transfer path needs the same wrapper).