@zoidz123/raydium-cli 0.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/LICENSE +21 -0
- package/README.md +74 -0
- package/dist/commands/clmm/index.js +2118 -0
- package/dist/commands/config/index.js +113 -0
- package/dist/commands/cpmm/index.js +480 -0
- package/dist/commands/launchpad/index.js +1677 -0
- package/dist/commands/pools/index.js +81 -0
- package/dist/commands/swap/index.js +490 -0
- package/dist/commands/tokens/index.js +93 -0
- package/dist/commands/wallet/index.js +267 -0
- package/dist/index.js +43 -0
- package/dist/lib/clmm-utils.js +296 -0
- package/dist/lib/codex-sdk.js +17 -0
- package/dist/lib/config-manager.js +67 -0
- package/dist/lib/connection.js +9 -0
- package/dist/lib/ipfs.js +117 -0
- package/dist/lib/output.js +59 -0
- package/dist/lib/paths.js +11 -0
- package/dist/lib/prompt.js +58 -0
- package/dist/lib/raydium-client.js +23 -0
- package/dist/lib/token-price.js +45 -0
- package/dist/lib/wallet-manager.js +173 -0
- package/dist/types/config.js +11 -0
- package/package.json +56 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getConnection = getConnection;
|
|
4
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
5
|
+
const config_manager_1 = require("./config-manager");
|
|
6
|
+
async function getConnection() {
|
|
7
|
+
const config = await (0, config_manager_1.loadConfig)({ createIfMissing: true });
|
|
8
|
+
return new web3_js_1.Connection(config["rpc-url"], "confirmed");
|
|
9
|
+
}
|
package/dist/lib/ipfs.js
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
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.uploadImageToIPFS = uploadImageToIPFS;
|
|
37
|
+
exports.uploadMetadataToIPFS = uploadMetadataToIPFS;
|
|
38
|
+
exports.uploadTokenMetadata = uploadTokenMetadata;
|
|
39
|
+
const pinata_1 = require("pinata");
|
|
40
|
+
const fs = __importStar(require("fs/promises"));
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
function getMimeType(ext) {
|
|
43
|
+
const mimeTypes = {
|
|
44
|
+
'png': 'image/png',
|
|
45
|
+
'jpg': 'image/jpeg',
|
|
46
|
+
'jpeg': 'image/jpeg',
|
|
47
|
+
'gif': 'image/gif',
|
|
48
|
+
'webp': 'image/webp',
|
|
49
|
+
'svg': 'image/svg+xml',
|
|
50
|
+
};
|
|
51
|
+
return mimeTypes[ext.toLowerCase()] || 'image/png';
|
|
52
|
+
}
|
|
53
|
+
async function uploadImageToIPFS(imagePath, apiKey) {
|
|
54
|
+
if (!apiKey) {
|
|
55
|
+
throw new Error('Pinata JWT not configured. Run: raydium config set pinata-jwt <your-jwt>\nGet a free JWT at https://pinata.cloud');
|
|
56
|
+
}
|
|
57
|
+
// Verify file exists and read it
|
|
58
|
+
let imageBuffer;
|
|
59
|
+
try {
|
|
60
|
+
imageBuffer = await fs.readFile(imagePath);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
throw new Error(`Image file not found: ${imagePath}`);
|
|
64
|
+
}
|
|
65
|
+
const pinata = new pinata_1.PinataSDK({ pinataJwt: apiKey });
|
|
66
|
+
const ext = path.extname(imagePath).slice(1) || 'png';
|
|
67
|
+
const mimeType = getMimeType(ext);
|
|
68
|
+
const fileName = path.basename(imagePath);
|
|
69
|
+
// Create a File object from the buffer (convert to Uint8Array for type compatibility)
|
|
70
|
+
const file = new File([new Uint8Array(imageBuffer)], fileName, { type: mimeType });
|
|
71
|
+
try {
|
|
72
|
+
const result = await pinata.upload.public.file(file);
|
|
73
|
+
return `https://gateway.pinata.cloud/ipfs/${result.cid}`;
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
77
|
+
if (message.includes('401') || message.includes('unauthorized') || message.includes('Unauthorized')) {
|
|
78
|
+
throw new Error('Invalid Pinata JWT. Get a new one at https://app.pinata.cloud/developers/api-keys');
|
|
79
|
+
}
|
|
80
|
+
throw new Error(`IPFS upload failed: ${message}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async function uploadMetadataToIPFS(metadata, apiKey) {
|
|
84
|
+
if (!apiKey) {
|
|
85
|
+
throw new Error('Pinata JWT not configured. Run: raydium config set pinata-jwt <your-jwt>\nGet a free JWT at https://pinata.cloud');
|
|
86
|
+
}
|
|
87
|
+
const pinata = new pinata_1.PinataSDK({ pinataJwt: apiKey });
|
|
88
|
+
try {
|
|
89
|
+
const result = await pinata.upload.public.json(metadata);
|
|
90
|
+
return `https://gateway.pinata.cloud/ipfs/${result.cid}`;
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
94
|
+
if (message.includes('401') || message.includes('unauthorized') || message.includes('Unauthorized')) {
|
|
95
|
+
throw new Error('Invalid Pinata JWT. Get a new one at https://app.pinata.cloud/developers/api-keys');
|
|
96
|
+
}
|
|
97
|
+
throw new Error(`IPFS upload failed: ${message}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
async function uploadTokenMetadata(opts) {
|
|
101
|
+
// 1. Upload image
|
|
102
|
+
const imageUrl = await uploadImageToIPFS(opts.imagePath, opts.apiKey);
|
|
103
|
+
// 2. Create and upload metadata
|
|
104
|
+
const metadata = {
|
|
105
|
+
name: opts.name,
|
|
106
|
+
symbol: opts.symbol,
|
|
107
|
+
image: imageUrl,
|
|
108
|
+
description: opts.description || `${opts.name} token`,
|
|
109
|
+
showName: true,
|
|
110
|
+
createdOn: 'https://raydium.io',
|
|
111
|
+
...(opts.twitter && { twitter: opts.twitter }),
|
|
112
|
+
...(opts.telegram && { telegram: opts.telegram }),
|
|
113
|
+
...(opts.website && { website: opts.website }),
|
|
114
|
+
};
|
|
115
|
+
const uri = await uploadMetadataToIPFS(metadata, opts.apiKey);
|
|
116
|
+
return { uri, imageUrl };
|
|
117
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
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.setJsonOutput = setJsonOutput;
|
|
7
|
+
exports.isJsonOutput = isJsonOutput;
|
|
8
|
+
exports.logJson = logJson;
|
|
9
|
+
exports.logInfo = logInfo;
|
|
10
|
+
exports.logSuccess = logSuccess;
|
|
11
|
+
exports.logError = logError;
|
|
12
|
+
exports.withSpinner = withSpinner;
|
|
13
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
14
|
+
const ora_1 = __importDefault(require("ora"));
|
|
15
|
+
let jsonOutput = false;
|
|
16
|
+
function setJsonOutput(enabled) {
|
|
17
|
+
jsonOutput = enabled;
|
|
18
|
+
}
|
|
19
|
+
function isJsonOutput() {
|
|
20
|
+
return jsonOutput;
|
|
21
|
+
}
|
|
22
|
+
function logJson(payload) {
|
|
23
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
24
|
+
}
|
|
25
|
+
function logInfo(message) {
|
|
26
|
+
if (!jsonOutput) {
|
|
27
|
+
console.log(message);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function logSuccess(message) {
|
|
31
|
+
if (!jsonOutput) {
|
|
32
|
+
console.log(chalk_1.default.green(message));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function logError(message, details) {
|
|
36
|
+
if (jsonOutput) {
|
|
37
|
+
logJson({ error: message, details });
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
console.error(chalk_1.default.red(message));
|
|
41
|
+
if (details) {
|
|
42
|
+
console.error(chalk_1.default.yellow(String(details)));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async function withSpinner(text, task) {
|
|
46
|
+
if (jsonOutput) {
|
|
47
|
+
return task();
|
|
48
|
+
}
|
|
49
|
+
const spinner = (0, ora_1.default)(text).start();
|
|
50
|
+
try {
|
|
51
|
+
const result = await task();
|
|
52
|
+
spinner.succeed();
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
spinner.fail(text);
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
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.WALLETS_DIR = exports.CONFIG_PATH = exports.CONFIG_DIR = void 0;
|
|
7
|
+
const os_1 = __importDefault(require("os"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
exports.CONFIG_DIR = path_1.default.join(os_1.default.homedir(), ".raydium-cli");
|
|
10
|
+
exports.CONFIG_PATH = path_1.default.join(exports.CONFIG_DIR, "config.json");
|
|
11
|
+
exports.WALLETS_DIR = path_1.default.join(exports.CONFIG_DIR, "wallets");
|
|
@@ -0,0 +1,58 @@
|
|
|
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.promptPassword = promptPassword;
|
|
7
|
+
exports.promptConfirm = promptConfirm;
|
|
8
|
+
exports.promptInput = promptInput;
|
|
9
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
10
|
+
async function promptPassword(message = "Enter password", confirm = false) {
|
|
11
|
+
const { password } = await inquirer_1.default.prompt([
|
|
12
|
+
{
|
|
13
|
+
type: "password",
|
|
14
|
+
name: "password",
|
|
15
|
+
message,
|
|
16
|
+
mask: "*",
|
|
17
|
+
validate: (input) => (input ? true : "Password is required")
|
|
18
|
+
}
|
|
19
|
+
]);
|
|
20
|
+
if (!confirm)
|
|
21
|
+
return password;
|
|
22
|
+
const { confirmPassword } = await inquirer_1.default.prompt([
|
|
23
|
+
{
|
|
24
|
+
type: "password",
|
|
25
|
+
name: "confirmPassword",
|
|
26
|
+
message: "Confirm password",
|
|
27
|
+
mask: "*",
|
|
28
|
+
validate: (input) => (input ? true : "Password confirmation is required")
|
|
29
|
+
}
|
|
30
|
+
]);
|
|
31
|
+
if (password !== confirmPassword) {
|
|
32
|
+
throw new Error("Passwords do not match");
|
|
33
|
+
}
|
|
34
|
+
return password;
|
|
35
|
+
}
|
|
36
|
+
async function promptConfirm(message, defaultValue = false) {
|
|
37
|
+
const { ok } = await inquirer_1.default.prompt([
|
|
38
|
+
{
|
|
39
|
+
type: "confirm",
|
|
40
|
+
name: "ok",
|
|
41
|
+
message,
|
|
42
|
+
default: defaultValue
|
|
43
|
+
}
|
|
44
|
+
]);
|
|
45
|
+
return ok;
|
|
46
|
+
}
|
|
47
|
+
async function promptInput(message, defaultValue) {
|
|
48
|
+
const { value } = await inquirer_1.default.prompt([
|
|
49
|
+
{
|
|
50
|
+
type: "input",
|
|
51
|
+
name: "value",
|
|
52
|
+
message,
|
|
53
|
+
default: defaultValue,
|
|
54
|
+
validate: (input) => (input ? true : "Value is required")
|
|
55
|
+
}
|
|
56
|
+
]);
|
|
57
|
+
return value;
|
|
58
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loadRaydium = loadRaydium;
|
|
4
|
+
const raydium_sdk_v2_1 = require("@raydium-io/raydium-sdk-v2");
|
|
5
|
+
const connection_1 = require("./connection");
|
|
6
|
+
const config_manager_1 = require("./config-manager");
|
|
7
|
+
async function loadRaydium(options) {
|
|
8
|
+
const connection = await (0, connection_1.getConnection)();
|
|
9
|
+
const config = await (0, config_manager_1.loadConfig)();
|
|
10
|
+
// Detect cluster from RPC URL
|
|
11
|
+
const rpcUrl = config["rpc-url"].toLowerCase();
|
|
12
|
+
const isDevnet = rpcUrl.includes("devnet");
|
|
13
|
+
const cluster = isDevnet ? "devnet" : "mainnet";
|
|
14
|
+
return raydium_sdk_v2_1.Raydium.load({
|
|
15
|
+
connection,
|
|
16
|
+
owner: options?.owner,
|
|
17
|
+
disableLoadToken: options?.disableLoadToken ?? false,
|
|
18
|
+
disableFeatureCheck: true,
|
|
19
|
+
blockhashCommitment: "confirmed",
|
|
20
|
+
apiRequestTimeout: 30000,
|
|
21
|
+
cluster
|
|
22
|
+
});
|
|
23
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getTokenPrices = getTokenPrices;
|
|
4
|
+
const config_manager_1 = require("./config-manager");
|
|
5
|
+
/**
|
|
6
|
+
* Fetch token USD prices via DAS getAssetBatch (Helius and compatible RPCs).
|
|
7
|
+
* Returns a Map of mint address -> price_per_token (USD).
|
|
8
|
+
* On unsupported RPCs or errors, returns an empty map.
|
|
9
|
+
*/
|
|
10
|
+
async function getTokenPrices(mintAddresses) {
|
|
11
|
+
const prices = new Map();
|
|
12
|
+
if (mintAddresses.length === 0)
|
|
13
|
+
return prices;
|
|
14
|
+
try {
|
|
15
|
+
const config = await (0, config_manager_1.loadConfig)({ createIfMissing: true });
|
|
16
|
+
const rpcUrl = config["rpc-url"];
|
|
17
|
+
const response = await fetch(rpcUrl, {
|
|
18
|
+
method: "POST",
|
|
19
|
+
headers: { "Content-Type": "application/json" },
|
|
20
|
+
body: JSON.stringify({
|
|
21
|
+
jsonrpc: "2.0",
|
|
22
|
+
id: "token-prices",
|
|
23
|
+
method: "getAssetBatch",
|
|
24
|
+
params: {
|
|
25
|
+
ids: mintAddresses
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
});
|
|
29
|
+
const json = await response.json();
|
|
30
|
+
const results = json?.result;
|
|
31
|
+
if (!Array.isArray(results))
|
|
32
|
+
return prices;
|
|
33
|
+
for (const asset of results) {
|
|
34
|
+
const mint = asset?.id;
|
|
35
|
+
const pricePerToken = asset?.token_info?.price_info?.price_per_token;
|
|
36
|
+
if (mint && typeof pricePerToken === "number") {
|
|
37
|
+
prices.set(mint, pricePerToken);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// RPC doesn't support DAS or network error — return empty
|
|
43
|
+
}
|
|
44
|
+
return prices;
|
|
45
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
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
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.assertValidWalletName = assertValidWalletName;
|
|
40
|
+
exports.ensureWalletDir = ensureWalletDir;
|
|
41
|
+
exports.listWallets = listWallets;
|
|
42
|
+
exports.walletExists = walletExists;
|
|
43
|
+
exports.getWalletPublicKey = getWalletPublicKey;
|
|
44
|
+
exports.createWallet = createWallet;
|
|
45
|
+
exports.importWalletFromPrivateKey = importWalletFromPrivateKey;
|
|
46
|
+
exports.importWalletFromMnemonic = importWalletFromMnemonic;
|
|
47
|
+
exports.decryptWallet = decryptWallet;
|
|
48
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
49
|
+
const path_1 = __importDefault(require("path"));
|
|
50
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
51
|
+
const bs58_1 = __importDefault(require("bs58"));
|
|
52
|
+
const bip39 = __importStar(require("bip39"));
|
|
53
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
54
|
+
const paths_1 = require("./paths");
|
|
55
|
+
const PBKDF2_ITERATIONS = 100000;
|
|
56
|
+
const WALLET_VERSION = 1;
|
|
57
|
+
function assertValidWalletName(name) {
|
|
58
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(name)) {
|
|
59
|
+
throw new Error("Wallet name must use letters, numbers, '_' or '-' only");
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function getWalletPath(name) {
|
|
63
|
+
return path_1.default.join(paths_1.WALLETS_DIR, `${name}.json`);
|
|
64
|
+
}
|
|
65
|
+
async function ensureWalletDir() {
|
|
66
|
+
await promises_1.default.mkdir(paths_1.WALLETS_DIR, { recursive: true });
|
|
67
|
+
}
|
|
68
|
+
async function listWallets() {
|
|
69
|
+
try {
|
|
70
|
+
const files = await promises_1.default.readdir(paths_1.WALLETS_DIR);
|
|
71
|
+
const summaries = [];
|
|
72
|
+
for (const file of files) {
|
|
73
|
+
if (!file.endsWith(".json"))
|
|
74
|
+
continue;
|
|
75
|
+
const raw = await promises_1.default.readFile(path_1.default.join(paths_1.WALLETS_DIR, file), "utf8");
|
|
76
|
+
const parsed = JSON.parse(raw);
|
|
77
|
+
summaries.push({ name: parsed.name, publicKey: parsed.publicKey });
|
|
78
|
+
}
|
|
79
|
+
return summaries.sort((a, b) => a.name.localeCompare(b.name));
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
if (error.code === "ENOENT")
|
|
83
|
+
return [];
|
|
84
|
+
throw error;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
async function walletExists(name) {
|
|
88
|
+
try {
|
|
89
|
+
await promises_1.default.access(getWalletPath(name));
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
async function getWalletPublicKey(name) {
|
|
97
|
+
const data = await loadWalletFile(name);
|
|
98
|
+
return new web3_js_1.PublicKey(data.publicKey);
|
|
99
|
+
}
|
|
100
|
+
async function createWallet(name, password) {
|
|
101
|
+
assertValidWalletName(name);
|
|
102
|
+
if (await walletExists(name))
|
|
103
|
+
throw new Error(`Wallet already exists: ${name}`);
|
|
104
|
+
const mnemonic = bip39.generateMnemonic();
|
|
105
|
+
const seed = bip39.mnemonicToSeedSync(mnemonic);
|
|
106
|
+
const keypair = web3_js_1.Keypair.fromSeed(seed.slice(0, 32));
|
|
107
|
+
const wallet = await encryptKeypair(name, keypair, password);
|
|
108
|
+
await saveWalletFile(wallet);
|
|
109
|
+
return { mnemonic, wallet };
|
|
110
|
+
}
|
|
111
|
+
async function importWalletFromPrivateKey(name, privateKeyBase58, password) {
|
|
112
|
+
assertValidWalletName(name);
|
|
113
|
+
if (await walletExists(name))
|
|
114
|
+
throw new Error(`Wallet already exists: ${name}`);
|
|
115
|
+
const secretKey = bs58_1.default.decode(privateKeyBase58);
|
|
116
|
+
const keypair = web3_js_1.Keypair.fromSecretKey(secretKey);
|
|
117
|
+
const wallet = await encryptKeypair(name, keypair, password);
|
|
118
|
+
await saveWalletFile(wallet);
|
|
119
|
+
return wallet;
|
|
120
|
+
}
|
|
121
|
+
async function importWalletFromMnemonic(name, mnemonic, password) {
|
|
122
|
+
assertValidWalletName(name);
|
|
123
|
+
if (await walletExists(name))
|
|
124
|
+
throw new Error(`Wallet already exists: ${name}`);
|
|
125
|
+
if (!bip39.validateMnemonic(mnemonic))
|
|
126
|
+
throw new Error("Invalid seed phrase");
|
|
127
|
+
const seed = bip39.mnemonicToSeedSync(mnemonic);
|
|
128
|
+
const keypair = web3_js_1.Keypair.fromSeed(seed.slice(0, 32));
|
|
129
|
+
const wallet = await encryptKeypair(name, keypair, password);
|
|
130
|
+
await saveWalletFile(wallet);
|
|
131
|
+
return wallet;
|
|
132
|
+
}
|
|
133
|
+
async function decryptWallet(name, password) {
|
|
134
|
+
const data = await loadWalletFile(name);
|
|
135
|
+
const salt = Buffer.from(data.salt, "base64");
|
|
136
|
+
const iv = Buffer.from(data.iv, "base64");
|
|
137
|
+
const authTag = Buffer.from(data.authTag, "base64");
|
|
138
|
+
const ciphertext = Buffer.from(data.ciphertext, "base64");
|
|
139
|
+
const key = crypto_1.default.pbkdf2Sync(password, salt, data.iterations, 32, "sha256");
|
|
140
|
+
const decipher = crypto_1.default.createDecipheriv("aes-256-gcm", key, iv);
|
|
141
|
+
decipher.setAuthTag(authTag);
|
|
142
|
+
const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
143
|
+
return web3_js_1.Keypair.fromSecretKey(plaintext);
|
|
144
|
+
}
|
|
145
|
+
async function loadWalletFile(name) {
|
|
146
|
+
const raw = await promises_1.default.readFile(getWalletPath(name), "utf8");
|
|
147
|
+
return JSON.parse(raw);
|
|
148
|
+
}
|
|
149
|
+
async function saveWalletFile(wallet) {
|
|
150
|
+
await ensureWalletDir();
|
|
151
|
+
await promises_1.default.writeFile(getWalletPath(wallet.name), JSON.stringify(wallet, null, 2));
|
|
152
|
+
}
|
|
153
|
+
async function encryptKeypair(name, keypair, password) {
|
|
154
|
+
const salt = crypto_1.default.randomBytes(16);
|
|
155
|
+
const iv = crypto_1.default.randomBytes(12);
|
|
156
|
+
const key = crypto_1.default.pbkdf2Sync(password, salt, PBKDF2_ITERATIONS, 32, "sha256");
|
|
157
|
+
const cipher = crypto_1.default.createCipheriv("aes-256-gcm", key, iv);
|
|
158
|
+
const ciphertext = Buffer.concat([cipher.update(Buffer.from(keypair.secretKey)), cipher.final()]);
|
|
159
|
+
const authTag = cipher.getAuthTag();
|
|
160
|
+
return {
|
|
161
|
+
version: WALLET_VERSION,
|
|
162
|
+
name,
|
|
163
|
+
publicKey: keypair.publicKey.toBase58(),
|
|
164
|
+
cipher: "aes-256-gcm",
|
|
165
|
+
kdf: "pbkdf2-sha256",
|
|
166
|
+
iterations: PBKDF2_ITERATIONS,
|
|
167
|
+
salt: salt.toString("base64"),
|
|
168
|
+
iv: iv.toString("base64"),
|
|
169
|
+
authTag: authTag.toString("base64"),
|
|
170
|
+
ciphertext: ciphertext.toString("base64"),
|
|
171
|
+
createdAt: new Date().toISOString()
|
|
172
|
+
};
|
|
173
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_CONFIG = void 0;
|
|
4
|
+
exports.DEFAULT_CONFIG = {
|
|
5
|
+
"rpc-url": "https://api.mainnet-beta.solana.com",
|
|
6
|
+
"default-slippage": 0.5,
|
|
7
|
+
"explorer": "solscan",
|
|
8
|
+
"priority-fee": 0,
|
|
9
|
+
"activeWallet": null,
|
|
10
|
+
"pinata-jwt": null
|
|
11
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@zoidz123/raydium-cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI tool for interacting with Raydium on Solana — swap, manage CLMM/CPMM positions, launch tokens, and more",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"bin": {
|
|
7
|
+
"raydium": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsc -p tsconfig.json",
|
|
14
|
+
"dev": "ts-node src/index.ts",
|
|
15
|
+
"prepublishOnly": "npm run build"
|
|
16
|
+
},
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=18"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"raydium",
|
|
22
|
+
"solana",
|
|
23
|
+
"cli",
|
|
24
|
+
"defi",
|
|
25
|
+
"clmm",
|
|
26
|
+
"cpmm",
|
|
27
|
+
"swap",
|
|
28
|
+
"liquidity",
|
|
29
|
+
"launchpad"
|
|
30
|
+
],
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@raydium-io/raydium-sdk-v2": "^0.2.31-alpha",
|
|
36
|
+
"@solana/spl-token": "^0.4.8",
|
|
37
|
+
"@solana/web3.js": "^1.95.3",
|
|
38
|
+
"bip39": "^3.1.0",
|
|
39
|
+
"bs58": "^6.0.0",
|
|
40
|
+
"chalk": "^5.3.0",
|
|
41
|
+
"commander": "^11.1.0",
|
|
42
|
+
"decimal.js": "^10.6.0",
|
|
43
|
+
"dotenv": "^16.4.5",
|
|
44
|
+
"inquirer": "^9.2.12",
|
|
45
|
+
"ora": "^7.0.1",
|
|
46
|
+
"pinata": "^2.5.3"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/bn.js": "^5.2.0",
|
|
50
|
+
"@types/inquirer": "^9.0.7",
|
|
51
|
+
"@types/node": "^20.11.10",
|
|
52
|
+
"ts-node": "^10.9.2",
|
|
53
|
+
"typescript": "^5.3.3"
|
|
54
|
+
},
|
|
55
|
+
"packageManager": "yarn@1.22.22+sha1.ac34549e6aa8e7ead463a7407e1c7390f61a6610"
|
|
56
|
+
}
|