Skip to main content
SUBMIT A PRSUBMIT AN ISSUElast edit: Mar 16, 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.

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

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 compose_call + sign_and_send_extrinsic path directly.

import bittensor as bt

sub = bt.Subtensor(network="finney")
wallet = bt.Wallet(name="my_wallet", hotkey="my_hotkey")
wallet.unlock_coldkey()

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

# Compose each inner call individually
call_1 = sub.compose_call(
call_module="SubtensorModule",
call_function="add_stake",
call_params={
"hotkey": hotkey_1,
"netuid": netuid,
"amount_staked": amount.rao,
},
)
call_2 = sub.compose_call(
call_module="SubtensorModule",
call_function="add_stake",
call_params={
"hotkey": hotkey_2,
"netuid": netuid,
"amount_staked": amount.rao,
},
)

# Wrap in a 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 the batch as a single extrinsic
success, error_message = sub.sign_and_send_extrinsic(
call=batch_call,
wallet=wallet,
wait_for_inclusion=True,
wait_for_finalization=False,
)
print(f"Success: {success}" if success else f"Failed: {error_message}")
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.