One-call flow (you hold the keypair)
When your backend controls the signing keypair — bots, agents, services you custody —open.execute runs the whole flow in one call. You supply a signTransactions callback,
so the SDK never touches the private key:
Split flow (browser wallet signs)
In a web app the SDK runs on your backend, but the signatures come from the user’s wallet in the browser. Use the lower-level methods and hop between the two:1
Backend: prepare
Cesto builds the unsigned transactions and retains the canonical bytes for ~60 seconds.Send
prepared.transactions to the browser immediately — the set expires ~60s after
prepare. Slippage uses the platform default; there is no slippage parameter.2
Browser: the user signs
Sign the transactions as-is — any modified byte is rejected at submit.
3
Backend: submit and wait
Method reference
open.prepare(params)
Solana address of the wallet opening the position — it signs and pays for every transaction.
Basket to open, by slug (a product id also works).
Amount to invest, in input-token base units. Serialized as a decimal string on the wire.
{ executionId, transactions, expiresAt }.
positions.submit(params)
Execution from the prepare response.
Every prepared transaction, signed by the wallet (base64).
submit is single-use and never retried automatically. If it times out client-side
it may still have been accepted — poll getExecution instead of re-sending. An expired
(~60s), replaced, or already-submitted preparation fails with a 409; run prepare again.positions.getExecution(executionId) / positions.waitForExecution(executionId, options?)
getExecution fetches the current status with per-transaction outcomes. waitForExecution
polls it until a terminal status and throws a CestoError on timeout (the execution keeps
running server-side).
Delay between status polls.
Max total wait before throwing.
Execution results
Terminal statuses areCOMPLETED, PARTIALLY_COMPLETED, and FAILED.
To read the resulting position, use
positions.getPosition — SDK positions
are self-custody and do not appear in positions.getPositions.
Common errors
Security model
- Ownership proof = the signatures. Only the wallet holder can produce a valid signature over the prepared message bytes; prepare takes the address only and creates nothing durable.
- Tamper-proof submit. Cesto keeps the canonical unsigned transactions it built. On submit, each transaction’s message bytes must be byte-identical — only signatures may be added. Any changed instruction, account, or fee payer is rejected.
- Single-use, short-lived preparations. One submit per prepare, ~60s expiry, and a new prepare for the same wallet + product replaces the previous one.
- Quotes locked at prepare. The swap quote is baked into the prepared transaction; slippage tolerance protects against drift between prepare and submit.
Constraints
- Open and close only — no rebalance via the SDK.
- Swap-only baskets (one or more tokens). No prediction markets, lending, or perps.
- One in-flight execution per wallet + product.
- User pays gas — the wallet must hold SOL.