solana-mint-recovery-engine 1.0.0 → 1.1.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 +278 -0
- package/dist/bin/smre.js +13 -1
- package/dist/bin/smre.js.map +1 -0
- package/dist/src/commands/build.js +86 -20
- package/dist/src/commands/build.js.map +1 -0
- package/dist/src/commands/config.js +6 -2
- package/dist/src/commands/config.js.map +1 -0
- package/dist/src/commands/doctor.js +64 -15
- package/dist/src/commands/doctor.js.map +1 -0
- package/dist/src/commands/inspect.js +48 -20
- package/dist/src/commands/inspect.js.map +1 -0
- package/dist/src/commands/recover.js +93 -30
- package/dist/src/commands/recover.js.map +1 -0
- package/dist/src/commands/scan.js +77 -25
- package/dist/src/commands/scan.js.map +1 -0
- package/dist/src/commands/sign.js +24 -8
- package/dist/src/commands/sign.js.map +1 -0
- package/dist/src/commands/simulate.d.ts +5 -0
- package/dist/src/commands/simulate.js +13 -6
- package/dist/src/commands/simulate.js.map +1 -0
- package/dist/src/commands/submit.js +44 -10
- package/dist/src/commands/submit.js.map +1 -0
- package/dist/src/core/constants.d.ts +65 -0
- package/dist/src/core/constants.js +93 -0
- package/dist/src/core/constants.js.map +1 -0
- package/dist/src/core/format.d.ts +35 -0
- package/dist/src/core/format.js +62 -0
- package/dist/src/core/format.js.map +1 -0
- package/dist/src/core/rent.d.ts +17 -1
- package/dist/src/core/rent.js +25 -4
- package/dist/src/core/rent.js.map +1 -0
- package/dist/src/core/transaction.d.ts +32 -5
- package/dist/src/core/transaction.js +74 -22
- package/dist/src/core/transaction.js.map +1 -0
- package/dist/src/core/validation.d.ts +66 -23
- package/dist/src/core/validation.js +31 -4
- package/dist/src/core/validation.js.map +1 -0
- package/dist/src/index.d.ts +14 -0
- package/dist/src/index.js +51 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/io/file.d.ts +5 -2
- package/dist/src/io/file.js +64 -11
- package/dist/src/io/file.js.map +1 -0
- package/dist/src/io/logger.d.ts +31 -0
- package/dist/src/io/logger.js +95 -0
- package/dist/src/io/logger.js.map +1 -0
- package/dist/src/io/rpc.d.ts +75 -5
- package/dist/src/io/rpc.js +213 -109
- package/dist/src/io/rpc.js.map +1 -0
- package/dist/src/signers/signer.d.ts +6 -3
- package/dist/src/signers/signer.js +38 -14
- package/dist/src/signers/signer.js.map +1 -0
- package/package.json +14 -5
package/README.md
ADDED
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
# Solana Mint & Token Account Recovery Engine (SMRE)
|
|
2
|
+
|
|
3
|
+
SMRE is an enterprise-grade Command Line Interface (CLI) tool and programmatic library designed to safely scan, inspect, build, sign, simulate, and submit transactions to recover excess SOL (lamports) lying above the rent-exempt minimum in Solana **Token-2022 (Token Extensions)** accounts, mints, and multisigs.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
1. [Core Features](#core-features)
|
|
9
|
+
2. [Architectural Highlights](#architectural-highlights)
|
|
10
|
+
3. [Installation & Setup](#installation--setup)
|
|
11
|
+
4. [CLI Command Reference](#cli-command-reference)
|
|
12
|
+
- [1. Diagnostics: `doctor`](#1-diagnostics-doctor)
|
|
13
|
+
- [2. Config Initialization: `config init`](#2-config-initialization-config-init)
|
|
14
|
+
- [3. Batch Scanning: `scan`](#3-batch-scanning-scan)
|
|
15
|
+
- [4. Single Inspection: `inspect`](#4-single-inspection-inspect)
|
|
16
|
+
- [5. Building Transaction Envelopes: `build`](#5-building-transaction-envelopes-build)
|
|
17
|
+
- [6. Simulation: `simulate`](#6-simulation-simulate)
|
|
18
|
+
- [7. Offline Signing: `sign`](#7-offline-signing-sign)
|
|
19
|
+
- [8. Submission: `submit`](#8-submission-submit)
|
|
20
|
+
- [9. Inline Recovery: `recover`](#9-inline-recovery-recover)
|
|
21
|
+
5. [Configuration Schema](#configuration-schema)
|
|
22
|
+
6. [Offline Transaction Envelope Schema](#offline-transaction-envelope-schema)
|
|
23
|
+
7. [Programmatic Library Usage](#programmatic-library-usage)
|
|
24
|
+
8. [NPM Publishing Guide (Developer Release Flow)](#npm-publishing-guide-developer-release-flow)
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Core Features
|
|
29
|
+
- **Zero-Loss Float Arithmetic:** Operates entirely with native TypeScript `BigInt` types to prevent floating-point inaccuracies for multi-million SOL recoveries.
|
|
30
|
+
- **Multisig Support:** Handles Token-2022 multisig accounts, tracking thresholds and collecting multi-signatures offline.
|
|
31
|
+
- **Offline / Air-Gapped Signature Collection:** Supports separation of duties. Build transactions online, sign offline, and broadcast from a secure node.
|
|
32
|
+
- **On-Chain Simulations:** Employs stable, public `@solana/web3.js` simulation APIs to run pre-execution checks and print execution logs.
|
|
33
|
+
- **Network Resilience:** Automatically injects Compute Budget and Priority Fee instructions to land transactions during times of high congestion.
|
|
34
|
+
- **Secure Key Handling:** Automatically scrubs private key arrays from memory immediately after keypair instantiation.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Architectural Highlights
|
|
39
|
+
|
|
40
|
+
### Precision-Safe Fee Routing (HIGH-08 Fix)
|
|
41
|
+
In standard implementations, splitting fees requires both the destination address and the authority to sign the transaction. SMRE avoids this requirement:
|
|
42
|
+
1. Withdraws excess lamports from the Token-2022 account to the **Authority** address.
|
|
43
|
+
2. Transfers the platform fee from the **Authority** to the **Fee Wallet**.
|
|
44
|
+
3. Forwards the net amount from the **Authority** to the final **Destination**.
|
|
45
|
+
|
|
46
|
+
Since the **Authority** is already a required signer on the `WithdrawExcessLamports` instruction, no additional signature is required from the destination wallet.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Installation & Setup
|
|
51
|
+
|
|
52
|
+
### Requirements
|
|
53
|
+
- **Node.js:** >= 18.0.0
|
|
54
|
+
- **TypeScript:** >= 5.0.0
|
|
55
|
+
|
|
56
|
+
### Local Installation
|
|
57
|
+
1. Clone the repository and install dependencies:
|
|
58
|
+
```bash
|
|
59
|
+
npm install
|
|
60
|
+
```
|
|
61
|
+
2. Build the project:
|
|
62
|
+
```bash
|
|
63
|
+
npm run build
|
|
64
|
+
```
|
|
65
|
+
3. Link the CLI binary to your global system path:
|
|
66
|
+
```bash
|
|
67
|
+
npm link
|
|
68
|
+
```
|
|
69
|
+
You can now run the `smre` command from anywhere in your terminal.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## CLI Command Reference
|
|
74
|
+
|
|
75
|
+
### 1. Diagnostics: `doctor`
|
|
76
|
+
Performs comprehensive checks on the system environment, Node.js version, configuration file health, RPC connection latency, and rent exemption estimates.
|
|
77
|
+
```bash
|
|
78
|
+
smre doctor [--config <path>]
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### 2. Config Initialization: `config init`
|
|
82
|
+
Creates a default `smre.config.json` configuration file in the working directory.
|
|
83
|
+
```bash
|
|
84
|
+
smre config init [-o <output-path>]
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### 3. Batch Scanning: `scan`
|
|
88
|
+
Scans a text file containing base58-encoded Solana addresses for recoverable excess lamports. Uses high-performance batching (100 accounts per batch) and reports results in a clean table or structured JSON.
|
|
89
|
+
```bash
|
|
90
|
+
smre scan <address-file> [--json] [--config <path>]
|
|
91
|
+
```
|
|
92
|
+
*Note: The address file should contain one address per line. Lines starting with `#` or `//` are treated as comments.*
|
|
93
|
+
|
|
94
|
+
### 4. Single Inspection: `inspect`
|
|
95
|
+
Analyzes a single Token-2022 account, mint, or multisig on-chain, rendering details about owner program, balance, rent floor, authority, and recovery status.
|
|
96
|
+
```bash
|
|
97
|
+
smre inspect <address> [--json] [--config <path>]
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### 5. Building Transaction Envelopes: `build`
|
|
101
|
+
Constructs an unsigned transaction to recover excess lamports and saves it into an offline Transaction Envelope JSON file.
|
|
102
|
+
```bash
|
|
103
|
+
smre build <source-address> \
|
|
104
|
+
-d <destination-address> \
|
|
105
|
+
-o <output-envelope.json> \
|
|
106
|
+
[--fee-wallet <address>] \
|
|
107
|
+
[--fee-percent <number>] \
|
|
108
|
+
[--fee-payer <address>]
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### 6. Simulation: `simulate`
|
|
112
|
+
Runs an on-chain simulation of the constructed Transaction Envelope to verify instructions, print execution logs, and measure compute unit consumption without broadcasting.
|
|
113
|
+
```bash
|
|
114
|
+
smre simulate <envelope-file>
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### 7. Offline Signing: `sign`
|
|
118
|
+
Signs an offline Transaction Envelope. Supports JSON keypair files, raw base58 private key strings, or direct JSON private key arrays. Automatically tracks signature slots.
|
|
119
|
+
```bash
|
|
120
|
+
smre sign <envelope-file> \
|
|
121
|
+
-k <keypair-secret-or-path> \
|
|
122
|
+
[-o <output-envelope.json>]
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### 8. Submission: `submit`
|
|
126
|
+
Validates that the required signature threshold is met (including multisig threshold), broadcasts the transaction to the network, and polls the node for confirmation.
|
|
127
|
+
```bash
|
|
128
|
+
smre submit <envelope-file>
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### 9. Inline Recovery: `recover`
|
|
132
|
+
Convenience command that performs inspection, building, signing, simulation, submission, and confirmation in a single online step.
|
|
133
|
+
```bash
|
|
134
|
+
smre recover <source-address> \
|
|
135
|
+
-k <keypair-secret-or-path> \
|
|
136
|
+
-d <destination-address> \
|
|
137
|
+
[--fee-wallet <address>] \
|
|
138
|
+
[--fee-percent <number>]
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Configuration Schema
|
|
144
|
+
Configuration options can be placed in `smre.config.json` or passed as a custom file using the `-c` flag.
|
|
145
|
+
```json
|
|
146
|
+
{
|
|
147
|
+
"rpcUrl": "https://api.mainnet-beta.solana.com",
|
|
148
|
+
"commitment": "confirmed",
|
|
149
|
+
"defaultDestination": "9xQdBNbLqP55ePE1R24g5652uhGzTsw34JmUCwm",
|
|
150
|
+
"feeWallet": "EGMQSHdW5vLvTZ4ro8BkGjWerXcWH7oHVW2EDMJqPA4x",
|
|
151
|
+
"feePercentage": 2.5,
|
|
152
|
+
"priorityFee": 5000
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## Offline Transaction Envelope Schema
|
|
159
|
+
Transaction envelopes use a strict, schema-validated JSON format:
|
|
160
|
+
```json
|
|
161
|
+
{
|
|
162
|
+
"version": "1.1.0",
|
|
163
|
+
"type": "withdraw-excess-lamports",
|
|
164
|
+
"metadata": {
|
|
165
|
+
"sourceAddress": "4zMMC9ZdZhLx2u223612q76Cc223612q76Cc223612",
|
|
166
|
+
"sourceType": "mint",
|
|
167
|
+
"destinationAddress": "9xQdBNbLqP55ePE1R24g5652uhGzTsw34JmUCwm",
|
|
168
|
+
"authorityAddress": "F223612q76Cc223612q76Cc223612q76Cc223612q",
|
|
169
|
+
"excessLamports": "2960720",
|
|
170
|
+
"rentFloor": "1461600",
|
|
171
|
+
"createdAt": "2026-07-07T13:25:28Z",
|
|
172
|
+
"blockhash": "D9yVwZ9p8N...WdQsK",
|
|
173
|
+
"lastValidBlockHeight": 268495034,
|
|
174
|
+
"feePercentage": 2.5,
|
|
175
|
+
"feeWallet": "EGMQSHdW5vLvTZ4ro8BkGjWerXcWH7oHVW2EDMJqPA4x",
|
|
176
|
+
"isToken2022": true
|
|
177
|
+
},
|
|
178
|
+
"rawTransactionB64": "AYADg...",
|
|
179
|
+
"signatures": [
|
|
180
|
+
{
|
|
181
|
+
"pubkey": "F223612q76Cc223612q76Cc223612q76Cc223612q",
|
|
182
|
+
"signature": "3uXp9G..."
|
|
183
|
+
}
|
|
184
|
+
]
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Programmatic Library Usage
|
|
191
|
+
SMRE is fully typed and can be imported directly into node-based scripts:
|
|
192
|
+
```typescript
|
|
193
|
+
import { RpcAdapter, buildRecoveryInstructions, lamportsToSolString } from 'solana-mint-recovery-engine';
|
|
194
|
+
import { PublicKey, Transaction } from '@solana/web3.js';
|
|
195
|
+
|
|
196
|
+
async function main() {
|
|
197
|
+
const rpc = new RpcAdapter('https://api.mainnet-beta.solana.com', 'confirmed');
|
|
198
|
+
|
|
199
|
+
// Inspect
|
|
200
|
+
const target = new PublicKey('...');
|
|
201
|
+
const account = await rpc.inspectAccount(target);
|
|
202
|
+
|
|
203
|
+
const rentFloor = await rpc.getMinimumBalanceForRentExemption(account.dataSize);
|
|
204
|
+
const excess = account.balance - rentFloor;
|
|
205
|
+
|
|
206
|
+
console.log(`Recoverable amount: ${lamportsToSolString(excess)} SOL`);
|
|
207
|
+
|
|
208
|
+
if (excess > 0n && account.authority) {
|
|
209
|
+
const instructions = buildRecoveryInstructions({
|
|
210
|
+
source: target,
|
|
211
|
+
destination: new PublicKey('...'),
|
|
212
|
+
authority: account.authority,
|
|
213
|
+
excessAmount: excess,
|
|
214
|
+
priorityFee: 1000,
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
const tx = new Transaction().add(...instructions);
|
|
218
|
+
// Continue with sign/submit...
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## NPM Publishing Guide (Developer Release Flow)
|
|
226
|
+
|
|
227
|
+
Follow these steps to publish or update the package on the NPM registry:
|
|
228
|
+
|
|
229
|
+
### 1. Prerequisite Checks
|
|
230
|
+
Before publishing, ensure the codebase is completely clean, builds successfully, and passes all tests.
|
|
231
|
+
```bash
|
|
232
|
+
# Verify TypeScript compiles without errors
|
|
233
|
+
npm run lint
|
|
234
|
+
|
|
235
|
+
# Run Jest tests and verification tests
|
|
236
|
+
npm run test
|
|
237
|
+
npm run test:run
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### 2. Configure package.json Metadata
|
|
241
|
+
Ensure the following fields are correct in `package.json`:
|
|
242
|
+
- `"name"`: Set to your package name (e.g., `solana-mint-recovery-engine` or a scoped package name like `@my-org/smre`).
|
|
243
|
+
- `"publishConfig"`: If using a scoped package, you may want to set it to public:
|
|
244
|
+
```json
|
|
245
|
+
"publishConfig": {
|
|
246
|
+
"access": "public"
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### 3. Versioning
|
|
251
|
+
Increment the package version according to Semantic Versioning (SemVer):
|
|
252
|
+
- **Patch release** (bug fixes): `npm version patch` (e.g., `1.1.0` → `1.1.1`)
|
|
253
|
+
- **Minor release** (new backward-compatible features): `npm version minor` (e.g., `1.1.0` → `1.2.0`)
|
|
254
|
+
- **Major release** (breaking changes): `npm version major` (e.g., `1.1.0` → `2.0.0`)
|
|
255
|
+
|
|
256
|
+
This command updates `package.json`, `package-lock.json`, and creates a local Git tag.
|
|
257
|
+
|
|
258
|
+
### 4. Authenticate with NPM
|
|
259
|
+
If you are not logged in, run:
|
|
260
|
+
```bash
|
|
261
|
+
npm login
|
|
262
|
+
```
|
|
263
|
+
Provide your username, password, email, and one-time password (OTP).
|
|
264
|
+
|
|
265
|
+
### 5. Publish to the Registry
|
|
266
|
+
Publish the package. The `"prepack"` script in `package.json` will automatically trigger `npm run build` to compile the TypeScript code to `./dist` before packaging:
|
|
267
|
+
```bash
|
|
268
|
+
npm publish
|
|
269
|
+
```
|
|
270
|
+
If you are publishing a scoped package for the first time:
|
|
271
|
+
```bash
|
|
272
|
+
npm publish --access public
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
## License
|
|
278
|
+
MIT
|
package/dist/bin/smre.js
CHANGED
|
@@ -11,11 +11,22 @@ const submit_1 = require("../src/commands/submit");
|
|
|
11
11
|
const recover_1 = require("../src/commands/recover");
|
|
12
12
|
const config_1 = require("../src/commands/config");
|
|
13
13
|
const doctor_1 = require("../src/commands/doctor");
|
|
14
|
+
// HIGH-05 FIX: Read version dynamically from package.json
|
|
15
|
+
const packageJson = require('../package.json');
|
|
16
|
+
// MED-12: Global error handler for unhandled exceptions/rejections
|
|
17
|
+
process.on('unhandledRejection', (reason) => {
|
|
18
|
+
console.error(`\n✘ Unhandled error: ${reason}`);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
});
|
|
21
|
+
process.on('uncaughtException', (err) => {
|
|
22
|
+
console.error(`\n✘ Fatal error: ${err.message}`);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
});
|
|
14
25
|
const program = new commander_1.Command();
|
|
15
26
|
program
|
|
16
27
|
.name('smre')
|
|
17
28
|
.description('Solana Mint & Token Account Recovery Engine (SMRE) CLI')
|
|
18
|
-
.version(
|
|
29
|
+
.version(packageJson.version)
|
|
19
30
|
.option('-c, --config <path>', 'Path to custom configuration JSON file');
|
|
20
31
|
program
|
|
21
32
|
.command('scan <address-file>')
|
|
@@ -104,3 +115,4 @@ program
|
|
|
104
115
|
(0, doctor_1.runDoctor)(program.opts().config);
|
|
105
116
|
});
|
|
106
117
|
program.parse(process.argv);
|
|
118
|
+
//# sourceMappingURL=smre.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"smre.js","sourceRoot":"","sources":["../../bin/smre.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,40 +9,82 @@ const rpc_1 = require("../io/rpc");
|
|
|
9
9
|
const rent_1 = require("../core/rent");
|
|
10
10
|
const transaction_1 = require("../core/transaction");
|
|
11
11
|
const file_1 = require("../io/file");
|
|
12
|
+
const constants_1 = require("../core/constants");
|
|
13
|
+
const format_1 = require("../core/format");
|
|
14
|
+
const logger_1 = require("../io/logger");
|
|
12
15
|
const chalk_1 = __importDefault(require("chalk"));
|
|
13
16
|
async function runBuild(addressStr, options) {
|
|
14
17
|
try {
|
|
15
18
|
const config = (0, file_1.loadConfig)(options.config);
|
|
16
19
|
const rpc = new rpc_1.RpcAdapter(config.rpcUrl, config.commitment);
|
|
17
|
-
|
|
20
|
+
// MED-07: Validate public key input
|
|
21
|
+
let source;
|
|
22
|
+
try {
|
|
23
|
+
source = new web3_js_1.PublicKey(addressStr);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
throw new Error(`Invalid source address: "${addressStr}". Expected a valid base58-encoded public key.`);
|
|
27
|
+
}
|
|
18
28
|
// 1. Inspect account state
|
|
29
|
+
logger_1.logger.step(1, 8, 'Inspecting target account...');
|
|
19
30
|
const account = await rpc.inspectAccount(source);
|
|
31
|
+
// CRIT-03: Reject legacy token accounts
|
|
20
32
|
if (!account.isToken2022) {
|
|
21
|
-
throw new Error(`Account is
|
|
33
|
+
throw new Error(`Account ${source.toBase58()} is owned by the legacy SPL Token Program. ` +
|
|
34
|
+
`The WithdrawExcessLamports instruction is a Token-2022-only feature. ` +
|
|
35
|
+
`Recovery from legacy token accounts requires a different mechanism.`);
|
|
36
|
+
}
|
|
37
|
+
// HIGH-04: Use RPC-based rent floor
|
|
38
|
+
let rentFloor;
|
|
39
|
+
let excess;
|
|
40
|
+
try {
|
|
41
|
+
rentFloor = await rpc.getMinimumBalanceForRentExemption(account.dataSize);
|
|
42
|
+
excess = account.balance > rentFloor ? account.balance - rentFloor : 0n;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
const offlineResult = (0, rent_1.calculateExcessLamports)(account.dataSize, account.balance);
|
|
46
|
+
rentFloor = offlineResult.rentFloor;
|
|
47
|
+
excess = offlineResult.excess;
|
|
22
48
|
}
|
|
23
|
-
const { excess, rentFloor } = (0, rent_1.calculateExcessLamports)(account.dataSize, account.balance);
|
|
24
49
|
if (excess <= 0n) {
|
|
25
50
|
throw new Error(`Account has no excess lamports above the rent floor (${rentFloor.toLocaleString()} lamports).`);
|
|
26
51
|
}
|
|
27
52
|
// 2. Resolve destination
|
|
53
|
+
logger_1.logger.step(2, 8, 'Resolving destination...');
|
|
28
54
|
const destinationStr = options.destination || config.defaultDestination;
|
|
29
55
|
if (!destinationStr) {
|
|
30
56
|
throw new Error(`No destination address specified. Use --destination <address> or define defaultDestination in config.`);
|
|
31
57
|
}
|
|
32
|
-
|
|
58
|
+
let destination;
|
|
59
|
+
try {
|
|
60
|
+
destination = new web3_js_1.PublicKey(destinationStr);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
throw new Error(`Invalid destination address: "${destinationStr}". Expected a valid base58-encoded public key.`);
|
|
64
|
+
}
|
|
33
65
|
// 3. Resolve authority
|
|
34
|
-
|
|
66
|
+
logger_1.logger.step(3, 8, 'Resolving authority...');
|
|
67
|
+
let authority = account.authority;
|
|
35
68
|
if (!authority) {
|
|
36
|
-
|
|
69
|
+
if (account.type === 'mint') {
|
|
70
|
+
authority = source;
|
|
71
|
+
// CRIT-05: Clear messaging about what this means
|
|
72
|
+
logger_1.logger.warn(`Mint authority is revoked. Recovery will require the ORIGINAL MINT KEYPAIR ` +
|
|
73
|
+
`(the private key that created mint address ${source.toBase58()}). ` +
|
|
74
|
+
`If you do not possess this keypair, the transaction will fail to sign.`);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
throw new Error(`No authority found for account ${source.toBase58()}. Recovery is not possible.`);
|
|
78
|
+
}
|
|
37
79
|
}
|
|
38
80
|
// 4. Resolve fee payer
|
|
81
|
+
logger_1.logger.step(4, 8, 'Resolving fee payer...');
|
|
39
82
|
let feePayer = authority;
|
|
40
83
|
if (account.type === 'multisig' && account.multisigSigners && account.multisigSigners.length > 0) {
|
|
41
84
|
if (options.feePayer) {
|
|
42
85
|
feePayer = new web3_js_1.PublicKey(options.feePayer);
|
|
43
86
|
}
|
|
44
87
|
else {
|
|
45
|
-
// Default to the first co-signer for multisig accounts
|
|
46
88
|
feePayer = account.multisigSigners[0];
|
|
47
89
|
}
|
|
48
90
|
}
|
|
@@ -50,14 +92,26 @@ async function runBuild(addressStr, options) {
|
|
|
50
92
|
feePayer = new web3_js_1.PublicKey(options.feePayer);
|
|
51
93
|
}
|
|
52
94
|
// 5. Resolve platform fee wallet & percentage
|
|
95
|
+
logger_1.logger.step(5, 8, 'Resolving fee configuration...');
|
|
53
96
|
const feeWalletStr = options.feeWallet || config.feeWallet;
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
97
|
+
// CRIT-06 FIX: Proper fee percentage parsing with validation
|
|
98
|
+
let feePercentage = config.feePercentage;
|
|
99
|
+
if (options.feePercent !== undefined) {
|
|
100
|
+
const parsed = parseFloat(options.feePercent);
|
|
101
|
+
if (isNaN(parsed) || parsed < 0 || parsed > 100) {
|
|
102
|
+
throw new Error(`Invalid fee percentage: "${options.feePercent}". Must be a number between 0 and 100.`);
|
|
103
|
+
}
|
|
104
|
+
feePercentage = parsed;
|
|
105
|
+
}
|
|
57
106
|
const feeWallet = feeWalletStr ? new web3_js_1.PublicKey(feeWalletStr) : undefined;
|
|
58
|
-
|
|
59
|
-
|
|
107
|
+
if (feeWallet && feePercentage > 0) {
|
|
108
|
+
logger_1.logger.info(`Platform fee: ${feePercentage}% to ${feeWallet.toBase58()}`);
|
|
109
|
+
}
|
|
110
|
+
// 6. Fetch latest blockhash (HIGH-02: include lastValidBlockHeight)
|
|
111
|
+
logger_1.logger.step(6, 8, 'Fetching latest blockhash...');
|
|
112
|
+
const { blockhash, lastValidBlockHeight } = await rpc.getLatestBlockhash();
|
|
60
113
|
// 7. Build instructions
|
|
114
|
+
logger_1.logger.step(7, 8, 'Building transaction instructions...');
|
|
61
115
|
const instructions = (0, transaction_1.buildRecoveryInstructions)({
|
|
62
116
|
source,
|
|
63
117
|
destination,
|
|
@@ -66,8 +120,11 @@ async function runBuild(addressStr, options) {
|
|
|
66
120
|
developerFeeWallet: feeWallet,
|
|
67
121
|
feePercentage,
|
|
68
122
|
multiSigners: account.multisigSigners,
|
|
123
|
+
programId: account.ownerProgram,
|
|
124
|
+
priorityFee: config.priorityFee,
|
|
69
125
|
});
|
|
70
126
|
// 8. Compile transaction
|
|
127
|
+
logger_1.logger.step(8, 8, 'Compiling transaction...');
|
|
71
128
|
const tx = new web3_js_1.Transaction();
|
|
72
129
|
tx.add(...instructions);
|
|
73
130
|
tx.recentBlockhash = blockhash;
|
|
@@ -78,9 +135,9 @@ async function runBuild(addressStr, options) {
|
|
|
78
135
|
verifySignatures: false,
|
|
79
136
|
});
|
|
80
137
|
const rawTransactionB64 = serialized.toString('base64');
|
|
81
|
-
//
|
|
138
|
+
// Create envelope JSON
|
|
82
139
|
const envelope = {
|
|
83
|
-
version:
|
|
140
|
+
version: constants_1.ENVELOPE_VERSION,
|
|
84
141
|
type: 'withdraw-excess-lamports',
|
|
85
142
|
metadata: {
|
|
86
143
|
sourceAddress: source.toBase58(),
|
|
@@ -91,6 +148,10 @@ async function runBuild(addressStr, options) {
|
|
|
91
148
|
rentFloor: rentFloor.toString(),
|
|
92
149
|
createdAt: new Date().toISOString(),
|
|
93
150
|
blockhash,
|
|
151
|
+
lastValidBlockHeight,
|
|
152
|
+
isToken2022: account.isToken2022,
|
|
153
|
+
feePercentage: feeWallet ? feePercentage : undefined,
|
|
154
|
+
feeWallet: feeWallet?.toBase58(),
|
|
94
155
|
},
|
|
95
156
|
rawTransactionB64,
|
|
96
157
|
signatures: [],
|
|
@@ -120,20 +181,25 @@ async function runBuild(addressStr, options) {
|
|
|
120
181
|
}
|
|
121
182
|
// Write envelope file
|
|
122
183
|
(0, file_1.writeEnvelope)(options.output, envelope);
|
|
123
|
-
|
|
124
|
-
console.log('\nTransaction
|
|
184
|
+
logger_1.logger.success(`Built transaction envelope → ${options.output}`);
|
|
185
|
+
console.log('\nTransaction Details:');
|
|
125
186
|
console.log(` Source: ${envelope.metadata.sourceAddress} (${envelope.metadata.sourceType})`);
|
|
126
187
|
console.log(` Destination: ${envelope.metadata.destinationAddress}`);
|
|
127
188
|
console.log(` Authority: ${envelope.metadata.authorityAddress}`);
|
|
128
189
|
console.log(` Fee Payer: ${feePayer.toBase58()}`);
|
|
129
|
-
console.log(` Recoverable: ${
|
|
130
|
-
if (feeWallet) {
|
|
131
|
-
|
|
190
|
+
console.log(` Recoverable: ${(0, format_1.formatSol)(excess)}`);
|
|
191
|
+
if (feeWallet && feePercentage > 0) {
|
|
192
|
+
const feeAmount = (excess * BigInt(Math.round(feePercentage * 100))) / 10000n;
|
|
193
|
+
const netAmount = excess - feeAmount;
|
|
194
|
+
console.log(` Fee Split: ${feePercentage}% → ${feeWallet.toBase58()} (${(0, format_1.formatSol)(feeAmount)})`);
|
|
195
|
+
console.log(` Net Amount: ${(0, format_1.formatSol)(netAmount)}`);
|
|
132
196
|
}
|
|
133
197
|
console.log(` Blockhash: ${envelope.metadata.blockhash}`);
|
|
198
|
+
console.log(chalk_1.default.yellow(` ⚠ Blockhash expires in ~60-90 seconds. Sign and submit promptly.`));
|
|
134
199
|
}
|
|
135
200
|
catch (err) {
|
|
136
|
-
|
|
201
|
+
logger_1.logger.fail(`Failed to build transaction: ${err.message}`);
|
|
137
202
|
process.exit(1);
|
|
138
203
|
}
|
|
139
204
|
}
|
|
205
|
+
//# sourceMappingURL=build.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build.js","sourceRoot":"","sources":["../../../src/commands/build.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,16 +6,20 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.initConfig = initConfig;
|
|
7
7
|
const file_1 = require("../io/file");
|
|
8
8
|
const validation_1 = require("../core/validation");
|
|
9
|
+
const logger_1 = require("../io/logger");
|
|
9
10
|
const chalk_1 = __importDefault(require("chalk"));
|
|
10
11
|
function initConfig(outputPath = 'smre.config.json') {
|
|
11
12
|
try {
|
|
12
13
|
const defaultConfig = validation_1.ConfigSchema.parse({});
|
|
13
14
|
(0, file_1.writeConfig)(outputPath, defaultConfig);
|
|
14
|
-
|
|
15
|
+
logger_1.logger.success(`Initialized default configuration at ${outputPath}`);
|
|
15
16
|
console.log(JSON.stringify(defaultConfig, null, 2));
|
|
17
|
+
console.log(chalk_1.default.yellow(`\n ⚠ Review and customize this file before use.\n` +
|
|
18
|
+
` In particular, set your defaultDestination and review fee settings.`));
|
|
16
19
|
}
|
|
17
20
|
catch (err) {
|
|
18
|
-
|
|
21
|
+
logger_1.logger.fail(`Failed to initialize configuration: ${err.message}`);
|
|
19
22
|
process.exit(1);
|
|
20
23
|
}
|
|
21
24
|
}
|
|
25
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/commands/config.ts"],"names":[],"mappings":";;;;;AAKA,gCAcC;AAnBD,qCAAyC;AACzC,mDAAkD;AAClD,yCAAsC;AACtC,kDAA0B;AAE1B,SAAgB,UAAU,CAAC,aAAqB,kBAAkB;IAChE,IAAI,CAAC;QACH,MAAM,aAAa,GAAG,yBAAY,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC7C,IAAA,kBAAW,EAAC,UAAU,EAAE,aAAa,CAAC,CAAC;QACvC,eAAM,CAAC,OAAO,CAAC,wCAAwC,UAAU,EAAE,CAAC,CAAC;QACrE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QACpD,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,MAAM,CACtB,oDAAoD;YACpD,uEAAuE,CACxE,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,eAAM,CAAC,IAAI,CAAC,uCAAwC,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QAC7E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC"}
|
|
@@ -6,39 +6,88 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.runDoctor = runDoctor;
|
|
7
7
|
const web3_js_1 = require("@solana/web3.js");
|
|
8
8
|
const file_1 = require("../io/file");
|
|
9
|
+
const constants_1 = require("../core/constants");
|
|
10
|
+
const logger_1 = require("../io/logger");
|
|
9
11
|
const chalk_1 = __importDefault(require("chalk"));
|
|
10
12
|
async function runDoctor(configPath) {
|
|
11
|
-
console.log(chalk_1.default.bold('
|
|
13
|
+
console.log(chalk_1.default.bold('┌─────────────────────────────────────────┐'));
|
|
14
|
+
console.log(chalk_1.default.bold('│ SMRE Doctor — System Diagnostics │'));
|
|
15
|
+
console.log(chalk_1.default.bold('└─────────────────────────────────────────┘'));
|
|
12
16
|
let isHealthy = true;
|
|
13
|
-
|
|
17
|
+
let checkCount = 0;
|
|
18
|
+
const totalChecks = 5;
|
|
19
|
+
// 1. Check Node.js version (MED-09)
|
|
20
|
+
checkCount++;
|
|
21
|
+
logger_1.logger.step(checkCount, totalChecks, 'Checking Node.js version...');
|
|
22
|
+
const nodeVersion = process.versions.node;
|
|
23
|
+
const majorVersion = parseInt(nodeVersion.split('.')[0], 10);
|
|
24
|
+
if (majorVersion >= constants_1.MIN_NODE_VERSION) {
|
|
25
|
+
logger_1.logger.success(`Node.js v${nodeVersion} (>= v${constants_1.MIN_NODE_VERSION} required)`);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
logger_1.logger.fail(`Node.js v${nodeVersion} — version >= v${constants_1.MIN_NODE_VERSION} is required!`);
|
|
29
|
+
isHealthy = false;
|
|
30
|
+
}
|
|
31
|
+
// 2. Check dependency availability
|
|
32
|
+
checkCount++;
|
|
33
|
+
logger_1.logger.step(checkCount, totalChecks, 'Checking critical dependencies...');
|
|
34
|
+
try {
|
|
35
|
+
require('@solana/web3.js');
|
|
36
|
+
require('@solana/spl-token');
|
|
37
|
+
require('bs58');
|
|
38
|
+
logger_1.logger.success('All critical dependencies available.');
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
logger_1.logger.fail(`Missing dependency: ${err.message}`);
|
|
42
|
+
console.log(chalk_1.default.yellow(' Run: npm install'));
|
|
43
|
+
isHealthy = false;
|
|
44
|
+
}
|
|
45
|
+
// 3. Validate Config Load
|
|
46
|
+
checkCount++;
|
|
47
|
+
logger_1.logger.step(checkCount, totalChecks, 'Validating configuration...');
|
|
14
48
|
try {
|
|
15
49
|
const config = (0, file_1.loadConfig)(configPath);
|
|
16
|
-
|
|
17
|
-
console.log(` RPC Endpoint:
|
|
18
|
-
console.log(` Commitment:
|
|
19
|
-
console.log(` Default Destination: ${config.defaultDestination || '
|
|
20
|
-
console.log(`
|
|
21
|
-
|
|
22
|
-
console.log(
|
|
50
|
+
logger_1.logger.success('Configuration loaded successfully.');
|
|
51
|
+
console.log(` RPC Endpoint: ${config.rpcUrl}`);
|
|
52
|
+
console.log(` Commitment: ${config.commitment}`);
|
|
53
|
+
console.log(` Default Destination: ${config.defaultDestination || chalk_1.default.yellow('Not configured')}`);
|
|
54
|
+
console.log(` Fee Wallet: ${config.feeWallet || chalk_1.default.gray('None (no fee)')}`);
|
|
55
|
+
console.log(` Fee Percentage: ${config.feePercentage}%`);
|
|
56
|
+
console.log(` Priority Fee: ${config.priorityFee} micro-lamports/CU`);
|
|
57
|
+
// 4. Validate RPC Connectivity
|
|
58
|
+
checkCount++;
|
|
59
|
+
logger_1.logger.step(checkCount, totalChecks, 'Testing RPC connectivity...');
|
|
23
60
|
const startTime = Date.now();
|
|
24
61
|
const connection = new web3_js_1.Connection(config.rpcUrl, config.commitment);
|
|
25
62
|
const version = await connection.getVersion();
|
|
26
63
|
const latency = Date.now() - startTime;
|
|
27
|
-
|
|
28
|
-
console.log(` Latency: ${latency}ms`);
|
|
64
|
+
logger_1.logger.success(`Connected to Solana node v${version['solana-core']}`);
|
|
65
|
+
console.log(` Latency: ${latency}ms ${latency > 1000 ? chalk_1.default.yellow('(slow — consider a dedicated RPC)') : chalk_1.default.green('(good)')}`);
|
|
66
|
+
// 5. Test blockhash and slot
|
|
67
|
+
checkCount++;
|
|
68
|
+
logger_1.logger.step(checkCount, totalChecks, 'Verifying chain state...');
|
|
29
69
|
const slot = await connection.getSlot();
|
|
30
|
-
|
|
70
|
+
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
|
|
71
|
+
logger_1.logger.success(`Latest confirmed slot: ${slot}`);
|
|
72
|
+
console.log(` Blockhash: ${blockhash}`);
|
|
73
|
+
console.log(` Last valid block height: ${lastValidBlockHeight}`);
|
|
74
|
+
// Test rent RPC
|
|
75
|
+
const rentExempt = await connection.getMinimumBalanceForRentExemption(165);
|
|
76
|
+
logger_1.logger.success(`Rent exempt (165 bytes): ${rentExempt.toLocaleString()} lamports`);
|
|
31
77
|
}
|
|
32
78
|
catch (err) {
|
|
33
|
-
|
|
79
|
+
logger_1.logger.fail(`Diagnostic failed: ${err.message}`);
|
|
34
80
|
isHealthy = false;
|
|
35
81
|
}
|
|
82
|
+
// Final verdict
|
|
83
|
+
console.log('');
|
|
36
84
|
if (isHealthy) {
|
|
37
|
-
console.log(chalk_1.default.bold.green('
|
|
85
|
+
console.log(chalk_1.default.bold.green('✔ All checks passed! Environment is healthy.'));
|
|
38
86
|
process.exit(0);
|
|
39
87
|
}
|
|
40
88
|
else {
|
|
41
|
-
console.log(chalk_1.default.bold.red('
|
|
89
|
+
console.log(chalk_1.default.bold.red('✘ Some checks failed. Please review the output above.'));
|
|
42
90
|
process.exit(1);
|
|
43
91
|
}
|
|
44
92
|
}
|
|
93
|
+
//# sourceMappingURL=doctor.js.map
|