psol-wrapper
A trustless 1:1 wrapper between native SOL and pSOL, a program-minted
Token-2022 wrapper. Program id 5LQUmd355dcXkETDheBXavpaBzJq4JFWLb9mfkku31ZR
(devnet).
Not on the active path. The current note-model dApp wraps SOL as native wrapped SOL on the legacy Token program (create wSOL ATA, transfer,
SyncNative,CloseAccount), not as pSOL. psol-wrapper is from the earlier Token-2022 Confidential Transfer design and is documented for completeness. If you are integrating today, you wrap SOL natively; see Quickstart step 4.
What pSOL is
pSOL is a Token-2022 mint with 9 decimals (so base units equal lamports) whose
mint authority is this program's mint-auth PDA. That means this program is
the only thing that can ever mint pSOL, and every pSOL in existence is backed 1:1
by lamports locked in the program's vault PDA. Mint id
Bfp9F8zcfQWvm3jWsBq7BYR6WYXr21FgJR1qzhfksKR4.
PDAs and config
| PDA | Seeds | Purpose |
|---|---|---|
| vault | ["vault"] | A plain system account holding the wrapped lamports. |
| mint authority | ["mint-auth"] | Signs MintTo; is the pSOL mint authority. |
| config | ["config"] | Stores psol_mint, wrap_fee_lamports, fee_treasury. |
import { findVaultPda, findMintAuthPda, findConfigPda, fetchWrapFeeLamports } from "@p15/blind-mode";
const fee = await fetchWrapFeeLamports(rpc); // current per-wrap feeInstructions
initialize
One-time setup. Registers the pSOL mint in the config PDA and records the fee
treasury. It asserts the mint's authority is the mint-auth PDA, so the program
cannot be pointed at a mint it does not control. Run once after deploy; the SDK's
psol:init script does this.
wrap
Locks amount lamports in the vault and mints amount pSOL to the user's token
account. A flat protocol fee (config.wrap_fee_lamports, default 0.01 SOL) is
debited separately from principal and paid straight to config.fee_treasury.
The user is debited amount + fee and receives the full amount of pSOL. Emits a
WrapEvent.
Args: amount: u64.
Accounts (in order): user (signer, writable), vault (writable), fee_treasury (writable, pinned to config), config, psol_mint (writable), user_token (writable), mint_auth, token program (Token-2022), system program.
import { buildWrapInstruction } from "@p15/blind-mode";
const wrap = await buildWrapInstruction({ user, amount, userToken });unwrap
Burns amount pSOL from the user's token account and releases the same amount
of lamports from the vault to a destination. No fee, and the 1:1 invariant is
preserved.
Args: amount: u64.
Accounts (in order): user (signer, writable), vault (writable), config, psol_mint (writable), user_token (writable), destination (writable), token program, system program.
import { buildUnwrapInstruction } from "@p15/blind-mode";
const unwrap = await buildUnwrapInstruction({ user, amount, userToken, destination });In the wallet flow
You usually do not call these directly. "Make private" wraps SOL into the wallet's
wSOL ATA and deposits it into note-vault in a single transaction; withdraw releases
wSOL and unwraps it back to native SOL. The dApp uses the native
@solana-program/token wSOL path (create ATA, transfer, SyncNative,
CloseAccount) for SOL specifically; the psol-wrapper builders above are for the
pSOL mint when you want the program-minted wrapper instead of native wSOL.
Errors
ZeroAmount, WrongMint (mint does not match the registered pSOL mint),
BadMintAuthority (mint authority is not the mint-auth PDA), WrongTreasury
(fee treasury does not match config).