Skip to content

Capability to forward tx fees and swap fees to block builder#2426

Merged
sam0x17 merged 10 commits intodevnet-readyfrom
feat/tx-fees-swap-fees-go-to-block-builder
Feb 18, 2026
Merged

Capability to forward tx fees and swap fees to block builder#2426
sam0x17 merged 10 commits intodevnet-readyfrom
feat/tx-fees-swap-fees-go-to-block-builder

Conversation

@gztensor
Copy link
Contributor

@gztensor gztensor commented Feb 16, 2026

Description

Currently the transaction fees are burned and swap fees go to swap pool. This PR enables:

  1. Forwarding of transaction fees to the block producer
  2. Forwarding fraction of swap fees to the block producer

Also, this PR will raise transaction fees by 10x.

Closes #2424

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Other (please describe):

Checklist

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have run ./scripts/fix_rust.sh to ensure my code is formatted and linted correctly
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published in downstream modules

@gztensor gztensor self-assigned this Feb 16, 2026
@gztensor gztensor added the skip-cargo-audit This PR fails cargo audit but needs to be merged anyway label Feb 16, 2026
sam0x17
sam0x17 previously approved these changes Feb 17, 2026
Comment on lines +457 to +482
pub struct BlockAuthorFromAura<F>(core::marker::PhantomData<F>);

impl<F: FindAuthor<u32>> BlockAuthorFromAura<F> {
pub fn get_block_author() -> Option<AccountId32> {
let binding = frame_system::Pallet::<Runtime>::digest();
let digest_logs = binding.logs();
let author_index = F::find_author(digest_logs.iter().filter_map(|d| d.as_pre_runtime()))?;
let authority_id = pallet_aura::Authorities::<Runtime>::get()
.get(author_index as usize)?
.clone();
Some(AccountId32::new(authority_id.to_raw_vec().try_into().ok()?))
}
}

impl AuthorshipInfo<AccountId32> for Runtime {
fn author() -> Option<AccountId32> {
BlockAuthorFromAura::<Aura>::get_block_author()
}
}

impl<F: FindAuthor<u32>> AuthorshipInfo<sp_runtime::AccountId32> for BlockAuthorFromAura<F> {
fn author() -> Option<sp_runtime::AccountId32> {
Self::get_block_author()
}
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a reimplementation of FindAccountFromAuthorIndex, no?

If we add the pallet_authorship to the runtime, this becomes a single line in the config, we could have pallet_authorship::Config be a supertrait of pallet_subtensor::Config and access author directly, just adding a pallet helper to derive the AccountId32 from the AuthorityId.

In your tests you would just have to set the pallet_autorship::Author storage value to what you want.

Wdyt?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pallet transaction-fee doesn't have config, so we would still have to implement AuthorshipInfo for Runtime. Seems like the trade off is ~15 lines of code vs. including pallet-authorship. IMO this is better, but I'm open to discuss. Let's talk about it in the Nucleus standup.

Comment on lines 71 to +72
pub fee_paid: PaidIn,
pub fee_to_block_author: PaidIn,
Copy link
Collaborator

@l0r1s l0r1s Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't these two be the same? Or if I understand correctly you can only pay a part of the fee to the block author and rest goes to the pool?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct: Part of the fee may be paid to the block author.

@gztensor gztensor dismissed stale reviews from shamil-gadelshin and sam0x17 via d1bc5cd February 18, 2026 14:46
});

// Decrease Alpha outstanding.
// TODO: Deprecate, not accurate in v3 anymore
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

off topic, but this removed comment is still true.

// Swap (in a fee-less way) the block builder alpha fee
let mut fee_outflow = 0_u64;
let maybe_block_author_coldkey = T::AuthorshipProvider::author();
if let Some(block_author_coldkey) = maybe_block_author_coldkey {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay, this took me forever to come to the conclusion that it is correct, but it is correct. I would have taken fees on the claim_fees event that the protocol position calls at each block step because then it's super obivous that accounting is correct, but the accounting here is in fact correct. Just took me a long time to realize it.

} else {
// Block author is not found - burn this TAO
// Pallet balances total issuance was taken care of when balance was withdrawn for this swap
TotalIssuance::<T>::mutate(|ti| {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: probably should use the burning utils we have.


// Increase the balance of the block author
let maybe_block_author_coldkey = T::AuthorshipProvider::author();
if let Some(block_author_coldkey) = maybe_block_author_coldkey {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

symmetric to alpha case except we don't have to swap back to TAO.


log::trace!("Delta out: {}", swap_result.delta_out);
log::trace!("Fees: {}", swap_result.fee_paid);
log::trace!("Fees for block author: {}", swap_result.fee_to_block_author);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets goo, I believe that Greg and I originally added this long block of trace logs, but I never suspected they would make it into the codebase nor that be expanded upon later.


// Hold the fees
Self::add_fees(self.netuid, self.fee);
// Split fees according to DefaultFeeSplit between liquidity pool and
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this section we comment that if we want to take the whole fee we can just do fee_to_block_author = self.fee.

I'm totally okay with this, we just have to make 100% certain that add_fees is called with 0. If it's a positive value we will be double counting fees and create TAO or alpha out of nothing.

fee_paid: self.fee,
delta_in: self.delta_in,
delta_out,
fee_to_block_author,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just making a note that units are in Alpha or TAO here. Looks good everywhere, but we can't ever assume fee_to_block_author is in TAO terms when working with the SwapStepResult type.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fee_to_block_author is declared as PaidIn type, so if we're buying, then PaidIn is TaoBalance, and if we're selling, then PaidIn is AlphaBalance. All with type safety.

@sam0x17 sam0x17 added the breaking-change This PR introduces a noteworthy breaking change label Feb 18, 2026
@github-actions
Copy link
Contributor

@opentensor/cerebrum / @opentensor/gyrus / @opentensor/cortex breaking change detected! Please prepare accordingly!

@sam0x17
Copy link
Contributor

sam0x17 commented Feb 18, 2026

marked as breaking change only because some e2es break

@sam0x17 sam0x17 merged commit d147ebd into devnet-ready Feb 18, 2026
189 of 193 checks passed
@gztensor
Copy link
Contributor Author

gztensor commented Feb 19, 2026

Manually tested on a baedeker clone with a transfer. The diff shows:

  • One account (destination) replenished with 0.1 balance
  • One account (origin) reduced balance
  • One account (authority) created with 13756 rao balance (fee)
$ diff accounts-before.json accounts-after-transfer.json 
151434c151434
<   "5H3qhPGzKMNV9fTPuizxzp8azyFRMd4BnheSuwN9Qxb5Cz3u": "0",
---
>   "5H3qhPGzKMNV9fTPuizxzp8azyFRMd4BnheSuwN9Qxb5Cz3u": "100000000",
299743a299744
>   "5G6okTjVk3urYHR1MyJLXmF6AtSvZ9qiwzWhCatNBXV9JMJd": "13756",
332880c332881
<   "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY": "1000000000",
---
>   "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY": "899986244",

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking-change This PR introduces a noteworthy breaking change skip-cargo-audit This PR fails cargo audit but needs to be merged anyway

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants