A local, resumable tool for distributing SOL or an SPL token to the holders of a pump.fun (SOL-paired) token. It is built for large audiences where a mistake is expensive, so it takes a snapshot, builds a plan, requires confirmation, and can resume exactly where it left off if the run is interrupted.
Nothing in this repository contains keys or secrets. The funder wallet is imported into a password-encrypted file that is ignored by git and never published.
The flow is: snapshot, plan, dry run, arm, execute, monitor, resume.
- Snapshot the holders of a target token using Helius (
getTokenAccounts, paginated). Balances are summed per owner and written to a hashed JSON file so the exact audience is reproducible. - Build a plan to send SOL or an SPL token from a funding wallet, split either pro-rata by holdings or equally. You can set a minimum and maximum holding band, a top-N limit, an exclude list, and spend caps.
- Dry run builds and signs a real sample transaction without broadcasting it, to confirm the transaction is valid and shows the full cost estimate.
- Arm requires you to re-type the exact total and the word EXECUTE.
- Execute sends with bounded concurrency, retries, and a durable ledger.
- Resume after any interruption. Transactions that were broadcast but not confirmed are checked on-chain by signature status before anything is retried, so a transfer is never sent twice.
Real wallets are valid points on the ed25519 curve. Liquidity pools, bonding curve vaults and other program accounts are Program Derived Addresses, which are off the curve. pissdrop uses this to flag and (by default) exclude off-curve owners, so pools do not receive the airdrop. There are no hard-coded address lists to maintain.
Read this before an SPL run.
- SOL airdrop: no token account rent, roughly 18 transfers per transaction.
- SPL airdrop: most recipients need a new associated token account. Rent is about 0.00204 SOL each and is paid by the funder, so 130,000 recipients is roughly 265 SOL in rent alone, at about 6 transfers per transaction. The plan screen shows the exact worst-case figure and will not let you arm if the funder cannot cover the amount plus fees plus rent.
- The private key is encrypted at rest with AES-256-GCM using a key derived from your password with scrypt. The plaintext key only exists in memory while the wallet is unlocked, and is dropped on lock or exit.
- The server binds to 127.0.0.1 only and requires a bearer token for every request that changes state. When the UI is served by the API, the token is injected into the page so there is nothing to copy.
- A batch is signed and its signature saved before it is broadcast, so an interruption always leaves a signature that can be reconciled on resume.
- Guardrails: solvency check, maximum total and per-recipient caps, recipient count check against the snapshot, exclude list, and a pause and stop control.
cp .env.example .env # set RPC_URL and HELIUS_API_KEY (use a paid endpoint)
npm install
npm run web:build # build the UI once
npm start # serves the API and UI on http://127.0.0.1:8899
Open http://127.0.0.1:8899 and import the funder wallet in the UI. After importing, clear your clipboard.
npm start # terminal 1: API
npm run web:dev # terminal 2: UI on :5180
npm run keystore:import
src/
config.js environment and paths
keystore.js encrypted wallet
ledger.js SQLite job and recipient ledger
snapshot.js Helius holder snapshot and hashing
planner.js filters, split, cost and solvency, plan hash
engine.js sender with reconcile, retries, pause and resume
server.js local API and UI host
cli.js optional key import
web/ React UI
test/ offline checks
MIT