Wrapped SOL
Wrapped SOL (wSOL) is native SOL held inside an SPL token account. DeFi programs expect SPL token CPIs, so SOL is temporarily wrapped, used, then unwrapped back to native lamports.
Recipe
spl-token wrap 1.5
spl-token unwrap <WSOL_TOKEN_ACCOUNT>use anchor_lang::system_program;
use anchor_spl::token::{self, sync_native, TokenAccount};
// Deposit lamports into WSOL ATA, then sync_nativeWhen to reach for this:
- Paying or swapping on AMMs that only accept SPL tokens
- Pool deposits where one side is SOL
- Programs that unify SOL and SPL in one transfer code path
- Closing WSOL ATA to reclaim SOL after DeFi interaction
Working Example
solana balance
spl-token address --token So11111111111111111111111111111111111111112
spl-token wrap 0.5
spl-token balance So11111111111111111111111111111111111111112
spl-token unwrap <WSOL_ATA_ADDRESS>
solana balanceuse anchor_lang::prelude::*;
use anchor_spl::token::{self, Token, TokenAccount, SyncNative};
pub const WSOL_MINT: Pubkey = anchor_spl::token::spl_token::native_mint::ID;
#[derive(Accounts)]
pub struct WrapSol<'info> {
#[account(mut)]
pub payer: Signer<'info>,
#[account(
mut,
constraint = wsol_ata.mint == WSOL_MINT @ ErrorCode::NotWsol,
)]
pub wsol_ata: Account<'info, TokenAccount>,
pub token_program: Program<'info, Token>,
pub system_program: Program<'info, System>,
}
pub fn wrap_sol(ctx: Context<WrapSol>, lamports: u64) -> Result<()> {
system_program::transfer(
CpiContext::new(
ctx.accounts.system_program.to_account_info(),
system_program::Transfer {
from: ctx.accounts.payer.to_account_info(),
to: ctx.accounts.wsol_ata.to_account_info(),
},
),
lamports,
)?;
token::sync_native(CpiContext::new(
ctx.accounts.token_program.to_account_info(),
SyncNative {
account: ctx.accounts.wsol_ata.to_account_info(),
},
))?;
Ok(())
}
#[error_code]
pub enum ErrorCode {
NotWsol,
}import { createSolanaRpc, address } from "@solana/kit";
const WSOL_MINT = address("So11111111111111111111111111111111111111112");
const rpc = createSolanaRpc("https://api.devnet.solana.com");
// Many DEX SDKs auto-wrap SOL; manual flows use syncNative instruction after lamport transferWhat this demonstrates:
- WSOL mint is the well-known native mint pubkey
- Lamports transfer to token account +
sync_nativeupdates SPL balance - Unwrap closes or drains WSOL ATA back to native SOL wallet
Deep Dive
How It Works
- Native mint address:
So11111111111111111111111111111111111111112 - Token account can hold lamports directly;
sync_nativereconciles SPL amount field - 9 decimals - matches SOL lamport precision
- AMMs often create WSOL ATA, wrap, swap, unwrap in one transaction
Wrap vs Native SOL
| Form | Account type | Used by |
|---|---|---|
| Native SOL | System-owned wallet | Transfers, fees, staking |
| wSOL | SPL token account (same mint) | AMMs, unified token vaults |
Rust Notes
// Close WSOL account to unwrap - returns lamports to destination
use anchor_spl::token::CloseAccount;
// Ensure amount is 0 or use withdraw pattern per your SDKGotchas
- Forgetting sync_native - lamports sit in account but SPL amount is 0. Fix: always
sync_nativeafter lamport deposit. - Using wrong mint - only native mint is WSOL. Fix: compare to
native_mint::ID. - Leaving dust in WSOL ATA - locked lamports until unwrapped. Fix: unwrap or close after DeFi flow.
- Paying rent twice - WSOL ATA needs rent-exempt funding. Fix: reuse persistent WSOL ATA per wallet.
- Assuming programs accept native SOL - many only CPI SPL transfer. Fix: wrap or use program's native deposit instruction.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| Native SOL transfer | Simple payments | AMM requires SPL CPI |
Program deposit_sol helper | Protocol abstracts wrap | Custom instruction set |
| wSOL ATA persistent | Frequent trader | One-off payment |
FAQs
What is wrapped SOL?
SOL deposited into an SPL token account for the native mint, making it behave like any SPL token in programs.
What is the WSOL mint address?
So11111111111111111111111111111111111111112 on all clusters.
Why wrap SOL at all?
SPL Token program CPIs cannot move native lamports from wallets without wrapping pattern.
How many decimals does WSOL use?
9, matching lamports per SOL.
Does wrap reduce wallet SOL balance?
Yes. Lamports move into WSOL token account (plus fees).
What does sync_native do?
Updates token account amount field to match lamports held by the account.
Can I send WSOL like any SPL token?
Yes. Standard transfer between WSOL token accounts.
How do I unwrap?
spl-token unwrap or close/drain WSOL ATA per SDK patterns.
Do Jupiter swaps auto-wrap?
Yes for most SOL-input swaps - SDK handles wrap/unwrap in transaction.
Is WSOL mint authority revocable?
Native mint is protocol-defined - not a typical launch mint.
Can programs hold WSOL?
Yes in PDA-owned WSOL ATAs with proper signer seeds on transfer.
WSOL on Token-2022?
WSOL uses classic native mint on Token program, not Token-2022.
Related
- Associated Token Accounts (ATAs) - WSOL ATA
- Decimals & Amounts - 9 decimals
- SPL Token in Programs - sync_native CPI
Stack versions: This page was written for Agave 4.1.1, Solana CLI 3.0.10, Anchor 0.32.1, anchor-lang 0.32.1, Rust 1.91.1, @solana/kit 7.0.0, Surfpool 0.12.0, and LiteSVM 0.6.x.