Transactions
unstake-all
Unstake everything from a hotkey across all subnets.
Sweeps the signing coldkey's entire stake held on this hotkey across every
subnet (root included) back to TAO in the coldkey's free balance. Subnets
where subtoken trading is disabled or where the position fails validation
are silently skipped, so the call can
succeed while leaving some positions untouched. Alpha positions are sold
at each pool's current price with no limit protection, so large positions
can incur significant slippage. Use remove_stake to exit a single
subnet, or unstake_all_alpha to consolidate onto root while staying
staked.
Pass claim=True to redeem this validator's whole root basket
entitlement first, then unstake everything. That is not a proportional
payout: the claim pays 100% of the basket.
| Signer | Origin | Pallet | Wraps |
|---|---|---|---|
coldkey | signed account (pallet role may apply) | SubtensorModule | SubtensorModule.unstake_all, SubtensorModule.claim_root_with_hotkey, Utility.batch_all |
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
hotkey_ss58 | string | yes | Hotkey whose entire stake is removed. |
claim | boolean | no | Also redeem this validator's whole root basket entitlement before unstaking, in one atomic batch. This is not a proportional payout: the claim pays 100% of the basket. Claimed yield is restaked on root, then unstake-all takes principal and that yield out together. Unavailable while RootStakeUnlockInterval is nonzero; claim first, wait for the hold, then unstake in that mode. |
Address parameters (--hotkey, --coldkey, --dest, ...) accept a raw ss58
address, an address-book or proxy-book name, or a local wallet/hotkey name.
CLI
Preview with --dry-run (shows fee, effects, and policy result without
submitting), then submit:
btcli tx unstake-all \
--hotkey <ss58|name> --dry-run
btcli tx unstake-all \
--hotkey <ss58|name> -w my_coldkeyPython
import bittensor as bt
from bittensor.wallet import Wallet
wallet = Wallet(name="my_coldkey", hotkey="my_hotkey")
intent = bt.UnstakeAll(hotkey_ss58="5F...")
sub = bt.Subtensor()
plan = sub.plan(intent, wallet) # fee, effects, policy — no submission
result = sub.execute(intent, wallet)
if not result.success:
print(result.error.code, result.error.remediation)(bt.Subtensor is also the async client — async with bt.Subtensor() as client:
— see The client.) Or build the intent by op name, as
an agent would:
result = sub.execute_tool("unstake_all", {...}, wallet)On-chain implementation
SubtensorModule.unstake_all — pallets/subtensor/src/macros/dispatches.rs#L1236:
#[pallet::call_index(83)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::unstake_all())]
pub fn unstake_all(origin: OriginFor<T>, hotkey: T::AccountId) -> DispatchResult {
Self::do_unstake_all(origin, hotkey)
}Delegates to do_unstake_all.
SubtensorModule.claim_root_with_hotkey — pallets/subtensor/src/macros/dispatches.rs#L1993:
#[pallet::call_index(148)]
#[pallet::weight(
<T as crate::pallet::Config>::WeightInfo::claim_root(crate::MAX_ROOT_CLAIM_WORK)
)]
pub fn claim_root_with_hotkey(
origin: OriginFor<T>,
hotkey: T::AccountId,
) -> DispatchResultWithPostInfo {
let coldkey: T::AccountId = ensure_signed(origin)?;
ensure!(
Self::root_claim_fits_declared_budget(core::slice::from_ref(&hotkey)),
Error::<T>::RootClaimTooHeavy
);
let outcome = Self::do_root_claim(coldkey.clone(), vec![hotkey])?;
Self::maybe_add_coldkey_index(&coldkey);
let weight = Self::root_claim_actual_weight(1, &outcome);
Ok((Some(weight), Pays::Yes).into())
}Delegates to root_claim_fits_declared_budget, do_root_claim, maybe_add_coldkey_index.
Utility.batch_all — pallets/utility/src/lib.rs#L314:
#[pallet::call_index(2)]
#[pallet::weight({
let (dispatch_weight, pays) = Pallet::<T>::weight_and_dispatch_class(calls);
let dispatch_weight = dispatch_weight.saturating_add(T::WeightInfo::batch_all(calls.len() as u32));
(dispatch_weight, DispatchClass::Normal, pays)
})]
pub fn batch_all(
origin: OriginFor<T>,
calls: Vec<<T as Config>::RuntimeCall>,
) -> DispatchResultWithPostInfo {
// Do not allow the `None` origin.
if ensure_none(origin.clone()).is_ok() {
return Err(BadOrigin.into());
}
let is_root = ensure_root(origin.clone()).is_ok();
let calls_len = calls.len();
ensure!(
calls_len <= Self::batched_calls_limit() as usize,
Error::<T>::TooManyCalls
);
// Track the actual weight of each of the batch calls.
let mut weight = Weight::zero();
for (index, call) in calls.into_iter().enumerate() {
let info = call.get_dispatch_info();
// If origin is root, bypass any dispatch filter; root can call anything.
let result = if is_root {
call.dispatch_bypass_filter(origin.clone())
} else {
let mut filtered_origin = origin.clone();
// Don't allow users to nest `batch_all` calls.
filtered_origin.add_filter(
move |c: &<T as frame_system::Config>::RuntimeCall| {
let c = <T as Config>::RuntimeCall::from_ref(c);
!matches!(c.is_sub_type(), Some(Call::batch_all { .. }))
},
);
call.dispatch(filtered_origin)
};
// Add the weight of this call.
weight = weight.saturating_add(extract_actual_weight(&result, &info));
result.map_err(|mut err| {
// Take the weight of this function itself into account.
let base_weight = T::WeightInfo::batch_all(index.saturating_add(1) as u32);
// Return the actual used weight + base_weight of this call.
err.post_info = Some(base_weight.saturating_add(weight)).into();
err
})?;
Self::deposit_event(Event::ItemCompleted);
}
Self::deposit_event(Event::BatchCompleted);
let base_weight = T::WeightInfo::batch_all(calls_len as u32);
Ok(Some(base_weight.saturating_add(weight)).into())
}Every file is browsable under /code exactly as built into the runtime, or as plain text under /code/raw/<path> (index: /code/index.json).