nos-rescue

Rescue staked NOS — and every other SOL / SPL token — from a compromised Solana wallet, even though the attacker holds the same private key.

⬇ Download nos-rescue.html Open the hosted app Source on GitHub
Wallet just hacked? Do this now:
  1. Download the one-file app and open it in your browser (double-click it — it runs entirely on your machine). We recommend this over the hosted copy: a local file can't silently change under you, and you can verify its checksum.
  2. Create a fresh fee-payer wallet and fund it with a little SOL (~0.1 is plenty).
  3. Pick a safe destination address (a wallet the attacker has no access to — ideally brand new).
  4. Paste the compromised key, the fee-payer key, and the safe address. Click Start rescue and keep the tab open.

What it does

The trick: a separate, uncompromised wallet pays every transaction fee and every rent deposit, so the compromised wallet never needs — and never keeps — any SOL. Without SOL the attacker cannot broadcast a single transaction. The tool then races them:

The three wallets

RoleWhat it isNeeds SOL?
CompromisedThe hacked wallet holding the stake. You provide its key.No
Fee payerA separate, clean wallet you control. Pays all fees + rent.Yes — fund it with some SOL
SafeDestination address. Receives all rescued NOS/SOL/tokens.No

Keys are accepted as a base58 private key or a keypair.json array (Solana CLI format).

Quickstart

Option A — one HTML file (easiest)

  1. Download nos-rescue.html (also attached to every GitHub release).
  2. Double-click it. It opens in your browser and works from file:// — no install, no server, no network access except the Solana RPC you choose.
  3. Fill in the three wallets, optionally tick Dry run first, then Start rescue. Keep the tab open — the watcher loop lives there.

If your browser refuses to run it from file://, serve it locally instead: npx serve in the folder containing the file, then open the printed URL.

Option B — npx (terminal, best for unattended runs)

# Read-only status of any wallet — no keys needed
npx nos-rescue status <WALLET_ADDRESS>

# Full rescue daemon
npx nos-rescue rescue \
  --rpc "https://your-fast-rpc" \
  --safe <SAFE_ADDRESS> \
  --hacked-key ./hacked.json \
  --payer-key ./payer.json

Requires Node.js ≥ 20.18. Run it on a server close to a fast RPC for the best race position.

CLI reference

Global options (all commands)

FlagEnv varMeaning
--rpc <url>NOS_RESCUE_RPCSolana RPC endpoint. A fast private RPC is strongly recommended.
--ws <url>NOS_RESCUE_WSWebsocket endpoint. Default: the RPC URL with ws(s)://.
--network <net>mainnet (default) or devnet.
--hacked-key <key>NOS_RESCUE_HACKED_KEYCompromised wallet: base58 key or keypair.json path.
--payer-key <key>NOS_RESCUE_PAYER_KEYFee payer wallet: base58 key or keypair.json path.
--safe <address>NOS_RESCUE_SAFESafe destination wallet address.
--dry-runSimulate every transaction instead of sending. Nothing moves on-chain.

Keys omitted from flags and env vars are asked for with a hidden interactive prompt — the safest option, since flags land in shell history.

status <owner>

Read-only view of a wallet's SOL/NOS balances, stake account, vesting timeline, and pending rewards. No keys needed.

npx nos-rescue status 9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin

rescue

The full daemon: sweeps continuously (websocket-triggered, interval polling as backup), unstakes, withdraws released NOS on a cadence, closes accounts at full vest, and keeps sweeping forever.

OptionDefaultMeaning
--sweep-interval <s>15Backup poll cadence; websocket events sweep instantly regardless.
--withdraw-interval <s>3600Minimum seconds between partial withdrawals. 0 = only withdraw at full vest.
--no-auto-withdrawNever auto-withdraw; sweep only. Use the withdraw command manually.
--min-withdraw <nos>0Skip partial withdrawals smaller than this many NOS.
export NOS_RESCUE_RPC="https://your-fast-rpc"
export NOS_RESCUE_SAFE="<SAFE_ADDRESS>"
export NOS_RESCUE_HACKED_KEY="<base58 or path>"
export NOS_RESCUE_PAYER_KEY="<base58 or path>"
npx nos-rescue rescue

unstake

One-shot: claims pending rewards to the safe wallet, closes the reward account, and unstakes — all in one atomic transaction.

npx nos-rescue unstake --safe <SAFE> --hacked-key ./hacked.json --payer-key ./payer.json

withdraw

One-shot: withdraws the currently released NOS to the safe wallet, and closes the stake account if fully vested.

npx nos-rescue withdraw --safe <SAFE> --hacked-key ./hacked.json --payer-key ./payer.json

sweep

Moves all SOL and SPL/Token-2022 balances out of the compromised wallet, closing drained token accounts so their rent goes to the safe wallet too. Add --watch to keep watching and sweeping.

npx nos-rescue sweep --watch --interval 15 \
  --safe <SAFE> --hacked-key ./hacked.json --payer-key ./payer.json

Dry run

Add --dry-run to any command to build and sign every transaction exactly as for real, run it through simulateTransaction, and log the outcome — nothing is broadcast. rescue --dry-run does one full pass and exits.

npx nos-rescue --dry-run rescue

RPC recommendations

Everything works on the default public RPC, but public RPCs are slow and rate-limited — in a race against an attacker that can cost you vesting slices. Use a fast private RPC: the free tiers of Helius, QuickNode, or Triton all work. Pass it with --rpc / NOS_RESCUE_RPC (or the RPC field in the web UI). If your provider uses a separate websocket URL, pass --ws / NOS_RESCUE_WS.

Security notes