Transactions
swap-basket
Rebalance a root validator's basket: sell one holding to buy another.
Sells amount of the fund's origin_netuid holding for TAO and buys
dest_netuid with it. Either side may be netuid 0, the fund's TAO cash
slot. Fund shares and staker entitlements do not change; only the fund's
composition moves. Signed by the coldkey that owns hotkey_ss58 or by
a BasketTrading proxy of it (the intended setup for a trader
multisig).
Guardrails enforced on chain: each AMM leg must fill fully within 2% of
the strictest of the subnet's slow moving price, its fast moving price
(a two-hour EMA of spot written each block from the previous block's
close, so a price someone just moved cannot be traded at) and its spot
price (SlippageTooHigh otherwise); the TAO through the middle is
taken from the fund's turnover bucket, which holds
BasketDailyTurnoverCap of the fund's guarded NAV (each holding at
the lower of its realizable value and its slow-moving-price value) and
refills over 7200 blocks (BasketTurnoverBudgetExceeded); the
destination holding may not end above the BasketLiquidityCap share
of the destination pool's alpha reserve (BasketLiquidityCapExceeded)
nor above the BasketConcentrationCap share of the guarded NAV
(BasketConcentrationCapExceeded). Trading
must be enabled network-wide and not frozen for the hotkey. Query
basket_trading_status for the remaining budget and
validator_basket for current holdings before trading. Pass all
to sell the fund's whole origin holding (e.g. to clear a small balance).
min_amount_out is your own floor on top of the protocol band: the
buy leg must credit at least this much destination alpha (TAO when the
destination is netuid 0), after fees, or the whole trade rolls back with
BasketMinOutNotMet. The default 0 sets no floor. btcli root swap derives it from a quote and --max-slippage.
| Signer | Origin | Pallet | Wraps |
|---|---|---|---|
coldkey | signed account (pallet role may apply) | SubtensorModule | SubtensorModule.swap_basket |
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
hotkey_ss58 | string | yes | Root-registered validator whose basket to rebalance. |
origin_netuid | integer | yes | Subnet to sell out of (0 = the fund's TAO cash slot). |
dest_netuid | integer | yes | Subnet to buy into (0 = the fund's TAO cash slot). |
amount | number | "all" | yes | How much of the origin holding to sell, in the origin subnet's alpha (TAO when the origin is netuid 0), or all for the whole holding. |
min_amount_out | number | "all" | no | Least amount the buy leg must credit, in the destination subnet's alpha (TAO when the destination is netuid 0), after fees; the trade rolls back with BasketMinOutNotMet below it. 0 (default) sets no floor. The 2% protocol band applies regardless. |
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 swap-basket \
--hotkey <ss58|name> \
--origin-netuid <int> \
--dest-netuid <int> \
--amount <amount|all> --dry-run
btcli tx swap-basket \
--hotkey <ss58|name> \
--origin-netuid <int> \
--dest-netuid <int> \
--amount <amount|all> -w my_coldkeyPython
import bittensor as bt
from bittensor.wallet import Wallet
wallet = Wallet(name="my_coldkey", hotkey="my_hotkey")
intent = bt.SwapBasket(hotkey_ss58="5F...", origin_netuid=1, dest_netuid=2, amount=1.0)
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("swap_basket", {...}, wallet)On-chain implementation
SubtensorModule.swap_basket — pallets/subtensor/src/macros/dispatches.rs#L2105:
#[pallet::call_index(150)]
// Declared weight is a cap sized for 256 holdings (one NAV sim-swap sweep, two
// post-trade re-quotes, and two AMM legs) plus the flat pending-deposit flush
// allowance every flushing extrinsic declares; the actual weight is computed in
// `do_swap_basket` from the real holding count and flush work and refunded
// post-dispatch, mirroring `stake_into_basket` / `claim_root`.
#[pallet::weight((Pallet::<T>::swap_basket_declared_weight(), DispatchClass::Normal, Pays::Yes))]
pub fn swap_basket(
origin: OriginFor<T>,
hotkey: T::AccountId,
origin_netuid: NetUid,
destination_netuid: NetUid,
amount: AlphaBalance,
min_amount_out: u64,
) -> DispatchResultWithPostInfo {
let coldkey: T::AccountId = ensure_signed(origin)?;
let weight = Self::do_swap_basket(
coldkey,
hotkey,
origin_netuid,
destination_netuid,
amount.to_u64(),
min_amount_out,
)?;
Ok((Some(weight), Pays::Yes).into())
}Delegates to do_swap_basket.
Every file is browsable under /code exactly as built into the runtime, or as plain text under /code/raw/<path> (index: /code/index.json).