Skip to content

Feat/recycle burn chain exts#2560

Merged
sam0x17 merged 17 commits into
opentensor:devnet-readyfrom
LandynDev:feat/recycle-burn-chain-exts
Apr 30, 2026
Merged

Feat/recycle burn chain exts#2560
sam0x17 merged 17 commits into
opentensor:devnet-readyfrom
LandynDev:feat/recycle-burn-chain-exts

Conversation

@LandynDev
Copy link
Copy Markdown

@LandynDev LandynDev commented Apr 2, 2026

Description

Add 4 new chain extension functions (IDs 16-19) enabling WASM smart contracts to recycle and burn alpha stake.

  • RecycleAlphaV1 (16): Recycle alpha stake
  • BurnAlphaV1 (17): Burn alpha stake
  • AddStakeRecycleV1 (18): Atomically add TAO stake then recycle
  • AddStakeBurnV1 (19): Atomically add TAO stake then burn

All functions return the actual alpha amount via the output buffer.

Related Issue(s)

Closes #2564

Type of Change

  • New feature (non-breaking change which adds functionality)

Breaking Change

No breaking changes. Existing chain extension functions (IDs 0-15) are unaffected. New function IDs 16-19 are additive.

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

Additional Notes

7 unit tests cover: success paths for all 4 functions, root subnet rejection, nonexistent subnet, and insufficient balance. E2E validated on localnet with an ink! contract calling add_stake_recycle via chain extension.

I also ran some local E2E testing with a local running subtensor chain (with the new extensions), deployed contract, etc.

Screenshot 1 shows:

  • Accumulated fees of 0.0034 TAO on the contract (to be recycled)
  • Total recycled fees of 0.0000 TAO on the contract
  • Contract Balance of 5.0000 TAO
image

Screenshot 2 shows:

  • Recycle of 0.0034 TAO succeeded (CLI -> Smart contract func -> New add stake + recycle chain ext)
  • Accumulated fees of 0.0000 TAO on the contract (it got recycled)
  • Total recycled fees of 0.0034 TAO on the contract
  • Contract Balance of 4.9966 TAO
image

Unit tests successfully passing:
image

@open-junius
Copy link
Copy Markdown
Contributor

Thanks for the contribution. Can you add the ticket for the requirement? I am wondering the extension like AddStakeRecycleV1, who really want to do it. just confirm all is from the real requirements.

@LandynDev
Copy link
Copy Markdown
Author

Thanks for the contribution. Can you add the ticket for the requirement? I am wondering the extension like AddStakeRecycleV1, who really want to do it. just confirm all is from the real requirements.

Just got it ticketed up here - #2564

Particular snippet from the issue to elaborate on the AddStakeBurn + AddStakeRecycle funcs and why those 2 are included:

The current AddStakeV1 chain extension only returns a success/error code, not the resulting alpha amount. While a contract could work around this by calling AddStakeV1 followed by RecycleAlphaV1(MAX), this recycles all alpha the contract holds on that subnet — not just the alpha from that specific stake. This is fine for simple contracts, but may break down for contracts that hold alpha for multiple purposes on the same subnet.

The AddStakeRecycleV1 and AddStakeBurnV1 functions handle this atomically in a single call, recycling/burning only the alpha produced by that specific stake. This provides the robustness needed for a general-purpose chain extension interface.

@LandynDev LandynDev marked this pull request as ready for review April 3, 2026 16:11
@open-junius
Copy link
Copy Markdown
Contributor

Looks good to me, please fix the 'Cargo fmt'

open-junius
open-junius previously approved these changes Apr 7, 2026
@LandynDev
Copy link
Copy Markdown
Author

Looks good to me, please fix the 'Cargo fmt'

👍
it dismissed your review bc of the repush, but should be good to go now

@open-junius open-junius added the skip-cargo-audit This PR fails cargo audit but needs to be merged anyway label Apr 7, 2026
@LandynDev LandynDev force-pushed the feat/recycle-burn-chain-exts branch from e527c74 to 1c4f180 Compare April 7, 2026 02:01
@LandynDev
Copy link
Copy Markdown
Author

it also needed devnet-ready rebase, got outdated but thats not situated

open-junius
open-junius previously approved these changes Apr 7, 2026
Copy link
Copy Markdown
Contributor

@open-junius open-junius left a comment

Choose a reason for hiding this comment

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

Looks good for me.

Comment thread chain-extensions/src/lib.rs Outdated
Comment thread chain-extensions/src/lib.rs Outdated
Comment thread chain-extensions/src/lib.rs Outdated
Comment thread chain-extensions/src/tests.rs
@LandynDev
Copy link
Copy Markdown
Author

LandynDev commented Apr 7, 2026

Addressed the review feedback:

  • Removed redundant .min() clamping in RecycleAlphaV1/BurnAlphaV1. Pallet handles this internally
  • Wrapped AddStakeRecycleV1/AddStakeBurnV1 in with_transaction for atomicity. If recycle/burn fails, stake is fully rolled back
  • Added 2 rollback tests (add_stake_recycle_rollback_on_recycle_failure, add_stake_burn_rollback_on_burn_failure)
image

@evgeny-s / @open-junius if you could give this a re-look when you have a chance, thanks

Commit of changes: 65357d3

Comment thread chain-extensions/src/lib.rs Outdated

match call_result {
Ok(_) => {
env.write_output(&amount.encode())
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I can see you remove the real_amount, and put the amount into result directly. but it could be wrong if the amount is more than you staked. So I suggest we can update the recycle_alpha function with the real recycled alpha as result, or doesn't return any value in RecycleAlphaV1. and the same for burn function.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good call, I got it updated w this commit - 60dffb3

Trying to keep the diff concise with what i add/change. Reran unit tests and passing

Comment thread chain-extensions/src/lib.rs Outdated

let caller = env.caller();

let result = transactional::with_transaction(|| {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

There is do_add_stake_burn function, can we use it instead of call both do_add_stake and do_burn_alpha

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Looked into this but I don't think do_add_stake_burn is usable from a chain ext

  • It's pub(crate), so it's not accessible from the chain-extensions crate.
  • Also it calls ensure_subnet_owner on the origin which we won't want

Because of this, I kept it as the do_add_stake + do_burn_alpha composition

<<T as pallet_subtensor::Config>::WeightInfo as SubtensorWeightInfo>::recycle_alpha(),
);

env.charge_weight(weight)?;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

we can charge the add_stake part first. If do_add_stake is Ok, then continue with recycle_alpha weight

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Okay, I updated this.. AddStakeRecycleV1 now charges add_stake() upfront and only charges recycle_alpha() after do_add_stake returns Ok. Applied the same split to AddStakeBurnV1 for consistency. Both ops still run inside with_transaction, & tests updated and passing.

Strip development log::info!/log::error! calls from dispatch entry
and AddStakeRecycleV1 handler. Normalize AddStakeRecycleV1 to use
the same concise ? pattern as all other handlers.
Replaces hardcoded Weight::from_parts values with calls to
pallet_subtensor::weights::WeightInfo, matching the pattern junius
introduced in PR 2550. Weights now auto-track benchmark updates.

AddStakeRecycleV1 sums add_stake() + recycle_alpha() since the pallet
has no add_stake_recycle weight; AddStakeBurnV1 uses add_stake_burn()
directly (1:1 with pallet's do_add_stake_burn).
For AddStakeRecycleV1 and AddStakeBurnV1, charge add_stake() upfront and
only charge the second-stage weight (recycle_alpha()/burn_alpha()) after
do_add_stake returns Ok. Atomicity is preserved by keeping both ops
inside with_transaction and tracking attempt state via a stack flag.
- cargo fmt wraps the add_stake() call in AddStakeRecycleV1/AddStakeBurnV1
- mock charge_weight replaces saturating_add with checked_add().unwrap()
  to satisfy the ForbidSaturatingMath custom lint
Swap the decode tuple in RecycleAlphaV1 and BurnAlphaV1 from
(hotkey, amount, netuid) to (hotkey, netuid, amount) so the position
of netuid matches AddStakeRecycleV1 and AddStakeBurnV1. Addresses
evgeny-s's consistency feedback on opentensor#2560.
Adds pub fn do_add_stake_recycle and do_add_stake_burn_permissionless
to pallet_subtensor; each wraps do_add_stake + do_{recycle,burn}_alpha
in a transactional::with_transaction so atomicity lives in the pallet.
No subnet-owner guard or rate limit — those stay on do_add_stake_burn
for the owner-priority path (per shamil-gadelshin).

Chain-extension handlers AddStakeRecycleV1 and AddStakeBurnV1 collapse
to a single pallet call, matching the thin-handler pattern of the rest
of the extension. Weight is charged once upfront as add_stake + the
matching tail weight. Addresses evgeny-s's architectural feedback on
opentensor#2560.
@LandynDev LandynDev force-pushed the feat/recycle-burn-chain-exts branch from 0179ac5 to 46989aa Compare April 21, 2026 16:18
evgeny-s
evgeny-s previously approved these changes Apr 24, 2026
open-junius
open-junius previously approved these changes Apr 24, 2026
Applies the bench-patch artifact from validate-benchmarks CI
(run 24733556355). Drift exceeded the 40% weight-compare
threshold for pallet_subtensor, pallet_admin_utils, and
pallet_subtensor_proxy.

Autogenerated weights only - no logic change.
Unblocks the Devnet Deploy Check — devnet chain has caught up
to spec_version 397, so local must be strictly greater.
@LandynDev LandynDev dismissed stale reviews from open-junius and evgeny-s via 78ad491 April 24, 2026 13:50
Resolves conflicts from upstream precompile test port (opentensor#2616),
remove_stake benchmark fix (opentensor#2602), TransactionFeePaidWithAlpha
netuid event, and spec_version bumps.

- pallets/proxy/src/weights.rs: accept upstream regenerated weights
- pallets/subtensor/src/weights.rs: accept upstream regenerated weights
- runtime/src/lib.rs: bump spec_version 401 -> 403 (was 402 upstream)
- pallets/subtensor/src/macros/dispatches.rs: auto-merged
sam0x17
sam0x17 previously approved these changes Apr 24, 2026
Resolves conflicts from upstream chore/imbalances merge (opentensor#2524) and
small follow-on PRs (opentensor#2621, opentensor#2623).

- chain-extensions/src/types.rs: add upstream's CannotUseSystemAccount=20,
  shift our CannotBurnOrRecycleOnRootSubnet to 21 and SubtokenDisabled to 22
- chain-extensions/src/tests.rs: switch our recycle/burn test calls from
  pallet_subtensor::Pallet::<mock::Test>::add_balance_to_coldkey_account
  to the mock free function (pallet method removed in opentensor#2524)
- docs/wasm-contracts.md: align Output table with new discriminants 21/22
- pallets/admin-utils/src/weights.rs: accept upstream regenerated
  sudo_set_total_issuance weights
@sam0x17 sam0x17 merged commit 5f56213 into opentensor:devnet-ready Apr 30, 2026
380 of 382 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

apply-benchmark-patch 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