Skip to main content
Baskets are versioned — a creator can publish a new version that changes the token mix. Rebalancing migrates a wallet’s position from the version it currently holds to the basket’s latest version. Both models converge on the same target — the latest version’s allocations, measured against what the position actually holds today rather than what it held at open — but they get there differently:
  • BYOW swaps the old basket tokens directly into the new ones (e.g. NVDAon → QQQon) in a single batch, because the user signs every transaction up front. It is self-custody — Cesto never holds the key.
  • Managed runs the migration in two server-signed phases: sell the old tokens to USDC, then buy the new ones from those proceeds. Only the sell proceeds are spent, so no other USDC in the wallet is touched.
The route differs; the resulting allocation does not.
Examples on this page move real mainnet funds. A rebalance swaps real holdings on Solana mainnet — test with small amounts first.

Existing Cesto users

When the wallet belongs to a Cesto account holder, the user approves once by signing a message and Cesto runs the rebalance with their Cesto wallet — no per-transaction signing. Same rebalance.start as the managed flow, plus a consent field:
What the user approves here is different in one way that matters. The message says “migrate my position in this basket to whatever its latest version is.” The target version is deliberately not part of the approval, because the backend resolves it when the job is enqueued. A user approving a rebalance is approving the move, not a specific destination version.
As on the open path, positions.getExecution / waitForExecution cannot see executions started with consent — they are scoped to the issuing API key, and these are recorded against the user. Track the outcome with getHoldings instead.
Like the open version, it resolves as soon as the job is queued and is never retried automatically. Unlike prepare, it is not limited to swap-only baskets — this path runs the same machinery as the Cesto app, so prediction and perps baskets rebalance here too.

Previewing a rebalance

rebalance.getAvailability is a read (works with a read-only key, and accepts any Solana address — BYOW or managed). Use it to show the user what a rebalance would do before they commit — the target version, the token diffs, and whether they’re eligible.
When available is false, reason explains why (e.g. no_positions, no_newer_version, rebalance_in_progress). When eligible is false, eligibilityErrors lists the blockers (and ineligibleReason is populated when the invested amount is below the new version’s minimum).

rebalance.getAvailability(params)

string
required
Solana address of the wallet whose position would be rebalanced.
string
required
Basket to rebalance — product id or slug.
Returns the availability preview: { available, reason?, currentVersionId?, targetVersionId?, currentVersionLabel?, targetVersionLabel?, tokensToSell, tokensToBuy, tokensUnchanged, positions, totalInvested, estimatedPlatformFee, eligible, eligibilityErrors?, ineligibleReason? }. estimatedPlatformFee is in the basket’s input-token base units (micro-USDC for a USDC basket), charged on the value the rebalance would MOVE rather than on the whole position — so correcting a small drift on a large position costs little. Treat it as a preview: the amount actually charged is computed from realised turnover at execution time.

Rebalance history

rebalance.getHistory lists the wallet’s past rebalances of the basket — each version move, the swaps, fees, and on-chain signatures.
getHistory takes the same { wallet, product } params as getAvailability and returns { history }.

Execution results

Terminal statuses are COMPLETED, PARTIALLY_COMPLETED, and FAILED — exactly as for an open, in both models.
Partial completion is real. A rebalance is several independent swaps with no atomicity across them. If a swap leg fails, the execution ends PARTIALLY_COMPLETED and result.transactions shows which legs landed (with signatures) and which failed. To finish the migration, simply rebalance again (rebalance.start / rebalance.prepare) — it re-reads the wallet’s current on-chain balances and targets only the tokens that still need migrating. There are no rounds to resume.
To read the resulting position, use positions.getHoldings — SDK positions are self-custody and do not appear in positions.list.

Constraints

  • Swap-only baskets. No prediction markets, lending, or perps.
  • Needs a position on an older version and a newer version published for the basket — otherwise getAvailability returns available: false and (BYOW) prepare returns done: true.
  • One in-flight execution per wallet + product.
  • Gas sponsored — Cesto pays network gas + ATA rent; the wallet needs no SOL (unless the deployment runs without a sponsor).