orca_open_position
This instruction creates an empty Orca Whirlpool position that the Sickle owns.
The position holds no liquidity after this instruction. Add liquidity with
orca_increase_liquidity in a later instruction or in the same transaction.
Who can call it
The owner. The owner signs as authority. A manager or a keeper cannot call
this instruction.
A second signer, the funder, pays the rent for the new accounts. The funder can be any wallet.
Arguments
| Name | Type | Meaning |
|---|---|---|
tick_lower_index | i32 | The lower tick of the price range. |
tick_upper_index | i32 | The upper tick of the price range. |
with_token_metadata | bool | If true, Orca writes metadata for the position NFT. Pass true. |
The tick range must satisfy these limits.
| Limit | Value |
|---|---|
Minimum tick_lower_index | -443,636 |
Maximum tick_upper_index | 443,636 |
tick_lower_index < tick_upper_index | required |
Orca applies two rules of its own that the program does not check. Each tick
must be a multiple of the pool tick spacing. A pool whose tick spacing is
32,768 or more accepts only its exact full range. Orca rejects the transaction
if either rule fails. The builder's buildOpenRange satisfies both.
Accounts
| # | Name | Signer | Writable | Meaning |
|---|---|---|---|---|
| 0 | funder | yes | yes | Pays the rent for the position and the position mint. |
| 1 | authority | yes | no | The owner. |
| 2 | global_config | no | no | The global config. Seeds: ["global_config"]. |
| 3 | sickle | no | no | The Sickle. Seeds: ["sickle", owner]. |
| 4 | whirlpool_program | no | no | The Orca Whirlpools program. |
| 5 | whirlpool | no | no | The Orca pool. |
| 6 | position | no | yes | The new position. Orca seeds: ["position", position_mint]. |
| 7 | position_mint | yes | yes | A new keypair. It becomes the Token-2022 mint of the position NFT. |
| 8 | position_token_account | no | yes | The Sickle's Token-2022 associated token account for position_mint. |
| 9 | token_2022_program | no | no | The Token-2022 program. |
| 10 | system_program | no | no | The System program. |
| 11 | associated_token_program | no | no | The Associated Token program. |
| 12 | metadata_update_auth | no | no | Orca's fixed metadata authority, 3axbTs2z5GBy6usVbNVoqEgZMng3vZvMnAoX29BFfwhr. |
| 13 | event_authority | no | no | The program's event authority. Seeds: ["__event_authority"]. |
| 14 | program | no | no | The Sickle program. |
The builder derives accounts 2, 3, 6, 8, 12, 13, and 14. You supply the rest.
What the program checks
The program runs these checks in this order.
- The program is not paused. Otherwise it returns
Paused. whirlpool_programequals the Orca entry in the allowlist. Otherwise it returnsProtocolNotAllowed.authorityequals the owner of the Sickle. Otherwise it returnsUnauthorized.whirlpoolis owned bywhirlpool_program. Otherwise it returnsInvalidAccountOwner.- The tick range satisfies the limits above. Otherwise it returns
InvalidTickRange. positionequals the address Orca derives fromposition_mint. Otherwise it returnsInvalidPosition.position_token_accountequals the Sickle's Token-2022 associated token account forposition_mint. Otherwise it returnsInvalidPosition.metadata_update_authequals Orca's fixed authority. Otherwise it returnsInvalidMetadataUpdateAuth.
After the Orca call, the program reads the new accounts and checks the
receipt (venue/open_increase.rs:102-151):
- the position, mint, and token account addresses equal the derived ones;
- the position's pool equals
whirlpool, its tick range equals the request, and it holds no liquidity; - the position account is owned by the Orca program, and the mint and token account are owned by the Token-2022 program;
- the token account balance is exactly 1.
It does not read the token account's authority after the call. The Sickle's
ownership of the NFT rests on the address check in step 7, which derives the
token account from the Sickle as owner. Failures return InvalidPosition,
InvalidProtocolAccountData, or InvalidAccountOwner.
What the program does
- The program calls Orca
open_position_with_token_extensionswith the Sickle as the position owner. - Orca creates the position account, mints one position NFT to
position_token_account, and charges the rent tofunder. - The program reads the new position and emits
PositionOpened.
The Sickle holds the position NFT. The owner does not hold it. Only the Sickle program can move liquidity in or out of the position.
Fees
This instruction charges no fee.
Events
| Event | Fields |
|---|---|
PositionOpened | sickle, protocol, pool, position, tick_lower_index, tick_upper_index |
The program emits the event twice: once as a self-CPI and once as a log line. Decode the self-CPI record. The log line can be truncated.
Errors
| Error | Cause | Fix |
|---|---|---|
Paused | The guardian or the admin paused the program. | Wait for the admin to unpause. |
ProtocolNotAllowed | whirlpool_program is not the allowlisted Orca program. | Pass the Orca Whirlpools program ID. |
Unauthorized | authority is not the owner. | Sign with the owner key. |
InvalidAccountOwner | whirlpool is not an Orca account, or a created account has an unexpected program owner. | Pass a pool account that the Orca program owns. The second case indicates a venue fault. |
InvalidTickRange | The ticks are inverted, equal, or outside the limits. | Pass tick_lower_index < tick_upper_index inside ±443,636. |
InvalidPosition | position or position_token_account has the wrong address. | Use the builder to derive both accounts from position_mint. |
InvalidMetadataUpdateAuth | metadata_update_auth is the wrong key. | Pass 3axbTs2z5GBy6usVbNVoqEgZMng3vZvMnAoX29BFfwhr. |
InvalidProtocolAccountData | Orca returned a position that does not match the request. | Report this. It indicates a program or venue fault. |
Example
import * as anchor from "@anchor-lang/core";
import {
Connection,
Keypair,
PublicKey,
Transaction,
sendAndConfirmTransaction,
} from "@solana/web3.js";
import { getPoolState } from "../scripts/builder/flows";
import { buildOpenPosition } from "../scripts/builder/instructions";
import { buildOpenRange } from "../scripts/builder/orca";
import type { Sickle } from "../target/types/sickle";
declare const connection: Connection;
declare const program: anchor.Program<Sickle>;
declare const owner: Keypair;
declare const poolAddress: PublicKey;
const ORCA_WHIRLPOOL_PROGRAM_ID = new PublicKey(
"whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc",
);
// Every builder input carries the decoded pool. `getPoolState` fetches the
// account and resolves each mint's token program before it decodes.
const pool = await getPoolState(connection, poolAddress);
const positionMint = Keypair.generate();
// Orca rejects a tick that is not a multiple of the pool's tick spacing,
// a range outside its full-range bounds, and anything but the exact full
// range on a pool with tick spacing 32768 or more. `buildOpenRange` asks for
// 10 spacings below and above the live tick and returns a range that
// satisfies all three rules for the decoded pool.
const { tickLowerIndex, tickUpperIndex } = buildOpenRange(
pool.tickCurrentIndex,
pool.tickSpacing,
10,
);
const ix = await buildOpenPosition(program, {
programId: program.programId,
owner: owner.publicKey,
authority: owner.publicKey,
whirlpoolProgram: ORCA_WHIRLPOOL_PROGRAM_ID,
whirlpool: poolAddress,
pool,
funder: owner.publicKey,
positionMint: positionMint.publicKey,
tickLowerIndex,
tickUpperIndex,
});
// The owner and the position mint keypair both sign.
const tx = new Transaction().add(ix);
await sendAndConfirmTransaction(connection, tx, [owner, positionMint]);
Related
orca_increase_liquidityadds liquidity to the position.orca_close_positioncloses an empty position and returns the rent to the owner.set_position_settingslets a keeper act on the position.- Style guide for the rules this page follows.
Design note
The EVM reference mints the position NFT inside its deposit flow. The Solana
program separates the mint from the deposit because an empty open moves no
tokens. See the "Shared CLMM open/increase contract" entry in
CLAUDE.md.