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

PDASeedsPurpose
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.
TypeScript
import { findVaultPda, findMintAuthPda, findConfigPda, fetchWrapFeeLamports } from "@p15/blind-mode";
 
const fee = await fetchWrapFeeLamports(rpc); // current per-wrap fee

Instructions

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.

TypeScript
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.

TypeScript
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).