vacuum-sol 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +362 -0
- package/dist/config.d.ts +15 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +58 -0
- package/dist/config.js.map +1 -0
- package/dist/core/detector.d.ts +36 -0
- package/dist/core/detector.d.ts.map +1 -0
- package/dist/core/detector.js +142 -0
- package/dist/core/detector.js.map +1 -0
- package/dist/core/monitor.d.ts +31 -0
- package/dist/core/monitor.d.ts.map +1 -0
- package/dist/core/monitor.js +172 -0
- package/dist/core/monitor.js.map +1 -0
- package/dist/core/reclaimer.d.ts +30 -0
- package/dist/core/reclaimer.d.ts.map +1 -0
- package/dist/core/reclaimer.js +182 -0
- package/dist/core/reclaimer.js.map +1 -0
- package/dist/core/types.d.ts +125 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -0
- package/dist/db/accounts.d.ts +71 -0
- package/dist/db/accounts.d.ts.map +1 -0
- package/dist/db/accounts.js +205 -0
- package/dist/db/accounts.js.map +1 -0
- package/dist/db/index.d.ts +14 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +104 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/operators.d.ts +48 -0
- package/dist/db/operators.d.ts.map +1 -0
- package/dist/db/operators.js +201 -0
- package/dist/db/operators.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +473 -0
- package/dist/index.js.map +1 -0
- package/dist/server/index.d.ts +5 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +121 -0
- package/dist/server/index.js.map +1 -0
- package/dist/services/reporter.d.ts +28 -0
- package/dist/services/reporter.d.ts.map +1 -0
- package/dist/services/reporter.js +107 -0
- package/dist/services/reporter.js.map +1 -0
- package/dist/services/solana.d.ts +59 -0
- package/dist/services/solana.d.ts.map +1 -0
- package/dist/services/solana.js +162 -0
- package/dist/services/solana.js.map +1 -0
- package/dist/services/telegram.d.ts +20 -0
- package/dist/services/telegram.d.ts.map +1 -0
- package/dist/services/telegram.js +213 -0
- package/dist/services/telegram.js.map +1 -0
- package/dist/utils/helpers.d.ts +55 -0
- package/dist/utils/helpers.d.ts.map +1 -0
- package/dist/utils/helpers.js +116 -0
- package/dist/utils/helpers.js.map +1 -0
- package/dist/utils/logger.d.ts +14 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +70 -0
- package/dist/utils/logger.js.map +1 -0
- package/package.json +69 -0
package/README.md
ADDED
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
# ๐งน Vacuum
|
|
2
|
+
|
|
3
|
+
**Suck up forgotten rent from Solana accounts.**
|
|
4
|
+
|
|
5
|
+
Vacuum is an automated rent-reclaim bot that monitors **operator-owned** token accounts and safely reclaims rent SOL when accounts are closed or have zero balance. Perfect for custodial services, development environments, and anyone managing their own Solana token accounts at scale.
|
|
6
|
+
|
|
7
|
+

|
|
8
|
+

|
|
9
|
+

|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## ๐ Quick Start
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Clone and install
|
|
17
|
+
git clone https://github.com/your-username/vacuum-sol
|
|
18
|
+
cd vacuum-sol
|
|
19
|
+
npm install && npm run build
|
|
20
|
+
|
|
21
|
+
# Configure
|
|
22
|
+
cp .env.example .env
|
|
23
|
+
# Edit .env with your TREASURY_ADDRESS
|
|
24
|
+
|
|
25
|
+
# Run
|
|
26
|
+
npm start -- scan # Find accounts
|
|
27
|
+
npm start -- check --all # Find reclaimable
|
|
28
|
+
npm start -- reclaim --dry-run # Preview reclaim
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## ๐ Understanding Solana Rent
|
|
34
|
+
|
|
35
|
+
### What is Rent?
|
|
36
|
+
|
|
37
|
+
Every Solana account must hold **rent** (SOL) to stay active:
|
|
38
|
+
|
|
39
|
+
| Account Type | Rent Cost |
|
|
40
|
+
| ------------------------- | ------------ |
|
|
41
|
+
| Token Account (165 bytes) | ~0.00204 SOL |
|
|
42
|
+
| 1KB Account | ~0.00696 SOL |
|
|
43
|
+
|
|
44
|
+
When accounts are **closed**, this rent is returned to a designated address.
|
|
45
|
+
|
|
46
|
+
### The Problem
|
|
47
|
+
|
|
48
|
+
- Developers and custodial services create thousands of token accounts
|
|
49
|
+
- Many get abandoned (testing, unused wallets, closed positions, etc.)
|
|
50
|
+
- Rent stays locked in zero-balance accounts forever unless reclaimed
|
|
51
|
+
- **Result**: Silent capital loss of ~0.00204 SOL per account
|
|
52
|
+
|
|
53
|
+
> **โ ๏ธ Important**: Vacuum reclaims rent from **accounts you own** (where your operator keypair is the account authority). It does NOT work for accounts where you merely paid transaction fees as a paymaster. See [Authority Model](#-authority-model) for details.
|
|
54
|
+
|
|
55
|
+
### The Solution
|
|
56
|
+
|
|
57
|
+
Vacuum automatically:
|
|
58
|
+
|
|
59
|
+
1. **Tracks** sponsored accounts in a local database
|
|
60
|
+
2. **Detects** accounts with 0 balance (safe to close)
|
|
61
|
+
3. **Reclaims** rent back to your treasury
|
|
62
|
+
4. **Reports** locked vs reclaimed totals
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## โจ Features
|
|
67
|
+
|
|
68
|
+
| Feature | Description |
|
|
69
|
+
| ----------------------- | ------------------------------------------------- |
|
|
70
|
+
| ๐ **Smart Detection** | Finds zero-balance token accounts safe to close |
|
|
71
|
+
| ๐ก๏ธ **Safety First** | Dry-run mode, whitelists, balance verification |
|
|
72
|
+
| ๐ค **Telegram Bot** | Monitor and trigger reclaims from your phone |
|
|
73
|
+
| ๐ **Audit Trail** | Every reclaim logged with TX signatures |
|
|
74
|
+
| โฐ **Automation Ready** | Run on schedule with cron, PM2, or GitHub Actions |
|
|
75
|
+
| ๐ป **CLI Interface** | 8 powerful commands for full control |
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## ๐ CLI Commands
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# Scanning
|
|
83
|
+
vacuum scan # Scan operator's token accounts
|
|
84
|
+
vacuum scan --tx <sig> # Scan specific transactions
|
|
85
|
+
|
|
86
|
+
# Checking
|
|
87
|
+
vacuum check --all # Find all reclaimable accounts
|
|
88
|
+
vacuum check --address <pubkey> # Check specific account
|
|
89
|
+
|
|
90
|
+
# Reclaiming
|
|
91
|
+
vacuum reclaim --dry-run # Preview reclaim (safe)
|
|
92
|
+
vacuum reclaim --yes # Actually reclaim
|
|
93
|
+
vacuum reclaim --max 20 # Limit to 20 accounts
|
|
94
|
+
|
|
95
|
+
# Reporting
|
|
96
|
+
vacuum report # Show summary
|
|
97
|
+
vacuum report --history # Show reclaim history
|
|
98
|
+
vacuum report --format json # Export as JSON
|
|
99
|
+
|
|
100
|
+
# Protection
|
|
101
|
+
vacuum protect --add <pubkey> --reason "Active user"
|
|
102
|
+
vacuum protect --remove <pubkey>
|
|
103
|
+
vacuum protect --list
|
|
104
|
+
|
|
105
|
+
# Listing
|
|
106
|
+
vacuum list # List all tracked accounts
|
|
107
|
+
vacuum list --status reclaimable # Filter by status
|
|
108
|
+
|
|
109
|
+
# Bot
|
|
110
|
+
vacuum bot # Start Telegram bot
|
|
111
|
+
|
|
112
|
+
# Config
|
|
113
|
+
vacuum config # Show configuration
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## ๐ค Telegram Integration
|
|
119
|
+
|
|
120
|
+
### Setup
|
|
121
|
+
|
|
122
|
+
1. Create a bot: Message `@BotFather` on Telegram โ `/newbot`
|
|
123
|
+
2. Get your chat ID: Message `@userinfobot`
|
|
124
|
+
3. Add to `.env`:
|
|
125
|
+
```env
|
|
126
|
+
TELEGRAM_BOT_TOKEN=your-bot-token
|
|
127
|
+
TELEGRAM_CHAT_ID=your-chat-id
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Run the Bot
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
npm start -- bot
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Telegram Commands
|
|
137
|
+
|
|
138
|
+
| Command | Action |
|
|
139
|
+
| ------------------ | ----------------- |
|
|
140
|
+
| `/status` | Show rent summary |
|
|
141
|
+
| `/scan` | Scan for accounts |
|
|
142
|
+
| `/check` | Find reclaimable |
|
|
143
|
+
| `/reclaim` | Preview reclaim |
|
|
144
|
+
| `/reclaim_execute` | Actually reclaim |
|
|
145
|
+
| `/history` | Recent reclaims |
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## โ๏ธ Configuration
|
|
150
|
+
|
|
151
|
+
Create a `.env` file:
|
|
152
|
+
|
|
153
|
+
```env
|
|
154
|
+
# Required
|
|
155
|
+
SOLANA_RPC_URL=https://api.devnet.solana.com
|
|
156
|
+
TREASURY_ADDRESS=<your-wallet-address>
|
|
157
|
+
OPERATOR_KEYPAIR_PATH=./operator-keypair.json
|
|
158
|
+
|
|
159
|
+
# Optional
|
|
160
|
+
DRY_RUN=true
|
|
161
|
+
COOLDOWN_HOURS=24
|
|
162
|
+
MIN_INACTIVE_DAYS=7
|
|
163
|
+
TELEGRAM_BOT_TOKEN=
|
|
164
|
+
TELEGRAM_CHAT_ID=
|
|
165
|
+
|
|
166
|
+
# Database
|
|
167
|
+
DB_PATH=./data/accounts.db
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## ๐ก๏ธ Safety Features
|
|
173
|
+
|
|
174
|
+
โ
**Dry-Run Mode** - Preview all actions before executing
|
|
175
|
+
โ
**Zero Balance Check** - Only closes accounts with 0 tokens
|
|
176
|
+
โ
**Protected Accounts** - Whitelist accounts to never reclaim
|
|
177
|
+
โ
**Authority Verification** - Confirms operator owns the account
|
|
178
|
+
โ
**Audit Trail** - All reclaims logged with TX signatures
|
|
179
|
+
โ
**Cooldown Periods** - Wait N days before reclaiming inactive accounts
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## ๐ Authority Model
|
|
184
|
+
|
|
185
|
+
**Critical Understanding**: Vacuum can only reclaim rent from accounts where your operator keypair is the **account owner/authority**.
|
|
186
|
+
|
|
187
|
+
### โ
Works For:
|
|
188
|
+
|
|
189
|
+
- Token accounts created by your operator wallet
|
|
190
|
+
- Custodial service accounts you manage
|
|
191
|
+
- Development/testing accounts you own
|
|
192
|
+
- Any account where `closeAuthority` is your operator
|
|
193
|
+
|
|
194
|
+
### โ Does NOT Work For:
|
|
195
|
+
|
|
196
|
+
- User-owned accounts (even if you paid fees as a Kora paymaster)
|
|
197
|
+
- Accounts where you're only the fee payer, not the owner
|
|
198
|
+
- Third-party wallets using your paymaster service
|
|
199
|
+
|
|
200
|
+
**Why?** On Solana, paying transaction fees โ account ownership. Only the account's `owner` or `closeAuthority` can close it and reclaim rent.
|
|
201
|
+
|
|
202
|
+
### Use Cases
|
|
203
|
+
|
|
204
|
+
1. **Custodial Services**: Reclaim rent from your users' accounts you manage
|
|
205
|
+
2. **Development**: Clean up test accounts from devnet/testnet
|
|
206
|
+
3. **Personal Management**: Monitor your own token accounts
|
|
207
|
+
4. **Bulk Account Management**: Manage rent across multiple operator profiles
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## ๐๏ธ Architecture
|
|
212
|
+
|
|
213
|
+
```
|
|
214
|
+
vacuum-sol/
|
|
215
|
+
โโโ src/
|
|
216
|
+
โ โโโ index.ts # CLI entry point
|
|
217
|
+
โ โโโ config.ts # Environment configuration
|
|
218
|
+
โ โโโ core/
|
|
219
|
+
โ โ โโโ types.ts # TypeScript interfaces
|
|
220
|
+
โ โ โโโ monitor.ts # Account scanning
|
|
221
|
+
โ โ โโโ detector.ts # Reclaimable detection
|
|
222
|
+
โ โ โโโ reclaimer.ts # Rent reclaim execution
|
|
223
|
+
โ โโโ db/
|
|
224
|
+
โ โ โโโ index.ts # SQLite setup
|
|
225
|
+
โ โ โโโ accounts.ts # CRUD operations
|
|
226
|
+
โ โโโ services/
|
|
227
|
+
โ โ โโโ solana.ts # Solana RPC wrapper
|
|
228
|
+
โ โ โโโ telegram.ts # Telegram bot
|
|
229
|
+
โ โ โโโ reporter.ts # Report generation
|
|
230
|
+
โ โโโ utils/
|
|
231
|
+
โ โโโ logger.ts # Colored logging
|
|
232
|
+
โ โโโ helpers.ts # Utilities
|
|
233
|
+
โโโ scripts/
|
|
234
|
+
โ โโโ simulate.ts # Devnet testing
|
|
235
|
+
โโโ landing/
|
|
236
|
+
โโโ index.html # Landing page
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## ๐งช Testing on Devnet
|
|
242
|
+
|
|
243
|
+
### 1. Setup
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
# Generate keypair
|
|
247
|
+
solana-keygen new -o operator-keypair.json
|
|
248
|
+
|
|
249
|
+
# Get devnet SOL
|
|
250
|
+
solana airdrop 2 --keypair operator-keypair.json --url devnet
|
|
251
|
+
|
|
252
|
+
# Configure for devnet
|
|
253
|
+
# Edit .env: SOLANA_RPC_URL=https://api.devnet.solana.com
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### 2. Create Test Accounts
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
npx tsx scripts/simulate.ts create
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
This creates token accounts with 0 balance for testing.
|
|
263
|
+
|
|
264
|
+
### 3. Run the Bot
|
|
265
|
+
|
|
266
|
+
```bash
|
|
267
|
+
npm start -- scan
|
|
268
|
+
npm start -- check --all
|
|
269
|
+
npm start -- reclaim --dry-run
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### 4. Actual Reclaim
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
DRY_RUN=false npm start -- reclaim --yes
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
## ๐ Example Report
|
|
281
|
+
|
|
282
|
+
```
|
|
283
|
+
๐ Rent Reclaim Summary
|
|
284
|
+
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
285
|
+
โ Total Accounts Tracked: 150 โ
|
|
286
|
+
โ โโ Active: 85 โ
|
|
287
|
+
โ โโ Reclaimable: 42 โ
|
|
288
|
+
โ โโ Reclaimed: 20 โ
|
|
289
|
+
โ โโ Protected: 3 โ
|
|
290
|
+
โ โ
|
|
291
|
+
โ ๐ฐ Rent Status โ
|
|
292
|
+
โ โโ Locked: 0.285 SOL โ
|
|
293
|
+
โ โโ Reclaimed: 0.041 SOL โ
|
|
294
|
+
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
## ๐ง Automation
|
|
300
|
+
|
|
301
|
+
### Cron
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
# Add to crontab for daily 6am checks
|
|
305
|
+
0 6 * * * cd /path/to/vacuum-sol && DRY_RUN=false npm start -- reclaim --yes
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### PM2
|
|
309
|
+
|
|
310
|
+
```bash
|
|
311
|
+
npm install -g pm2
|
|
312
|
+
pm2 start npm --name "vacuum-reclaim" --cron "0 6 * * *" -- start -- reclaim --yes
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### GitHub Actions
|
|
316
|
+
|
|
317
|
+
```yaml
|
|
318
|
+
name: Daily Reclaim
|
|
319
|
+
on:
|
|
320
|
+
schedule:
|
|
321
|
+
- cron: '0 6 * * *'
|
|
322
|
+
jobs:
|
|
323
|
+
reclaim:
|
|
324
|
+
runs-on: ubuntu-latest
|
|
325
|
+
steps:
|
|
326
|
+
- uses: actions/checkout@v4
|
|
327
|
+
- uses: actions/setup-node@v4
|
|
328
|
+
- run: npm install && npm run build
|
|
329
|
+
- run: npm start -- reclaim --yes
|
|
330
|
+
env:
|
|
331
|
+
TREASURY_ADDRESS: ${{ secrets.TREASURY_ADDRESS }}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
336
|
+
## ๐ค Contributing
|
|
337
|
+
|
|
338
|
+
Contributions welcome! Open an issue or PR.
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
## ๐ License
|
|
343
|
+
|
|
344
|
+
MIT License - see [LICENSE](LICENSE)
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
## ๐ Acknowledgements
|
|
349
|
+
|
|
350
|
+
- [Kora](https://kora.network) - Gasless transaction infrastructure
|
|
351
|
+
- [Solana](https://solana.com) - High-performance blockchain
|
|
352
|
+
- SuperteamNG - Bounty program
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
<div align="center">
|
|
357
|
+
<strong>Built with โค๏ธ for the Solana ecosystem</strong>
|
|
358
|
+
<br><br>
|
|
359
|
+
<a href="https://github.com/your-username/vacuum-sol">GitHub</a> โข
|
|
360
|
+
<a href="https://t.me/vacuumsol">Telegram</a> โข
|
|
361
|
+
<a href="https://your-landing-page.com">Website</a>
|
|
362
|
+
</div>
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { PublicKey } from '@solana/web3.js';
|
|
2
|
+
export interface Config {
|
|
3
|
+
rpcUrl: string;
|
|
4
|
+
treasuryAddress: PublicKey;
|
|
5
|
+
operatorKeypairPath: string;
|
|
6
|
+
koraNodeUrl?: string;
|
|
7
|
+
dryRun: boolean;
|
|
8
|
+
cooldownHours: number;
|
|
9
|
+
minInactiveDays: number;
|
|
10
|
+
dbPath: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function loadConfig(): Config;
|
|
13
|
+
export declare function loadOperatorKeypair(keypairPath: string): Uint8Array;
|
|
14
|
+
export declare function getConfig(): Config;
|
|
15
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAQ3C,MAAM,WAAW,MAAM;IAErB,MAAM,EAAE,MAAM,CAAA;IAGd,eAAe,EAAE,SAAS,CAAA;IAC1B,mBAAmB,EAAE,MAAM,CAAA;IAG3B,WAAW,CAAC,EAAE,MAAM,CAAA;IAGpB,MAAM,EAAE,OAAO,CAAA;IACf,aAAa,EAAE,MAAM,CAAA;IACrB,eAAe,EAAE,MAAM,CAAA;IAGvB,MAAM,EAAE,MAAM,CAAA;CACf;AAcD,wBAAgB,UAAU,IAAI,MAAM,CA+BnC;AAED,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,UAAU,CAYnE;AAKD,wBAAgB,SAAS,IAAI,MAAM,CAKlC"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { PublicKey } from '@solana/web3.js';
|
|
2
|
+
import { config as loadEnv } from 'dotenv';
|
|
3
|
+
import { existsSync, readFileSync } from 'fs';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
// Load environment variables
|
|
6
|
+
loadEnv();
|
|
7
|
+
function getEnvOrThrow(key) {
|
|
8
|
+
const value = process.env[key];
|
|
9
|
+
if (!value) {
|
|
10
|
+
throw new Error(`Missing required environment variable: ${key}`);
|
|
11
|
+
}
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
function getEnvOrDefault(key, defaultValue) {
|
|
15
|
+
return process.env[key] || defaultValue;
|
|
16
|
+
}
|
|
17
|
+
export function loadConfig() {
|
|
18
|
+
const treasuryAddressStr = process.env.TREASURY_ADDRESS;
|
|
19
|
+
if (!treasuryAddressStr) {
|
|
20
|
+
throw new Error('TREASURY_ADDRESS is required. Set it in your .env file.');
|
|
21
|
+
}
|
|
22
|
+
let treasuryAddress;
|
|
23
|
+
try {
|
|
24
|
+
treasuryAddress = new PublicKey(treasuryAddressStr);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
throw new Error(`Invalid TREASURY_ADDRESS: ${treasuryAddressStr}. Must be a valid Solana public key.`);
|
|
28
|
+
}
|
|
29
|
+
const operatorKeypairPath = getEnvOrDefault('OPERATOR_KEYPAIR_PATH', './operator-keypair.json');
|
|
30
|
+
return {
|
|
31
|
+
rpcUrl: getEnvOrDefault('SOLANA_RPC_URL', 'https://api.devnet.solana.com'),
|
|
32
|
+
treasuryAddress,
|
|
33
|
+
operatorKeypairPath,
|
|
34
|
+
koraNodeUrl: process.env.KORA_NODE_URL,
|
|
35
|
+
dryRun: getEnvOrDefault('DRY_RUN', 'true') === 'true',
|
|
36
|
+
cooldownHours: parseInt(getEnvOrDefault('COOLDOWN_HOURS', '24'), 10),
|
|
37
|
+
minInactiveDays: parseInt(getEnvOrDefault('MIN_INACTIVE_DAYS', '7'), 10),
|
|
38
|
+
dbPath: getEnvOrDefault('DB_PATH', './data/accounts.db'),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export function loadOperatorKeypair(keypairPath) {
|
|
42
|
+
const resolvedPath = path.resolve(keypairPath);
|
|
43
|
+
if (!existsSync(resolvedPath)) {
|
|
44
|
+
throw new Error(`Operator keypair not found at: ${resolvedPath}\n` +
|
|
45
|
+
'Generate one with: solana-keygen new -o operator-keypair.json');
|
|
46
|
+
}
|
|
47
|
+
const keypairData = JSON.parse(readFileSync(resolvedPath, 'utf-8'));
|
|
48
|
+
return Uint8Array.from(keypairData);
|
|
49
|
+
}
|
|
50
|
+
// Export a singleton config (lazy loaded)
|
|
51
|
+
let _config = null;
|
|
52
|
+
export function getConfig() {
|
|
53
|
+
if (!_config) {
|
|
54
|
+
_config = loadConfig();
|
|
55
|
+
}
|
|
56
|
+
return _config;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAE,MAAM,IAAI,OAAO,EAAE,MAAM,QAAQ,CAAA;AAC1C,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,IAAI,CAAA;AAC7C,OAAO,IAAI,MAAM,MAAM,CAAA;AAEvB,6BAA6B;AAC7B,OAAO,EAAE,CAAA;AAsBT,SAAS,aAAa,CAAC,GAAW;IAChC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IAC9B,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,0CAA0C,GAAG,EAAE,CAAC,CAAA;IAClE,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,SAAS,eAAe,CAAC,GAAW,EAAE,YAAoB;IACxD,OAAO,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,YAAY,CAAA;AACzC,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,MAAM,kBAAkB,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAA;IAEvD,IAAI,CAAC,kBAAkB,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAA;IAC5E,CAAC;IAED,IAAI,eAA0B,CAAA;IAC9B,IAAI,CAAC;QACH,eAAe,GAAG,IAAI,SAAS,CAAC,kBAAkB,CAAC,CAAA;IACrD,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CACb,6BAA6B,kBAAkB,sCAAsC,CACtF,CAAA;IACH,CAAC;IAED,MAAM,mBAAmB,GAAG,eAAe,CACzC,uBAAuB,EACvB,yBAAyB,CAC1B,CAAA;IAED,OAAO;QACL,MAAM,EAAE,eAAe,CAAC,gBAAgB,EAAE,+BAA+B,CAAC;QAC1E,eAAe;QACf,mBAAmB;QACnB,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,aAAa;QACtC,MAAM,EAAE,eAAe,CAAC,SAAS,EAAE,MAAM,CAAC,KAAK,MAAM;QACrD,aAAa,EAAE,QAAQ,CAAC,eAAe,CAAC,gBAAgB,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC;QACpE,eAAe,EAAE,QAAQ,CAAC,eAAe,CAAC,mBAAmB,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC;QACxE,MAAM,EAAE,eAAe,CAAC,SAAS,EAAE,oBAAoB,CAAC;KACzD,CAAA;AACH,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,WAAmB;IACrD,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAA;IAE9C,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CACb,kCAAkC,YAAY,IAAI;YAChD,+DAA+D,CAClE,CAAA;IACH,CAAC;IAED,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC,CAAA;IACnE,OAAO,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;AACrC,CAAC;AAED,0CAA0C;AAC1C,IAAI,OAAO,GAAkB,IAAI,CAAA;AAEjC,MAAM,UAAU,SAAS;IACvB,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,GAAG,UAAU,EAAE,CAAA;IACxB,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { PublicKey } from '@solana/web3.js';
|
|
2
|
+
import type { DetectionResult } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Detector for identifying reclaimable accounts
|
|
5
|
+
*/
|
|
6
|
+
export declare class ReclaimableDetector {
|
|
7
|
+
private get minInactiveDays();
|
|
8
|
+
/**
|
|
9
|
+
* Check if a single account is reclaimable
|
|
10
|
+
*/
|
|
11
|
+
checkAccount(pubkey: PublicKey): Promise<DetectionResult | null>;
|
|
12
|
+
/**
|
|
13
|
+
* Find all reclaimable accounts
|
|
14
|
+
*/
|
|
15
|
+
findAllReclaimable(): Promise<DetectionResult[]>;
|
|
16
|
+
/**
|
|
17
|
+
* Find only safe-to-reclaim accounts (zero balance token accounts)
|
|
18
|
+
*/
|
|
19
|
+
findSafeReclaimable(): Promise<DetectionResult[]>;
|
|
20
|
+
/**
|
|
21
|
+
* Quick check if an account is closed
|
|
22
|
+
*/
|
|
23
|
+
isAccountClosed(pubkey: PublicKey): Promise<boolean>;
|
|
24
|
+
/**
|
|
25
|
+
* Get summary of reclaimable rent
|
|
26
|
+
*/
|
|
27
|
+
getReclaimableSummary(): Promise<{
|
|
28
|
+
totalAccounts: number;
|
|
29
|
+
safeToReclaim: number;
|
|
30
|
+
unsafeNeedsReview: number;
|
|
31
|
+
totalReclaimableLamports: number;
|
|
32
|
+
safeReclaimableLamports: number;
|
|
33
|
+
}>;
|
|
34
|
+
}
|
|
35
|
+
export declare const detector: ReclaimableDetector;
|
|
36
|
+
//# sourceMappingURL=detector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detector.d.ts","sourceRoot":"","sources":["../../src/core/detector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAW3C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAEjD;;GAEG;AACH,qBAAa,mBAAmB;IAC9B,OAAO,KAAK,eAAe,GAE1B;IAED;;OAEG;IACG,YAAY,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAwFtE;;OAEG;IACG,kBAAkB,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;IA+BtD;;OAEG;IACG,mBAAmB,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;IAKvD;;OAEG;IACG,eAAe,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC;IAK1D;;OAEG;IACG,qBAAqB,IAAI,OAAO,CAAC;QACrC,aAAa,EAAE,MAAM,CAAA;QACrB,aAAa,EAAE,MAAM,CAAA;QACrB,iBAAiB,EAAE,MAAM,CAAA;QACzB,wBAAwB,EAAE,MAAM,CAAA;QAChC,uBAAuB,EAAE,MAAM,CAAA;KAChC,CAAC;CAoBH;AAGD,eAAO,MAAM,QAAQ,qBAA4B,CAAA"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { getConfig } from '../config.js';
|
|
2
|
+
import { getAllTrackedAccounts, getTrackedAccount, isAccountProtected, updateAccountState, } from '../db/accounts.js';
|
|
3
|
+
import { getAccountInfo, getTokenAccountData } from '../services/solana.js';
|
|
4
|
+
import { daysSince, formatSol } from '../utils/helpers.js';
|
|
5
|
+
import { logger } from '../utils/logger.js';
|
|
6
|
+
/**
|
|
7
|
+
* Detector for identifying reclaimable accounts
|
|
8
|
+
*/
|
|
9
|
+
export class ReclaimableDetector {
|
|
10
|
+
get minInactiveDays() {
|
|
11
|
+
return getConfig().minInactiveDays;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Check if a single account is reclaimable
|
|
15
|
+
*/
|
|
16
|
+
async checkAccount(pubkey) {
|
|
17
|
+
const trackedAccount = getTrackedAccount(pubkey);
|
|
18
|
+
if (!trackedAccount) {
|
|
19
|
+
logger.warn(`Account not tracked: ${pubkey.toBase58()}`);
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
// Skip protected accounts
|
|
23
|
+
if (isAccountProtected(pubkey)) {
|
|
24
|
+
logger.debug(`Account is protected: ${pubkey.toBase58()}`);
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
// Get current account state from chain
|
|
28
|
+
const accountInfo = await getAccountInfo(pubkey);
|
|
29
|
+
// Account is closed (no longer exists on chain)
|
|
30
|
+
if (!accountInfo) {
|
|
31
|
+
const result = {
|
|
32
|
+
account: trackedAccount,
|
|
33
|
+
reason: 'closed',
|
|
34
|
+
reclaimableLamports: trackedAccount.rentLamports,
|
|
35
|
+
safe: true,
|
|
36
|
+
details: 'Account no longer exists on-chain. Rent was already returned to original payer.',
|
|
37
|
+
};
|
|
38
|
+
// Update status in DB
|
|
39
|
+
updateAccountState(pubkey, { status: 'reclaimed', rentLamports: 0 });
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
// Check if it's a token account with zero balance
|
|
43
|
+
if (trackedAccount.accountType === 'token_account' ||
|
|
44
|
+
trackedAccount.accountType === 'ata') {
|
|
45
|
+
const tokenData = await getTokenAccountData(pubkey);
|
|
46
|
+
if (tokenData && tokenData.amount === 0n) {
|
|
47
|
+
const result = {
|
|
48
|
+
account: {
|
|
49
|
+
...trackedAccount,
|
|
50
|
+
rentLamports: tokenData.lamports,
|
|
51
|
+
},
|
|
52
|
+
reason: 'zero_balance',
|
|
53
|
+
reclaimableLamports: tokenData.lamports,
|
|
54
|
+
safe: true,
|
|
55
|
+
details: `Token account has 0 balance. Can close and reclaim ${formatSol(tokenData.lamports)}.`,
|
|
56
|
+
};
|
|
57
|
+
// Update status in DB
|
|
58
|
+
updateAccountState(pubkey, {
|
|
59
|
+
status: 'reclaimable',
|
|
60
|
+
rentLamports: tokenData.lamports,
|
|
61
|
+
});
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// Check for inactivity
|
|
66
|
+
if (trackedAccount.lastActivityAt) {
|
|
67
|
+
const inactiveDays = daysSince(trackedAccount.lastActivityAt);
|
|
68
|
+
if (inactiveDays >= this.minInactiveDays) {
|
|
69
|
+
const result = {
|
|
70
|
+
account: trackedAccount,
|
|
71
|
+
reason: 'inactive',
|
|
72
|
+
reclaimableLamports: accountInfo.lamports,
|
|
73
|
+
safe: false, // Mark as unsafe since we can't automatically close non-empty accounts
|
|
74
|
+
details: `Account inactive for ${inactiveDays} days. Manual review recommended.`,
|
|
75
|
+
};
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Account is active/not reclaimable
|
|
80
|
+
updateAccountState(pubkey, {
|
|
81
|
+
status: 'active',
|
|
82
|
+
rentLamports: accountInfo.lamports,
|
|
83
|
+
});
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Find all reclaimable accounts
|
|
88
|
+
*/
|
|
89
|
+
async findAllReclaimable() {
|
|
90
|
+
const accounts = getAllTrackedAccounts();
|
|
91
|
+
const results = [];
|
|
92
|
+
logger.info(`Checking ${accounts.length} tracked accounts...`);
|
|
93
|
+
for (const account of accounts) {
|
|
94
|
+
if (account.status === 'reclaimed' || account.status === 'protected') {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
try {
|
|
98
|
+
const result = await this.checkAccount(account.pubkey);
|
|
99
|
+
if (result) {
|
|
100
|
+
results.push(result);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
logger.error(`Error checking account ${account.pubkey.toBase58()}:`, error);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
logger.info(`Found ${results.length} reclaimable accounts out of ${accounts.length} total`);
|
|
108
|
+
return results;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Find only safe-to-reclaim accounts (zero balance token accounts)
|
|
112
|
+
*/
|
|
113
|
+
async findSafeReclaimable() {
|
|
114
|
+
const all = await this.findAllReclaimable();
|
|
115
|
+
return all.filter((r) => r.safe);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Quick check if an account is closed
|
|
119
|
+
*/
|
|
120
|
+
async isAccountClosed(pubkey) {
|
|
121
|
+
const info = await getAccountInfo(pubkey);
|
|
122
|
+
return info === null;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Get summary of reclaimable rent
|
|
126
|
+
*/
|
|
127
|
+
async getReclaimableSummary() {
|
|
128
|
+
const results = await this.findAllReclaimable();
|
|
129
|
+
const safeResults = results.filter((r) => r.safe);
|
|
130
|
+
const unsafeResults = results.filter((r) => !r.safe);
|
|
131
|
+
return {
|
|
132
|
+
totalAccounts: results.length,
|
|
133
|
+
safeToReclaim: safeResults.length,
|
|
134
|
+
unsafeNeedsReview: unsafeResults.length,
|
|
135
|
+
totalReclaimableLamports: results.reduce((sum, r) => sum + r.reclaimableLamports, 0),
|
|
136
|
+
safeReclaimableLamports: safeResults.reduce((sum, r) => sum + r.reclaimableLamports, 0),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// Export singleton instance
|
|
141
|
+
export const detector = new ReclaimableDetector();
|
|
142
|
+
//# sourceMappingURL=detector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detector.js","sourceRoot":"","sources":["../../src/core/detector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { PublicKey } from '@solana/web3.js';
|
|
2
|
+
import type { AccountType, ScanOptions, TrackedAccount } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Monitor for tracking sponsored accounts
|
|
5
|
+
*/
|
|
6
|
+
export declare class AccountMonitor {
|
|
7
|
+
/**
|
|
8
|
+
* Scan all token accounts owned by the operator and add them to tracking
|
|
9
|
+
*/
|
|
10
|
+
scanOperatorAccounts(): Promise<TrackedAccount[]>;
|
|
11
|
+
/**
|
|
12
|
+
* Scan accounts from transaction signatures (for Kora-sponsored accounts)
|
|
13
|
+
* This would parse transaction logs to find sponsored account creations
|
|
14
|
+
*/
|
|
15
|
+
scanFromSignatures(signatures: string[], options?: ScanOptions): Promise<TrackedAccount[]>;
|
|
16
|
+
/**
|
|
17
|
+
* Add a single account to tracking
|
|
18
|
+
*/
|
|
19
|
+
trackAccount(pubkey: PublicKey, sponsorTx?: string): Promise<TrackedAccount | null>;
|
|
20
|
+
/**
|
|
21
|
+
* Get tracking statistics
|
|
22
|
+
*/
|
|
23
|
+
getStats(): Promise<{
|
|
24
|
+
totalTracked: number;
|
|
25
|
+
byType: Record<AccountType, number>;
|
|
26
|
+
byStatus: Record<string, number>;
|
|
27
|
+
totalRentLocked: number;
|
|
28
|
+
}>;
|
|
29
|
+
}
|
|
30
|
+
export declare const monitor: AccountMonitor;
|
|
31
|
+
//# sourceMappingURL=monitor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"monitor.d.ts","sourceRoot":"","sources":["../../src/core/monitor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAc3C,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAE1E;;GAEG;AACH,qBAAa,cAAc;IACzB;;OAEG;IACG,oBAAoB,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IA0CvD;;;OAGG;IACG,kBAAkB,CACtB,UAAU,EAAE,MAAM,EAAE,EACpB,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,cAAc,EAAE,CAAC;IA8E5B;;OAEG;IACG,YAAY,CAChB,MAAM,EAAE,SAAS,EACjB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAqCjC;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC;QACxB,YAAY,EAAE,MAAM,CAAA;QACpB,MAAM,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAAA;QACnC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QAChC,eAAe,EAAE,MAAM,CAAA;KACxB,CAAC;CAmCH;AAGD,eAAO,MAAM,OAAO,gBAAuB,CAAA"}
|