Native SOL scenario
A worked, self-contained guide to acting on native SOL — as opposed to an SPL token like USDC. It covers a plain SOL transfer, and staking/unstaking native SOL through the Jito SPL Stake Pool (native SOL in, native SOL out, minting and burning the jitoSOL liquid-staking token). Each example is shown in both execution modes: steps-only (the intermediary already holds SOL / jitoSOL) and quote-with-steps (bridge SOL in, or bridge the proceeds out, in one signed request).
How native SOL differs from an SPL token
Native SOL is not an SPL token, and several rules change because of it:
No token account. SOL lives directly in the account's lamport balance — there is no associated token account (ATA) to derive or create. SOL is moved with an explicit System
Transferinstruction (programId11111111111111111111111111111111, discriminator02000000, au64lamportsarg, accounts[from, to]). There is novalue/lamports field on a step.No wrapping. The service does not wrap SOL into wrapped-SOL. The Jito stake pool takes native SOL in and out directly, which is why it — not a wrapped-SOL Kamino reserve — backs the native flow here.
9 decimals. 1 SOL =
1000000000lamports. (jitoSOL, an SPL token, also has 9 decimals.)The network fee is charged in native SOL (lamports), debited from the intermediary — see Fees and the rent reserve.
Exempt from the "must touch the destination token" guard. A steps-only SPL destination must include a step referencing the dest mint or its ATA. A native-SOL destination has no mint, so this check does not apply.
The liquid-staking token is an SPL token. jitoSOL is a normal SPL token, so the intermediary's jitoSOL ATA is a real ATA the service creates in-message if it's missing (billing its one-time rent only when it was). Only the SOL side needs no account.
Everything else — the request envelope, signing with your origin wallet, and the submit call — is the same as for any destination. For the conceptual overview and the two-mode contrast (steps-only vs quote-with-steps), see Using a Solana destination. The mechanics mirror Kamino stake and Kamino withdraw.
Addresses (Jito SPL Stake Pool, Solana mainnet)
Read off-chain 2026-07. The stake-pool accounts are specific to the Jito pool. The programs and sysvars are network constants.
SPL Stake Pool program
SPoo1Ku8WFXoNDMHPsrGSTSG1Y47rzgn41SLUNakuHy
Stake pool (Jito)
Jito4APyf642JPZPx3hGc6WWJ8zPKtRbRs4P815Awbb
Pool withdraw authority (PDA)
6iQKfEyhr3bZMotVkW6beNZz5CPAkiwvgV2CTje9pVSS
Reserve stake account
BgKUXdS29YcHCFrPm5M8oLHiTzZaMDjsebggjoaQ6KFL
Pool mint (jitoSOL)
J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn
Manager fee account
8yoigZfzZ1nNaadumY9uPVD118225UYHTDpmjpr2nrSa
System program
11111111111111111111111111111111
Stake program
Stake11111111111111111111111111111111111111
Clock sysvar
SysvarC1ock11111111111111111111111111111111
Stake history sysvar
SysvarStakeHistory1111111111111111111111111
SPL Token program
TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA
The one address you supply yourself for the stake-pool flows is:
<intermediary jitoSOL ATA>— the intermediary's associated token account for the jitoSOL pool mint. Derive it from your resolved Solana intermediary and the pool mint (a PDA of[ intermediary, TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA, J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn ]underATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL). Fetch the resolved intermediary fromGET /api/v1/executions/{wallet}/intermediary→result.solana.
The SPL Stake Pool discriminators are single-byte Borsh enum variant indices:
DepositSol
14
0e
WithdrawSol
16
10
System Transfer
—
02000000
DepositSol's sol-deposit authority and WithdrawSol's sol-withdraw authority are both None (permissionless) on this pool, so the optional trailing authority account is omitted from both instructions.
Example A — transfer native SOL (steps-only)
Moving SOL out of the intermediary is a single System Transfer. No ATA, no create step — the recipient is a plain wallet address.
That's one create request (plus submit). Because the fee is charged in SOL, keep an amount that leaves the intermediary with the fee plus its rent-exempt minimum — or use the three-request carve (Example B, step 1b) to size it exactly.
Example B — stake native SOL (Jito DepositSol)
DepositSol takes native SOL from the intermediary and mints jitoSOL into the intermediary's jitoSOL ATA. A single instruction — no refresh, no lookup tables.
The step
Mode 1 — steps-only (intermediary already holds SOL)
1a. Single-request path (dry: false)
If the amount leaves the intermediary with enough SOL for the fee (and rent), send one dry: false create with lamports set to a concrete value:
1b. Three-request path (adjust the amount by the fee)
To stake close to your whole balance you must leave SOL for the appended fee (and the rent-exempt minimum). Learn the fee, carve the amount, then execute — three requests:
Estimate —
dry: truecreate returnsresult.details.networkFee(lamports):Adjust
lamportstoamount − networkFee(and keep the rent-exempt reserve — see below):500000000 − 15000 = 499985000.Execute the same request with the carved
lamportsanddry: false.
The three requests are dry:true estimate → dry:false execute → submit.
Mode 2 — quote-with-steps (bridge SOL in, then stake)
Bridge SOL in from another chain and stake it in one signed request via the quote-backed create endpoint (requires x-api-key). Set lamports to the {MIN_AMOUNT_OUT} placeholder — the service fills the post-fee bridged amount (after also reserving the rent-exempt minimum for native SOL). This is a bridge-in (execution_mode = quote_with_steps).
EXACT_INPUT vs EXACT_OUTPUT — the step is identical. Only quote.swapType / quote.amount and the deposit differ ({AMOUNT_IN} is out-op only and rejected on a bridge-in):
EXACT_INPUT —
quote.amountis the origin token you send. You deposit exactly that. The service stakesarrived − fees.EXACT_OUTPUT —
quote.amountis the native SOL you want staked (≈ Y). The service grosses the quote up by the fee, and you depositresult.quote.amountIn(origin-token atomic units) to the returned deposit address.
Example C — unstake to native SOL (Jito WithdrawSol)
WithdrawSol burns jitoSOL from the intermediary's jitoSOL ATA and returns native SOL to the intermediary. Note the account order differs from DepositSol and the intermediary appears twice — once as the burn authority, once as the lamport recipient.
The step
Mode 1 — steps-only
Same shape as Example B, Mode 1: one dry: false create for a concrete poolTokens, or the three-request dry:true → dry:false → submit carve when you need to size against the fee. The redeemed SOL lands in the intermediary.
Mode 2 — quote-with-steps (unstake, then bridge SOL out)
To unstake and bridge the SOL out to another chain, use the quote-backed create endpoint with "outOperation": true. A second producer step — a native System Transfer — sends the withdrawn SOL to the 1Click deposit address (a bare wallet for native SOL, not an ATA):
EXACT_INPUT — put any
u64in the producerlamports(e.g. the amount you're withdrawing). The service overwrites it with a deterministicquote.amount − networkFee(known up front — not the withdraw's actual on-chain output). Size theWithdrawSolso the SOL it produces coversquote.amount, or the pre-sign simulation reverts (400). There is no{amount}sentinel on the wire — the only backend placeholders are{INTERMEDIARY},{MIN_AMOUNT_OUT},{DEPOSIT_ADDRESS}, and{AMOUNT_IN}.EXACT_OUTPUT — put the
{AMOUNT_IN}placeholder in the producerlamports. It substitutes to the origin commitmentquote.amountIn + network fee, but the producer's amount is then overwritten toquote.amountIn(the fee is a separate injected transfer). 1Click refunds unused slippage.{AMOUNT_IN}is valid only in this position.
An out-operation signs at creation and has no deposit phase.
Fees and the rent reserve
The fee is native SOL. For a native-SOL action the service uses its 2-signer gas model (a backend account that is both fee payer and durable-nonce authority, plus your intermediary). The service pays the on-chain SOL gas, then appends a native-lamport
Transferto its service-fee address that debits the intermediary. So the fee comes out of the intermediary's SOL balance — which is why staking or transferring your whole balance fails, and why the three-request carve exists.Rent-exempt reserve. A system account can't be left in the sub-rent "dust" band. On a bridge-in the service reserves the rent-exempt minimum as well as the fee when it resolves
{MIN_AMOUNT_OUT}. For a steps-only action you must leave that reserve yourself — size the amount tobalance − networkFee − rentReserve.Do not add compute-budget, nonce, or fee instructions — the service injects those. A
dry: truepreview omits those injected instructions. Steps-only and out-operation echoes are otherwise your submitted steps verbatim, while a bridge-in echo additionally resolves{MIN_AMOUNT_OUT}to its post-fee value. A real create returns the exact transaction the service signs.
Sign and submit
The create response returns, under result.details, a payload to sign and a signingStandard — your origin wallet's standard. Sign result.details.payload with your origin wallet exactly as for any destination — you never handle the inner Solana message — then submit:
The submit body is { signature, executionId } for an EVM or Tron origin. An ed25519 origin (Solana, NEAR, or Stellar) must also include publicKey. A TON origin includes both publicKey and a tonConnect envelope, so its body is { signature, executionId, publicKey, tonConnect }.
Last updated
