soluser 1.0.1 → 1.0.5
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 +18 -8
- package/bin/index.js +36 -9
- package/package.json +1 -1
- package/src/commands/address.js +22 -0
- package/src/commands/balance.js +33 -0
- package/src/commands/list.js +2 -2
- package/src/commands/new.js +29 -11
- package/src/commands/remove.js +2 -2
- package/src/commands/switch.js +2 -2
- package/src/develop.md +6 -0
- package/src/utils/path.js +12 -2
- package/src/utils/solana.js +3 -3
- package/src/utils/throw_error.js +12 -0
package/README.md
CHANGED
|
@@ -1,27 +1,37 @@
|
|
|
1
1
|
# soluser
|
|
2
|
-
|
|
2
|
+
## 安装
|
|
3
3
|
```shell
|
|
4
|
+
npm install -g soluser@latest
|
|
5
|
+
```
|
|
6
|
+
## 新建账号
|
|
7
|
+
```shell
|
|
8
|
+
$ soluser new charlie
|
|
4
9
|
$ soluser new alice --word-length 12
|
|
5
10
|
$ soluser new bob --word-length 24 --no-bip39-passphrase
|
|
6
|
-
$ soluser new charlie
|
|
7
11
|
```
|
|
8
12
|
|
|
9
|
-
|
|
13
|
+
## 切换账号
|
|
10
14
|
```shell
|
|
11
|
-
$ soluser switch
|
|
15
|
+
$ soluser switch bob
|
|
12
16
|
```
|
|
13
17
|
|
|
14
|
-
|
|
18
|
+
## 列出账号
|
|
15
19
|
```shell
|
|
16
20
|
$ soluser list
|
|
17
21
|
```
|
|
18
22
|
|
|
19
|
-
|
|
23
|
+
## 删除账号
|
|
20
24
|
```shell
|
|
21
25
|
$ soluser remove alice
|
|
22
26
|
```
|
|
23
27
|
|
|
24
|
-
|
|
28
|
+
## 查看alias对应的地址
|
|
25
29
|
```shell
|
|
26
|
-
|
|
30
|
+
$ soluser address alice
|
|
27
31
|
```
|
|
32
|
+
|
|
33
|
+
## 查看alias对应的余额
|
|
34
|
+
```shell
|
|
35
|
+
$ soluser balance alice
|
|
36
|
+
```
|
|
37
|
+
|
package/bin/index.js
CHANGED
|
@@ -6,6 +6,30 @@ const switchAccount = require('../src/commands/switch');
|
|
|
6
6
|
const listAccounts = require('../src/commands/list');
|
|
7
7
|
const removeAccount = require('../src/commands/remove');
|
|
8
8
|
|
|
9
|
+
// 导入地址查询命令
|
|
10
|
+
const showAddress = require('../src/commands/address');
|
|
11
|
+
|
|
12
|
+
// 定义地址查询命令
|
|
13
|
+
program
|
|
14
|
+
.command('address')
|
|
15
|
+
.description('Output the base58 address of a Solana account')
|
|
16
|
+
.argument('<alias>', 'Alias of the account to get address') // 接收别名作为位置参数
|
|
17
|
+
.action((alias) => {
|
|
18
|
+
showAddress(alias);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// 导入余额查询命令
|
|
22
|
+
const checkBalance = require('../src/commands/balance');
|
|
23
|
+
|
|
24
|
+
// 定义余额查询命令
|
|
25
|
+
program
|
|
26
|
+
.command('balance')
|
|
27
|
+
.description('Check the SOL balance of a Solana account')
|
|
28
|
+
.argument('<alias>', 'Alias of the account to check balance') // 接收别名作为位置参数
|
|
29
|
+
.action((alias) => {
|
|
30
|
+
checkBalance(alias);
|
|
31
|
+
});
|
|
32
|
+
|
|
9
33
|
|
|
10
34
|
// 定义新建账号命令
|
|
11
35
|
// 定义新建账号命令(修改后)
|
|
@@ -14,27 +38,30 @@ program
|
|
|
14
38
|
.description('Create a new Solana account')
|
|
15
39
|
.argument('<alias>', 'Alias for the new account (must start with a letter, contain letters, digits, hyphens, or underscores)') // 新增位置参数
|
|
16
40
|
.option('--word-length <number>', 'Number of words in seed phrase (12,15,18,21,24)', 12)
|
|
17
|
-
.option('--
|
|
41
|
+
.option('--without-passphrase', 'Do not prompt for BIP39 passphrase',false)
|
|
18
42
|
.action((alias, options) => { // 第一个参数为位置参数 alias,第二个为选项
|
|
43
|
+
// // 打印选项值验证(调试用)
|
|
44
|
+
// console.log('without-passphrase:', options.withoutPassphrase);
|
|
45
|
+
// console.log("type noPassphrase:", typeof(options.withoutPassphrase), "value:", options.withoutPassphrase)
|
|
46
|
+
|
|
19
47
|
newAccount(
|
|
20
48
|
alias, // 直接使用位置参数的 alias
|
|
21
49
|
parseInt(options.wordLength, 10), //10进制解析
|
|
22
|
-
options.
|
|
50
|
+
options.withoutPassphrase
|
|
23
51
|
);
|
|
24
52
|
});
|
|
25
53
|
|
|
26
|
-
|
|
54
|
+
|
|
55
|
+
// 定义切换账号命令(修改后)
|
|
27
56
|
program
|
|
28
57
|
.command('switch')
|
|
29
58
|
.description('Switch active Solana account')
|
|
30
|
-
.
|
|
31
|
-
.action((
|
|
32
|
-
|
|
33
|
-
throw new Error('Alias is required (use --address <alias>)');
|
|
34
|
-
}
|
|
35
|
-
switchAccount(options.address);
|
|
59
|
+
.argument('<alias>', 'Alias of the account to switch to') // 新增位置参数
|
|
60
|
+
.action((alias) => { // 直接使用位置参数 alias
|
|
61
|
+
switchAccount(alias);
|
|
36
62
|
});
|
|
37
63
|
|
|
64
|
+
|
|
38
65
|
// 定义列出账号命令
|
|
39
66
|
program
|
|
40
67
|
.command('list')
|
package/package.json
CHANGED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const { existsAccount, getKeyFilePath } = require('../utils/path');
|
|
2
|
+
const { getAddress } = require('../utils/solana');
|
|
3
|
+
|
|
4
|
+
function showAddress(alias) {
|
|
5
|
+
// 1. 检查账号是否存在
|
|
6
|
+
if (!existsAccount(alias)) {
|
|
7
|
+
console.error(`Error: Account "${alias}" does not exist.`);
|
|
8
|
+
console.error(` Use "soluser list" to view all available accounts.`);
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// 2. 解析并输出 base58 地址
|
|
13
|
+
try {
|
|
14
|
+
const address = getAddress(alias); // 复用之前的 getAddress 函数(基于 solana-keygen)
|
|
15
|
+
console.log(address); // 直接输出地址(方便脚本调用)
|
|
16
|
+
} catch (err) {
|
|
17
|
+
console.error(`Error: Failed to parse address for "${alias}": ${err.message}`);
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
module.exports = showAddress;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const { existsAccount } = require('../utils/path');
|
|
2
|
+
const { getAddress, execCommand } = require('../utils/solana');
|
|
3
|
+
|
|
4
|
+
function checkBalance(alias) {
|
|
5
|
+
// 1. 检查账号是否存在
|
|
6
|
+
if (!existsAccount(alias)) {
|
|
7
|
+
console.error(`Error: Account "${alias}" does not exist.`);
|
|
8
|
+
console.error(` Use "soluser list" to view all available accounts.`);
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// 2. 获取账号对应的公钥(address)
|
|
13
|
+
let address;
|
|
14
|
+
try {
|
|
15
|
+
address = getAddress(alias);
|
|
16
|
+
} catch (err) {
|
|
17
|
+
console.error(`Error: Failed to get address for "${alias}": ${err.message}`);
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// 3. 调用 solana balance 命令查询余额
|
|
22
|
+
try {
|
|
23
|
+
// solana balance 命令会返回类似 "1.2345 SOL" 的结果
|
|
24
|
+
const balance = execCommand(`solana balance ${address}`);
|
|
25
|
+
console.log(`${alias}: ${balance}`); // 输出格式:别名 + 余额
|
|
26
|
+
} catch (err) {
|
|
27
|
+
console.error(`Error: Failed to check balance for "${alias}": ${err.message}`);
|
|
28
|
+
console.error(` Ensure Solana CLI is configured with a valid network (e.g., "solana config set --url https://api.devnet.solana.com")`);
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = checkBalance;
|
package/src/commands/list.js
CHANGED
|
@@ -26,7 +26,7 @@ function listAccounts() {
|
|
|
26
26
|
// 3. 构建表格
|
|
27
27
|
const table = new Table({
|
|
28
28
|
head: ['alias', 'address', 'active'],
|
|
29
|
-
colWidths: [15,
|
|
29
|
+
colWidths: [15, 50, 8],
|
|
30
30
|
});
|
|
31
31
|
|
|
32
32
|
// 4. 填充表格数据
|
|
@@ -34,7 +34,7 @@ function listAccounts() {
|
|
|
34
34
|
const alias = path.basename(file, '.json');
|
|
35
35
|
const address = getAddress(alias);
|
|
36
36
|
const isActive = alias === activeAlias ? '*' : '';
|
|
37
|
-
console.log("alias: ", alias, "address: ", address, "isActive: ", isActive,'activeAlias',activeAlias )
|
|
37
|
+
//console.log("alias: ", alias, "address: ", address, "isActive: ", isActive,'activeAlias',activeAlias )
|
|
38
38
|
table.push([alias, address, isActive]);
|
|
39
39
|
});
|
|
40
40
|
|
package/src/commands/new.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
|
-
const { KEYS_DIR, validateAlias, getKeyFilePath } = require('../utils/path');
|
|
2
|
+
const { KEYS_DIR, validateAlias, getKeyFilePath ,existsAccount} = require('../utils/path');
|
|
3
3
|
const { execCommand } = require('../utils/solana');
|
|
4
|
+
const ThrowErorr = require('../utils/throw_error');
|
|
4
5
|
|
|
5
6
|
function newAccount(alias, wordLength = 12, noPassphrase = false) {
|
|
6
7
|
// 1. 确保密钥目录存在
|
|
@@ -10,21 +11,38 @@ function newAccount(alias, wordLength = 12, noPassphrase = false) {
|
|
|
10
11
|
|
|
11
12
|
// 2. 验证参数
|
|
12
13
|
validateAlias(alias);
|
|
14
|
+
|
|
15
|
+
// 3. 新增:检查账号是否已存在
|
|
16
|
+
if (existsAccount(alias)) {
|
|
17
|
+
console.error(`Error: Account "${alias}" already exists.`);
|
|
18
|
+
console.error(` Use "soluser list" to view existing accounts, or choose a different alias.`);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
// 4. 验证助记词长度(已有逻辑)
|
|
13
24
|
const validWordLengths = [12, 15, 18, 21, 24];
|
|
14
25
|
if (!validWordLengths.includes(wordLength)) {
|
|
15
|
-
|
|
26
|
+
ThrowErorr(`Word length must be one of: ${validWordLengths.join(', ')}`);
|
|
16
27
|
}
|
|
17
28
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
let command = `solana-keygen new --word-count ${wordLength} --outfile ${keyPath}`;
|
|
21
|
-
if (noPassphrase) {
|
|
22
|
-
command += ' --no-bip39-passphrase';
|
|
23
|
-
}
|
|
29
|
+
|
|
30
|
+
|
|
24
31
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
32
|
+
// 5. 生成密钥对(已有逻辑)
|
|
33
|
+
try {
|
|
34
|
+
let command = `solana-keygen new --word-count ${wordLength} --outfile ${getKeyFilePath(alias)}`;
|
|
35
|
+
|
|
36
|
+
if (noPassphrase) {
|
|
37
|
+
command += ' --no-bip39-passphrase';
|
|
38
|
+
}
|
|
39
|
+
console.log(`Generating key pair for ${alias}...`);
|
|
40
|
+
execCommand(command);
|
|
41
|
+
console.log(`Successfully created account: ${alias} (saved to ${getKeyFilePath(alias)})`);
|
|
42
|
+
} catch (err) {
|
|
43
|
+
console.error(`Error: Failed to generate key pair: ${err.message}`);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
28
46
|
}
|
|
29
47
|
|
|
30
48
|
module.exports = newAccount;
|
package/src/commands/remove.js
CHANGED
|
@@ -2,7 +2,7 @@ const fs = require('fs');
|
|
|
2
2
|
const { validateAlias, getKeyFilePath } = require('../utils/path');
|
|
3
3
|
const { getActiveKeyPath } = require('../utils/solana');
|
|
4
4
|
const path = require('path');
|
|
5
|
-
|
|
5
|
+
const ThrowErorr = require('../utils/throw_error');
|
|
6
6
|
function removeAccount(alias) {
|
|
7
7
|
// 1. 验证别名格式
|
|
8
8
|
validateAlias(alias);
|
|
@@ -10,7 +10,7 @@ function removeAccount(alias) {
|
|
|
10
10
|
// 2. 检查密钥文件是否存在
|
|
11
11
|
const keyPath = getKeyFilePath(alias);
|
|
12
12
|
if (!fs.existsSync(keyPath)) {
|
|
13
|
-
|
|
13
|
+
ThrowErorr(`Account "${alias}" not found. Use "soluser list" to check existing accounts.`);
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
// 3. 检查是否为当前活跃账号
|
package/src/commands/switch.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const { getKeyFilePath } = require('../utils/path');
|
|
3
3
|
const { execCommand } = require('../utils/solana');
|
|
4
|
-
|
|
4
|
+
const ThrowErorr = require('../utils/throw_error');
|
|
5
5
|
function switchAccount(alias) {
|
|
6
6
|
const keyPath = getKeyFilePath(alias);
|
|
7
7
|
|
|
8
8
|
// 验证密钥文件是否存在
|
|
9
9
|
if (!fs.existsSync(keyPath)) {
|
|
10
|
-
|
|
10
|
+
ThrowErorr(`Account ${alias} not found. Check alias or create it with "soluser new".`);
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
// 执行切换(调用 solana config set)
|
package/src/develop.md
ADDED
package/src/utils/path.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
2
|
const os = require('os');
|
|
3
|
-
|
|
3
|
+
const ThrowErorr = require('../utils/throw_error');
|
|
4
4
|
// 密钥存储目录:~/.config/solana/keys
|
|
5
5
|
const KEYS_DIR = path.join(os.homedir(), '.config', 'solana', 'keys');
|
|
6
6
|
|
|
@@ -8,7 +8,7 @@ const KEYS_DIR = path.join(os.homedir(), '.config', 'solana', 'keys');
|
|
|
8
8
|
function validateAlias(alias) {
|
|
9
9
|
const regex = /^[a-zA-Z][a-zA-Z0-9_-]*$/;
|
|
10
10
|
if (!regex.test(alias)) {
|
|
11
|
-
|
|
11
|
+
ThrowErorr('Alias must start with a letter and contain only letters, digits, hyphens, or underscores');
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
|
|
@@ -17,9 +17,19 @@ function getKeyFilePath(alias) {
|
|
|
17
17
|
return path.join(KEYS_DIR, `${alias}.json`);
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
// 检查账号是否存在(即密钥文件是否存在)
|
|
21
|
+
function existsAccount(alias) {
|
|
22
|
+
const keyPath = getKeyFilePath(alias);
|
|
23
|
+
return fs.existsSync(keyPath); // 返回布尔值:true=存在,false=不存在
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// 记得在顶部导入 fs
|
|
27
|
+
const fs = require('fs');
|
|
28
|
+
|
|
20
29
|
module.exports = {
|
|
21
30
|
KEYS_DIR,
|
|
22
31
|
validateAlias,
|
|
23
32
|
getKeyFilePath,
|
|
33
|
+
existsAccount,
|
|
24
34
|
};
|
|
25
35
|
|
package/src/utils/solana.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
const { execSync } = require('child_process');
|
|
2
2
|
const { getKeyFilePath } = require('./path');
|
|
3
|
-
|
|
3
|
+
const ThrowErorr = require('../utils/throw_error');
|
|
4
4
|
// 执行 shell 命令并返回输出
|
|
5
5
|
function execCommand(command) {
|
|
6
6
|
try {
|
|
7
7
|
return execSync(command, { encoding: 'utf8' }).trim();
|
|
8
8
|
} catch (error) {
|
|
9
|
-
|
|
9
|
+
ThrowErorr(`Command failed: ${command}\n${error.stderr}`);
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
12
|
|
|
@@ -21,7 +21,7 @@ function getActiveKeyPath() {
|
|
|
21
21
|
const config = execCommand('solana config get keypair');
|
|
22
22
|
// 从输出中提取路径(例如:"Keypair Path: /home/user/.config/solana/keys/alice.json")
|
|
23
23
|
const match = config.match(/Key Path: (.*)/);
|
|
24
|
-
console.log("config:",config,"config match :",match)
|
|
24
|
+
//console.log("config:",config,"config match :",match)
|
|
25
25
|
return match ? match[1] : null;
|
|
26
26
|
}
|
|
27
27
|
|