solana-mint-recovery-engine 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/dist/bin/smre.d.ts +2 -0
- package/dist/bin/smre.js +106 -0
- package/dist/src/commands/build.d.ts +8 -0
- package/dist/src/commands/build.js +139 -0
- package/dist/src/commands/config.d.ts +1 -0
- package/dist/src/commands/config.js +21 -0
- package/dist/src/commands/doctor.d.ts +1 -0
- package/dist/src/commands/doctor.js +44 -0
- package/dist/src/commands/inspect.d.ts +4 -0
- package/dist/src/commands/inspect.js +76 -0
- package/dist/src/commands/recover.d.ts +7 -0
- package/dist/src/commands/recover.js +109 -0
- package/dist/src/commands/scan.d.ts +4 -0
- package/dist/src/commands/scan.js +126 -0
- package/dist/src/commands/sign.d.ts +4 -0
- package/dist/src/commands/sign.js +55 -0
- package/dist/src/commands/simulate.d.ts +3 -0
- package/dist/src/commands/simulate.js +50 -0
- package/dist/src/commands/submit.d.ts +3 -0
- package/dist/src/commands/submit.js +57 -0
- package/dist/src/core/rent.d.ts +19 -0
- package/dist/src/core/rent.js +26 -0
- package/dist/src/core/transaction.d.ts +22 -0
- package/dist/src/core/transaction.js +54 -0
- package/dist/src/core/validation.d.ts +124 -0
- package/dist/src/core/validation.js +35 -0
- package/dist/src/io/file.d.ts +23 -0
- package/dist/src/io/file.js +140 -0
- package/dist/src/io/rpc.d.ts +36 -0
- package/dist/src/io/rpc.js +155 -0
- package/dist/src/signers/signer.d.ts +22 -0
- package/dist/src/signers/signer.js +114 -0
- package/package.json +40 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.runSign = runSign;
|
|
7
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
8
|
+
const file_1 = require("../io/file");
|
|
9
|
+
const signer_1 = require("../signers/signer");
|
|
10
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
11
|
+
const bs58_1 = __importDefault(require("bs58"));
|
|
12
|
+
async function runSign(envelopeFile, options) {
|
|
13
|
+
try {
|
|
14
|
+
// 1. Load envelope
|
|
15
|
+
const envelope = (0, file_1.loadEnvelope)(envelopeFile);
|
|
16
|
+
// 2. Load keypair
|
|
17
|
+
const keypair = (0, signer_1.loadKeypair)(options.keypair);
|
|
18
|
+
const signerPubkey = keypair.publicKey;
|
|
19
|
+
// 3. Decode transaction
|
|
20
|
+
const tx = web3_js_1.Transaction.from(Buffer.from(envelope.rawTransactionB64, 'base64'));
|
|
21
|
+
// Check if this keypair is expected to sign
|
|
22
|
+
const isExpected = envelope.signatures.some(s => s.pubkey === signerPubkey.toBase58());
|
|
23
|
+
if (!isExpected) {
|
|
24
|
+
console.warn(chalk_1.default.yellow(`⚠ Warning: Keypair ${signerPubkey.toBase58()} is not in the list of expected signers for this transaction.`));
|
|
25
|
+
}
|
|
26
|
+
// 4. Sign transaction
|
|
27
|
+
console.log(`Signing transaction using key: ${signerPubkey.toBase58()}...`);
|
|
28
|
+
tx.partialSign(keypair);
|
|
29
|
+
// 5. Update raw transaction and signature metadata in envelope
|
|
30
|
+
const updatedSerialized = tx.serialize({
|
|
31
|
+
requireAllSignatures: false,
|
|
32
|
+
verifySignatures: false,
|
|
33
|
+
});
|
|
34
|
+
envelope.rawTransactionB64 = updatedSerialized.toString('base64');
|
|
35
|
+
// Update signature slots in envelope metadata
|
|
36
|
+
let signedCount = 0;
|
|
37
|
+
for (const sigSlot of envelope.signatures) {
|
|
38
|
+
const pubkeyObj = new web3_js_1.PublicKey(sigSlot.pubkey);
|
|
39
|
+
const signatureBuffer = tx.signatures.find(s => s.publicKey.equals(pubkeyObj))?.signature;
|
|
40
|
+
if (signatureBuffer) {
|
|
41
|
+
sigSlot.signature = bs58_1.default.encode(signatureBuffer);
|
|
42
|
+
signedCount++;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// 6. Write signed envelope back
|
|
46
|
+
const outputPath = options.output || envelopeFile;
|
|
47
|
+
(0, file_1.writeEnvelope)(outputPath, envelope);
|
|
48
|
+
console.log(chalk_1.default.green(`✔ Successfully signed transaction envelope! Saved to ${outputPath}`));
|
|
49
|
+
console.log(`Signatures: ${signedCount} of ${envelope.signatures.length} complete.`);
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
console.error(chalk_1.default.red(`✘ Failed to sign transaction: ${err.message}`));
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.runSimulate = runSimulate;
|
|
7
|
+
const rpc_1 = require("../io/rpc");
|
|
8
|
+
const file_1 = require("../io/file");
|
|
9
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
+
async function runSimulate(envelopeFile, options) {
|
|
11
|
+
try {
|
|
12
|
+
const config = (0, file_1.loadConfig)(options.config);
|
|
13
|
+
const rpc = new rpc_1.RpcAdapter(config.rpcUrl, config.commitment);
|
|
14
|
+
const envelope = (0, file_1.loadEnvelope)(envelopeFile);
|
|
15
|
+
console.log(`Loading transaction from ${envelopeFile}...`);
|
|
16
|
+
const rawTxBuf = Buffer.from(envelope.rawTransactionB64, 'base64');
|
|
17
|
+
console.log('Simulating transaction on-chain...');
|
|
18
|
+
const result = await rpc.simulateTransaction(rawTxBuf);
|
|
19
|
+
if (result.value.err) {
|
|
20
|
+
console.error(chalk_1.default.red('\n✘ Simulation FAILED!'));
|
|
21
|
+
console.error(chalk_1.default.red(`Error details: ${JSON.stringify(result.value.err)}`));
|
|
22
|
+
if (result.value.logs) {
|
|
23
|
+
console.log('\nSimulation Logs:');
|
|
24
|
+
result.value.logs.forEach((log) => console.log(chalk_1.default.gray(` ${log}`)));
|
|
25
|
+
}
|
|
26
|
+
process.exit(5);
|
|
27
|
+
}
|
|
28
|
+
console.log(chalk_1.default.green('\n✔ Simulation SUCCESSFUL!'));
|
|
29
|
+
console.log(`Units Consumed: ${result.value.unitsConsumed || 'N/A'}`);
|
|
30
|
+
if (result.value.logs) {
|
|
31
|
+
console.log('\nSimulation Logs:');
|
|
32
|
+
result.value.logs.forEach((log) => {
|
|
33
|
+
if (log.includes('success') || log.includes('Success')) {
|
|
34
|
+
console.log(chalk_1.default.green(` ${log}`));
|
|
35
|
+
}
|
|
36
|
+
else if (log.includes('Instruction')) {
|
|
37
|
+
console.log(chalk_1.default.cyan(` ${log}`));
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
console.log(chalk_1.default.gray(` ${log}`));
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
process.exit(0);
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
console.error(chalk_1.default.red(`✘ Simulation error: ${err.message}`));
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.runSubmit = runSubmit;
|
|
7
|
+
const rpc_1 = require("../io/rpc");
|
|
8
|
+
const file_1 = require("../io/file");
|
|
9
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
10
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
11
|
+
async function runSubmit(envelopeFile, options) {
|
|
12
|
+
try {
|
|
13
|
+
const config = (0, file_1.loadConfig)(options.config);
|
|
14
|
+
const rpc = new rpc_1.RpcAdapter(config.rpcUrl, config.commitment);
|
|
15
|
+
const envelope = (0, file_1.loadEnvelope)(envelopeFile);
|
|
16
|
+
// Decode transaction to read feePayer
|
|
17
|
+
const tx = web3_js_1.Transaction.from(Buffer.from(envelope.rawTransactionB64, 'base64'));
|
|
18
|
+
const feePayer = tx.feePayer;
|
|
19
|
+
if (!feePayer) {
|
|
20
|
+
throw new Error('Transaction has no fee payer specified.');
|
|
21
|
+
}
|
|
22
|
+
const threshold = envelope.metadata.multisigThreshold;
|
|
23
|
+
if (threshold !== undefined) {
|
|
24
|
+
// Multisig Account validation:
|
|
25
|
+
// Count how many co-signers have signed
|
|
26
|
+
const multisigSigners = envelope.signatures.filter(s => s.pubkey !== feePayer.toBase58());
|
|
27
|
+
const signedCoSigners = multisigSigners.filter(s => s.signature !== null).length;
|
|
28
|
+
if (signedCoSigners < threshold) {
|
|
29
|
+
throw new Error(`Multisig threshold of ${threshold} co-signers not met. Current co-signatures: ${signedCoSigners}/${multisigSigners.length}.`);
|
|
30
|
+
}
|
|
31
|
+
// Check fee payer signature
|
|
32
|
+
const feePayerSlot = envelope.signatures.find(s => s.pubkey === feePayer.toBase58());
|
|
33
|
+
if (feePayerSlot && feePayerSlot.signature === null) {
|
|
34
|
+
throw new Error(`Fee payer ${feePayer.toBase58()} signature is missing.`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
// Standard Account validation: all signatures must be complete
|
|
39
|
+
const missingSignatures = envelope.signatures.filter(s => s.signature === null);
|
|
40
|
+
if (missingSignatures.length > 0) {
|
|
41
|
+
throw new Error(`Transaction envelope is missing signatures for: ${missingSignatures.map(s => s.pubkey).join(', ')}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
console.log(`Loading signed transaction from ${envelopeFile}...`);
|
|
45
|
+
const rawTxBuf = Buffer.from(envelope.rawTransactionB64, 'base64');
|
|
46
|
+
console.log('Broadcasting transaction to Solana network...');
|
|
47
|
+
const signature = await rpc.submitTransaction(rawTxBuf);
|
|
48
|
+
console.log(chalk_1.default.green('\n✔ Transaction submitted successfully!'));
|
|
49
|
+
console.log(`Signature: ${signature}`);
|
|
50
|
+
console.log(chalk_1.default.cyan(`Explorer Link: https://explorer.solana.com/tx/${signature}?cluster=custom&customUrl=${encodeURIComponent(config.rpcUrl)}`));
|
|
51
|
+
process.exit(0);
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
console.error(chalk_1.default.red(`✘ Submission failed: ${err.message}`));
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface RentParams {
|
|
2
|
+
lamportsPerByteYear: bigint;
|
|
3
|
+
exemptionMultiplier: bigint;
|
|
4
|
+
}
|
|
5
|
+
export declare const DEFAULT_RENT_PARAMS: RentParams;
|
|
6
|
+
/**
|
|
7
|
+
* Calculates the exact rent-exempt floor and any excess lamports in an account.
|
|
8
|
+
*
|
|
9
|
+
* Formula:
|
|
10
|
+
* RentFloor = (dataSize + 128 bytes metadata) * lamportsPerByteYear * exemptionMultiplier
|
|
11
|
+
*
|
|
12
|
+
* @param dataSize The size of the account data in bytes
|
|
13
|
+
* @param balance The current balance of the account in lamports
|
|
14
|
+
* @param rentParams Parameters for rent calculation (defaults to Solana Mainnet)
|
|
15
|
+
*/
|
|
16
|
+
export declare function calculateExcessLamports(dataSize: number, balance: bigint, rentParams?: RentParams): {
|
|
17
|
+
excess: bigint;
|
|
18
|
+
rentFloor: bigint;
|
|
19
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_RENT_PARAMS = void 0;
|
|
4
|
+
exports.calculateExcessLamports = calculateExcessLamports;
|
|
5
|
+
exports.DEFAULT_RENT_PARAMS = {
|
|
6
|
+
lamportsPerByteYear: 3480n,
|
|
7
|
+
exemptionMultiplier: 2n,
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Calculates the exact rent-exempt floor and any excess lamports in an account.
|
|
11
|
+
*
|
|
12
|
+
* Formula:
|
|
13
|
+
* RentFloor = (dataSize + 128 bytes metadata) * lamportsPerByteYear * exemptionMultiplier
|
|
14
|
+
*
|
|
15
|
+
* @param dataSize The size of the account data in bytes
|
|
16
|
+
* @param balance The current balance of the account in lamports
|
|
17
|
+
* @param rentParams Parameters for rent calculation (defaults to Solana Mainnet)
|
|
18
|
+
*/
|
|
19
|
+
function calculateExcessLamports(dataSize, balance, rentParams = exports.DEFAULT_RENT_PARAMS) {
|
|
20
|
+
const baseSize = BigInt(dataSize) + 128n;
|
|
21
|
+
const rentFloor = baseSize * rentParams.lamportsPerByteYear * rentParams.exemptionMultiplier;
|
|
22
|
+
if (balance <= rentFloor) {
|
|
23
|
+
return { excess: 0n, rentFloor };
|
|
24
|
+
}
|
|
25
|
+
return { excess: balance - rentFloor, rentFloor };
|
|
26
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { PublicKey, TransactionInstruction } from '@solana/web3.js';
|
|
2
|
+
export declare const TOKEN_2022_PROGRAM_ID: PublicKey;
|
|
3
|
+
/**
|
|
4
|
+
* Manually constructs the WithdrawExcessLamports instruction for Token-2022.
|
|
5
|
+
* Discriminator: 38
|
|
6
|
+
*/
|
|
7
|
+
export declare function createWithdrawExcessLamportsInstruction(source: PublicKey, destination: PublicKey, authority: PublicKey, multiSigners?: PublicKey[]): TransactionInstruction;
|
|
8
|
+
export interface RecoveryTxParams {
|
|
9
|
+
source: PublicKey;
|
|
10
|
+
destination: PublicKey;
|
|
11
|
+
authority: PublicKey;
|
|
12
|
+
excessAmount: bigint;
|
|
13
|
+
developerFeeWallet?: PublicKey;
|
|
14
|
+
feePercentage?: number;
|
|
15
|
+
multiSigners?: PublicKey[];
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Builds the transaction instructions for recovering excess lamports.
|
|
19
|
+
* Uses the Token-2022 WithdrawExcessLamports instruction.
|
|
20
|
+
* Optionally appends a SystemProgram transfer for fee splitting.
|
|
21
|
+
*/
|
|
22
|
+
export declare function buildRecoveryInstructions(params: RecoveryTxParams): TransactionInstruction[];
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TOKEN_2022_PROGRAM_ID = void 0;
|
|
4
|
+
exports.createWithdrawExcessLamportsInstruction = createWithdrawExcessLamportsInstruction;
|
|
5
|
+
exports.buildRecoveryInstructions = buildRecoveryInstructions;
|
|
6
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
7
|
+
exports.TOKEN_2022_PROGRAM_ID = new web3_js_1.PublicKey('TokenzQdBNbLqP55ePE1R24g5652uhGzTsw34JmUCwm');
|
|
8
|
+
/**
|
|
9
|
+
* Manually constructs the WithdrawExcessLamports instruction for Token-2022.
|
|
10
|
+
* Discriminator: 38
|
|
11
|
+
*/
|
|
12
|
+
function createWithdrawExcessLamportsInstruction(source, destination, authority, multiSigners = []) {
|
|
13
|
+
const keys = [
|
|
14
|
+
{ pubkey: source, isSigner: false, isWritable: true },
|
|
15
|
+
{ pubkey: destination, isSigner: false, isWritable: true },
|
|
16
|
+
{ pubkey: authority, isSigner: multiSigners.length === 0, isWritable: false },
|
|
17
|
+
];
|
|
18
|
+
for (const signer of multiSigners) {
|
|
19
|
+
keys.push({ pubkey: signer, isSigner: true, isWritable: false });
|
|
20
|
+
}
|
|
21
|
+
// Discriminator is 38
|
|
22
|
+
const data = Buffer.alloc(1);
|
|
23
|
+
data.writeUInt8(38, 0);
|
|
24
|
+
return new web3_js_1.TransactionInstruction({
|
|
25
|
+
keys,
|
|
26
|
+
programId: exports.TOKEN_2022_PROGRAM_ID,
|
|
27
|
+
data,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Builds the transaction instructions for recovering excess lamports.
|
|
32
|
+
* Uses the Token-2022 WithdrawExcessLamports instruction.
|
|
33
|
+
* Optionally appends a SystemProgram transfer for fee splitting.
|
|
34
|
+
*/
|
|
35
|
+
function buildRecoveryInstructions(params) {
|
|
36
|
+
const { source, destination, authority, excessAmount, developerFeeWallet, feePercentage = 10, multiSigners = [], } = params;
|
|
37
|
+
const instructions = [];
|
|
38
|
+
// 1. Create the WithdrawExcessLamports instruction
|
|
39
|
+
const withdrawInstruction = createWithdrawExcessLamportsInstruction(source, destination, authority, multiSigners);
|
|
40
|
+
instructions.push(withdrawInstruction);
|
|
41
|
+
// 2. If developer fee is requested, calculate and add the transfer instruction
|
|
42
|
+
if (developerFeeWallet && feePercentage > 0) {
|
|
43
|
+
const feeAmount = (excessAmount * BigInt(feePercentage)) / 100n;
|
|
44
|
+
if (feeAmount > 0n) {
|
|
45
|
+
const feeTransferInstruction = web3_js_1.SystemProgram.transfer({
|
|
46
|
+
fromPubkey: destination,
|
|
47
|
+
toPubkey: developerFeeWallet,
|
|
48
|
+
lamports: Number(feeAmount),
|
|
49
|
+
});
|
|
50
|
+
instructions.push(feeTransferInstruction);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return instructions;
|
|
54
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const ConfigSchema: z.ZodObject<{
|
|
3
|
+
rpcUrl: z.ZodDefault<z.ZodString>;
|
|
4
|
+
commitment: z.ZodDefault<z.ZodEnum<["processed", "confirmed", "finalized"]>>;
|
|
5
|
+
defaultDestination: z.ZodOptional<z.ZodString>;
|
|
6
|
+
feeWallet: z.ZodOptional<z.ZodString>;
|
|
7
|
+
feePercentage: z.ZodDefault<z.ZodNumber>;
|
|
8
|
+
rentOverride: z.ZodOptional<z.ZodObject<{
|
|
9
|
+
lamportsPerByteYear: z.ZodString;
|
|
10
|
+
exemptionMultiplier: z.ZodString;
|
|
11
|
+
}, "strip", z.ZodTypeAny, {
|
|
12
|
+
lamportsPerByteYear: string;
|
|
13
|
+
exemptionMultiplier: string;
|
|
14
|
+
}, {
|
|
15
|
+
lamportsPerByteYear: string;
|
|
16
|
+
exemptionMultiplier: string;
|
|
17
|
+
}>>;
|
|
18
|
+
}, "strip", z.ZodTypeAny, {
|
|
19
|
+
feePercentage: number;
|
|
20
|
+
rpcUrl: string;
|
|
21
|
+
commitment: "processed" | "confirmed" | "finalized";
|
|
22
|
+
defaultDestination?: string | undefined;
|
|
23
|
+
feeWallet?: string | undefined;
|
|
24
|
+
rentOverride?: {
|
|
25
|
+
lamportsPerByteYear: string;
|
|
26
|
+
exemptionMultiplier: string;
|
|
27
|
+
} | undefined;
|
|
28
|
+
}, {
|
|
29
|
+
feePercentage?: number | undefined;
|
|
30
|
+
rpcUrl?: string | undefined;
|
|
31
|
+
commitment?: "processed" | "confirmed" | "finalized" | undefined;
|
|
32
|
+
defaultDestination?: string | undefined;
|
|
33
|
+
feeWallet?: string | undefined;
|
|
34
|
+
rentOverride?: {
|
|
35
|
+
lamportsPerByteYear: string;
|
|
36
|
+
exemptionMultiplier: string;
|
|
37
|
+
} | undefined;
|
|
38
|
+
}>;
|
|
39
|
+
export type Config = z.infer<typeof ConfigSchema>;
|
|
40
|
+
export declare const TxEnvelopeSchema: z.ZodObject<{
|
|
41
|
+
version: z.ZodDefault<z.ZodString>;
|
|
42
|
+
type: z.ZodLiteral<"withdraw-excess-lamports">;
|
|
43
|
+
metadata: z.ZodObject<{
|
|
44
|
+
sourceAddress: z.ZodString;
|
|
45
|
+
sourceType: z.ZodEnum<["token-account", "mint", "multisig"]>;
|
|
46
|
+
destinationAddress: z.ZodString;
|
|
47
|
+
authorityAddress: z.ZodString;
|
|
48
|
+
excessLamports: z.ZodUnion<[z.ZodNumber, z.ZodString]>;
|
|
49
|
+
rentFloor: z.ZodUnion<[z.ZodNumber, z.ZodString]>;
|
|
50
|
+
createdAt: z.ZodString;
|
|
51
|
+
blockhash: z.ZodString;
|
|
52
|
+
multisigThreshold: z.ZodOptional<z.ZodNumber>;
|
|
53
|
+
}, "strip", z.ZodTypeAny, {
|
|
54
|
+
blockhash: string;
|
|
55
|
+
sourceAddress: string;
|
|
56
|
+
sourceType: "token-account" | "mint" | "multisig";
|
|
57
|
+
destinationAddress: string;
|
|
58
|
+
authorityAddress: string;
|
|
59
|
+
excessLamports: string | number;
|
|
60
|
+
rentFloor: string | number;
|
|
61
|
+
createdAt: string;
|
|
62
|
+
multisigThreshold?: number | undefined;
|
|
63
|
+
}, {
|
|
64
|
+
blockhash: string;
|
|
65
|
+
sourceAddress: string;
|
|
66
|
+
sourceType: "token-account" | "mint" | "multisig";
|
|
67
|
+
destinationAddress: string;
|
|
68
|
+
authorityAddress: string;
|
|
69
|
+
excessLamports: string | number;
|
|
70
|
+
rentFloor: string | number;
|
|
71
|
+
createdAt: string;
|
|
72
|
+
multisigThreshold?: number | undefined;
|
|
73
|
+
}>;
|
|
74
|
+
rawTransactionB64: z.ZodString;
|
|
75
|
+
signatures: z.ZodArray<z.ZodObject<{
|
|
76
|
+
pubkey: z.ZodString;
|
|
77
|
+
signature: z.ZodNullable<z.ZodString>;
|
|
78
|
+
}, "strip", z.ZodTypeAny, {
|
|
79
|
+
signature: string | null;
|
|
80
|
+
pubkey: string;
|
|
81
|
+
}, {
|
|
82
|
+
signature: string | null;
|
|
83
|
+
pubkey: string;
|
|
84
|
+
}>, "many">;
|
|
85
|
+
}, "strip", z.ZodTypeAny, {
|
|
86
|
+
signatures: {
|
|
87
|
+
signature: string | null;
|
|
88
|
+
pubkey: string;
|
|
89
|
+
}[];
|
|
90
|
+
version: string;
|
|
91
|
+
type: "withdraw-excess-lamports";
|
|
92
|
+
metadata: {
|
|
93
|
+
blockhash: string;
|
|
94
|
+
sourceAddress: string;
|
|
95
|
+
sourceType: "token-account" | "mint" | "multisig";
|
|
96
|
+
destinationAddress: string;
|
|
97
|
+
authorityAddress: string;
|
|
98
|
+
excessLamports: string | number;
|
|
99
|
+
rentFloor: string | number;
|
|
100
|
+
createdAt: string;
|
|
101
|
+
multisigThreshold?: number | undefined;
|
|
102
|
+
};
|
|
103
|
+
rawTransactionB64: string;
|
|
104
|
+
}, {
|
|
105
|
+
signatures: {
|
|
106
|
+
signature: string | null;
|
|
107
|
+
pubkey: string;
|
|
108
|
+
}[];
|
|
109
|
+
type: "withdraw-excess-lamports";
|
|
110
|
+
metadata: {
|
|
111
|
+
blockhash: string;
|
|
112
|
+
sourceAddress: string;
|
|
113
|
+
sourceType: "token-account" | "mint" | "multisig";
|
|
114
|
+
destinationAddress: string;
|
|
115
|
+
authorityAddress: string;
|
|
116
|
+
excessLamports: string | number;
|
|
117
|
+
rentFloor: string | number;
|
|
118
|
+
createdAt: string;
|
|
119
|
+
multisigThreshold?: number | undefined;
|
|
120
|
+
};
|
|
121
|
+
rawTransactionB64: string;
|
|
122
|
+
version?: string | undefined;
|
|
123
|
+
}>;
|
|
124
|
+
export type TxEnvelope = z.infer<typeof TxEnvelopeSchema>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TxEnvelopeSchema = exports.ConfigSchema = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
exports.ConfigSchema = zod_1.z.object({
|
|
6
|
+
rpcUrl: zod_1.z.string().url().default('https://api.mainnet-beta.solana.com'),
|
|
7
|
+
commitment: zod_1.z.enum(['processed', 'confirmed', 'finalized']).default('confirmed'),
|
|
8
|
+
defaultDestination: zod_1.z.string().optional(),
|
|
9
|
+
feeWallet: zod_1.z.string().optional(),
|
|
10
|
+
feePercentage: zod_1.z.number().min(0).max(100).default(10),
|
|
11
|
+
rentOverride: zod_1.z.object({
|
|
12
|
+
lamportsPerByteYear: zod_1.z.string(),
|
|
13
|
+
exemptionMultiplier: zod_1.z.string(),
|
|
14
|
+
}).optional(),
|
|
15
|
+
});
|
|
16
|
+
exports.TxEnvelopeSchema = zod_1.z.object({
|
|
17
|
+
version: zod_1.z.string().default('1.0.0'),
|
|
18
|
+
type: zod_1.z.literal('withdraw-excess-lamports'),
|
|
19
|
+
metadata: zod_1.z.object({
|
|
20
|
+
sourceAddress: zod_1.z.string(),
|
|
21
|
+
sourceType: zod_1.z.enum(['token-account', 'mint', 'multisig']),
|
|
22
|
+
destinationAddress: zod_1.z.string(),
|
|
23
|
+
authorityAddress: zod_1.z.string(),
|
|
24
|
+
excessLamports: zod_1.z.union([zod_1.z.number(), zod_1.z.string()]),
|
|
25
|
+
rentFloor: zod_1.z.union([zod_1.z.number(), zod_1.z.string()]),
|
|
26
|
+
createdAt: zod_1.z.string(),
|
|
27
|
+
blockhash: zod_1.z.string(),
|
|
28
|
+
multisigThreshold: zod_1.z.number().optional(),
|
|
29
|
+
}),
|
|
30
|
+
rawTransactionB64: zod_1.z.string(),
|
|
31
|
+
signatures: zod_1.z.array(zod_1.z.object({
|
|
32
|
+
pubkey: zod_1.z.string(),
|
|
33
|
+
signature: zod_1.z.string().nullable(),
|
|
34
|
+
})),
|
|
35
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { PublicKey } from '@solana/web3.js';
|
|
2
|
+
import { Config, TxEnvelope } from '../core/validation';
|
|
3
|
+
/**
|
|
4
|
+
* Loads and parses the configuration file. Falls back to default config if missing.
|
|
5
|
+
*/
|
|
6
|
+
export declare function loadConfig(configPath?: string): Config;
|
|
7
|
+
/**
|
|
8
|
+
* Writes the configuration file to the specified path.
|
|
9
|
+
*/
|
|
10
|
+
export declare function writeConfig(configPath: string, config: Config): void;
|
|
11
|
+
/**
|
|
12
|
+
* Reads a list of target public keys from a file.
|
|
13
|
+
* Supports comma, space, or newline-separated addresses.
|
|
14
|
+
*/
|
|
15
|
+
export declare function loadAddresses(filePath: string): PublicKey[];
|
|
16
|
+
/**
|
|
17
|
+
* Loads a transaction envelope from a file and validates it.
|
|
18
|
+
*/
|
|
19
|
+
export declare function loadEnvelope(filePath: string): TxEnvelope;
|
|
20
|
+
/**
|
|
21
|
+
* Writes a transaction envelope to a file.
|
|
22
|
+
*/
|
|
23
|
+
export declare function writeEnvelope(filePath: string, envelope: TxEnvelope): void;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.loadConfig = loadConfig;
|
|
37
|
+
exports.writeConfig = writeConfig;
|
|
38
|
+
exports.loadAddresses = loadAddresses;
|
|
39
|
+
exports.loadEnvelope = loadEnvelope;
|
|
40
|
+
exports.writeEnvelope = writeEnvelope;
|
|
41
|
+
const fs = __importStar(require("fs"));
|
|
42
|
+
const path = __importStar(require("path"));
|
|
43
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
44
|
+
const validation_1 = require("../core/validation");
|
|
45
|
+
const signer_1 = require("../signers/signer");
|
|
46
|
+
/**
|
|
47
|
+
* Loads and parses the configuration file. Falls back to default config if missing.
|
|
48
|
+
*/
|
|
49
|
+
function loadConfig(configPath) {
|
|
50
|
+
const defaultPath = path.join(process.cwd(), 'smre.config.json');
|
|
51
|
+
const targetPath = configPath ? (0, signer_1.resolveHome)(configPath) : defaultPath;
|
|
52
|
+
if (!fs.existsSync(targetPath)) {
|
|
53
|
+
// Return default Zod values
|
|
54
|
+
return validation_1.ConfigSchema.parse({});
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
const raw = fs.readFileSync(targetPath, 'utf8');
|
|
58
|
+
const parsed = JSON.parse(raw);
|
|
59
|
+
return validation_1.ConfigSchema.parse(parsed);
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
throw new Error(`Invalid configuration file at ${targetPath}: ${err.message}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Writes the configuration file to the specified path.
|
|
67
|
+
*/
|
|
68
|
+
function writeConfig(configPath, config) {
|
|
69
|
+
const resolved = (0, signer_1.resolveHome)(configPath);
|
|
70
|
+
try {
|
|
71
|
+
const dir = path.dirname(resolved);
|
|
72
|
+
if (!fs.existsSync(dir)) {
|
|
73
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
74
|
+
}
|
|
75
|
+
fs.writeFileSync(resolved, JSON.stringify(config, null, 2), 'utf8');
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
throw new Error(`Failed to write config to ${resolved}: ${err.message}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Reads a list of target public keys from a file.
|
|
83
|
+
* Supports comma, space, or newline-separated addresses.
|
|
84
|
+
*/
|
|
85
|
+
function loadAddresses(filePath) {
|
|
86
|
+
const resolved = (0, signer_1.resolveHome)(filePath);
|
|
87
|
+
if (!fs.existsSync(resolved)) {
|
|
88
|
+
throw new Error(`Target address file not found at: ${filePath}`);
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
const raw = fs.readFileSync(resolved, 'utf8');
|
|
92
|
+
const lines = raw.split(/[\r\n, \t]+/).map(line => line.trim()).filter(line => line.length > 0);
|
|
93
|
+
const keys = [];
|
|
94
|
+
for (const line of lines) {
|
|
95
|
+
try {
|
|
96
|
+
keys.push(new web3_js_1.PublicKey(line));
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
throw new Error(`Invalid public key "${line}" found in address list.`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return keys;
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
throw new Error(`Failed to parse target addresses: ${err.message}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Loads a transaction envelope from a file and validates it.
|
|
110
|
+
*/
|
|
111
|
+
function loadEnvelope(filePath) {
|
|
112
|
+
const resolved = (0, signer_1.resolveHome)(filePath);
|
|
113
|
+
if (!fs.existsSync(resolved)) {
|
|
114
|
+
throw new Error(`Transaction envelope file not found at: ${filePath}`);
|
|
115
|
+
}
|
|
116
|
+
try {
|
|
117
|
+
const raw = fs.readFileSync(resolved, 'utf8');
|
|
118
|
+
const parsed = JSON.parse(raw);
|
|
119
|
+
return validation_1.TxEnvelopeSchema.parse(parsed);
|
|
120
|
+
}
|
|
121
|
+
catch (err) {
|
|
122
|
+
throw new Error(`Invalid transaction envelope at ${filePath}: ${err.message}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Writes a transaction envelope to a file.
|
|
127
|
+
*/
|
|
128
|
+
function writeEnvelope(filePath, envelope) {
|
|
129
|
+
const resolved = (0, signer_1.resolveHome)(filePath);
|
|
130
|
+
try {
|
|
131
|
+
const dir = path.dirname(resolved);
|
|
132
|
+
if (!fs.existsSync(dir)) {
|
|
133
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
134
|
+
}
|
|
135
|
+
fs.writeFileSync(resolved, JSON.stringify(envelope, null, 2), 'utf8');
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
throw new Error(`Failed to write transaction envelope to ${resolved}: ${err.message}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { PublicKey, Commitment } from '@solana/web3.js';
|
|
2
|
+
export type AccountType = 'token-account' | 'mint' | 'multisig';
|
|
3
|
+
export interface InspectedAccount {
|
|
4
|
+
address: PublicKey;
|
|
5
|
+
type: AccountType;
|
|
6
|
+
ownerProgram: PublicKey;
|
|
7
|
+
balance: bigint;
|
|
8
|
+
dataSize: number;
|
|
9
|
+
authority: PublicKey | null;
|
|
10
|
+
multisigSigners?: PublicKey[];
|
|
11
|
+
multisigThreshold?: number;
|
|
12
|
+
isToken2022: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Adapter class to query Solana RPC node.
|
|
16
|
+
*/
|
|
17
|
+
export declare class RpcAdapter {
|
|
18
|
+
private connection;
|
|
19
|
+
constructor(rpcUrl: string, commitment?: Commitment);
|
|
20
|
+
/**
|
|
21
|
+
* Fetches the latest blockhash from the RPC.
|
|
22
|
+
*/
|
|
23
|
+
getLatestBlockhash(): Promise<string>;
|
|
24
|
+
/**
|
|
25
|
+
* Submits a serialized transaction to the network.
|
|
26
|
+
*/
|
|
27
|
+
submitTransaction(rawTransactionBuf: Buffer): Promise<string>;
|
|
28
|
+
/**
|
|
29
|
+
* Simulates a transaction.
|
|
30
|
+
*/
|
|
31
|
+
simulateTransaction(rawTransactionBuf: Buffer): Promise<any>;
|
|
32
|
+
/**
|
|
33
|
+
* Inspects a Solana token or mint account.
|
|
34
|
+
*/
|
|
35
|
+
inspectAccount(address: PublicKey): Promise<InspectedAccount>;
|
|
36
|
+
}
|