arc402-cli 0.6.0 → 0.7.1
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/commands/backup.d.ts +3 -0
- package/dist/commands/backup.d.ts.map +1 -0
- package/dist/commands/backup.js +106 -0
- package/dist/commands/backup.js.map +1 -0
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +11 -1
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/discover.d.ts.map +1 -1
- package/dist/commands/discover.js +60 -15
- package/dist/commands/discover.js.map +1 -1
- package/dist/commands/doctor.d.ts +3 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +205 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/wallet.d.ts.map +1 -1
- package/dist/commands/wallet.js +192 -58
- package/dist/commands/wallet.js.map +1 -1
- package/dist/commands/watch.d.ts.map +1 -1
- package/dist/commands/watch.js +146 -9
- package/dist/commands/watch.js.map +1 -1
- package/dist/config.d.ts +9 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +35 -3
- package/dist/config.js.map +1 -1
- package/dist/daemon/index.d.ts.map +1 -1
- package/dist/daemon/index.js +359 -220
- package/dist/daemon/index.js.map +1 -1
- package/dist/endpoint-notify.d.ts +9 -1
- package/dist/endpoint-notify.d.ts.map +1 -1
- package/dist/endpoint-notify.js +116 -3
- package/dist/endpoint-notify.js.map +1 -1
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -1
- package/dist/program.d.ts.map +1 -1
- package/dist/program.js +4 -0
- package/dist/program.js.map +1 -1
- package/dist/repl.d.ts.map +1 -1
- package/dist/repl.js +45 -34
- package/dist/repl.js.map +1 -1
- package/dist/ui/format.d.ts.map +1 -1
- package/dist/ui/format.js +2 -0
- package/dist/ui/format.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/backup.ts +117 -0
- package/src/commands/config.ts +12 -2
- package/src/commands/discover.ts +74 -21
- package/src/commands/doctor.ts +172 -0
- package/src/commands/wallet.ts +194 -57
- package/src/commands/watch.ts +207 -10
- package/src/config.ts +48 -2
- package/src/daemon/index.ts +297 -152
- package/src/endpoint-notify.ts +86 -3
- package/src/index.ts +26 -0
- package/src/program.ts +4 -0
- package/src/repl.ts +53 -42
- package/src/ui/format.ts +1 -0
package/dist/commands/wallet.js
CHANGED
|
@@ -26,6 +26,26 @@ const tree_1 = require("../ui/tree");
|
|
|
26
26
|
const spinner_1 = require("../ui/spinner");
|
|
27
27
|
const colors_1 = require("../ui/colors");
|
|
28
28
|
const POLICY_ENGINE_DEFAULT = "0x44102e70c2A366632d98Fe40d892a2501fC7fFF2";
|
|
29
|
+
const GUARDIAN_KEY_PATH = path_1.default.join(os_1.default.homedir(), ".arc402", "guardian.key");
|
|
30
|
+
/** Save guardian private key to a restricted standalone file (never to config.json). */
|
|
31
|
+
function saveGuardianKey(privateKey) {
|
|
32
|
+
fs_1.default.mkdirSync(path_1.default.dirname(GUARDIAN_KEY_PATH), { recursive: true, mode: 0o700 });
|
|
33
|
+
fs_1.default.writeFileSync(GUARDIAN_KEY_PATH, privateKey + "\n", { mode: 0o400 });
|
|
34
|
+
}
|
|
35
|
+
/** Load guardian private key from file, falling back to config for backwards compat. */
|
|
36
|
+
function loadGuardianKey(config) {
|
|
37
|
+
try {
|
|
38
|
+
return fs_1.default.readFileSync(GUARDIAN_KEY_PATH, "utf-8").trim();
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// Migration: if key still in config, migrate it to the file now
|
|
42
|
+
if (config.guardianPrivateKey) {
|
|
43
|
+
saveGuardianKey(config.guardianPrivateKey);
|
|
44
|
+
return config.guardianPrivateKey;
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
29
49
|
function parseAmount(raw) {
|
|
30
50
|
const lower = raw.toLowerCase();
|
|
31
51
|
if (lower.endsWith("eth")) {
|
|
@@ -161,6 +181,9 @@ async function runCompleteOnboardingCeremony(walletAddress, ownerAddress, config
|
|
|
161
181
|
await sendTx({ to: walletAddress, data: mkIface.encodeFunctionData("authorizeMachineKey", [machineKeyAddress]), value: "0x0" }, "authorizeMachineKey");
|
|
162
182
|
console.log(" " + colors_1.c.success + " Machine key authorized");
|
|
163
183
|
}
|
|
184
|
+
// Save progress after machine key step
|
|
185
|
+
config.onboardingProgress = { walletAddress, step: 2, completedSteps: ["machineKey"] };
|
|
186
|
+
(0, config_1.saveConfig)(config);
|
|
164
187
|
// ── Step 3: Passkey ───────────────────────────────────────────────────────
|
|
165
188
|
console.log("\n" + colors_1.c.dim("── Step 3: Passkey (Face ID / WebAuthn) ──────────────────────"));
|
|
166
189
|
let passkeyActive = false;
|
|
@@ -192,6 +215,9 @@ async function runCompleteOnboardingCeremony(walletAddress, ownerAddress, config
|
|
|
192
215
|
console.log(" " + colors_1.c.success + " Passkey set (via browser)");
|
|
193
216
|
}
|
|
194
217
|
}
|
|
218
|
+
// Save progress after passkey step
|
|
219
|
+
config.onboardingProgress = { walletAddress, step: 3, completedSteps: ["machineKey", "passkey"] };
|
|
220
|
+
(0, config_1.saveConfig)(config);
|
|
195
221
|
// ── Step 4: Policy ────────────────────────────────────────────────────────
|
|
196
222
|
console.log("\n" + colors_1.c.dim("── Step 4: Policy ─────────────────────────────────────────────"));
|
|
197
223
|
// 4a) setVelocityLimit
|
|
@@ -240,12 +266,15 @@ async function runCompleteOnboardingCeremony(walletAddress, ownerAddress, config
|
|
|
240
266
|
const guardianInput = guardianAns.guardian?.trim() ?? "";
|
|
241
267
|
if (guardianInput.toLowerCase() === "g") {
|
|
242
268
|
const generatedGuardian = ethers_1.ethers.Wallet.createRandom();
|
|
243
|
-
|
|
269
|
+
// Save guardian private key to a separate restricted file, NOT config.json
|
|
270
|
+
const guardianKeyPath = path_1.default.join(os_1.default.homedir(), ".arc402", "guardian.key");
|
|
271
|
+
fs_1.default.mkdirSync(path_1.default.dirname(guardianKeyPath), { recursive: true, mode: 0o700 });
|
|
272
|
+
fs_1.default.writeFileSync(guardianKeyPath, generatedGuardian.privateKey + "\n", { mode: 0o400 });
|
|
273
|
+
// Only save address (not private key) to config
|
|
244
274
|
config.guardianAddress = generatedGuardian.address;
|
|
245
275
|
(0, config_1.saveConfig)(config);
|
|
246
276
|
guardianAddress = generatedGuardian.address;
|
|
247
|
-
console.log("\n " + colors_1.c.warning + "
|
|
248
|
-
console.log(" " + colors_1.c.dim(generatedGuardian.privateKey));
|
|
277
|
+
console.log("\n " + colors_1.c.warning + " Guardian key saved to ~/.arc402/guardian.key — move offline for security");
|
|
249
278
|
console.log(" " + colors_1.c.dim("Address: ") + colors_1.c.white(generatedGuardian.address) + "\n");
|
|
250
279
|
const guardianIface = new ethers_1.ethers.Interface(["function setGuardian(address _guardian) external"]);
|
|
251
280
|
await sendTx({ to: walletAddress, data: guardianIface.encodeFunctionData("setGuardian", [guardianAddress]), value: "0x0" }, "setGuardian");
|
|
@@ -297,6 +326,9 @@ async function runCompleteOnboardingCeremony(walletAddress, ownerAddress, config
|
|
|
297
326
|
await sendTx({ to: policyAddress, data: contractInteractionIface.encodeFunctionData("enableContractInteraction", [walletAddress, handshakeAddress]), value: "0x0" }, "enableContractInteraction: Handshake");
|
|
298
327
|
(0, config_1.saveConfig)(config);
|
|
299
328
|
console.log(" " + colors_1.c.success + " Policy configured");
|
|
329
|
+
// Save progress after policy step
|
|
330
|
+
config.onboardingProgress = { walletAddress, step: 4, completedSteps: ["machineKey", "passkey", "policy"] };
|
|
331
|
+
(0, config_1.saveConfig)(config);
|
|
300
332
|
// ── Step 5: Agent Registration ─────────────────────────────────────────────
|
|
301
333
|
console.log("\n" + colors_1.c.dim("── Step 5: Agent Registration ─────────────────────────────────"));
|
|
302
334
|
let agentAlreadyRegistered = false;
|
|
@@ -374,6 +406,9 @@ async function runCompleteOnboardingCeremony(walletAddress, ownerAddress, config
|
|
|
374
406
|
else {
|
|
375
407
|
console.log(" " + colors_1.c.warning + " AgentRegistry address not configured — skipping");
|
|
376
408
|
}
|
|
409
|
+
// Save progress after agent step, then clear on ceremony complete
|
|
410
|
+
config.onboardingProgress = { walletAddress, step: 5, completedSteps: ["machineKey", "passkey", "policy", "agent"] };
|
|
411
|
+
(0, config_1.saveConfig)(config);
|
|
377
412
|
// ── Step 7: Workroom Init ─────────────────────────────────────────────────
|
|
378
413
|
console.log("\n" + colors_1.c.dim("── Step 7: Workroom ────────────────────────────────────────────"));
|
|
379
414
|
let workroomInitialized = false;
|
|
@@ -475,6 +510,9 @@ async function runCompleteOnboardingCeremony(walletAddress, ownerAddress, config
|
|
|
475
510
|
const endpointLabel = agentEndpoint
|
|
476
511
|
? colors_1.c.white(agentEndpoint) + colors_1.c.dim(` → localhost:${relayPort}`)
|
|
477
512
|
: colors_1.c.dim("—");
|
|
513
|
+
// Clear onboarding progress — ceremony complete
|
|
514
|
+
delete config.onboardingProgress;
|
|
515
|
+
(0, config_1.saveConfig)(config);
|
|
478
516
|
console.log("\n " + colors_1.c.success + colors_1.c.white(" Onboarding complete"));
|
|
479
517
|
(0, tree_1.renderTree)([
|
|
480
518
|
{ label: "Wallet", value: colors_1.c.white(walletAddress) },
|
|
@@ -637,16 +675,40 @@ function registerWalletCommands(program) {
|
|
|
637
675
|
console.log(colors_1.c.dim("Next: fund your wallet with ETH, then run: arc402 wallet deploy"));
|
|
638
676
|
});
|
|
639
677
|
// ─── import ────────────────────────────────────────────────────────────────
|
|
640
|
-
wallet.command("import
|
|
641
|
-
.description("Import an existing private key")
|
|
678
|
+
wallet.command("import")
|
|
679
|
+
.description("Import an existing private key (use --key-file or stdin prompt)")
|
|
642
680
|
.option("--network <network>", "Network (base-mainnet or base-sepolia)", "base-sepolia")
|
|
643
|
-
.
|
|
681
|
+
.option("--key-file <path>", "Read private key from file instead of prompting")
|
|
682
|
+
.action(async (opts) => {
|
|
644
683
|
const network = opts.network;
|
|
645
684
|
const defaults = config_1.NETWORK_DEFAULTS[network];
|
|
646
685
|
if (!defaults) {
|
|
647
686
|
console.error(`Unknown network: ${network}. Use base-mainnet or base-sepolia.`);
|
|
648
687
|
process.exit(1);
|
|
649
688
|
}
|
|
689
|
+
let privateKey;
|
|
690
|
+
if (opts.keyFile) {
|
|
691
|
+
try {
|
|
692
|
+
privateKey = fs_1.default.readFileSync(opts.keyFile, "utf-8").trim();
|
|
693
|
+
}
|
|
694
|
+
catch (e) {
|
|
695
|
+
console.error(`Cannot read key file: ${e instanceof Error ? e.message : String(e)}`);
|
|
696
|
+
process.exit(1);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
else {
|
|
700
|
+
// Interactive prompt — hidden input avoids shell history
|
|
701
|
+
const answer = await (0, prompts_1.default)({
|
|
702
|
+
type: "password",
|
|
703
|
+
name: "key",
|
|
704
|
+
message: "Paste private key (hidden):",
|
|
705
|
+
});
|
|
706
|
+
privateKey = (answer.key ?? "").trim();
|
|
707
|
+
if (!privateKey) {
|
|
708
|
+
console.error("No private key entered.");
|
|
709
|
+
process.exit(1);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
650
712
|
let imported;
|
|
651
713
|
try {
|
|
652
714
|
imported = new ethers_1.ethers.Wallet(privateKey);
|
|
@@ -794,8 +856,31 @@ function registerWalletCommands(program) {
|
|
|
794
856
|
.option("--smart-wallet", "Connect via Base Smart Wallet (Coinbase Wallet SDK) instead of WalletConnect")
|
|
795
857
|
.option("--hardware", "Hardware wallet mode: show raw wc: URI only (for Ledger Live, Trezor Suite, etc.)")
|
|
796
858
|
.option("--sponsored", "Use CDP paymaster for gas sponsorship (requires paymasterUrl + cdpKeyName + CDP_PRIVATE_KEY env)")
|
|
859
|
+
.option("--dry-run", "Simulate the deployment ceremony without sending transactions")
|
|
797
860
|
.action(async (opts) => {
|
|
798
861
|
const config = (0, config_1.loadConfig)();
|
|
862
|
+
if (opts.dryRun) {
|
|
863
|
+
const factoryAddr = config.walletFactoryAddress ?? config_1.NETWORK_DEFAULTS[config.network]?.walletFactoryAddress ?? "(not configured)";
|
|
864
|
+
const chainId = config.network === "base-mainnet" ? 8453 : 84532;
|
|
865
|
+
console.log();
|
|
866
|
+
console.log(" " + colors_1.c.dim("── Dry run: wallet deploy ──────────────────────────────────────"));
|
|
867
|
+
console.log(" " + colors_1.c.dim("Network: ") + colors_1.c.white(config.network));
|
|
868
|
+
console.log(" " + colors_1.c.dim("Chain ID: ") + colors_1.c.white(String(chainId)));
|
|
869
|
+
console.log(" " + colors_1.c.dim("RPC: ") + colors_1.c.white(config.rpcUrl));
|
|
870
|
+
console.log(" " + colors_1.c.dim("WalletFactory: ") + colors_1.c.white(factoryAddr));
|
|
871
|
+
console.log(" " + colors_1.c.dim("Signing method: ") + colors_1.c.white(opts.smartWallet ? "Base Smart Wallet" : opts.hardware ? "Hardware (WC URI)" : "WalletConnect"));
|
|
872
|
+
console.log(" " + colors_1.c.dim("Sponsored: ") + colors_1.c.white(opts.sponsored ? "yes" : "no"));
|
|
873
|
+
console.log();
|
|
874
|
+
console.log(" " + colors_1.c.dim("Steps that would run:"));
|
|
875
|
+
console.log(" 1. Connect " + (opts.smartWallet ? "Coinbase Smart Wallet" : "WalletConnect") + " session");
|
|
876
|
+
console.log(" 2. Call WalletFactory.createWallet() → deploy ARC402Wallet");
|
|
877
|
+
console.log(" 3. Save walletContractAddress to config");
|
|
878
|
+
console.log(" 4. Run onboarding ceremony (PolicyEngine, machine key, agent registration)");
|
|
879
|
+
console.log();
|
|
880
|
+
console.log(" " + colors_1.c.dim("No transactions sent (--dry-run mode)."));
|
|
881
|
+
console.log();
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
799
884
|
const factoryAddress = config.walletFactoryAddress ?? config_1.NETWORK_DEFAULTS[config.network]?.walletFactoryAddress;
|
|
800
885
|
if (!factoryAddress) {
|
|
801
886
|
console.error("walletFactoryAddress not found in config or NETWORK_DEFAULTS. Add walletFactoryAddress to your config.");
|
|
@@ -936,12 +1021,44 @@ function registerWalletCommands(program) {
|
|
|
936
1021
|
const telegramOpts = config.telegramBotToken && config.telegramChatId
|
|
937
1022
|
? { botToken: config.telegramBotToken, chatId: config.telegramChatId, threadId: config.telegramThreadId }
|
|
938
1023
|
: undefined;
|
|
1024
|
+
// ── Resume check ──────────────────────────────────────────────────────
|
|
1025
|
+
const resumeProgress = config.onboardingProgress;
|
|
1026
|
+
const isResuming = !!(resumeProgress?.walletAddress &&
|
|
1027
|
+
resumeProgress.walletAddress === config.walletContractAddress &&
|
|
1028
|
+
config.ownerAddress);
|
|
1029
|
+
if (isResuming) {
|
|
1030
|
+
const stepNames = {
|
|
1031
|
+
2: "machine key", 3: "passkey", 4: "policy setup", 5: "agent registration",
|
|
1032
|
+
};
|
|
1033
|
+
const nextStep = (resumeProgress.step ?? 1) + 1;
|
|
1034
|
+
console.log(" " + colors_1.c.dim(`◈ Resuming onboarding from step ${nextStep} (${stepNames[nextStep] ?? "ceremony"})...`));
|
|
1035
|
+
}
|
|
1036
|
+
// ── Gas estimation ─────────────────────────────────────────────────────
|
|
1037
|
+
if (!isResuming) {
|
|
1038
|
+
let gasMsg = "~0.003 ETH (6 transactions on Base)";
|
|
1039
|
+
try {
|
|
1040
|
+
const feeData = await provider.getFeeData();
|
|
1041
|
+
const gasPrice = feeData.maxFeePerGas ?? feeData.gasPrice ?? BigInt(1500000000);
|
|
1042
|
+
const deployGas = await provider.estimateGas({
|
|
1043
|
+
to: factoryAddress,
|
|
1044
|
+
data: factoryInterface.encodeFunctionData("createWallet", ["0x0000000071727De22E5E9d8BAf0edAc6f37da032"]),
|
|
1045
|
+
}).catch(() => BigInt(280000));
|
|
1046
|
+
const ceremonyGas = BigInt(700000); // ~5 ceremony txs × ~140k each
|
|
1047
|
+
const totalGasEth = parseFloat(ethers_1.ethers.formatEther((deployGas + ceremonyGas) * gasPrice));
|
|
1048
|
+
gasMsg = `~${totalGasEth.toFixed(4)} ETH (6 transactions on Base)`;
|
|
1049
|
+
}
|
|
1050
|
+
catch { /* use default */ }
|
|
1051
|
+
console.log(" " + colors_1.c.dim(`◈ Estimated gas: ${gasMsg}`));
|
|
1052
|
+
}
|
|
939
1053
|
// ── Step 1: Connect ────────────────────────────────────────────────────
|
|
940
|
-
const
|
|
1054
|
+
const connectPrompt = isResuming
|
|
1055
|
+
? "Connect wallet to resume onboarding"
|
|
1056
|
+
: "Approve ARC402Wallet deployment — you will be set as owner";
|
|
1057
|
+
const { client, session, account } = await (0, walletconnect_1.connectPhoneWallet)(config.walletConnectProjectId, chainId, config, { telegramOpts, prompt: connectPrompt, hardware: !!opts.hardware });
|
|
941
1058
|
const networkName = chainId === 8453 ? "Base" : "Base Sepolia";
|
|
942
1059
|
const shortAddr = `${account.slice(0, 6)}...${account.slice(-5)}`;
|
|
943
1060
|
console.log("\n" + colors_1.c.success + colors_1.c.white(` Connected: ${shortAddr} on ${networkName}`));
|
|
944
|
-
if (telegramOpts) {
|
|
1061
|
+
if (telegramOpts && !isResuming) {
|
|
945
1062
|
// Send "connected" message with a deploy confirmation button.
|
|
946
1063
|
// TODO: wire up full callback_data round-trip when a persistent bot process is available.
|
|
947
1064
|
await (0, telegram_notify_1.sendTelegramMessage)({
|
|
@@ -952,50 +1069,59 @@ function registerWalletCommands(program) {
|
|
|
952
1069
|
buttons: [[{ text: "🚀 Deploy ARC-402 Wallet", callback_data: "arc402_deploy_confirm" }]],
|
|
953
1070
|
});
|
|
954
1071
|
}
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
data: factoryInterface.encodeFunctionData("createWallet", ["0x0000000071727De22E5E9d8BAf0edAc6f37da032"]),
|
|
961
|
-
value: "0x0",
|
|
962
|
-
});
|
|
963
|
-
console.log(`\nTransaction submitted: ${txHash}`);
|
|
964
|
-
console.log("Waiting for confirmation...");
|
|
965
|
-
const receipt = await provider.waitForTransaction(txHash);
|
|
966
|
-
if (!receipt) {
|
|
967
|
-
console.error("Transaction not confirmed. Check on-chain.");
|
|
968
|
-
process.exit(1);
|
|
1072
|
+
let walletAddress;
|
|
1073
|
+
if (isResuming) {
|
|
1074
|
+
// Resume: skip deploy, use existing wallet
|
|
1075
|
+
walletAddress = config.walletContractAddress;
|
|
1076
|
+
console.log(" " + colors_1.c.dim(`◈ Using existing wallet: ${walletAddress}`));
|
|
969
1077
|
}
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
1078
|
+
else {
|
|
1079
|
+
// ── Step 2: Confirm & Deploy ─────────────────────────────────────────
|
|
1080
|
+
// WalletConnect approval already confirmed intent — sending automatically
|
|
1081
|
+
console.log("Deploying...");
|
|
1082
|
+
const txHash = await (0, walletconnect_1.sendTransactionWithSession)(client, session, account, chainId, {
|
|
1083
|
+
to: factoryAddress,
|
|
1084
|
+
data: factoryInterface.encodeFunctionData("createWallet", ["0x0000000071727De22E5E9d8BAf0edAc6f37da032"]),
|
|
1085
|
+
value: "0x0",
|
|
1086
|
+
});
|
|
1087
|
+
console.log(`\nTransaction submitted: ${txHash}`);
|
|
1088
|
+
console.log("Waiting for confirmation...");
|
|
1089
|
+
const receipt = await provider.waitForTransaction(txHash);
|
|
1090
|
+
if (!receipt) {
|
|
1091
|
+
console.error("Transaction not confirmed. Check on-chain.");
|
|
1092
|
+
process.exit(1);
|
|
1093
|
+
}
|
|
1094
|
+
let deployedWallet = null;
|
|
1095
|
+
const factoryContract = new ethers_1.ethers.Contract(factoryAddress, abis_1.WALLET_FACTORY_ABI, provider);
|
|
1096
|
+
for (const log of receipt.logs) {
|
|
1097
|
+
try {
|
|
1098
|
+
const parsed = factoryContract.interface.parseLog(log);
|
|
1099
|
+
if (parsed?.name === "WalletCreated") {
|
|
1100
|
+
deployedWallet = parsed.args.walletAddress;
|
|
1101
|
+
break;
|
|
1102
|
+
}
|
|
978
1103
|
}
|
|
1104
|
+
catch { /* skip unparseable logs */ }
|
|
979
1105
|
}
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
1106
|
+
if (!deployedWallet) {
|
|
1107
|
+
console.error("Could not find WalletCreated event in receipt. Check the transaction on-chain.");
|
|
1108
|
+
process.exit(1);
|
|
1109
|
+
}
|
|
1110
|
+
walletAddress = deployedWallet;
|
|
1111
|
+
// ── Step 1 complete: save wallet + owner immediately ─────────────────
|
|
1112
|
+
config.walletContractAddress = walletAddress;
|
|
1113
|
+
config.ownerAddress = account;
|
|
1114
|
+
(0, config_1.saveConfig)(config);
|
|
1115
|
+
try {
|
|
1116
|
+
fs_1.default.chmodSync((0, config_1.getConfigPath)(), 0o600);
|
|
1117
|
+
}
|
|
1118
|
+
catch { /* best-effort */ }
|
|
1119
|
+
console.log("\n " + colors_1.c.success + colors_1.c.white(" Wallet deployed"));
|
|
1120
|
+
(0, tree_1.renderTree)([
|
|
1121
|
+
{ label: "Wallet", value: walletAddress },
|
|
1122
|
+
{ label: "Owner", value: account, last: true },
|
|
1123
|
+
]);
|
|
992
1124
|
}
|
|
993
|
-
catch { /* best-effort */ }
|
|
994
|
-
console.log("\n " + colors_1.c.success + colors_1.c.white(" Wallet deployed"));
|
|
995
|
-
(0, tree_1.renderTree)([
|
|
996
|
-
{ label: "Wallet", value: walletAddress },
|
|
997
|
-
{ label: "Owner", value: account, last: true },
|
|
998
|
-
]);
|
|
999
1125
|
// ── Steps 2–6: Complete onboarding ceremony (same WalletConnect session)
|
|
1000
1126
|
const sendTxCeremony = async (call, description) => {
|
|
1001
1127
|
console.log(" " + colors_1.c.dim(`◈ ${description}`));
|
|
@@ -1036,8 +1162,11 @@ function registerWalletCommands(program) {
|
|
|
1036
1162
|
const guardianWallet = ethers_1.ethers.Wallet.createRandom();
|
|
1037
1163
|
config.walletContractAddress = walletAddress;
|
|
1038
1164
|
config.ownerAddress = address;
|
|
1039
|
-
config.guardianPrivateKey = guardianWallet.privateKey;
|
|
1040
1165
|
config.guardianAddress = guardianWallet.address;
|
|
1166
|
+
// Save key to restricted file — never store in config.json
|
|
1167
|
+
saveGuardianKey(guardianWallet.privateKey);
|
|
1168
|
+
if (config.guardianPrivateKey)
|
|
1169
|
+
delete config.guardianPrivateKey;
|
|
1041
1170
|
(0, config_1.saveConfig)(config);
|
|
1042
1171
|
// Call setGuardian on the deployed wallet
|
|
1043
1172
|
const walletContract = new ethers_1.ethers.Contract(walletAddress, abis_1.ARC402_WALLET_GUARDIAN_ABI, signer);
|
|
@@ -1058,7 +1187,7 @@ function registerWalletCommands(program) {
|
|
|
1058
1187
|
{ label: "Wallet", value: walletAddress },
|
|
1059
1188
|
{ label: "Guardian", value: guardianWallet.address, last: true },
|
|
1060
1189
|
]);
|
|
1061
|
-
console.log(`Guardian private key saved to
|
|
1190
|
+
console.log(`Guardian private key saved to ~/.arc402/guardian.key (chmod 400 — keep it safe, used for emergency freeze only)`);
|
|
1062
1191
|
console.log(`Your wallet contract is ready for policy enforcement`);
|
|
1063
1192
|
printOpenShellHint();
|
|
1064
1193
|
}
|
|
@@ -1255,12 +1384,13 @@ function registerWalletCommands(program) {
|
|
|
1255
1384
|
console.error("walletContractAddress not set in config. Run `arc402 wallet deploy` first.");
|
|
1256
1385
|
process.exit(1);
|
|
1257
1386
|
}
|
|
1258
|
-
|
|
1259
|
-
|
|
1387
|
+
const guardianKey = loadGuardianKey(config);
|
|
1388
|
+
if (!guardianKey) {
|
|
1389
|
+
console.error(`Guardian key not found. Expected at ~/.arc402/guardian.key (or guardianPrivateKey in config for legacy setups).`);
|
|
1260
1390
|
process.exit(1);
|
|
1261
1391
|
}
|
|
1262
1392
|
const provider = new ethers_1.ethers.JsonRpcProvider(config.rpcUrl);
|
|
1263
|
-
const guardianSigner = new ethers_1.ethers.Wallet(
|
|
1393
|
+
const guardianSigner = new ethers_1.ethers.Wallet(guardianKey, provider);
|
|
1264
1394
|
const walletContract = new ethers_1.ethers.Contract(config.walletContractAddress, abis_1.ARC402_WALLET_GUARDIAN_ABI, guardianSigner);
|
|
1265
1395
|
let tx;
|
|
1266
1396
|
if (opts.drain) {
|
|
@@ -1380,12 +1510,14 @@ function registerWalletCommands(program) {
|
|
|
1380
1510
|
value: "0x0",
|
|
1381
1511
|
});
|
|
1382
1512
|
await provider.waitForTransaction(txHash);
|
|
1383
|
-
|
|
1513
|
+
saveGuardianKey(guardianWallet.privateKey);
|
|
1514
|
+
if (config.guardianPrivateKey)
|
|
1515
|
+
delete config.guardianPrivateKey;
|
|
1384
1516
|
config.guardianAddress = guardianWallet.address;
|
|
1385
1517
|
(0, config_1.saveConfig)(config);
|
|
1386
1518
|
console.log("\n" + colors_1.c.success + colors_1.c.white(` Guardian set to: ${guardianWallet.address}`));
|
|
1387
1519
|
console.log(" " + colors_1.c.dim("Tx:") + " " + colors_1.c.white(txHash));
|
|
1388
|
-
console.log(" " + colors_1.c.dim("Guardian private key saved to
|
|
1520
|
+
console.log(" " + colors_1.c.dim("Guardian private key saved to ~/.arc402/guardian.key (chmod 400)."));
|
|
1389
1521
|
console.log(" " + colors_1.c.warning + " " + colors_1.c.yellow("The guardian key can freeze your wallet. Store it separately from your hot key."));
|
|
1390
1522
|
});
|
|
1391
1523
|
// ─── policy-engine freeze / unfreeze (legacy — for PolicyEngine-level freeze) ──
|
|
@@ -2096,9 +2228,11 @@ function registerWalletCommands(program) {
|
|
|
2096
2228
|
txHashes.push(txHash);
|
|
2097
2229
|
}
|
|
2098
2230
|
}
|
|
2099
|
-
// Persist guardian key if generated
|
|
2231
|
+
// Persist guardian key if generated — save to restricted file, not config.json
|
|
2100
2232
|
if (guardianWallet) {
|
|
2101
|
-
|
|
2233
|
+
saveGuardianKey(guardianWallet.privateKey);
|
|
2234
|
+
if (config.guardianPrivateKey)
|
|
2235
|
+
delete config.guardianPrivateKey;
|
|
2102
2236
|
config.guardianAddress = guardianWallet.address;
|
|
2103
2237
|
(0, config_1.saveConfig)(config);
|
|
2104
2238
|
}
|
|
@@ -2110,7 +2244,7 @@ function registerWalletCommands(program) {
|
|
|
2110
2244
|
txHashes.forEach((h, i) => console.log(" " + colors_1.c.dim(`Tx ${i + 1}:`) + " " + colors_1.c.white(h)));
|
|
2111
2245
|
}
|
|
2112
2246
|
if (guardianWallet) {
|
|
2113
|
-
console.log(" " + colors_1.c.success + colors_1.c.dim(` Guardian key saved to
|
|
2247
|
+
console.log(" " + colors_1.c.success + colors_1.c.dim(` Guardian key saved to ~/.arc402/guardian.key — address: ${guardianWallet.address}`));
|
|
2114
2248
|
console.log(" " + colors_1.c.warning + " " + colors_1.c.yellow("Store the guardian private key separately from your hot key."));
|
|
2115
2249
|
}
|
|
2116
2250
|
console.log(colors_1.c.dim("\nVerify with: arc402 wallet status && arc402 wallet policy show"));
|