Orca

Orca Whirlpools price limits and partial fills

Orca Whirlpools on Solana can stop an exact-input swap at an explicit price limit before spending the full input amount. That partial fill can succeed if its actual output meets the separate minimum-output requirement and all other checks pass. The unused input stays in the source token account.

The pool price boundary behind a partial fill

A Whirlpool price limit constrains the pool price reached during a swap through its concentrated liquidity. The program processes successive price intervals, calculating input, output, and swap fees within each interval. Crossing an initialized tick applies its net liquidity change before calculations continue. Liquidity providers choose position ranges, so total deposits alone do not describe the liquidity accessible before a particular boundary.

Processing stops when the specified amount runs out or the pool reaches the limit. If the limit arrives first during an input-specified swap, the calculated input can fall below the requested amount. The boundary constrains the endpoint of the pool’s price movement. The average exchange rate also reflects the earlier intervals and fees, so the endpoint does not describe every token’s execution rate.

Can a partial fill pass the minimum-output check?

A partial fill can pass when its actual output equals or exceeds the minimum output encoded in the swap instruction. The price boundary and the output threshold test different properties.

An absolute output requirement

In exact-input mode, other_amount_threshold specifies a minimum quantity of the output token. The program compares that threshold with the calculated output after any applicable output-token transfer fee. It does not automatically reduce the threshold in proportion to the input left unspent. A boundary-limited fill can therefore produce an acceptable exchange rate while failing the transaction’s required output quantity.

The boundary can stop a fill without approving it

The price limit governs how far swap calculations proceed; the threshold determines whether their result qualifies for execution. Lowering the minimum output accepts a smaller quantity without adding liquidity or moving the pool boundary.

Input mode and output mode change the contract

The swap’s amount-mode flag determines which token the requested amount measures and which side the separate threshold protects. Its setting matters even when the tokens and price limit stay unchanged.

A specified input amount

With amount_specified_is_input set to true, amount describes the input budget. Swap calculations subtract both exchanged input and trading fees from that budget. A successful partial fill spends less than the budget while satisfying the minimum-output condition. The term exact input identifies this calculation mode; an explicit price boundary can still prevent full consumption.

A specified output amount

With the flag set to false, amount describes the requested output, and the threshold caps input. The program rejects an incomplete exact-output swap when the instruction uses the no-explicit-limit sentinel. An explicit price limit can permit incomplete output instead, subject to other checks. An integration requiring the entire output target needs enforcement matching that requirement; the maximum-input threshold alone does not enforce it.

Encoding the limit in the correct direction

The program encodes its price boundary as sqrt_price_limit, a square-root price in Q64.64 fixed-point form. Conversion from a displayed token price depends on the pool’s token ordering and both mints’ decimal counts. Entering a familiar decimal price directly into this integer field produces a different value. Reversing the displayed pair also changes the interpretation.

For a token A-to-token B swap, a valid explicit limit lies below the starting square-root price. The reverse direction requires a limit above it. Equality fails the direction check, and the program also enforces its supported price bounds. Direction follows the pool’s token A and token B definitions, not whichever asset a screen lists first.

Graphic: Orca - Encoding the limit in the correct direction

Open full-size image

What happens to the unspent input?

Unspent input remains in the source token account for a direct, successful Whirlpool swap. The transfer uses the calculated debit, so the instruction does not take the whole requested amount and later refund the difference. Within that instruction, unused input equals the requested budget less the actual gross debit. The account’s final balance also depends on its starting balance and any other instructions in the transaction. Account creation, cleanup, or wrapping instructions require separate accounting when an integration includes them.

Quote estimates and instruction parameters can diverge

A swap quote estimates execution from the pool state and accounts available to its calculation. The signed instruction carries the actual constraints the program enforces.

Price movement after quoting

Another swap or a liquidity change can alter the state before execution. Estimated input, output, ending price, and fees therefore describe the quote’s assumptions. A changed state can produce a different fill or a rejection. Changing the encoded price limit without recalculating its associated output estimate and threshold creates a mismatch between the preview and the transaction.

Graphic: Orca: Price movement after quoting

Open full-size image

Helper behavior and protocol capability

The Rust SDK’s high-level swap builder sets the price-limit field to zero. The program interprets zero as no explicit boundary and substitutes its directional price extreme. Lower-level instructions accept an explicit boundary. A slippage setting alone does not establish which boundary an interface encodes, and protocol support does not establish whether a particular interface exposes that control.

Keeping the boundary or revising the remaining swap

Consider a hypothetical direct exact-input swap with a chosen price boundary and minimum output. Its normal outcome consumes the input budget before reaching the boundary. The narrower case here reaches the boundary first but produces enough output to pass the threshold. Successful execution settles that smaller fill. Before submission, changing the draft changes no balances; after settlement, another trade has its own price movement and costs.

The remaining input presents two choices. Keeping the original boundary preserves the chosen price restriction, but a pool already at that boundary needs favorable movement before another valid swap can start under it. Moving the boundary farther in the swap direction permits a fresh calculation across additional price movement. Neither choice fixes the cost of completion: usable liquidity, input quantity, pool fees, and any applicable token transfer fees determine the new quote.

  • If the first transaction’s outcome remains unresolved, establish its status before calculating a replacement amount.
  • If it settled partially, use the actual gross input debit to determine the unspent part of the original budget.
  • If the original boundary equals the pool’s starting price, the direction check rejects another swap under that same limit.
  • If a revised boundary allows further price movement, recalculate the output estimate and minimum-output requirement together.
  • If a later swap succeeds, reconcile its token movements separately before combining totals with the first fill.
Illustration: Orca: Keeping the boundary or revising the remaining swap

Open full-size image

A later transaction does not reverse the first successful fill. Completion of the intended swap requires settled input debits and the output credits those trades produced. A remaining balance alone cannot explain whether an earlier transaction succeeded, failed, or never landed.

Execution amounts and fee deductions

The swap’s Traded event records input, output, pool price before and after execution, and fee fields. Transaction token balances provide the account-level changes. In SwapV2 exact-input mode, the program checks minimum output after deducting any applicable output-token transfer fee. Its event can therefore report a gross output amount exceeding the spendable credit. The same instruction adjusts the gross input debit for applicable transfer fees when the pool calculation consumes only part of the available input.

Pool trading fees belong within the executed input accounting. Adding them again to a gross debit double counts them. Network fees occupy a separate transaction-level balance change. A partial fill consequently need not reduce every cost in the same proportion as the input it spends.

Transaction rejection and route boundaries

Solana executes a transaction atomically: an instruction failure rolls back its state changes, although transaction fees can remain. A minimum-output rejection therefore does not leave the rejected swap’s calculated partial fill settled. Likewise, missing or unsuitable tick-array accounts can prevent execution even with a valid price boundary. Tick arrays supply the liquidity information the swap needs along its path; a price limit does not replace them.

A routed transaction may impose additional constraints beyond a single Whirlpool instruction. Its final result follows the entire transaction’s checks. A transaction signature identifies a transaction, while successful confirmation and execution records establish its outcome. A pool calculation or preview alone cannot establish a completed fill.

Things people ask

Is a price-limited Whirlpool swap a standing limit order?

A direct price-limited swap executes against the pool state during that transaction; it does not create a standing order for unused input. The price boundary stops execution within the swap. Keeping the remaining tokens in the source account creates no instruction to trade them automatically when the price changes. A later swap requires another transaction.

Must the price limit align with an initialized tick?

An explicit square-root price limit can fall between initialized ticks. The swap calculation chooses the nearer boundary between the next relevant tick price and the supplied limit. The limit must still satisfy the supported price bounds and trade-direction check. Tick spacing governs liquidity-position boundaries, so it does not require every swap price limit to coincide with an initialized tick.

Can separate price limits govern both hops of a swap?

The Whirlpools two-hop instruction accepts a separate square-root price limit for each pool. In its original token-program form, the first hop’s calculated output must equal the second hop’s calculated input. If a boundary causes those intermediate quantities to differ, the instruction rejects the route. Single-pool partial-fill behavior therefore does not guarantee that a two-hop transaction will accept an incomplete fill.

How can a remaining token amount become too small to swap?

Integer rounding and applicable fees can leave a small input unable to produce usable output. Token amounts use integer base units, and pool prices determine their exchange relationship. SwapV2 rejects the swap if an input-token transfer fee reduces the tradable input to zero; a positive minimum-output requirement can also reject a calculation producing no output. There is no single token-independent remainder size that guarantees a successful swap.

When can a pool reject a price-limited swap before trading starts?

A pool with an initialized oracle account rejects trading before its configured trade-enable timestamp. The program compares that timestamp with the transaction’s on-chain clock and can return TradeIsNotEnabled. A valid price limit does not bypass this condition. This timestamp check applies through that oracle configuration; it does not establish a universal waiting period for every Whirlpool.

Does changing a price limit require new transaction signatures?

Changing an instruction’s price limit changes the transaction message and requires signatures for the revised message. Existing signatures authorize the earlier contents. An interface can revise an unsigned draft, but modifying a signed transaction invalidates its existing authorization. A new signature also says nothing about whether an earlier submitted transaction landed; its status remains a separate question.

Which Token-2022 feature can reject an otherwise acceptable partial fill?

A token’s transfer hook can impose transfer requirements beyond the pool’s price and amount checks. SwapV2 supports supplying the additional accounts these hooks require. Missing required accounts or a hook rejecting the transfer can make the transaction fail even when its swap calculation passes. Compatibility therefore depends on the token’s extensions and the integration’s account support, not only its price-limit setting.

Updated