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 by swapping the old basket tokens directly into the new ones (e.g. NVDAon → QQQon) — not selling to USDC and rebuying. In the BYOW model it is self-custody — the user’s wallet signs every transaction and Cesto never holds the key; in the managed model Cesto signs server-side.
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? }.

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