Skip to content

Latest commit

 

History

History
128 lines (107 loc) · 5.06 KB

batch.mdx

File metadata and controls

128 lines (107 loc) · 5.06 KB
title batch
description Execute several intents atomically in one extrinsic (all-or-nothing).

{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */}

Wraps the child calls in Utility.batch_all: they run in order and if any one fails the whole extrinsic reverts, so there is never a partially-applied result. Use it for multi-step operations that must land together (e.g. move funds then act on them) instead of submitting the steps separately and risking a half-done state. All children must share one signer — an extrinsic has a single signature — and batches cannot contain other batches. Spend limits and policy checks aggregate across every child, and the transaction plan lists each child's effects.

Signer Origin Pallet Wraps
coldkey signed account (pallet role may apply) Utility Utility.batch_all

Parameters

Parameter Type Required Description
intents array yes The calls to execute, in order, as a JSON list of objects {"op": <intent name>, ...args}. At least one; all must share a signer; batches cannot nest.

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 batch \
  --intents <a,b,c> --dry-run
btcli tx batch \
  --intents <a,b,c> -w my_coldkey

Python

import bittensor as bt
from bittensor.wallet import Wallet

wallet = Wallet(name="my_coldkey", hotkey="my_hotkey")
intent = bt.Batch(intents=[...])

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("batch", {...}, wallet)

On-chain implementation

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).