@elytro/cli 0.5.0 → 0.5.2
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/index.js +239 -69
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -233,9 +233,9 @@ var FileStore = class {
|
|
|
233
233
|
return join(this.root, `${key}.json`);
|
|
234
234
|
}
|
|
235
235
|
async load(key) {
|
|
236
|
-
const
|
|
236
|
+
const path2 = this.filePath(key);
|
|
237
237
|
try {
|
|
238
|
-
const raw = await readFile(
|
|
238
|
+
const raw = await readFile(path2, "utf-8");
|
|
239
239
|
return JSON.parse(raw);
|
|
240
240
|
} catch (err) {
|
|
241
241
|
if (err.code === "ENOENT") {
|
|
@@ -245,15 +245,15 @@ var FileStore = class {
|
|
|
245
245
|
}
|
|
246
246
|
}
|
|
247
247
|
async save(key, data) {
|
|
248
|
-
const
|
|
249
|
-
await mkdir(dirname(
|
|
250
|
-
await writeFile(
|
|
248
|
+
const path2 = this.filePath(key);
|
|
249
|
+
await mkdir(dirname(path2), { recursive: true });
|
|
250
|
+
await writeFile(path2, JSON.stringify(data, null, 2), "utf-8");
|
|
251
251
|
}
|
|
252
252
|
async remove(key) {
|
|
253
|
-
const { unlink } = await import("fs/promises");
|
|
254
|
-
const
|
|
253
|
+
const { unlink: unlink2 } = await import("fs/promises");
|
|
254
|
+
const path2 = this.filePath(key);
|
|
255
255
|
try {
|
|
256
|
-
await
|
|
256
|
+
await unlink2(path2);
|
|
257
257
|
} catch (err) {
|
|
258
258
|
if (err.code !== "ENOENT") {
|
|
259
259
|
throw err;
|
|
@@ -261,9 +261,9 @@ var FileStore = class {
|
|
|
261
261
|
}
|
|
262
262
|
}
|
|
263
263
|
async exists(key) {
|
|
264
|
-
const
|
|
264
|
+
const path2 = this.filePath(key);
|
|
265
265
|
try {
|
|
266
|
-
await access(
|
|
266
|
+
await access(path2);
|
|
267
267
|
return true;
|
|
268
268
|
} catch {
|
|
269
269
|
return false;
|
|
@@ -521,6 +521,7 @@ var PUBLIC_RPC = {
|
|
|
521
521
|
1: "https://ethereum-rpc.publicnode.com",
|
|
522
522
|
10: "https://optimism-rpc.publicnode.com",
|
|
523
523
|
42161: "https://arbitrum-one-rpc.publicnode.com",
|
|
524
|
+
8453: "https://base-rpc.publicnode.com",
|
|
524
525
|
11155111: "https://ethereum-sepolia-rpc.publicnode.com",
|
|
525
526
|
11155420: "https://optimism-sepolia-rpc.publicnode.com"
|
|
526
527
|
};
|
|
@@ -528,6 +529,7 @@ var PUBLIC_BUNDLER = {
|
|
|
528
529
|
1: "https://public.pimlico.io/v2/1/rpc",
|
|
529
530
|
10: "https://public.pimlico.io/v2/10/rpc",
|
|
530
531
|
42161: "https://public.pimlico.io/v2/42161/rpc",
|
|
532
|
+
8453: "https://public.pimlico.io/v2/8453/rpc",
|
|
531
533
|
11155111: "https://public.pimlico.io/v2/11155111/rpc",
|
|
532
534
|
11155420: "https://public.pimlico.io/v2/11155420/rpc"
|
|
533
535
|
};
|
|
@@ -541,6 +543,7 @@ var ALCHEMY_NETWORK = {
|
|
|
541
543
|
1: "eth-mainnet",
|
|
542
544
|
10: "opt-mainnet",
|
|
543
545
|
42161: "arb-mainnet",
|
|
546
|
+
8453: "base-mainnet",
|
|
544
547
|
11155111: "eth-sepolia",
|
|
545
548
|
11155420: "opt-sepolia"
|
|
546
549
|
};
|
|
@@ -548,6 +551,7 @@ var PIMLICO_SLUG = {
|
|
|
548
551
|
1: "ethereum",
|
|
549
552
|
10: "optimism",
|
|
550
553
|
42161: "arbitrum",
|
|
554
|
+
8453: "base",
|
|
551
555
|
11155111: "sepolia",
|
|
552
556
|
11155420: "optimism-sepolia"
|
|
553
557
|
};
|
|
@@ -584,6 +588,12 @@ var CHAIN_META = [
|
|
|
584
588
|
nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
|
|
585
589
|
blockExplorer: "https://arbiscan.io"
|
|
586
590
|
},
|
|
591
|
+
{
|
|
592
|
+
id: 8453,
|
|
593
|
+
name: "Base",
|
|
594
|
+
nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
|
|
595
|
+
blockExplorer: "https://basescan.org"
|
|
596
|
+
},
|
|
587
597
|
{
|
|
588
598
|
id: 11155111,
|
|
589
599
|
name: "Sepolia",
|
|
@@ -1434,6 +1444,27 @@ var AccountService = class {
|
|
|
1434
1444
|
await this.persist();
|
|
1435
1445
|
return account;
|
|
1436
1446
|
}
|
|
1447
|
+
// ─── Rename ───────────────────────────────────────────────────────
|
|
1448
|
+
/**
|
|
1449
|
+
* Rename an account's alias.
|
|
1450
|
+
* @param aliasOrAddress - Current alias or address to identify the account.
|
|
1451
|
+
* @param newAlias - The new alias. Must be unique.
|
|
1452
|
+
*/
|
|
1453
|
+
async renameAccount(aliasOrAddress, newAlias) {
|
|
1454
|
+
const account = this.resolveAccount(aliasOrAddress);
|
|
1455
|
+
if (!account) {
|
|
1456
|
+
throw new Error(`Account "${aliasOrAddress}" not found.`);
|
|
1457
|
+
}
|
|
1458
|
+
const conflict = this.state.accounts.find(
|
|
1459
|
+
(a) => a.alias.toLowerCase() === newAlias.toLowerCase() && a.address !== account.address
|
|
1460
|
+
);
|
|
1461
|
+
if (conflict) {
|
|
1462
|
+
throw new Error(`Alias "${newAlias}" is already taken by ${conflict.address}.`);
|
|
1463
|
+
}
|
|
1464
|
+
account.alias = newAlias;
|
|
1465
|
+
await this.persist();
|
|
1466
|
+
return account;
|
|
1467
|
+
}
|
|
1437
1468
|
// ─── Activation ───────────────────────────────────────────────────
|
|
1438
1469
|
/**
|
|
1439
1470
|
* Mark an account as deployed on-chain.
|
|
@@ -2141,120 +2172,227 @@ var SecurityHookService = class {
|
|
|
2141
2172
|
}
|
|
2142
2173
|
};
|
|
2143
2174
|
|
|
2144
|
-
// src/providers/
|
|
2145
|
-
import {
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
var KeychainProvider = class {
|
|
2149
|
-
name = "macos-keychain";
|
|
2175
|
+
// src/providers/keyringProvider.ts
|
|
2176
|
+
import { Entry } from "@napi-rs/keyring";
|
|
2177
|
+
var KeyringProvider = class {
|
|
2178
|
+
name;
|
|
2150
2179
|
service = "elytro-wallet";
|
|
2151
2180
|
account = "vault-key";
|
|
2181
|
+
constructor() {
|
|
2182
|
+
const platform = process.platform;
|
|
2183
|
+
if (platform === "darwin") this.name = "macos-keychain";
|
|
2184
|
+
else if (platform === "win32") this.name = "windows-credential-manager";
|
|
2185
|
+
else this.name = "linux-secret-service";
|
|
2186
|
+
}
|
|
2152
2187
|
async available() {
|
|
2153
|
-
if (process.platform !== "darwin") return false;
|
|
2154
2188
|
try {
|
|
2155
|
-
|
|
2189
|
+
const entry = new Entry(this.service, this.account);
|
|
2190
|
+
entry.getSecret();
|
|
2156
2191
|
return true;
|
|
2157
|
-
} catch {
|
|
2158
|
-
|
|
2192
|
+
} catch (err) {
|
|
2193
|
+
const msg = err.message || "";
|
|
2194
|
+
if (isNotFoundError(msg)) return true;
|
|
2195
|
+
return false;
|
|
2159
2196
|
}
|
|
2160
2197
|
}
|
|
2161
2198
|
async store(secret) {
|
|
2162
2199
|
validateKeyLength(secret);
|
|
2200
|
+
try {
|
|
2201
|
+
const entry = new Entry(this.service, this.account);
|
|
2202
|
+
entry.setSecret(Buffer.from(secret));
|
|
2203
|
+
} catch (err) {
|
|
2204
|
+
throw new Error(`Failed to store vault key in OS credential store: ${err.message}`);
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
async load() {
|
|
2208
|
+
try {
|
|
2209
|
+
const entry = new Entry(this.service, this.account);
|
|
2210
|
+
const raw = entry.getSecret();
|
|
2211
|
+
if (!raw || raw.length === 0) return null;
|
|
2212
|
+
const key = new Uint8Array(raw);
|
|
2213
|
+
if (key.length !== 32) {
|
|
2214
|
+
throw new Error(
|
|
2215
|
+
`OS credential store returned vault key with invalid length: expected 32 bytes, got ${key.length}.`
|
|
2216
|
+
);
|
|
2217
|
+
}
|
|
2218
|
+
return key;
|
|
2219
|
+
} catch (err) {
|
|
2220
|
+
const msg = err.message || "";
|
|
2221
|
+
if (isNotFoundError(msg)) return null;
|
|
2222
|
+
throw new Error(`Failed to load vault key from OS credential store: ${msg}`);
|
|
2223
|
+
}
|
|
2224
|
+
}
|
|
2225
|
+
async delete() {
|
|
2226
|
+
try {
|
|
2227
|
+
const entry = new Entry(this.service, this.account);
|
|
2228
|
+
entry.deleteCredential();
|
|
2229
|
+
} catch {
|
|
2230
|
+
}
|
|
2231
|
+
}
|
|
2232
|
+
};
|
|
2233
|
+
function isNotFoundError(msg) {
|
|
2234
|
+
const lower = msg.toLowerCase();
|
|
2235
|
+
return lower.includes("not found") || lower.includes("no matching") || lower.includes("no such") || lower.includes("itemnotfound") || lower.includes("element not found") || // Windows
|
|
2236
|
+
lower.includes("no result") || lower.includes("no password");
|
|
2237
|
+
}
|
|
2238
|
+
function validateKeyLength(key) {
|
|
2239
|
+
if (key.length !== 32) {
|
|
2240
|
+
throw new Error(`Invalid vault key: expected 32 bytes, got ${key.length}.`);
|
|
2241
|
+
}
|
|
2242
|
+
}
|
|
2243
|
+
|
|
2244
|
+
// src/providers/fileProvider.ts
|
|
2245
|
+
import * as fs from "fs/promises";
|
|
2246
|
+
import * as path from "path";
|
|
2247
|
+
import { constants } from "fs";
|
|
2248
|
+
var FileProvider = class {
|
|
2249
|
+
name = "file-protected";
|
|
2250
|
+
keyPath;
|
|
2251
|
+
/**
|
|
2252
|
+
* @param dataDir — the ~/.elytro/ directory path (from FileStore.dataDir).
|
|
2253
|
+
* Defaults to ~/.elytro if not provided.
|
|
2254
|
+
*/
|
|
2255
|
+
constructor(dataDir) {
|
|
2256
|
+
const base = dataDir ?? path.join(process.env.HOME || "~", ".elytro");
|
|
2257
|
+
this.keyPath = path.join(base, ".vault-key");
|
|
2258
|
+
}
|
|
2259
|
+
/**
|
|
2260
|
+
* FileProvider is always technically available on any OS (filesystem always exists).
|
|
2261
|
+
* But it should only be used on Linux when the KeyringProvider is not available.
|
|
2262
|
+
* The resolution logic in resolveProvider handles this gating — FileProvider
|
|
2263
|
+
* itself does not restrict by platform.
|
|
2264
|
+
*/
|
|
2265
|
+
async available() {
|
|
2266
|
+
try {
|
|
2267
|
+
await fs.access(this.keyPath, constants.R_OK);
|
|
2268
|
+
return true;
|
|
2269
|
+
} catch {
|
|
2270
|
+
try {
|
|
2271
|
+
await fs.access(path.dirname(this.keyPath), constants.W_OK);
|
|
2272
|
+
return true;
|
|
2273
|
+
} catch {
|
|
2274
|
+
return false;
|
|
2275
|
+
}
|
|
2276
|
+
}
|
|
2277
|
+
}
|
|
2278
|
+
async store(secret) {
|
|
2279
|
+
validateKeyLength2(secret);
|
|
2163
2280
|
const b64 = Buffer.from(secret).toString("base64");
|
|
2281
|
+
const tmpPath = this.keyPath + ".tmp";
|
|
2164
2282
|
try {
|
|
2165
|
-
await
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
"-s",
|
|
2169
|
-
this.service,
|
|
2170
|
-
"-a",
|
|
2171
|
-
this.account,
|
|
2172
|
-
"-w",
|
|
2173
|
-
b64
|
|
2174
|
-
]);
|
|
2283
|
+
await fs.writeFile(tmpPath, b64, { encoding: "utf-8", mode: 384 });
|
|
2284
|
+
await fs.rename(tmpPath, this.keyPath);
|
|
2285
|
+
await fs.chmod(this.keyPath, 384);
|
|
2175
2286
|
} catch (err) {
|
|
2176
|
-
|
|
2287
|
+
try {
|
|
2288
|
+
await fs.unlink(tmpPath);
|
|
2289
|
+
} catch {
|
|
2290
|
+
}
|
|
2291
|
+
throw new Error(`Failed to store vault key to file: ${err.message}`);
|
|
2177
2292
|
}
|
|
2178
2293
|
}
|
|
2179
2294
|
async load() {
|
|
2180
2295
|
try {
|
|
2181
|
-
const
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2296
|
+
const stat2 = await fs.stat(this.keyPath);
|
|
2297
|
+
const mode = stat2.mode & 511;
|
|
2298
|
+
if (mode !== 384) {
|
|
2299
|
+
throw new Error(
|
|
2300
|
+
`Vault key file has insecure permissions: ${modeToOctal(mode)} (expected 0600).
|
|
2301
|
+
Fix with: chmod 600 ${this.keyPath}
|
|
2302
|
+
Refusing to load until permissions are corrected.`
|
|
2303
|
+
);
|
|
2304
|
+
}
|
|
2305
|
+
const raw = await fs.readFile(this.keyPath, "utf-8");
|
|
2306
|
+
const trimmed = raw.trim();
|
|
2190
2307
|
if (!trimmed) return null;
|
|
2191
2308
|
const key = Buffer.from(trimmed, "base64");
|
|
2192
2309
|
if (key.length !== 32) {
|
|
2193
|
-
throw new Error(
|
|
2310
|
+
throw new Error(
|
|
2311
|
+
`Vault key file has invalid content: expected 32 bytes (base64), got ${key.length}.`
|
|
2312
|
+
);
|
|
2194
2313
|
}
|
|
2195
2314
|
return new Uint8Array(key);
|
|
2196
2315
|
} catch (err) {
|
|
2197
2316
|
const msg = err.message || "";
|
|
2198
|
-
if (msg.includes("
|
|
2199
|
-
|
|
2200
|
-
}
|
|
2201
|
-
throw new Error(`Failed to load vault key from Keychain: ${msg}`);
|
|
2317
|
+
if (msg.includes("ENOENT")) return null;
|
|
2318
|
+
throw err;
|
|
2202
2319
|
}
|
|
2203
2320
|
}
|
|
2204
2321
|
async delete() {
|
|
2205
2322
|
try {
|
|
2206
|
-
await
|
|
2323
|
+
await fs.unlink(this.keyPath);
|
|
2207
2324
|
} catch {
|
|
2208
2325
|
}
|
|
2209
2326
|
}
|
|
2210
2327
|
};
|
|
2211
|
-
function
|
|
2328
|
+
function validateKeyLength2(key) {
|
|
2212
2329
|
if (key.length !== 32) {
|
|
2213
2330
|
throw new Error(`Invalid vault key: expected 32 bytes, got ${key.length}.`);
|
|
2214
2331
|
}
|
|
2215
2332
|
}
|
|
2333
|
+
function modeToOctal(mode) {
|
|
2334
|
+
return "0" + mode.toString(8);
|
|
2335
|
+
}
|
|
2216
2336
|
|
|
2217
2337
|
// src/providers/envVarProvider.ts
|
|
2218
2338
|
var ENV_KEY = "ELYTRO_VAULT_SECRET";
|
|
2339
|
+
var ENV_ALLOW = "ELYTRO_ALLOW_ENV";
|
|
2219
2340
|
var EnvVarProvider = class {
|
|
2220
2341
|
name = "env-var";
|
|
2221
2342
|
async available() {
|
|
2222
|
-
return !!process.env[ENV_KEY];
|
|
2343
|
+
return !!process.env[ENV_KEY] && process.env[ENV_ALLOW] === "1";
|
|
2223
2344
|
}
|
|
2224
2345
|
async store(_secret) {
|
|
2225
2346
|
throw new Error(
|
|
2226
|
-
"EnvVarProvider is read-only. Cannot store vault key in an environment variable
|
|
2347
|
+
"EnvVarProvider is read-only. Cannot store vault key in an environment variable.\nUse a persistent provider (OS keychain or file-protected) or store the secret manually."
|
|
2227
2348
|
);
|
|
2228
2349
|
}
|
|
2229
2350
|
async load() {
|
|
2351
|
+
if (process.env[ENV_ALLOW] !== "1") return null;
|
|
2230
2352
|
const raw = process.env[ENV_KEY];
|
|
2231
2353
|
if (!raw) return null;
|
|
2232
2354
|
delete process.env[ENV_KEY];
|
|
2355
|
+
delete process.env[ENV_ALLOW];
|
|
2233
2356
|
const key = Buffer.from(raw, "base64");
|
|
2234
2357
|
if (key.length !== 32) {
|
|
2235
2358
|
throw new Error(
|
|
2236
|
-
`${ENV_KEY} has invalid length: expected 32 bytes (base64), got ${key.length}.
|
|
2359
|
+
`${ENV_KEY} has invalid length: expected 32 bytes (base64), got ${key.length}.
|
|
2360
|
+
The value must be a base64-encoded 256-bit key.`
|
|
2237
2361
|
);
|
|
2238
2362
|
}
|
|
2239
2363
|
return new Uint8Array(key);
|
|
2240
2364
|
}
|
|
2241
2365
|
async delete() {
|
|
2242
2366
|
delete process.env[ENV_KEY];
|
|
2367
|
+
delete process.env[ENV_ALLOW];
|
|
2243
2368
|
}
|
|
2244
2369
|
};
|
|
2245
2370
|
|
|
2246
2371
|
// src/providers/resolveProvider.ts
|
|
2247
2372
|
async function resolveProvider() {
|
|
2248
|
-
const
|
|
2373
|
+
const keyringProvider = new KeyringProvider();
|
|
2374
|
+
const fileProvider = new FileProvider();
|
|
2249
2375
|
const envProvider = new EnvVarProvider();
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2376
|
+
if (await keyringProvider.available()) {
|
|
2377
|
+
return {
|
|
2378
|
+
initProvider: keyringProvider,
|
|
2379
|
+
loadProvider: keyringProvider
|
|
2380
|
+
};
|
|
2381
|
+
}
|
|
2382
|
+
if (process.platform === "linux" && await fileProvider.available()) {
|
|
2383
|
+
return {
|
|
2384
|
+
initProvider: fileProvider,
|
|
2385
|
+
loadProvider: fileProvider
|
|
2386
|
+
};
|
|
2387
|
+
}
|
|
2388
|
+
if (await envProvider.available()) {
|
|
2389
|
+
return {
|
|
2390
|
+
initProvider: null,
|
|
2391
|
+
// Cannot store via env var
|
|
2392
|
+
loadProvider: envProvider
|
|
2393
|
+
};
|
|
2394
|
+
}
|
|
2395
|
+
return { initProvider: null, loadProvider: null };
|
|
2258
2396
|
}
|
|
2259
2397
|
|
|
2260
2398
|
// src/context.ts
|
|
@@ -2274,14 +2412,15 @@ async function createAppContext() {
|
|
|
2274
2412
|
if (isInitialized) {
|
|
2275
2413
|
if (!loadProvider) {
|
|
2276
2414
|
throw new Error(
|
|
2277
|
-
"Wallet is initialized but no secret provider is available.\n" + (
|
|
2415
|
+
"Wallet is initialized but no secret provider is available.\n" + noProviderHint()
|
|
2278
2416
|
);
|
|
2279
2417
|
}
|
|
2280
2418
|
const vaultKey = await loadProvider.load();
|
|
2281
2419
|
if (!vaultKey) {
|
|
2282
2420
|
throw new Error(
|
|
2283
2421
|
`Wallet is initialized but vault key not found in ${loadProvider.name}.
|
|
2284
|
-
|
|
2422
|
+
The credential may have been deleted. Re-run \`elytro init\` to create a new wallet,
|
|
2423
|
+
or import a backup with \`elytro import\`.`
|
|
2285
2424
|
);
|
|
2286
2425
|
}
|
|
2287
2426
|
try {
|
|
@@ -2316,6 +2455,16 @@ The vault key may not match the encrypted keyring. Re-run \`elytro init\` or imp
|
|
|
2316
2455
|
}
|
|
2317
2456
|
return { store, keyring, chain, sdk, walletClient, account, secretProvider: loadProvider };
|
|
2318
2457
|
}
|
|
2458
|
+
function noProviderHint() {
|
|
2459
|
+
switch (process.platform) {
|
|
2460
|
+
case "darwin":
|
|
2461
|
+
return "macOS Keychain access failed. Check Keychain permissions or security settings.";
|
|
2462
|
+
case "win32":
|
|
2463
|
+
return "Windows Credential Manager access failed. Run as the same user who initialized the wallet.";
|
|
2464
|
+
default:
|
|
2465
|
+
return "No secret provider available. Options:\n 1. Install and start a Secret Service provider (GNOME Keyring or KWallet)\n 2. The vault key file (~/.elytro/.vault-key) may have been deleted\n 3. For CI: set ELYTRO_VAULT_SECRET and ELYTRO_ALLOW_ENV=1";
|
|
2466
|
+
}
|
|
2467
|
+
}
|
|
2319
2468
|
|
|
2320
2469
|
// src/commands/init.ts
|
|
2321
2470
|
import { webcrypto as webcrypto2 } from "crypto";
|
|
@@ -2386,7 +2535,9 @@ function registerInitCommand(program2, ctx) {
|
|
|
2386
2535
|
dataDir: ctx.store.dataDir,
|
|
2387
2536
|
secretProvider: providerName,
|
|
2388
2537
|
...vaultSecretB64 ? { vaultSecret: vaultSecretB64 } : {},
|
|
2389
|
-
...vaultSecretB64 ? {
|
|
2538
|
+
...vaultSecretB64 ? {
|
|
2539
|
+
hint: "No persistent secret provider available. Save this vault key securely \u2014 it will NOT be shown again.\nFor CI: set ELYTRO_VAULT_SECRET=<key> and ELYTRO_ALLOW_ENV=1."
|
|
2540
|
+
} : {},
|
|
2390
2541
|
nextStep: "Run `elytro account create --chain <chainId>` to create your first smart account."
|
|
2391
2542
|
});
|
|
2392
2543
|
} catch (err) {
|
|
@@ -2463,6 +2614,14 @@ function registerAccountCommand(program2, ctx) {
|
|
|
2463
2614
|
outputError(ERR_INVALID_PARAMS, "Invalid chain ID.", { chain: opts.chain });
|
|
2464
2615
|
return;
|
|
2465
2616
|
}
|
|
2617
|
+
const chainConfig = ctx.chain.chains.find((c) => c.id === chainId);
|
|
2618
|
+
if (!chainConfig) {
|
|
2619
|
+
const supported = ctx.chain.chains.map((c) => `${c.id} (${c.name})`);
|
|
2620
|
+
outputError(ERR_INVALID_PARAMS, `Chain ${chainId} is not supported.`, {
|
|
2621
|
+
supportedChains: supported
|
|
2622
|
+
});
|
|
2623
|
+
return;
|
|
2624
|
+
}
|
|
2466
2625
|
let dailyLimitUsd;
|
|
2467
2626
|
if (opts.dailyLimit !== void 0) {
|
|
2468
2627
|
dailyLimitUsd = parseFloat(opts.dailyLimit);
|
|
@@ -2477,12 +2636,9 @@ function registerAccountCommand(program2, ctx) {
|
|
|
2477
2636
|
} : void 0;
|
|
2478
2637
|
const spinner = ora2("Creating smart account...").start();
|
|
2479
2638
|
try {
|
|
2480
|
-
const
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
await ctx.sdk.initForChain(chainConfig);
|
|
2484
|
-
ctx.walletClient.initForChain(chainConfig);
|
|
2485
|
-
}
|
|
2639
|
+
const chainName = chainConfig.name;
|
|
2640
|
+
await ctx.sdk.initForChain(chainConfig);
|
|
2641
|
+
ctx.walletClient.initForChain(chainConfig);
|
|
2486
2642
|
const accountInfo = await ctx.account.createAccount(chainId, opts.alias, securityIntent);
|
|
2487
2643
|
spinner.text = "Registering with backend...";
|
|
2488
2644
|
const { guardianHash, guardianSafePeriod } = ctx.sdk.initDefaults;
|
|
@@ -2738,6 +2894,20 @@ function registerAccountCommand(program2, ctx) {
|
|
|
2738
2894
|
outputError(ERR_INTERNAL, err.message);
|
|
2739
2895
|
}
|
|
2740
2896
|
});
|
|
2897
|
+
account.command("rename").description("Rename an account alias").argument("<account>", "Current alias or address").argument("<newAlias>", "New alias").action(async (target, newAlias) => {
|
|
2898
|
+
try {
|
|
2899
|
+
const renamed = await ctx.account.renameAccount(target, newAlias);
|
|
2900
|
+
const chainConfig = ctx.chain.chains.find((c) => c.id === renamed.chainId);
|
|
2901
|
+
outputResult({
|
|
2902
|
+
alias: renamed.alias,
|
|
2903
|
+
address: renamed.address,
|
|
2904
|
+
chain: chainConfig?.name ?? String(renamed.chainId),
|
|
2905
|
+
chainId: renamed.chainId
|
|
2906
|
+
});
|
|
2907
|
+
} catch (err) {
|
|
2908
|
+
outputError(ERR_INTERNAL, err.message);
|
|
2909
|
+
}
|
|
2910
|
+
});
|
|
2741
2911
|
account.command("switch").description("Switch the active account").argument("[account]", "Alias or address").action(async (target) => {
|
|
2742
2912
|
const accounts = ctx.account.allAccounts;
|
|
2743
2913
|
if (accounts.length === 0) {
|
|
@@ -4228,7 +4398,7 @@ import { execSync } from "child_process";
|
|
|
4228
4398
|
import { createRequire } from "module";
|
|
4229
4399
|
function resolveVersion() {
|
|
4230
4400
|
if (true) {
|
|
4231
|
-
return "0.5.
|
|
4401
|
+
return "0.5.2";
|
|
4232
4402
|
}
|
|
4233
4403
|
try {
|
|
4234
4404
|
const require2 = createRequire(import.meta.url);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elytro/cli",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"description": "Elytro ERC-4337 Smart Account Wallet CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
"@elytro/abi": "latest",
|
|
42
42
|
"@elytro/sdk": "latest",
|
|
43
43
|
"@inquirer/prompts": "^7.0.0",
|
|
44
|
+
"@napi-rs/keyring": "^1.2.0",
|
|
44
45
|
"chalk": "^5.3.0",
|
|
45
46
|
"commander": "^13.0.0",
|
|
46
47
|
"graphql": "^16.9.0",
|