sovereignclaw 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.
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Wallet Management for SovereignClaw
3
+ * Secure key generation, encryption, and storage
4
+ */
5
+
6
+ import { privateKeyToAccount, generateMnemonic, mnemonicToAccount, english } from 'viem/accounts';
7
+ import * as fs from 'fs';
8
+ import * as crypto from 'crypto';
9
+
10
+ const WALLET_PATH = process.env.WALLET_PATH || './.wallet.enc';
11
+ const ENCRYPTION_KEY = process.env.WALLET_KEY || 'sovereignclaw-default-key-change-me';
12
+
13
+ interface WalletData {
14
+ address: string;
15
+ encryptedKey: string;
16
+ iv: string;
17
+ createdAt: string;
18
+ }
19
+
20
+ // ============ Encryption ============
21
+
22
+ function encrypt(text: string, password: string): { encrypted: string; iv: string } {
23
+ const key = crypto.scryptSync(password, 'salt', 32);
24
+ const iv = crypto.randomBytes(16);
25
+ const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
26
+ let encrypted = cipher.update(text, 'utf8', 'hex');
27
+ encrypted += cipher.final('hex');
28
+ return { encrypted, iv: iv.toString('hex') };
29
+ }
30
+
31
+ function decrypt(encrypted: string, iv: string, password: string): string {
32
+ const key = crypto.scryptSync(password, 'salt', 32);
33
+ const decipher = crypto.createDecipheriv('aes-256-cbc', key, Buffer.from(iv, 'hex'));
34
+ let decrypted = decipher.update(encrypted, 'hex', 'utf8');
35
+ decrypted += decipher.final('utf8');
36
+ return decrypted;
37
+ }
38
+
39
+ // ============ Wallet Operations ============
40
+
41
+ export function createWallet(): { mnemonic: string; address: string } {
42
+ const mnemonic = generateMnemonic(english);
43
+ const account = mnemonicToAccount(mnemonic);
44
+
45
+ const { encrypted, iv } = encrypt(mnemonic, ENCRYPTION_KEY);
46
+ const walletData: WalletData = {
47
+ address: account.address,
48
+ encryptedKey: encrypted,
49
+ iv,
50
+ createdAt: new Date().toISOString(),
51
+ };
52
+
53
+ fs.writeFileSync(WALLET_PATH, JSON.stringify(walletData, null, 2));
54
+
55
+ return { mnemonic, address: account.address };
56
+ }
57
+
58
+ export function importWallet(privateKeyOrMnemonic: string): string {
59
+ let address: string;
60
+ let toEncrypt: string;
61
+
62
+ if (privateKeyOrMnemonic.includes(' ')) {
63
+ const account = mnemonicToAccount(privateKeyOrMnemonic);
64
+ address = account.address;
65
+ toEncrypt = privateKeyOrMnemonic;
66
+ } else {
67
+ const normalized = privateKeyOrMnemonic.startsWith('0x')
68
+ ? privateKeyOrMnemonic
69
+ : `0x${privateKeyOrMnemonic}`;
70
+ const account = privateKeyToAccount(normalized as `0x${string}`);
71
+ address = account.address;
72
+ toEncrypt = normalized;
73
+ }
74
+
75
+ const { encrypted, iv } = encrypt(toEncrypt, ENCRYPTION_KEY);
76
+ const walletData: WalletData = {
77
+ address,
78
+ encryptedKey: encrypted,
79
+ iv,
80
+ createdAt: new Date().toISOString(),
81
+ };
82
+
83
+ fs.writeFileSync(WALLET_PATH, JSON.stringify(walletData, null, 2));
84
+ return address;
85
+ }
86
+
87
+ export function loadWallet(): { address: string; privateKey: string } | null {
88
+ if (!fs.existsSync(WALLET_PATH)) {
89
+ return null;
90
+ }
91
+
92
+ const data: WalletData = JSON.parse(fs.readFileSync(WALLET_PATH, 'utf8'));
93
+ const decrypted = decrypt(data.encryptedKey, data.iv, ENCRYPTION_KEY);
94
+
95
+ if (decrypted.includes(' ')) {
96
+ const account = mnemonicToAccount(decrypted);
97
+ return { address: account.address, privateKey: decrypted };
98
+ } else {
99
+ return { address: data.address, privateKey: decrypted };
100
+ }
101
+ }
102
+
103
+ export function getWalletAddress(): string | null {
104
+ if (!fs.existsSync(WALLET_PATH)) {
105
+ return null;
106
+ }
107
+ const data: WalletData = JSON.parse(fs.readFileSync(WALLET_PATH, 'utf8'));
108
+ return data.address;
109
+ }
110
+
111
+ export function walletExists(): boolean {
112
+ return fs.existsSync(WALLET_PATH);
113
+ }
114
+
115
+ // ============ CLI ============
116
+
117
+ // Only run CLI when called directly (not when imported)
118
+ const isMainModule = import.meta.url === `file://${process.argv[1]}` ||
119
+ process.argv[1]?.endsWith('wallet.ts');
120
+
121
+ async function main() {
122
+ if (!isMainModule) return;
123
+ const args = process.argv.slice(2);
124
+ const [action] = args;
125
+
126
+ if (action === 'create') {
127
+ if (walletExists()) {
128
+ console.error('❌ Wallet already exists. Delete .wallet.enc to create a new one.');
129
+ process.exit(1);
130
+ }
131
+
132
+ const { mnemonic, address } = createWallet();
133
+ console.log(`
134
+ 🔐 SovereignClaw Treasury Created
135
+
136
+ ⚠️ CRITICAL: Save this seed phrase NOW.
137
+ It will NEVER be shown again.
138
+
139
+ ┌────────────────────────────────────────────────────────────┐
140
+ ${mnemonic}
141
+ └────────────────────────────────────────────────────────────┘
142
+
143
+ 📍 Address: ${address}
144
+
145
+ After saving securely, delete this terminal output.
146
+ `);
147
+ return;
148
+ }
149
+
150
+ if (action === 'import') {
151
+ const keyOrMnemonic = args.slice(1).join(' ');
152
+ if (!keyOrMnemonic) {
153
+ console.error('Usage: npx tsx wallet.ts import <private-key-or-mnemonic>');
154
+ process.exit(1);
155
+ }
156
+
157
+ const address = importWallet(keyOrMnemonic);
158
+ console.log(`✅ Wallet imported: ${address}`);
159
+ console.log('⚠️ Delete the command containing your key from terminal history!');
160
+ return;
161
+ }
162
+
163
+ if (action === 'address') {
164
+ const address = getWalletAddress();
165
+ if (!address) {
166
+ console.error('No wallet found. Run: npx tsx wallet.ts create');
167
+ process.exit(1);
168
+ }
169
+ console.log(address);
170
+ return;
171
+ }
172
+
173
+ if (action === 'exists') {
174
+ console.log(walletExists() ? 'true' : 'false');
175
+ return;
176
+ }
177
+
178
+ console.log(`
179
+ SovereignClaw Wallet Manager
180
+
181
+ Usage:
182
+ npx tsx wallet.ts create Create new wallet (shows seed ONCE)
183
+ npx tsx wallet.ts import <key|phrase> Import existing wallet
184
+ npx tsx wallet.ts address Show wallet address
185
+ npx tsx wallet.ts exists Check if wallet exists
186
+
187
+ Environment:
188
+ WALLET_PATH Path to encrypted wallet file (default: .wallet.enc)
189
+ WALLET_KEY Encryption password (default: built-in, change for production!)
190
+ `);
191
+ }
192
+
193
+ if (isMainModule) {
194
+ main().catch(console.error);
195
+ }