Skip to content

Conversation

@sigurpol
Copy link
Contributor

@sigurpol sigurpol commented Dec 10, 2025

This PR introduces pallet-dap-satellite, allowing system chains different from AssetHub to collect funds (to be periodically sent to DAP on AssetHub in a future PR) instead of burning them directly.

It also introduces BurnHandler trait to make any burns configurable (direct burn vs redirect to a buffer).
In Westend AH, RC and system chains, all burns and all fees now go to DAP or DAP satellite.

Changes

  • frame-support: New BurnHandler trait and DirectBurn struct. DirectBurn implements BurnHandler::burn_from to reduce total issuance.

  • pallet-dap-satellite: New pallet that implements BurnHandler and OnUnbalanced by accumulating funds in a satellite account (total issuance unchanged).

  • DAP and DAP satellite funds are excluded from active_issuance.

  • Fee split set to 100% DAP / DAP satellites on Westend system chains.

  • Redirect all burns to DAP or DAP satellite on Westend system chains

The BurnHandler::burn_from method controls the entire burn operation, including balance reduction and issuance handling.
Runtimes must explicitly configure this:

  • Non-DAP runtimes: DirectBurn<Balances> — reduces total issuance via set_total_issuance()
  • DAP-integrated runtimes: pallet_dap::Pallet<Runtime> (AssetHub) or pallet_dap_satellite::Pallet<Runtime> (other system chains) — credits buffer via increase_balance, then deactivate(amount). Total issuance unchanged.

Note: non-native asset burns via pallet-assets use the default fungible implementation, not pallet-balances's BurnHandler.

🔜 Coming next

In a followup PR, we will hook the logic to periodically send funds from the satellite to the main DAP on AssetHub.

When doing so, we will also add related tests under cumulus/parachains/integration-tests/emulated/tests. Doing it now without the XCM transfer, wouldn't provide any additional value vs runtime tests added in the scope of this PR.

Related PRs / issues

Followup of #10576.
Close #10488.

@sigurpol sigurpol requested review from a team as code owners December 10, 2025 10:57
@sigurpol sigurpol added T2-pallets This PR/Issue is related to a particular pallet. A4-backport-stable2512 Pull request must be backported to the stable2512 release branch labels Dec 10, 2025
@sigurpol sigurpol changed the title Introduce pallet-dap-satellite for fund collection on relay-chain and system chains 🚧 Introduce pallet-dap-satellite for fund collection on relay-chain and system chains Dec 10, 2025
@sigurpol sigurpol changed the title 🚧 Introduce pallet-dap-satellite for fund collection on relay-chain and system chains 🚧 Introduce pallet-dap-satellite and redirect all burns for native tokens to DAP/ DAP satellite on Westend runtimes Dec 10, 2025
@sigurpol sigurpol force-pushed the sigurpol-dap-satellite branch from 4ecbd80 to 5025686 Compare December 10, 2025 22:19
@sigurpol sigurpol force-pushed the sigurpol-dap-satellite branch from 137b6f5 to ee054ae Compare December 15, 2025 08:34
@sigurpol sigurpol force-pushed the sigurpol-dap-satellite branch from ee054ae to 1ad4d04 Compare December 29, 2025 13:58
@sigurpol sigurpol requested review from a team as code owners December 29, 2025 13:58
@sigurpol sigurpol removed the A4-backport-stable2512 Pull request must be backported to the stable2512 release branch label Dec 29, 2025
@sigurpol sigurpol force-pushed the sigurpol-dap-satellite branch 10 times, most recently from 9d68694 to 85200d8 Compare December 30, 2025 15:55
@sigurpol sigurpol changed the title 🚧 Introduce pallet-dap-satellite and redirect all burns for native tokens to DAP/ DAP satellite on Westend runtimes Introduce pallet-dap-satellite and redirect all burns for native tokens to DAP/ DAP satellite on Westend runtimes Dec 30, 2025
@sigurpol sigurpol requested review from Ank4n, kianenigma and seadanda and removed request for a team December 31, 2025 13:12
@sigurpol sigurpol added the A4-backport-stable2512 Pull request must be backported to the stable2512 release branch label Jan 29, 2026
@sigurpol sigurpol changed the title Introduce pallet-dap-satellite and redirect all burns for native tokens to DAP/ DAP satellite on Westend runtimes Introduce pallet-dap-satellite and BurnHandler to make tokens burn configurable Jan 30, 2026
@kianenigma
Copy link
Contributor

About #10597 (comment): I think we need to add this fix to the normal DAP pallet as well; any DOT that is stored in DAP is not part of the active issuance.

// Mark funds as inactive so they don't participate in governance voting.
// TODO: When implementing XCM transfer to AssetHub, call `reactivate(amount)` before
// sending.
T::Currency::deactivate(amount);
Copy link
Contributor

Choose a reason for hiding this comment

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

The original code of pallet-balances in this case will reduce the issuance. Is that correct? We are not increasing it
here, but marking something is inactive 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think it's the correct behavior and no - we are not reducing the total issuance.
Current flow with BurnHandler = Dap / Dap satellite:

  1. decrease_balance(who) ( here https://github.com/paritytech/polkadot-sdk/blob/sigurpol-dap-satellite/substrate/frame/balances/src/impl_fungible.rs#L203-L203 in burn_from) -> removes from user's balance, does NOT touch total issuance
  2. increase_balance(satellite) - adds to satellite's balance, does NOT touch total issuance
  3. Total issuance: unchanged (funds moved to DAP, nothing created / destroyed)
  4. deactivate(amount) - increases InactiveIssuance storage

or maybe I am not understanding your comment correctly 😓

);

// Mark funds as inactive so they don't participate in governance voting.
// TODO: When implementing XCM transfer to AssetHub, call `reactivate(amount)` before
Copy link
Contributor

Choose a reason for hiding this comment

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

The flow of funds that go to DAP should be:

  1. Some pallet burns
  2. They come to satellite
  3. TI is not change (or am I missing sth?)
  4. These funds are inactive now
  5. Nothing wrt. active/inactive/issuance changes when they are sent from the satellite account to the main DAP account.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

improved the comment. AFAIK here we need to reactivate before sending and on DAP side we need to deactivate when receiving

Copy link
Contributor

@kianenigma kianenigma left a comment

Choose a reason for hiding this comment

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

I have doubts around how TI issuance is handled; Let me recap what I knew before this:

  • Pallet balances implements fungible::Mutate, which has as burn_from method.
  • This is used for both the burn transaction, and if another pallet calls T::Currency::burn() (test should cover this too btw)
  • At the moment, this implementation is hardcoded in pallet-balances, and does what is sensible: burn the funds, reduce TI.
  • Now we want pallet-balances to allow for customization of its fungible::Mutate::burn_from.
  • I think in the new model, the whole implementation of BurnFrom should come from a type BurnHandler, not just the destination part.
  • This will allow our new BurnHandler to manage TI as it wants, while allowing the original DirectBurn to remain exactly as-is.
  • The PRdoc and PR description should say clearly that all existing users should use type BurnHandler = pallet_balances::DirectBurn and it will be a noop.

@sigurpol
Copy link
Contributor Author

sigurpol commented Jan 30, 2026

About #10597 (comment): I think we need to add this fix to the normal DAP pallet as well; any DOT that is stored in DAP is not part of the active issuance.

It is part of this PR already after your previous comment. But if we decide NOT to ship this PR for 2.0.6 then yes, we need to add that fix in ad-hoc small PR targeting 2512 / 2.0.6

@sigurpol
Copy link
Contributor Author

I have doubts around how TI issuance is handled; Let me recap what I knew before this:

  • Pallet balances implements fungible::Mutate, which has as burn_from method.
  • This is used for both the burn transaction, and if another pallet calls T::Currency::burn() (test should cover this too btw)
  • At the moment, this implementation is hardcoded in pallet-balances, and does what is sensible: burn the funds, reduce TI.
  • Now we want pallet-balances to allow for customization of its fungible::Mutate::burn_from.
  • I think in the new model, the whole implementation of BurnFrom should come from a type BurnHandler, not just the destination part.
  • This will allow our new BurnHandler to manage TI as it wants, while allowing the original DirectBurn to remain exactly as-is.
  • The PRdoc and PR description should say clearly that all existing users should use type BurnHandler = pallet_balances::DirectBurn and it will be a noop.

I think the current behavior is correct but your approach is definitely much cleaner so I will go for BurnHandler manage the whole burn_from as per #10597 (comment)

@sigurpol
Copy link
Contributor Author

I think the current behavior is correct but your approach is definitely much cleaner so I will go for BurnHandler manage the whole burn_from as per #10597 (comment)

Implemented here f91cb59

@paritytech-workflow-stopper
Copy link

All GitHub workflows were cancelled due to failure one of the required jobs.
Failed workflow url: https://github.com/paritytech/polkadot-sdk/actions/runs/21520140321
Failed job name: fmt

Comment on lines +251 to +252
/// `DirectBurn<Balances>` or a custom implementation.
type BurnHandler = ();
Copy link
Contributor

Choose a reason for hiding this comment

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

Can't we set this to DirectBurn then a lot of tests and mocks don't need to change.

Other option is to make the impl for () be actually what DirectBurn does.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I am cooking the latter as we speak since I realized we can make things much better assuming default = DirectBurn for all test and non Westend runtimes

let mut split =
fees.ration(dap_percent.deconstruct() as u32, other_percent.deconstruct() as u32);
if let Some(tips) = fees_then_tips.next() {
// Tips go 100% to other handler.
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this intentional?

I suppose most runtimes will want to configure this as "everything goes to DAP". In this case the tips still go whatever
is OtherHandler.

In a similar vein, while DealWithFeesSplit is useful, am I correct that in most real cases we want to use type DealWithFees = Dap, which directly uses the OnUnbalanced impl below?

mod burn_handler;
mod deal_with_fees_split;
mod genesis;
mod on_unbalanced;
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a good place to have the tests where:

  1. You create a fake pallet in the mock.rs that has type Currency in it.
  2. It calls T::Currency::burn
  3. Burn ends up in DAP.

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

Labels

A4-backport-stable2512 Pull request must be backported to the stable2512 release branch T2-pallets This PR/Issue is related to a particular pallet.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

pallet-dap-satellites: local collection to avoid burn

3 participants