Staking screen map · mobile

Phantom Wallet · PR #24615 · screenshots from PR #23967
Non-liquid (native) Liquid (PSOL) Bridge: Convert Money-mover (this PR) Real review / broadcast Status / spinner Native unstake (kill candidate)
Life of a SOL state machine · on-chain truth
What happens to a SOL between the screens above. Solid edges are user-triggered ix broadcasts; dashed edges are automatic epoch-boundary transitions (~1–2 days each). Includes both Deactivate and Withdraw ix as distinct steps.
tap — user action in the app waits ~2 days — automatic on-chain timer, no app interaction needed ⚠ user must come back to the app after the wait to finish [MONEY MOVER] — money-mover amount screen sits on this edge (3 surfaces in this PR)
stateDiagram-v2 direction TB SOL: SOL in wallet Delegating: Delegating - frozen, warming up Active: Active stake (or Activating) - earning rewards Deactivating: Deactivating - frozen, cooling Inactive: Inactive stake - detail page always shows BOTH Withdraw and Convert (PSOL push deep-links straight to Convert review) PSOL: PSOL - transferable, earns via exchange rate [*] --> SOL SOL --> Delegating: [MONEY MOVER] tap Stake Delegating --> Active: waits ~2 days Active --> Deactivating: tap Unstake Active --> Deactivating: tap Convert to PSOL (step 1 of 2 - same on-chain effect as Unstake) Deactivating --> Inactive: waits ~2 days Inactive --> SOL: tap Withdraw Inactive --> PSOL: tap Convert to PSOL (step 2 - mints PSOL) Inactive --> Delegating: tap Restake SOL --> PSOL: [MONEY MOVER] tap Mint PSOL PSOL --> SOL: [MONEY MOVER] tap Unstake (waits ~2 days) classDef nativeStyle fill:#3a2a14,stroke:#fb923c,color:#fde2c4 classDef liquidStyle fill:#0e2a3a,stroke:#38bdf8,color:#bae6fd classDef wallet fill:#1a1a20,stroke:#82828e,color:#e8e8ee classDef warnState fill:#3a1f25,stroke:#fb7185,color:#fda4af classDef choiceState fill:#3a2f0e,stroke:#fbbf24,color:#fde68a class SOL wallet class Delegating,Deactivating,Active nativeStyle class Inactive choiceState class PSOL liquidStyle
Read one SOL through the machine. Start as SOL → Stake-in (Journey A) → Delegating → ⏱ epoch → Active stake. From Active, the user taps Unstake (or active-mode Convert): Active → Deactivating → ⏱ epoch → Inactive stake. Inactive is a dead-end until the user comes back: they must re-open the app and either (a) tap Withdraw to return to SOL, or (b) re-enter Convert (Bridge inactive-mode) which atomically withdraws + deposits into the stake pool, minting PSOL. Liquid side: SOL → Mint (Journey B) → PSOL. Liquid exit (Journey C) has two paths — the instant withdrawSol path is gated behind the enable-withdraw-sol flag (prod default off); the prod-default withdrawStake path returns a deactivating stake account, dropping the SOL back into the native cooling-down → withdraw cycle.
PM frame. Both red ⚠ MUST RETURN edges and one full epoch wait sit downstream of any Active → Inactive transition — that's the unavoidable cost of leaving native delegation. The only on-chain difference between Journey D Unstake and Bridge active-mode Convert is which screen broadcasts the same StakeProgram.deactivate ix. Killing Journey D's standalone Unstake doesn't change the on-chain SOL lifecycle — it removes one of two screen entry points into the same edge.
Verified against codeuseCreateStakeAccountAndDelegateStake.ts (Journey A combines createAccount + delegate in one tx), useDeactivateStake.ts and useWithdrawStake.ts (Journey D ix), useConvertStakeAccountReviewPageProps.ts + getConvertToPSOLTransaction.ts (Bridge — active branch broadcasts deactivate only; inactive branch bundles StakeProgram.withdraw + stakePool.depositSol atomically), getMintPoolTokenTransaction.ts (Journey B = depositSol), getWithdrawLiquidStakeTransaction.ts + useGetWithdrawLiquidStakeTransaction.ts (Journey C tries withdrawSol only if enable-withdraw-sol flag is on, otherwise falls back to withdrawStake).

User-triggered transitions (ix broadcasts)

  • Stake-in (Journey A): createStakeAccount + delegate → Liquid → Delegating
  • Native Deactivate (Journey D): deactivate ix → Active → Deactivating
  • Native Withdraw (Journey D): withdraw ix → Inactive → Liquid
  • Mint PSOL (Journey B): stake-pool deposit (SOL) → Liquid → PSOL
  • Bridge active-mode: deactivate ix → Active → Deactivating (same as native Deactivate)
  • Bridge inactive-mode: depositStake ix → Inactive → PSOL
  • Liquid unstake (Journey C): withdrawStake ix → PSOL → UnstakingPSOL

Automatic transitions (epoch boundaries)

  • Delegating → Active: stake warm-up. ~1 epoch (~2 days).
  • Deactivating → Inactive: cool-down. ~1 epoch.
  • UnstakingPSOL → Liquid: stake-pool queues a withdrawal that the user can claim after the deactivation cycle. ~1–2 days.

PM frame: the only on-chain difference between Journey D's standalone Unstake and the Bridge's active-mode Convert is what screen the user is on. Both emit the same deactivate ix and wait the same epoch.

Non-liquid native SOL · delegated stake accounts
SOL goes in / out via on-chain delegation. Stake accounts are owned-by-user, deactivated per-epoch. Crosses into the liquid domain via Convert to PSOL only.
A — Stake-in SOL → delegated stake account
SOL detail → More → Stake SOL → Native Staking card.
SOL Detail
→ More menu
testID: unifiedFungibleDetail-stakeSol-cta
StakingMethodSelection
picker
ValidatorList
picker
StakeAmount
money-mover · this PR
StakeReview
real broadcast
CreateAndDelegateStatus
status
D — Native unstake kill candidate
From a delegated stake account. Deactivates the whole account (no amount picker). Withdraw is a separate return trip after the epoch boundary.
StakeAccountsList
picker
StakeAccountDetail
Unstake btnConvert btn ↘
StakeAccountDeactivateStatus
broadcasts deactivate ix · no PR #23967 screenshot
native unstake
StakeAccountWithdrawStatus
return trip after epoch · withdraw raw SOL
status
Kill tradeoff for the PM. Convert (bridge below) already absorbs the deactivate step for active accounts, so removing the standalone Unstake + Withdraw buttons is functionally covered — but a user who wants raw SOL back goes from one epoch wait (deactivate → withdraw) to two epoch waits (Convert deactivate → wait → Convert mint PSOL → liquid unstake → wait).
Bridge — Convert to PSOL non-liquid → liquid
The only way for SOL inside a native delegated stake account to enter the liquid domain. Same Convert review page branches on the source account's activation state — see useConvertStakeAccountReviewPageProps.ts.
StakeAccountDetail → Convert
entry from non-liquid
ConvertToPSOLInfo
info · first run
ConvertStakeAccountList
picker
ConvertStakeAccountReview (branches)
real broadcasttwo-mode

Source = active / activating

Broadcasts deactivate ix only. User waits until the next epoch boundary (~1–2 days), then re-enters Convert to finish. Same on-chain effect as Journey D's standalone Unstake — this is why Convert can subsume Journey D.

ConvertStakeAccountStatus
deactivate only
Re-enter Convert
after epoch ends
deep link supported

Source = inactive (already deactivated)

Broadcasts the stake-pool depositStake ix. The deactivated SOL is consumed by the pool and PSOL is minted to the user's wallet immediately. The user now sits in the liquid domain below.

ConvertStakeAccountStatus
mints PSOL
PSOL in wallet
crosses into liquid domain
Deep link entry: DeepLinkDestination.ConvertStakeAccountConvertStakeAccountDeepLinkPage:
DeepLinkPage
deep link
DeepLinkPage · error
deep link · error
Liquid PSOL · Phantom LST pool
Tokenized stake position — PSOL is a fungible token in the wallet. Entry is direct (Mint PSOL from SOL detail) or via the Bridge above. Exit back to raw SOL goes through liquid unstake.
B — Mint PSOL SOL → PSOL
SOL detail → Mint PSOL CTA, or Stake SOL → Liquid Staking card. Stakes SOL with the Phantom LST pool; mints PSOL in return.
SOL Detail
→ Mint PSOL
testID: unifiedFungibleDetail-mintLST-cta
MintPSOLInfo
info · first run
MintLiquidStakeAmount
money-mover · this PR
MintLiquidStakeReview
real broadcast
MintLiquidStakeStatus
status
Info-page variants by region / poolMintPSOLInfoPage shown; alternates are MintPSOLUKInfoPage, MintJitoSOLInfoPage, PSOLMarketingPage. All funnel into MintLiquidStakeAmountPage.
C — Liquid unstake PSOL → SOL
PSOL detail → More → Unstake. Burns PSOL, queues SOL withdrawal. Only path back to raw SOL from the liquid domain.
PSOL Detail
→ More menu
→ Unstake
testID: unifiedFungibleDetail-unstake-cta
UnstakeLiquidStakeAmount
money-mover · this PR
UnstakeLiquidStakeReview
real broadcast
UnstakeLiquidStakeStatus
status
Money-mover review disclaimer: "Your unstaked PSOL can take up to 2 days to become available in your wallet" (commit 46e30f8).
Feature flag controlling A/B/C amount pages: enable-money-mover-staking · devDefaultValue: true · prod default false.
Real review pages (green) own broadcast; money-mover Confirm (purple) routes to them via navigation.navigate() after commit 96f2622.
Screenshots from PR #23967 · downloaded and self-hosted to avoid GitHub user-attachments JWT expiry.