Skip to main content
SUBMIT A PRSUBMIT AN ISSUElast edit: Mar 23, 2026

Batch Transactions

The Bittensor runtime's utility pallet exposes three extrinsics — batch, batch_all, and force_batch — that let you submit multiple calls as a single on-chain transaction. This is useful when you want to stake to multiple hotkeys, perform multiple operations atomically, or reduce the number of round-trips to the chain.

For how fees are calculated across a batch. See Batch Transaction Fees.

batch vs batch_all vs force_batch

The three variants differ only in how they handle errors. Choose based on whether partial success is acceptable:

ExtrinsicOn error
batchStops at first failure; prior calls succeed. Emits BatchInterrupted.
batch_allReverts all calls atomically on any failure.
force_batchContinues past failures; failed calls are skipped.
info

Use batch_all when all inner calls must succeed or none should. Use batch if partial success is acceptable, or force_batch to continue past failures.

Source code: batch pallets/utility/src/lib.rs:197–201, batch_all pallets/utility/src/lib.rs:309–313, force_batch pallets/utility/src/lib.rs:408–412.

Using batch calls with the SDK

Use a proxy coldkey for these operations

The operations on this page require a coldkey. Your primary coldkey should remain in cold storage (hardware wallet) and never be loaded onto a machine running btcli or the Bittensor SDK. Use a scoped, delayed proxy coldkey to perform these operations via btcli or the SDK. See Coldkey and Hotkey Workstation Security and Proxies.

The SDK's add_stake_multiple and unstake_multiple send individual extrinsics sequentially, not a single batch extrinsic.

To submit a true batch (one extrinsic on-chain), use the low-level pallet builder + proxy path. The batch call is wrapped in a proxy extrinsic signed by your proxy wallet.

Proxy type for batch calls

The Staking proxy type allows specific staking extrinsics (add_stake, remove_stake, etc.) but does not allow Utility::batch_all as the outer call. For batch staking operations, use a NonCritical proxy, which prohibits only destructive operations (dissolve_network, root_register, burned_register, Sudo) and allows everything else, including batch wrappers.

import os
import bittensor as bt
from bittensor.core.chain_data.proxy import ProxyType
from bittensor.core.extrinsics.pallets import SubtensorModule

sub = bt.Subtensor(network="finney")
proxy_wallet = bt.Wallet(name=os.environ['BT_PROXY_WALLET_NAME'])
real_account_ss58 = os.environ['BT_REAL_ACCOUNT_SS58']

hotkey_1 = "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"
hotkey_2 = "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty"
netuid = 1
amount = bt.Balance.from_tao(10)

pallet = SubtensorModule(sub)
call_1 = pallet.add_stake(netuid=netuid, hotkey=hotkey_1, amount_staked=amount.rao)
call_2 = pallet.add_stake(netuid=netuid, hotkey=hotkey_2, amount_staked=amount.rao)

# Wrap in Utility.batch_all — reverts all calls atomically on any failure
batch_call = sub.compose_call(
call_module="Utility",
call_function="batch_all",
call_params={"calls": [call_1, call_2]},
)

# Submit via proxy — NonCritical proxy type is required for batch wrappers
response = sub.proxy(
wallet=proxy_wallet,
real_account_ss58=real_account_ss58,
force_proxy_type=ProxyType.NonCritical,
call=batch_call,
)
print(response)
add_stake_multiple is not a batch extrinsic

subtensor.add_stake_multiple() and subtensor.unstake_multiple() loop over their inputs and submit one extrinsic per hotkey. Each transaction is settled independently — they are not atomic. Use the Utility.batch_all pattern above when you need all-or-nothing semantics or want to pay a single transaction fee for the group.