swap_basket_many

Call v470 → current New in v470 #151

Rebalances several legs of one root validator beta basket in a single all-or-nothing call : each leg sells alpha of one holding and buys another, under the same bounds as swap_basket.

View calls on chain

Click items to navigate. Pan and zoom to explore.

Used by: validatorsdevelopersanalytics

The Big Picture

Added in v470 as the batched form of swap_basket. The single-leg call stays; this one exists for efficiency and atomicity: one dividend flush and one net asset value sweep for the whole batch, and all legs commit together. Source: macros/dispatches.rs call index 151, staking/basket_trade.rs do_swap_basket_many_tracked, and MAX_BASKET_SWAP_LEGS in lib.rs, at 923fd1fa7 (v470).

Why This Matters

An indexer that expects one BasketSwapped per extrinsic will miscount this call: it emits one per leg. The flush that runs first is not part of the atomic unit, so a failed call can still have credited queued dividends to the fund.

Example Scenario

A validator submits three legs: subnet 3 to 7, subnet 9 to 7, and subnet 12 to root. The first two fill and the third misses its min_amount_out. The call fails with BasketMinOutNotMet and none of the three legs applies, but the pending deposits flushed at the start stay credited.

Common Questions

Which leg failed?
The call returns the error of the failing leg. Before execution, legs are checked in order and the first invalid one fails the call. During execution, the first leg that breaks a bound rolls back all of them.
Are the bounds checked per leg or for the whole batch?
Per leg, against the state left by the legs before it. The turnover bucket is shared, so later legs see what earlier ones used.

Use Cases

  • Rebalance a fund across many subnets in one extrinsic instead of one swap_basket per leg
  • Make a multi-subnet rotation atomic, so a later leg failing cannot leave the fund half-moved
  • Attribute a burst of BasketSwapped events in one extrinsic to a single rebalance

From Chain Metadata

Rebalance several legs of one validator basket atomically. Pending dividend deposits are flushed once and the basket is valued once. Each leg then applies the same ownership, availability, slippage, turnover, liquidity, concentration, and caller-floor checks as [`Pallet::swap_basket`]. A failed leg rolls back every trade leg in this call; the preceding dividend flush remains settled, matching the failure behavior of the single-leg call. Each tuple is `(origin_netuid, destination_netuid, amount, min_amount_out)`.

Input Parameters

#NameTypeDescription
0
hotkey
AccountId hotkey: Account address (32 bytes, SS58-encoded) (hex -> SS58)
1
legs
BoundedVec BoundedVec<(NetUid, NetUid, AlphaBalance, u64), ConstU32< MAX_BASKET_SWAP_LEGS>>legs (BoundedVec)

Permissions

Origin
Unknown
Required Role

Permission data inferred from metadata. May be incomplete.

Requirements

  • legs holds at least one leg (otherwise BasketSwapBatchEmpty) and at most 128 (MAX_BASKET_SWAP_LEGS, enforced when the call is decoded)
  • Each leg is (origin_netuid, destination_netuid, amount, min_amount_out)
  • Before anything executes, every leg passes the read-only swap_basket checks in leg order: trading enabled and not frozen, seed migration idle, distinct subnets, caller owns the hotkey or is its BasketTrading proxy, hotkey on root, both subnets exist, SubtokenEnabled on each non-root side, amount above zero. The first failing leg fails the whole call with its own error
  • During execution each leg must hold amount on its origin (NotEnoughStakeToWithdraw), sell for at least InitialMinStake of TAO (AmountTooLow), and clear the price band, min_amount_out, turnover, liquidity and concentration bounds that swap_basket applies (SlippageTooHigh, BasketMinOutNotMet, BasketTurnoverBudgetExceeded, BasketLiquidityCapExceeded, BasketConcentrationCapExceeded)

Effects

Events Emitted

Postconditions

  • Every leg applies in order, or none does: the legs run in one storage transaction and a failing leg rolls back all of them
  • The fund composition changes by the sum of the legs; later legs see the holdings and marks left by earlier ones
  • BasketTradeBucket is debited by the TAO through the middle of every leg
  • Fund shares, staker entitlements and BasketClaimed watermarks are unchanged

Side Effects

  • Flushes the hotkey pending dividend deposits once, BEFORE the leg transaction, so the flush stays settled even when the legs roll back
  • Values the basket once, then updates the two affected marks after each leg instead of re-sweeping every holding
  • Emits one BasketSwapped per leg, in leg order, only after every leg has succeeded
  • Each leg pays the ordinary AMM swap fees and moves the pool prices

Code Examples

// ----------------------------------------------------------------------
// HEADS UP: 1 arg below has a complex type with no usable default.
// Look for `undefined as any` and replace it with real value
// before running — the snippet compiles, but will fail at runtime as-is.
// ----------------------------------------------------------------------
import { createClient, Binary } from "polkadot-api";
import { getWsProvider } from "polkadot-api/ws";
import { sub } from "@polkadot-api/descriptors"; // generated by: npx papi add sub -w wss://entrypoint-finney.opentensor.ai:443

const client = createClient(getWsProvider("wss://entrypoint-finney.opentensor.ai:443"));
const api = client.getTypedApi(sub);

// Build swap_basket_many call (typed, named args)
const hotkey = "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY";
const legs = undefined as any /* BoundedVec — replace with real value */;

const tx = api.tx.SubtensorModule.swap_basket_many({
  hotkey,
  legs,
});

Runtime Info

View Source
Pallet Index
7
Call Index
151
First Version
v470
Current Version
v472