arc402-cli 1.8.7 → 1.8.9

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.
Files changed (92) hide show
  1. package/dist/abis.js +1 -1
  2. package/dist/abis.js.map +1 -1
  3. package/dist/approval/broker.d.ts +9 -0
  4. package/dist/approval/broker.d.ts.map +1 -0
  5. package/dist/approval/broker.js +45 -0
  6. package/dist/approval/broker.js.map +1 -0
  7. package/dist/approval/config.d.ts +20 -0
  8. package/dist/approval/config.d.ts.map +1 -0
  9. package/dist/approval/config.js +42 -0
  10. package/dist/approval/config.js.map +1 -0
  11. package/dist/approval/init.d.ts +4 -0
  12. package/dist/approval/init.d.ts.map +1 -0
  13. package/dist/approval/init.js +128 -0
  14. package/dist/approval/init.js.map +1 -0
  15. package/dist/approval/passkey-requests.d.ts +22 -0
  16. package/dist/approval/passkey-requests.d.ts.map +1 -0
  17. package/dist/approval/passkey-requests.js +85 -0
  18. package/dist/approval/passkey-requests.js.map +1 -0
  19. package/dist/approval/transports/local-qr.d.ts +3 -0
  20. package/dist/approval/transports/local-qr.d.ts.map +1 -0
  21. package/dist/approval/transports/local-qr.js +42 -0
  22. package/dist/approval/transports/local-qr.js.map +1 -0
  23. package/dist/approval/transports/telegram-passkey-link.d.ts +3 -0
  24. package/dist/approval/transports/telegram-passkey-link.d.ts.map +1 -0
  25. package/dist/approval/transports/telegram-passkey-link.js +137 -0
  26. package/dist/approval/transports/telegram-passkey-link.js.map +1 -0
  27. package/dist/approval/transports/telegram-walletconnect.d.ts +3 -0
  28. package/dist/approval/transports/telegram-walletconnect.d.ts.map +1 -0
  29. package/dist/approval/transports/telegram-walletconnect.js +53 -0
  30. package/dist/approval/transports/telegram-walletconnect.js.map +1 -0
  31. package/dist/approval/types.d.ts +66 -0
  32. package/dist/approval/types.d.ts.map +1 -0
  33. package/dist/approval/types.js +3 -0
  34. package/dist/approval/types.js.map +1 -0
  35. package/dist/bundler.d.ts +1 -0
  36. package/dist/bundler.d.ts.map +1 -1
  37. package/dist/bundler.js +31 -0
  38. package/dist/bundler.js.map +1 -1
  39. package/dist/commands/accept.js +1 -1
  40. package/dist/commands/accept.js.map +1 -1
  41. package/dist/commands/agent.d.ts.map +1 -1
  42. package/dist/commands/agent.js +22 -7
  43. package/dist/commands/agent.js.map +1 -1
  44. package/dist/commands/approvals.d.ts +3 -0
  45. package/dist/commands/approvals.d.ts.map +1 -0
  46. package/dist/commands/approvals.js +78 -0
  47. package/dist/commands/approvals.js.map +1 -0
  48. package/dist/commands/arena-handshake.d.ts.map +1 -1
  49. package/dist/commands/arena-handshake.js +9 -18
  50. package/dist/commands/arena-handshake.js.map +1 -1
  51. package/dist/commands/arena-v2.d.ts.map +1 -1
  52. package/dist/commands/arena-v2.js +23 -38
  53. package/dist/commands/arena-v2.js.map +1 -1
  54. package/dist/commands/compute.d.ts.map +1 -1
  55. package/dist/commands/compute.js +3 -4
  56. package/dist/commands/compute.js.map +1 -1
  57. package/dist/commands/config.d.ts.map +1 -1
  58. package/dist/commands/config.js +1 -0
  59. package/dist/commands/config.js.map +1 -1
  60. package/dist/commands/deliver.js +1 -1
  61. package/dist/commands/deliver.js.map +1 -1
  62. package/dist/commands/hire.d.ts.map +1 -1
  63. package/dist/commands/hire.js +3 -4
  64. package/dist/commands/hire.js.map +1 -1
  65. package/dist/commands/job.d.ts.map +1 -1
  66. package/dist/commands/job.js +1 -1
  67. package/dist/commands/job.js.map +1 -1
  68. package/dist/commands/setup.d.ts.map +1 -1
  69. package/dist/commands/setup.js +4 -8
  70. package/dist/commands/setup.js.map +1 -1
  71. package/dist/commands/wallet.d.ts.map +1 -1
  72. package/dist/commands/wallet.js +598 -204
  73. package/dist/commands/wallet.js.map +1 -1
  74. package/dist/config.d.ts +20 -0
  75. package/dist/config.d.ts.map +1 -1
  76. package/dist/config.js +30 -0
  77. package/dist/config.js.map +1 -1
  78. package/dist/daemon/index.d.ts.map +1 -1
  79. package/dist/daemon/index.js +59 -0
  80. package/dist/daemon/index.js.map +1 -1
  81. package/dist/daemon/passkey-approvals.d.ts +24 -0
  82. package/dist/daemon/passkey-approvals.d.ts.map +1 -0
  83. package/dist/daemon/passkey-approvals.js +31 -0
  84. package/dist/daemon/passkey-approvals.js.map +1 -0
  85. package/dist/endpoint-notify.d.ts +1 -2
  86. package/dist/endpoint-notify.d.ts.map +1 -1
  87. package/dist/endpoint-notify.js +1 -3
  88. package/dist/endpoint-notify.js.map +1 -1
  89. package/dist/program.d.ts.map +1 -1
  90. package/dist/program.js +2 -0
  91. package/dist/program.js.map +1 -1
  92. package/package.json +1 -1
@@ -1,37 +1,4 @@
1
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
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
36
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
4
  };
@@ -50,6 +17,8 @@ const format_1 = require("../utils/format");
50
17
  const abis_1 = require("../abis");
51
18
  const config_2 = require("../config");
52
19
  const walletconnect_1 = require("../walletconnect");
20
+ const config_3 = require("../approval/config");
21
+ const broker_1 = require("../approval/broker");
53
22
  const bundler_1 = require("../bundler");
54
23
  const walletconnect_session_1 = require("../walletconnect-session");
55
24
  const wallet_router_1 = require("../wallet-router");
@@ -60,6 +29,89 @@ const spinner_1 = require("../ui/spinner");
60
29
  const colors_1 = require("../ui/colors");
61
30
  const rpc_fallback_1 = require("../ui/rpc-fallback");
62
31
  const GUARDIAN_KEY_PATH = path_1.default.join(os_1.default.homedir(), ".arc402", "guardian.key");
32
+ function requireApprovedWalletConnectSession(approval) {
33
+ if (approval.status !== "approved" || !approval.session || approval.session.kind !== "walletconnect") {
34
+ console.error(colors_1.c.failure + " " + colors_1.c.red(approval.error ?? "Approval transport failed."));
35
+ process.exit(1);
36
+ }
37
+ return approval.session;
38
+ }
39
+ async function requestWalletConnectApprovalSession(config, intent) {
40
+ return requireApprovedWalletConnectSession(await (0, broker_1.requestOwnerApproval)(intent, config));
41
+ }
42
+ const PASSKEY_GOVERNANCE_SELECTORS = new Set([
43
+ ethers_1.ethers.id("unfreeze()").slice(0, 10).toLowerCase(),
44
+ ethers_1.ethers.id("setGuardian(address)").slice(0, 10).toLowerCase(),
45
+ ethers_1.ethers.id("proposeRegistryUpdate(address)").slice(0, 10).toLowerCase(),
46
+ ethers_1.ethers.id("executeRegistryUpdate()").slice(0, 10).toLowerCase(),
47
+ ethers_1.ethers.id("cancelRegistryUpdate()").slice(0, 10).toLowerCase(),
48
+ ethers_1.ethers.id("setAuthorizedInterceptor(address)").slice(0, 10).toLowerCase(),
49
+ ethers_1.ethers.id("setVelocityLimit(uint256)").slice(0, 10).toLowerCase(),
50
+ ethers_1.ethers.id("authorizeMachineKey(address)").slice(0, 10).toLowerCase(),
51
+ ethers_1.ethers.id("revokeMachineKey(address)").slice(0, 10).toLowerCase(),
52
+ ]);
53
+ function canUsePasskeyForWalletGovernance(config, walletAddress, tx) {
54
+ const approval = (0, config_3.getApprovalConfig)(config);
55
+ const selector = tx.data.slice(0, 10).toLowerCase();
56
+ return approval.defaultTransport === "telegram_passkey_link"
57
+ && tx.to.toLowerCase() === walletAddress.toLowerCase()
58
+ && PASSKEY_GOVERNANCE_SELECTORS.has(selector);
59
+ }
60
+ async function requestWalletGovernanceExecution(config, intent) {
61
+ const walletAddress = intent.walletAddress;
62
+ const tx = intent.txs[0];
63
+ if (!walletAddress || !tx || intent.txs.length !== 1) {
64
+ return {
65
+ kind: "walletconnect",
66
+ session: await requestWalletConnectApprovalSession(config, intent),
67
+ };
68
+ }
69
+ if (!canUsePasskeyForWalletGovernance(config, walletAddress, tx)) {
70
+ return {
71
+ kind: "walletconnect",
72
+ session: await requestWalletConnectApprovalSession(config, intent),
73
+ };
74
+ }
75
+ const provider = new ethers_1.ethers.JsonRpcProvider(config.rpcUrl);
76
+ const nonceReader = new ethers_1.ethers.Contract(bundler_1.DEFAULT_ENTRY_POINT, ["function getNonce(address sender, uint192 key) external view returns (uint256)"], provider);
77
+ const nonce = await nonceReader.getNonce(walletAddress, 0);
78
+ const bundler = new bundler_1.BundlerClient(process.env.BUNDLER_URL ?? bundler_1.DEFAULT_BUNDLER_URL, bundler_1.DEFAULT_ENTRY_POINT, intent.chainId);
79
+ const userOp = await (0, bundler_1.buildUserOp)(tx.data, walletAddress, nonce, config);
80
+ try {
81
+ const estimate = await bundler.estimateUserOperationGas(userOp);
82
+ userOp.callGasLimit = estimate.callGasLimit;
83
+ userOp.verificationGasLimit = estimate.verificationGasLimit;
84
+ userOp.preVerificationGas = estimate.preVerificationGas;
85
+ }
86
+ catch {
87
+ // keep conservative defaults if estimation is unavailable
88
+ }
89
+ const challenge = (0, bundler_1.getUserOperationHash)(userOp, bundler_1.DEFAULT_ENTRY_POINT, intent.chainId);
90
+ const approval = await (0, broker_1.requestOwnerApproval)({
91
+ ...intent,
92
+ signerMode: "passkey",
93
+ metadata: {
94
+ ...intent.metadata,
95
+ passkeyChallenge: challenge,
96
+ },
97
+ }, config);
98
+ if (approval.status !== "approved" || !approval.passkeyApproval?.signature) {
99
+ console.error(colors_1.c.failure + " " + colors_1.c.red(approval.error ?? "Passkey approval failed."));
100
+ process.exit(1);
101
+ }
102
+ userOp.signature = approval.passkeyApproval.signature;
103
+ const userOpHash = await bundler.sendUserOperation(userOp);
104
+ const receipt = await bundler.getUserOperationReceipt(userOpHash);
105
+ if (!receipt.success) {
106
+ console.error(colors_1.c.failure + " " + colors_1.c.red("UserOperation failed on-chain."));
107
+ process.exit(1);
108
+ }
109
+ return {
110
+ kind: "passkey",
111
+ userOpHash,
112
+ txHash: String(receipt.receipt.transactionHash),
113
+ };
114
+ }
63
115
  function buildFreshConfigFromDefaults(existing, network, privateKey, ownerAddress) {
64
116
  const defaults = config_1.NETWORK_DEFAULTS[network];
65
117
  if (!defaults)
@@ -74,6 +126,7 @@ function buildFreshConfigFromDefaults(existing, network, privateKey, ownerAddres
74
126
  telegramBotToken: existing?.telegramBotToken,
75
127
  telegramChatId: existing?.telegramChatId,
76
128
  telegramThreadId: existing?.telegramThreadId,
129
+ approval: existing?.approval,
77
130
  deviceId: existing?.deviceId,
78
131
  chat: existing?.chat,
79
132
  policyEngineAddress: defaults.policyEngineAddress,
@@ -131,9 +184,7 @@ function parseAmount(raw) {
131
184
  return BigInt(raw);
132
185
  }
133
186
  function resolvePolicyEngineAddress(config) {
134
- return (config.policyEngineAddress ??
135
- config_1.NETWORK_DEFAULTS[config.network]?.policyEngineAddress ??
136
- "0x9449B15268bE7042C0b473F3f711a41A29220866");
187
+ return (0, config_1.getCanonicalNetworkAddress)(config, "policyEngineAddress");
137
188
  }
138
189
  function getWalletAddressFromFactoryLogs(factoryInterface, logs) {
139
190
  for (const log of logs) {
@@ -158,6 +209,13 @@ function getWalletAddressFromFactoryLogs(factoryInterface, logs) {
158
209
  }
159
210
  return null;
160
211
  }
212
+ async function assertAddressHasCode(provider, address, label) {
213
+ const code = await provider.getCode(address);
214
+ if (code !== "0x")
215
+ return;
216
+ throw new Error(`${label} ${address} has no contract code. ` +
217
+ `This usually means an owner EOA was saved instead of the deployed ARC402Wallet contract.`);
218
+ }
161
219
  // Standard onboarding categories required for a newly deployed wallet.
162
220
  // These cover the full ARC-402 flow: spending, hiring, compute, research, protocol ops.
163
221
  const ONBOARDING_CATEGORIES = [
@@ -175,7 +233,6 @@ const ONBOARDING_CATEGORIES = [
175
233
  */
176
234
  async function runWalletOnboardingCeremony(walletAddress, ownerAddress, config, provider, sendTx) {
177
235
  const policyAddress = resolvePolicyEngineAddress(config);
178
- const executeIface = new ethers_1.ethers.Interface(abis_1.ARC402_WALLET_EXECUTE_ABI);
179
236
  const govIface = new ethers_1.ethers.Interface(abis_1.POLICY_ENGINE_GOVERNANCE_ABI);
180
237
  const limitsIface = new ethers_1.ethers.Interface(abis_1.POLICY_ENGINE_LIMITS_ABI);
181
238
  const policyGov = new ethers_1.ethers.Contract(policyAddress, abis_1.POLICY_ENGINE_GOVERNANCE_ABI, provider);
@@ -198,15 +255,8 @@ async function runWalletOnboardingCeremony(walletAddress, ownerAddress, config,
198
255
  if (!alreadyRegistered) {
199
256
  const registerCalldata = govIface.encodeFunctionData("registerWallet", [walletAddress, ownerAddress]);
200
257
  await sendTx({
201
- to: walletAddress,
202
- data: executeIface.encodeFunctionData("executeContractCall", [{
203
- target: policyAddress,
204
- data: registerCalldata,
205
- value: 0n,
206
- minReturnValue: 0n,
207
- maxApprovalAmount: 0n,
208
- approvalToken: ethers_1.ethers.ZeroAddress,
209
- }]),
258
+ to: policyAddress,
259
+ data: registerCalldata,
210
260
  value: "0x0",
211
261
  }, "registerWallet on PolicyEngine");
212
262
  }
@@ -254,11 +304,8 @@ async function runWalletOnboardingCeremony(walletAddress, ownerAddress, config,
254
304
  */
255
305
  async function runCompleteOnboardingCeremony(walletAddress, ownerAddress, config, provider, sendTx) {
256
306
  const policyAddress = resolvePolicyEngineAddress(config);
257
- const agentRegistryAddress = config.agentRegistryV2Address ??
258
- config_1.NETWORK_DEFAULTS[config.network]?.agentRegistryV2Address;
259
- const handshakeAddress = config.handshakeAddress ??
260
- config_1.NETWORK_DEFAULTS[config.network]?.handshakeAddress ??
261
- "0x4F5A38Bb746d7E5d49d8fd26CA6beD141Ec2DDb3";
307
+ const agentRegistryAddress = (0, config_1.getCanonicalAgentRegistryAddress)(config);
308
+ const handshakeAddress = (0, config_1.getCanonicalNetworkAddress)(config, "handshakeAddress");
262
309
  // ── Step 2: Machine Key ────────────────────────────────────────────────────
263
310
  console.log("\n" + colors_1.c.dim("── Step 2: Machine Key ────────────────────────────────────────"));
264
311
  let machineKeyAddress;
@@ -486,11 +533,11 @@ async function runCompleteOnboardingCeremony(walletAddress, ownerAddress, config
486
533
  // Whitelist all contracts an agent will need for the full ARC-402 flow.
487
534
  const protocolContracts = [
488
535
  { address: agentRegistryAddress ?? "", name: "AgentRegistry" },
489
- { address: config.serviceAgreementAddress ?? config_1.NETWORK_DEFAULTS[config.network]?.serviceAgreementAddress ?? "0xC98B402CAB9156da68A87a69E3B4bf167A3CCcF6", name: "ServiceAgreement" },
490
- { address: config.computeAgreementAddress ?? config_1.NETWORK_DEFAULTS[config.network]?.computeAgreementAddress ?? "0xf898A8A2cF9900A588B174d9f96349BBA95e57F3", name: "ComputeAgreement" },
491
- { address: config.subscriptionAgreementAddress ?? config_1.NETWORK_DEFAULTS[config.network]?.subscriptionAgreementAddress ?? "0x809c1D997Eab3531Eb2d01FCD5120Ac786D850D6", name: "SubscriptionAgreement" },
492
- { address: config.handshakeAddress ?? config_1.NETWORK_DEFAULTS[config.network]?.handshakeAddress ?? "0x4F5A38Bb746d7E5d49d8fd26CA6beD141Ec2DDb3", name: "Handshake" },
493
- { address: config.sessionChannelsAddress ?? config_1.NETWORK_DEFAULTS[config.network]?.sessionChannelsAddress ?? "0x578f8d1bd82E8D6268E329d664d663B4d985BE61", name: "SessionChannels" },
536
+ { address: config_1.NETWORK_DEFAULTS[config.network]?.serviceAgreementAddress ?? "", name: "ServiceAgreement" },
537
+ { address: config_1.NETWORK_DEFAULTS[config.network]?.computeAgreementAddress ?? "", name: "ComputeAgreement" },
538
+ { address: config_1.NETWORK_DEFAULTS[config.network]?.subscriptionAgreementAddress ?? "", name: "SubscriptionAgreement" },
539
+ { address: config_1.NETWORK_DEFAULTS[config.network]?.handshakeAddress ?? "", name: "Handshake" },
540
+ { address: config_1.NETWORK_DEFAULTS[config.network]?.sessionChannelsAddress ?? "", name: "SessionChannels" },
494
541
  ].filter(c => c.address && c.address !== "");
495
542
  for (const contract of protocolContracts) {
496
543
  const isWhitelisted = await peContract.isContractWhitelisted(walletAddress, contract.address).catch(() => false);
@@ -1002,7 +1049,7 @@ function registerWalletCommands(program) {
1002
1049
  console.log(colors_1.c.dim(` ◈ --force: cleared wallet config. Factory reset to ${config.walletFactoryAddress} (V6).`));
1003
1050
  }
1004
1051
  if (opts.dryRun) {
1005
- const factoryAddr = config.walletFactoryAddress ?? config_1.NETWORK_DEFAULTS[config.network]?.walletFactoryAddress ?? "(not configured)";
1052
+ const factoryAddr = config_1.NETWORK_DEFAULTS[config.network]?.walletFactoryAddress ?? "(not configured)";
1006
1053
  const chainId = config.network === "base-mainnet" ? 8453 : 84532;
1007
1054
  console.log();
1008
1055
  console.log(" " + colors_1.c.dim("── Dry run: wallet deploy ──────────────────────────────────────"));
@@ -1100,6 +1147,7 @@ function registerWalletCommands(program) {
1100
1147
  console.error("UserOperation failed on-chain.");
1101
1148
  process.exit(1);
1102
1149
  }
1150
+ await assertAddressHasCode(provider, senderAddress, "Deployed wallet");
1103
1151
  config.walletContractAddress = senderAddress;
1104
1152
  config.ownerAddress = ownerAddress;
1105
1153
  (0, config_1.saveConfig)(config);
@@ -1137,6 +1185,7 @@ function registerWalletCommands(program) {
1137
1185
  console.error("Could not find WalletDeployed/WalletCreated event in receipt. Check the transaction on-chain.");
1138
1186
  process.exit(1);
1139
1187
  }
1188
+ await assertAddressHasCode(provider, walletAddress, "Deployed wallet");
1140
1189
  config.walletContractAddress = walletAddress;
1141
1190
  config.ownerAddress = account;
1142
1191
  (0, config_1.saveConfig)(config);
@@ -1229,6 +1278,7 @@ function registerWalletCommands(program) {
1229
1278
  console.error("Could not find WalletDeployed/WalletCreated event in receipt. Check the transaction on-chain.");
1230
1279
  process.exit(1);
1231
1280
  }
1281
+ await assertAddressHasCode(provider, deployedWallet, "Deployed wallet");
1232
1282
  walletAddress = deployedWallet;
1233
1283
  // ── Step 1 complete: save wallet + owner immediately ─────────────────
1234
1284
  config.walletContractAddress = walletAddress;
@@ -1269,6 +1319,7 @@ function registerWalletCommands(program) {
1269
1319
  deploySpinner.fail("Could not find WalletDeployed/WalletCreated event in receipt. Check the transaction on-chain.");
1270
1320
  process.exit(1);
1271
1321
  }
1322
+ await assertAddressHasCode(provider, walletAddress, "Deployed wallet");
1272
1323
  deploySpinner.succeed("Wallet deployed");
1273
1324
  // Generate guardian key (separate from hot key) and call setGuardian
1274
1325
  const guardianWallet = ethers_1.ethers.Wallet.createRandom();
@@ -1327,14 +1378,20 @@ function registerWalletCommands(program) {
1327
1378
  const policyAddress = resolvePolicyEngineAddress(config);
1328
1379
  const chainId = config.network === "base-mainnet" ? 8453 : 84532;
1329
1380
  const provider = new ethers_1.ethers.JsonRpcProvider(config.rpcUrl);
1381
+ try {
1382
+ await assertAddressHasCode(provider, walletAddress, "Configured walletContractAddress");
1383
+ }
1384
+ catch (error) {
1385
+ console.error(colors_1.c.failure + " " + colors_1.c.red(error instanceof Error ? error.message : String(error)));
1386
+ console.error(colors_1.c.dim("Tip: use the deployed wallet contract address, not the owner EOA, then rerun onboarding."));
1387
+ process.exit(1);
1388
+ }
1330
1389
  // Resolve protocol contracts to whitelist
1331
- const handshakeAddress = config.handshakeAddress ??
1332
- config_1.NETWORK_DEFAULTS[config.network]?.handshakeAddress ??
1333
- "0x4F5A38Bb746d7E5d49d8fd26CA6beD141Ec2DDb3";
1390
+ const handshakeAddress = config_1.NETWORK_DEFAULTS[config.network]?.handshakeAddress ?? "";
1334
1391
  const protocolContracts = [
1335
- { address: config.serviceAgreementAddress ?? config_1.NETWORK_DEFAULTS[config.network]?.serviceAgreementAddress ?? "", name: "ServiceAgreement" },
1336
- { address: config.computeAgreementAddress ?? config_1.NETWORK_DEFAULTS[config.network]?.computeAgreementAddress ?? "", name: "ComputeAgreement" },
1337
- { address: config.subscriptionAgreementAddress ?? config_1.NETWORK_DEFAULTS[config.network]?.subscriptionAgreementAddress ?? "", name: "SubscriptionAgreement" },
1392
+ { address: config_1.NETWORK_DEFAULTS[config.network]?.serviceAgreementAddress ?? "", name: "ServiceAgreement" },
1393
+ { address: config_1.NETWORK_DEFAULTS[config.network]?.computeAgreementAddress ?? "", name: "ComputeAgreement" },
1394
+ { address: config_1.NETWORK_DEFAULTS[config.network]?.subscriptionAgreementAddress ?? "", name: "SubscriptionAgreement" },
1338
1395
  { address: handshakeAddress, name: "Handshake" },
1339
1396
  ].filter(pc => pc.address && pc.address !== "");
1340
1397
  // Pre-check on-chain state to count pending txs and decide what to skip
@@ -1408,23 +1465,31 @@ function registerWalletCommands(program) {
1408
1465
  return;
1409
1466
  }
1410
1467
  console.log(" " + colors_1.c.dim(`◈ ${pendingTxCount} transaction(s) pending — your phone will need to approve each.`));
1411
- // Telegram notification before first tx
1412
- const telegramOpts = config.telegramBotToken && config.telegramChatId
1413
- ? { botToken: config.telegramBotToken, chatId: config.telegramChatId, threadId: config.telegramThreadId }
1414
- : undefined;
1415
- // Connect WalletConnect ONCE — reuse session for all txs
1416
- const { client, session, account } = await (0, walletconnect_1.connectPhoneWallet)(config.walletConnectProjectId, chainId, config, { telegramOpts, prompt: `Approve ${pendingTxCount} wallet onboarding transaction(s)`, hardware: !!opts.hardware });
1468
+ const approval = await (0, broker_1.requestOwnerApproval)({
1469
+ actionType: "wallet_onboard",
1470
+ signerMode: "owner_wallet",
1471
+ chainId,
1472
+ walletAddress,
1473
+ txs: Array.from({ length: pendingTxCount }, () => ({ to: walletAddress, data: "0x", value: "0x0" })),
1474
+ ui: {
1475
+ title: "Wallet onboarding",
1476
+ summary: `Approve ${pendingTxCount} wallet onboarding transaction(s)`,
1477
+ risk: "medium",
1478
+ },
1479
+ metadata: {
1480
+ expectedApprovalCount: pendingTxCount,
1481
+ sourceRuntime: "cli",
1482
+ hardware: !!opts.hardware,
1483
+ },
1484
+ }, config);
1485
+ if (approval.status !== "approved" || !approval.session || approval.session.kind !== "walletconnect") {
1486
+ console.error(colors_1.c.failure + " " + colors_1.c.red(approval.error ?? "Approval transport failed."));
1487
+ process.exit(1);
1488
+ }
1489
+ const { client, session, account } = approval.session;
1417
1490
  const networkName = chainId === 8453 ? "Base" : "Base Sepolia";
1418
1491
  const shortAddr = `${account.slice(0, 6)}...${account.slice(-5)}`;
1419
1492
  console.log("\n" + colors_1.c.success + colors_1.c.white(` Connected: ${shortAddr} on ${networkName}`));
1420
- if (telegramOpts) {
1421
- await (0, telegram_notify_1.sendTelegramMessage)({
1422
- botToken: telegramOpts.botToken,
1423
- chatId: telegramOpts.chatId,
1424
- threadId: telegramOpts.threadId,
1425
- text: `◈ arc402 wallet onboard: ${pendingTxCount} txs pending for ${shortAddr}`,
1426
- });
1427
- }
1428
1493
  const sendTx = async (call, description) => {
1429
1494
  const txSpin = (0, spinner_1.startSpinner)(description);
1430
1495
  const hash = await (0, walletconnect_1.sendTransactionWithSession)(client, session, account, chainId, call);
@@ -1432,24 +1497,22 @@ function registerWalletCommands(program) {
1432
1497
  txSpin.succeed(description);
1433
1498
  return hash;
1434
1499
  };
1435
- const executeIface = new ethers_1.ethers.Interface(abis_1.ARC402_WALLET_EXECUTE_ABI);
1436
1500
  const govIface = new ethers_1.ethers.Interface(abis_1.POLICY_ENGINE_GOVERNANCE_ABI);
1437
1501
  const limitsIface = new ethers_1.ethers.Interface(abis_1.POLICY_ENGINE_LIMITS_ABI);
1438
1502
  const ownerIface = new ethers_1.ethers.Interface(abis_1.ARC402_WALLET_OWNER_ABI);
1503
+ const walletOwnerReader = new ethers_1.ethers.Contract(walletAddress, ["function owner() external view returns (address)"], provider);
1504
+ const walletOwner = await walletOwnerReader.owner().catch(() => account);
1505
+ if (walletOwner.toLowerCase() !== account.toLowerCase()) {
1506
+ console.error(colors_1.c.failure + " " + colors_1.c.red(`Connected wallet ${account} is not the ARC402Wallet owner ${walletOwner}.`));
1507
+ process.exit(1);
1508
+ }
1439
1509
  console.log("\n" + colors_1.c.dim("── Onboarding ceremony ────────────────────────────────────────"));
1440
1510
  // 1. registerWallet
1441
1511
  if (needsRegister) {
1442
- const registerCalldata = govIface.encodeFunctionData("registerWallet", [walletAddress, account]);
1512
+ const registerCalldata = govIface.encodeFunctionData("registerWallet", [walletAddress, walletOwner]);
1443
1513
  await sendTx({
1444
- to: walletAddress,
1445
- data: executeIface.encodeFunctionData("executeContractCall", [{
1446
- target: policyAddress,
1447
- data: registerCalldata,
1448
- value: 0n,
1449
- minReturnValue: 0n,
1450
- maxApprovalAmount: 0n,
1451
- approvalToken: ethers_1.ethers.ZeroAddress,
1452
- }]),
1514
+ to: policyAddress,
1515
+ data: registerCalldata,
1453
1516
  value: "0x0",
1454
1517
  }, "registerWallet on PolicyEngine");
1455
1518
  }
@@ -1574,15 +1637,33 @@ function registerWalletCommands(program) {
1574
1637
  process.exit(1);
1575
1638
  }
1576
1639
  const provider = new ethers_1.ethers.JsonRpcProvider(config.rpcUrl);
1577
- const { txHash } = await (0, walletconnect_1.requestPhoneWalletSignature)(config.walletConnectProjectId, chainId, (account) => ({
1640
+ const approval = await (0, broker_1.requestOwnerApproval)({
1641
+ actionType: "spend_limit_set",
1642
+ signerMode: "owner_wallet",
1643
+ chainId,
1644
+ walletAddress: walletAddr,
1645
+ txs: [{
1646
+ to: policyAddress,
1647
+ data: policyInterface.encodeFunctionData("setCategoryLimitFor", [walletAddr, opts.category, amount]),
1648
+ value: "0x0",
1649
+ }],
1650
+ ui: {
1651
+ title: "Set spend limit",
1652
+ summary: `Approve spend limit: ${opts.category} → ${opts.amount} ETH`,
1653
+ risk: "medium",
1654
+ },
1655
+ metadata: { sourceRuntime: "cli" },
1656
+ }, config);
1657
+ if (approval.status !== "approved" || !approval.session || approval.session.kind !== "walletconnect") {
1658
+ console.error(colors_1.c.failure + " " + colors_1.c.red(approval.error ?? "Approval transport failed."));
1659
+ process.exit(1);
1660
+ }
1661
+ const { client, session, account } = approval.session;
1662
+ const txHash = await (0, walletconnect_1.sendTransactionWithSession)(client, session, account, chainId, {
1578
1663
  to: policyAddress,
1579
1664
  data: policyInterface.encodeFunctionData("setCategoryLimitFor", [walletAddr, opts.category, amount]),
1580
1665
  value: "0x0",
1581
- }), `Approve spend limit: ${opts.category} → ${opts.amount} ETH`, config.telegramBotToken && config.telegramChatId ? {
1582
- botToken: config.telegramBotToken,
1583
- chatId: config.telegramChatId,
1584
- threadId: config.telegramThreadId,
1585
- } : undefined, config);
1666
+ });
1586
1667
  console.log(`\nTransaction submitted: ${txHash}`);
1587
1668
  await provider.waitForTransaction(txHash);
1588
1669
  console.log(`Spend limit for ${opts.category} set to ${opts.amount} ETH`);
@@ -1625,15 +1706,33 @@ function registerWalletCommands(program) {
1625
1706
  const policyInterface = new ethers_1.ethers.Interface(abis_1.POLICY_ENGINE_LIMITS_ABI);
1626
1707
  if (config.walletConnectProjectId) {
1627
1708
  const provider = new ethers_1.ethers.JsonRpcProvider(config.rpcUrl);
1628
- const { txHash } = await (0, walletconnect_1.requestPhoneWalletSignature)(config.walletConnectProjectId, chainId, () => ({
1709
+ const approval = await (0, broker_1.requestOwnerApproval)({
1710
+ actionType: "policy_update",
1711
+ signerMode: "owner_wallet",
1712
+ chainId,
1713
+ walletAddress: walletAddr,
1714
+ txs: [{
1715
+ to: policyAddress,
1716
+ data: policyInterface.encodeFunctionData("setDailyLimitFor", [walletAddr, opts.category, amount]),
1717
+ value: "0x0",
1718
+ }],
1719
+ ui: {
1720
+ title: "Set daily limit",
1721
+ summary: `Approve daily limit: ${opts.category} → ${opts.amount} ETH`,
1722
+ risk: "medium",
1723
+ },
1724
+ metadata: { sourceRuntime: "cli" },
1725
+ }, config);
1726
+ if (approval.status !== "approved" || !approval.session || approval.session.kind !== "walletconnect") {
1727
+ console.error(colors_1.c.failure + " " + colors_1.c.red(approval.error ?? "Approval transport failed."));
1728
+ process.exit(1);
1729
+ }
1730
+ const { client, session, account } = approval.session;
1731
+ const txHash = await (0, walletconnect_1.sendTransactionWithSession)(client, session, account, chainId, {
1629
1732
  to: policyAddress,
1630
1733
  data: policyInterface.encodeFunctionData("setDailyLimitFor", [walletAddr, opts.category, amount]),
1631
1734
  value: "0x0",
1632
- }), `Approve daily limit: ${opts.category} → ${opts.amount} ETH`, config.telegramBotToken && config.telegramChatId ? {
1633
- botToken: config.telegramBotToken,
1634
- chatId: config.telegramChatId,
1635
- threadId: config.telegramThreadId,
1636
- } : undefined, config);
1735
+ });
1637
1736
  await provider.waitForTransaction(txHash);
1638
1737
  console.log(`Daily limit for ${opts.category} set to ${opts.amount} ETH (12/24h rolling window)`);
1639
1738
  }
@@ -1674,24 +1773,30 @@ function registerWalletCommands(program) {
1674
1773
  ];
1675
1774
  const policyInterface = new ethers_1.ethers.Interface(abis_1.POLICY_ENGINE_LIMITS_ABI);
1676
1775
  const provider = new ethers_1.ethers.JsonRpcProvider(config.rpcUrl);
1677
- const telegramOpts = config.telegramBotToken && config.telegramChatId ? {
1678
- botToken: config.telegramBotToken,
1679
- chatId: config.telegramChatId,
1680
- threadId: config.telegramThreadId,
1681
- } : undefined;
1682
1776
  console.log(colors_1.c.white("\n◈ Spend limit init — 1 connection, 5 approvals\n"));
1683
1777
  console.log(colors_1.c.dim(" Connect once — MetaMask will prompt 5 times in sequence."));
1684
- // Connect once, reuse session for all 5 txs
1685
- const { client, session, account } = await (0, walletconnect_1.connectPhoneWallet)(config.walletConnectProjectId, chainId, config, { telegramOpts, prompt: "Connect wallet for spend limit setup (5 approvals)" });
1778
+ const approval = await (0, broker_1.requestOwnerApproval)({
1779
+ actionType: "spend_limit_set",
1780
+ signerMode: "owner_wallet",
1781
+ chainId,
1782
+ walletAddress: walletAddr,
1783
+ txs: categories.map(() => ({ to: policyAddress, data: "0x", value: "0x0" })),
1784
+ ui: {
1785
+ title: "Initialize spend limits",
1786
+ summary: "Connect wallet for spend limit setup (5 approvals)",
1787
+ risk: "medium",
1788
+ },
1789
+ metadata: { expectedApprovalCount: categories.length, sourceRuntime: "cli" },
1790
+ }, config);
1791
+ if (approval.status !== "approved" || !approval.session || approval.session.kind !== "walletconnect") {
1792
+ console.error(colors_1.c.failure + " " + colors_1.c.red(approval.error ?? "Approval transport failed."));
1793
+ process.exit(1);
1794
+ }
1795
+ const { client, session, account } = approval.session;
1686
1796
  for (const cat of categories) {
1687
1797
  const amount = ethers_1.ethers.parseEther(cat.amount);
1688
1798
  const data = policyInterface.encodeFunctionData("setCategoryLimitFor", [walletAddr, cat.name, amount]);
1689
1799
  console.log(colors_1.c.dim(`\n Setting ${cat.name} → ${cat.amount} ETH...`));
1690
- // Send Telegram notification for each
1691
- if (telegramOpts) {
1692
- const { sendTelegramMessage: sendTg } = await Promise.resolve().then(() => __importStar(require("../telegram-notify.js")));
1693
- await sendTg({ ...telegramOpts, text: `Approve spend limit: ${cat.name} → ${cat.amount} ETH` });
1694
- }
1695
1800
  const txHash = await (0, walletconnect_1.sendTransactionWithSession)(client, session, account, chainId, {
1696
1801
  to: policyAddress,
1697
1802
  data,
@@ -1736,14 +1841,33 @@ function registerWalletCommands(program) {
1736
1841
  console.log(`\nWallet: ${config.walletContractAddress}`);
1737
1842
  console.log(`Current policy: ${currentPolicy}`);
1738
1843
  console.log(`New policy: ${policyIdHex}`);
1739
- const telegramOpts = config.telegramBotToken && config.telegramChatId
1740
- ? { botToken: config.telegramBotToken, chatId: config.telegramChatId, threadId: config.telegramThreadId }
1741
- : undefined;
1742
- const { txHash } = await (0, walletconnect_1.requestPhoneWalletSignature)(config.walletConnectProjectId, chainId, () => ({
1844
+ const approval = await (0, broker_1.requestOwnerApproval)({
1845
+ actionType: "policy_update",
1846
+ signerMode: "owner_wallet",
1847
+ chainId,
1848
+ walletAddress: config.walletContractAddress,
1849
+ txs: [{
1850
+ to: config.walletContractAddress,
1851
+ data: ownerInterface.encodeFunctionData("updatePolicy", [policyIdHex]),
1852
+ value: "0x0",
1853
+ }],
1854
+ ui: {
1855
+ title: "Update active policy",
1856
+ summary: `Approve: update policy to ${policyIdHex}`,
1857
+ risk: "medium",
1858
+ },
1859
+ metadata: { sourceRuntime: "cli" },
1860
+ }, config);
1861
+ if (approval.status !== "approved" || !approval.session || approval.session.kind !== "walletconnect") {
1862
+ console.error(colors_1.c.failure + " " + colors_1.c.red(approval.error ?? "Approval transport failed."));
1863
+ process.exit(1);
1864
+ }
1865
+ const { client, session, account } = approval.session;
1866
+ const txHash = await (0, walletconnect_1.sendTransactionWithSession)(client, session, account, chainId, {
1743
1867
  to: config.walletContractAddress,
1744
1868
  data: ownerInterface.encodeFunctionData("updatePolicy", [policyIdHex]),
1745
1869
  value: "0x0",
1746
- }), `Approve: update policy to ${policyIdHex}`, telegramOpts, config);
1870
+ });
1747
1871
  await provider.waitForTransaction(txHash);
1748
1872
  console.log("\n" + colors_1.c.success + colors_1.c.white(" Active policy updated"));
1749
1873
  console.log(" " + colors_1.c.dim("Tx:") + " " + colors_1.c.white(txHash));
@@ -1814,10 +1938,35 @@ function registerWalletCommands(program) {
1814
1938
  const chainId = config.network === "base-mainnet" ? 8453 : 84532;
1815
1939
  const provider = new ethers_1.ethers.JsonRpcProvider(config.rpcUrl);
1816
1940
  const walletInterface = new ethers_1.ethers.Interface(abis_1.ARC402_WALLET_GUARDIAN_ABI);
1817
- const telegramOpts = config.telegramBotToken && config.telegramChatId
1818
- ? { botToken: config.telegramBotToken, chatId: config.telegramChatId, threadId: config.telegramThreadId }
1819
- : undefined;
1820
- const { client, session, account } = await (0, walletconnect_1.connectPhoneWallet)(config.walletConnectProjectId, chainId, config, { telegramOpts, prompt: "Approve: unfreeze ARC402Wallet", hardware: !!opts.hardware });
1941
+ const execution = await requestWalletGovernanceExecution(config, {
1942
+ actionType: "custom_tx",
1943
+ signerMode: "owner_wallet",
1944
+ chainId,
1945
+ walletAddress: config.walletContractAddress,
1946
+ txs: [{
1947
+ to: config.walletContractAddress,
1948
+ data: walletInterface.encodeFunctionData("unfreeze", []),
1949
+ value: "0x0",
1950
+ }],
1951
+ ui: {
1952
+ title: "Unfreeze wallet",
1953
+ summary: "Approve: unfreeze ARC402Wallet",
1954
+ risk: "high",
1955
+ },
1956
+ metadata: { sourceRuntime: "cli", hardware: !!opts.hardware },
1957
+ });
1958
+ if (execution.kind === "passkey") {
1959
+ if (opts.json) {
1960
+ console.log(JSON.stringify({ txHash: execution.txHash, walletAddress: config.walletContractAddress, userOpHash: execution.userOpHash }));
1961
+ }
1962
+ else {
1963
+ console.log("\n" + colors_1.c.success + colors_1.c.white(` Wallet ${config.walletContractAddress} unfrozen`));
1964
+ console.log(" " + colors_1.c.dim("Tx:") + " " + colors_1.c.white(execution.txHash));
1965
+ console.log(" " + colors_1.c.dim("UserOp:") + " " + colors_1.c.white(execution.userOpHash));
1966
+ }
1967
+ return;
1968
+ }
1969
+ const { client, session, account } = execution.session;
1821
1970
  const networkName = chainId === 8453 ? "Base" : "Base Sepolia";
1822
1971
  const shortAddr = `${account.slice(0, 6)}...${account.slice(-5)}`;
1823
1972
  console.log("\n" + colors_1.c.success + colors_1.c.white(` Connected: ${shortAddr} on ${networkName}`));
@@ -1875,10 +2024,37 @@ function registerWalletCommands(program) {
1875
2024
  const walletInterface = new ethers_1.ethers.Interface(abis_1.ARC402_WALLET_GUARDIAN_ABI);
1876
2025
  console.log(`\nGuardian address: ${guardianWallet.address}`);
1877
2026
  console.log(`Wallet contract: ${config.walletContractAddress}`);
1878
- const telegramOpts = config.telegramBotToken && config.telegramChatId
1879
- ? { botToken: config.telegramBotToken, chatId: config.telegramChatId, threadId: config.telegramThreadId }
1880
- : undefined;
1881
- const { client, session, account } = await (0, walletconnect_1.connectPhoneWallet)(config.walletConnectProjectId, chainId, config, { telegramOpts, prompt: `Approve: set guardian to ${guardianWallet.address}`, hardware: !!opts.hardware });
2027
+ const execution = await requestWalletGovernanceExecution(config, {
2028
+ actionType: "custom_tx",
2029
+ signerMode: "owner_wallet",
2030
+ chainId,
2031
+ walletAddress: config.walletContractAddress,
2032
+ txs: [{
2033
+ to: config.walletContractAddress,
2034
+ data: walletInterface.encodeFunctionData("setGuardian", [guardianWallet.address]),
2035
+ value: "0x0",
2036
+ }],
2037
+ ui: {
2038
+ title: "Set guardian",
2039
+ summary: `Approve: set guardian to ${guardianWallet.address}`,
2040
+ risk: "high",
2041
+ },
2042
+ metadata: { sourceRuntime: "cli", hardware: !!opts.hardware },
2043
+ });
2044
+ if (execution.kind === "passkey") {
2045
+ saveGuardianKey(guardianWallet.privateKey);
2046
+ if (config.guardianPrivateKey)
2047
+ delete config.guardianPrivateKey;
2048
+ config.guardianAddress = guardianWallet.address;
2049
+ (0, config_1.saveConfig)(config);
2050
+ console.log("\n" + colors_1.c.success + colors_1.c.white(` Guardian set to: ${guardianWallet.address}`));
2051
+ console.log(" " + colors_1.c.dim("Tx:") + " " + colors_1.c.white(execution.txHash));
2052
+ console.log(" " + colors_1.c.dim("UserOp:") + " " + colors_1.c.white(execution.userOpHash));
2053
+ console.log(" " + colors_1.c.dim("Guardian private key saved to ~/.arc402/guardian.key (chmod 400)."));
2054
+ console.log(" " + colors_1.c.warning + " " + colors_1.c.yellow("The guardian key can freeze your wallet. Store it separately from your hot key."));
2055
+ return;
2056
+ }
2057
+ const { client, session, account } = execution.session;
1882
2058
  const networkName = chainId === 8453 ? "Base" : "Base Sepolia";
1883
2059
  const shortAddr = `${account.slice(0, 6)}...${account.slice(-5)}`;
1884
2060
  console.log("\n" + colors_1.c.success + colors_1.c.white(` Connected: ${shortAddr} on ${networkName}`));
@@ -1974,10 +2150,36 @@ function registerWalletCommands(program) {
1974
2150
  console.log(` Value: 0x0`);
1975
2151
  return;
1976
2152
  }
1977
- const telegramOpts = config.telegramBotToken && config.telegramChatId
1978
- ? { botToken: config.telegramBotToken, chatId: config.telegramChatId, threadId: config.telegramThreadId }
1979
- : undefined;
1980
- const { client, session, account } = await (0, walletconnect_1.connectPhoneWallet)(config.walletConnectProjectId, chainId, config, { telegramOpts, prompt: "Approve registry upgrade proposal on ARC402Wallet", hardware: !!opts.hardware });
2153
+ const execution = await requestWalletGovernanceExecution(config, {
2154
+ actionType: "custom_tx",
2155
+ signerMode: "owner_wallet",
2156
+ chainId,
2157
+ walletAddress: config.walletContractAddress,
2158
+ txs: [{ to: config.walletContractAddress, data: calldata, value: "0x0" }],
2159
+ ui: {
2160
+ title: "Propose registry upgrade",
2161
+ summary: "Approve registry upgrade proposal on ARC402Wallet",
2162
+ risk: "high",
2163
+ },
2164
+ metadata: {
2165
+ sourceRuntime: "cli",
2166
+ hardware: !!opts.hardware,
2167
+ },
2168
+ });
2169
+ if (execution.kind === "passkey") {
2170
+ const unlockAt = new Date(Date.now() + 2 * 24 * 60 * 60 * 1000);
2171
+ console.log("\n" + colors_1.c.success + colors_1.c.white(" Registry upgrade proposed"));
2172
+ console.log(" " + colors_1.c.dim("Tx:") + " " + colors_1.c.white(execution.txHash));
2173
+ console.log(" " + colors_1.c.dim("UserOp:") + " " + colors_1.c.white(execution.userOpHash));
2174
+ console.log(" " + colors_1.c.dim("Unlock at:") + " " + colors_1.c.white(unlockAt.toISOString()) + colors_1.c.dim(" (approximately)"));
2175
+ console.log(`\nNext steps:`);
2176
+ console.log(` Wait 2 days, then run:`);
2177
+ console.log(` arc402 wallet execute-registry-upgrade`);
2178
+ console.log(`\nTo cancel before execution:`);
2179
+ console.log(` arc402 wallet cancel-registry-upgrade`);
2180
+ return;
2181
+ }
2182
+ const { client, session, account } = execution.session;
1981
2183
  const networkName = chainId === 8453 ? "Base" : "Base Sepolia";
1982
2184
  const shortAddr = `${account.slice(0, 6)}...${account.slice(-5)}`;
1983
2185
  console.log("\n" + colors_1.c.success + colors_1.c.white(` Connected: ${shortAddr} on ${networkName}`));
@@ -2043,15 +2245,39 @@ function registerWalletCommands(program) {
2043
2245
  }
2044
2246
  console.log(`Pending registry: ${pendingRegistry}`);
2045
2247
  console.log("Timelock elapsed — proceeding with executeRegistryUpdate()");
2046
- const telegramOpts = config.telegramBotToken && config.telegramChatId
2047
- ? { botToken: config.telegramBotToken, chatId: config.telegramChatId, threadId: config.telegramThreadId }
2048
- : undefined;
2049
2248
  const walletInterface = new ethers_1.ethers.Interface(abis_1.ARC402_WALLET_REGISTRY_ABI);
2050
- const { txHash } = await (0, walletconnect_1.requestPhoneWalletSignature)(config.walletConnectProjectId, chainId, () => ({
2249
+ const txData = {
2051
2250
  to: config.walletContractAddress,
2052
2251
  data: walletInterface.encodeFunctionData("executeRegistryUpdate", []),
2053
2252
  value: "0x0",
2054
- }), "Approve registry upgrade execution on ARC402Wallet", telegramOpts, config);
2253
+ };
2254
+ const execution = await requestWalletGovernanceExecution(config, {
2255
+ actionType: "custom_tx",
2256
+ signerMode: "owner_wallet",
2257
+ chainId,
2258
+ walletAddress: config.walletContractAddress,
2259
+ txs: [txData],
2260
+ ui: {
2261
+ title: "Execute registry upgrade",
2262
+ summary: "Approve registry upgrade execution on ARC402Wallet",
2263
+ risk: "high",
2264
+ },
2265
+ metadata: { sourceRuntime: "cli" },
2266
+ });
2267
+ if (execution.kind === "passkey") {
2268
+ let confirmedRegistry = pendingRegistry;
2269
+ try {
2270
+ confirmedRegistry = await walletContract.registry();
2271
+ }
2272
+ catch { /* use pendingRegistry as fallback */ }
2273
+ console.log("\n" + colors_1.c.success + colors_1.c.white(" Registry upgrade executed"));
2274
+ console.log(" " + colors_1.c.dim("Tx:") + " " + colors_1.c.white(execution.txHash));
2275
+ console.log(" " + colors_1.c.dim("UserOp:") + " " + colors_1.c.white(execution.userOpHash));
2276
+ console.log(" " + colors_1.c.dim("New registry:") + " " + colors_1.c.white(confirmedRegistry));
2277
+ return;
2278
+ }
2279
+ const { client, session, account } = execution.session;
2280
+ const txHash = await (0, walletconnect_1.sendTransactionWithSession)(client, session, account, chainId, txData);
2055
2281
  // Wait for tx to confirm, then read back the active registry (J6-02)
2056
2282
  await provider.waitForTransaction(txHash);
2057
2283
  let confirmedRegistry = pendingRegistry;
@@ -2118,18 +2344,33 @@ function registerWalletCommands(program) {
2118
2344
  console.log(`\nWallet: ${config.walletContractAddress}`);
2119
2345
  console.log(`PolicyEngine: ${policyAddress}`);
2120
2346
  console.log(`Whitelisting: ${checksumTarget}`);
2121
- const telegramOpts = config.telegramBotToken && config.telegramChatId
2122
- ? { botToken: config.telegramBotToken, chatId: config.telegramChatId, threadId: config.telegramThreadId }
2123
- : undefined;
2124
2347
  const policyIface = new ethers_1.ethers.Interface(peAbi);
2125
- const { txHash } = await (0, walletconnect_1.requestPhoneWalletSignature)(config.walletConnectProjectId, chainId, () => ({
2348
+ const txData = {
2126
2349
  to: policyAddress,
2127
2350
  data: policyIface.encodeFunctionData("whitelistContract", [
2128
2351
  config.walletContractAddress,
2129
2352
  checksumTarget,
2130
2353
  ]),
2131
2354
  value: "0x0",
2132
- }), `Approve: whitelist ${checksumTarget} on PolicyEngine for your wallet`, telegramOpts, config);
2355
+ };
2356
+ const { client, session, account } = await requestWalletConnectApprovalSession(config, {
2357
+ actionType: "policy_update",
2358
+ signerMode: "owner_wallet",
2359
+ chainId,
2360
+ walletAddress: config.walletContractAddress,
2361
+ txs: [txData],
2362
+ ui: {
2363
+ title: "Whitelist contract",
2364
+ summary: `Approve: whitelist ${checksumTarget} on PolicyEngine for your wallet`,
2365
+ risk: "high",
2366
+ },
2367
+ metadata: {
2368
+ category: "policy",
2369
+ sourceRuntime: "cli",
2370
+ hardware: !!opts.hardware,
2371
+ },
2372
+ });
2373
+ const txHash = await (0, walletconnect_1.sendTransactionWithSession)(client, session, account, chainId, txData);
2133
2374
  await provider.waitForTransaction(txHash);
2134
2375
  if (opts.json) {
2135
2376
  console.log(JSON.stringify({ ok: true, txHash, wallet: config.walletContractAddress, target: checksumTarget }));
@@ -2176,14 +2417,33 @@ function registerWalletCommands(program) {
2176
2417
  console.log(`\nWallet: ${config.walletContractAddress}`);
2177
2418
  console.log(`Current interceptor: ${currentInterceptor}`);
2178
2419
  console.log(`New interceptor: ${checksumAddress}`);
2179
- const telegramOpts = config.telegramBotToken && config.telegramChatId
2180
- ? { botToken: config.telegramBotToken, chatId: config.telegramChatId, threadId: config.telegramThreadId }
2181
- : undefined;
2182
- const { txHash } = await (0, walletconnect_1.requestPhoneWalletSignature)(config.walletConnectProjectId, chainId, () => ({
2420
+ const txData = {
2183
2421
  to: config.walletContractAddress,
2184
2422
  data: ownerInterface.encodeFunctionData("setAuthorizedInterceptor", [checksumAddress]),
2185
2423
  value: "0x0",
2186
- }), `Approve: set X402 interceptor to ${checksumAddress}`, telegramOpts, config);
2424
+ };
2425
+ const execution = await requestWalletGovernanceExecution(config, {
2426
+ actionType: "custom_tx",
2427
+ signerMode: "owner_wallet",
2428
+ chainId,
2429
+ walletAddress: config.walletContractAddress,
2430
+ txs: [txData],
2431
+ ui: {
2432
+ title: "Set X402 interceptor",
2433
+ summary: `Approve: set X402 interceptor to ${checksumAddress}`,
2434
+ risk: "high",
2435
+ },
2436
+ metadata: { sourceRuntime: "cli" },
2437
+ });
2438
+ if (execution.kind === "passkey") {
2439
+ console.log("\n" + colors_1.c.success + colors_1.c.white(" X402 interceptor updated"));
2440
+ console.log(" " + colors_1.c.dim("Tx:") + " " + colors_1.c.white(execution.txHash));
2441
+ console.log(" " + colors_1.c.dim("UserOp:") + " " + colors_1.c.white(execution.userOpHash));
2442
+ console.log(" " + colors_1.c.dim("Interceptor:") + " " + colors_1.c.white(checksumAddress));
2443
+ return;
2444
+ }
2445
+ const { client, session, account } = execution.session;
2446
+ const txHash = await (0, walletconnect_1.sendTransactionWithSession)(client, session, account, chainId, txData);
2187
2447
  await provider.waitForTransaction(txHash);
2188
2448
  console.log("\n" + colors_1.c.success + colors_1.c.white(" X402 interceptor updated"));
2189
2449
  console.log(" " + colors_1.c.dim("Tx:") + " " + colors_1.c.white(txHash));
@@ -2227,14 +2487,36 @@ function registerWalletCommands(program) {
2227
2487
  console.log(`\nWallet: ${config.walletContractAddress}`);
2228
2488
  console.log(`Current limit: ${currentLimit}`);
2229
2489
  console.log(`New limit: ${limitEth} ETH (max ETH per rolling window)`);
2230
- const telegramOpts = config.telegramBotToken && config.telegramChatId
2231
- ? { botToken: config.telegramBotToken, chatId: config.telegramChatId, threadId: config.telegramThreadId }
2232
- : undefined;
2233
- const { txHash } = await (0, walletconnect_1.requestPhoneWalletSignature)(config.walletConnectProjectId, chainId, () => ({
2490
+ const txData = {
2234
2491
  to: config.walletContractAddress,
2235
2492
  data: ownerInterface.encodeFunctionData("setVelocityLimit", [limitWei]),
2236
2493
  value: "0x0",
2237
- }), `Approve: set velocity limit to ${limitEth} ETH`, telegramOpts, config);
2494
+ };
2495
+ const execution = await requestWalletGovernanceExecution(config, {
2496
+ actionType: "policy_update",
2497
+ signerMode: "owner_wallet",
2498
+ chainId,
2499
+ walletAddress: config.walletContractAddress,
2500
+ txs: [txData],
2501
+ ui: {
2502
+ title: "Set velocity limit",
2503
+ summary: `Approve: set velocity limit to ${limitEth} ETH`,
2504
+ risk: "high",
2505
+ },
2506
+ metadata: {
2507
+ category: "policy",
2508
+ sourceRuntime: "cli",
2509
+ },
2510
+ });
2511
+ if (execution.kind === "passkey") {
2512
+ console.log("\n" + colors_1.c.success + colors_1.c.white(" Velocity limit updated"));
2513
+ console.log(" " + colors_1.c.dim("Tx:") + " " + colors_1.c.white(execution.txHash));
2514
+ console.log(" " + colors_1.c.dim("UserOp:") + " " + colors_1.c.white(execution.userOpHash));
2515
+ console.log(" " + colors_1.c.dim("New limit:") + " " + colors_1.c.white(`${limitEth} ETH per rolling window`));
2516
+ return;
2517
+ }
2518
+ const { client, session, account } = execution.session;
2519
+ const txHash = await (0, walletconnect_1.sendTransactionWithSession)(client, session, account, chainId, txData);
2238
2520
  await provider.waitForTransaction(txHash);
2239
2521
  console.log("\n" + colors_1.c.success + colors_1.c.white(" Velocity limit updated"));
2240
2522
  console.log(" " + colors_1.c.dim("Tx:") + " " + colors_1.c.white(txHash));
@@ -2242,9 +2524,8 @@ function registerWalletCommands(program) {
2242
2524
  });
2243
2525
  // ─── register-policy ───────────────────────────────────────────────────────
2244
2526
  //
2245
- // Calls registerWallet(walletAddress, ownerAddress) on PolicyEngine via
2246
- // executeContractCall on the ARC402Wallet. PolicyEngine requires msg.sender == wallet,
2247
- // so this must go through the wallet contract — not called directly by the owner key.
2527
+ // Calls registerWallet(walletAddress, ownerAddress) directly on PolicyEngine
2528
+ // from the owner wallet.
2248
2529
  wallet.command("register-policy")
2249
2530
  .description("Register this wallet on PolicyEngine (required before spend limits can be set)")
2250
2531
  .option("--hardware", "Hardware wallet mode: show raw wc: URI only")
@@ -2281,33 +2562,45 @@ function registerWalletCommands(program) {
2281
2562
  }
2282
2563
  }
2283
2564
  }
2565
+ const resolvedOwnerAddress = ownerAddress;
2284
2566
  // Encode registerWallet(wallet, owner) calldata — called on PolicyEngine
2285
2567
  const policyInterface = new ethers_1.ethers.Interface([
2286
2568
  "function registerWallet(address wallet, address owner) external",
2287
2569
  ]);
2288
2570
  const registerCalldata = policyInterface.encodeFunctionData("registerWallet", [
2289
2571
  config.walletContractAddress,
2290
- ownerAddress,
2572
+ resolvedOwnerAddress,
2291
2573
  ]);
2292
- const executeInterface = new ethers_1.ethers.Interface(abis_1.ARC402_WALLET_EXECUTE_ABI);
2293
2574
  console.log(`\nWallet: ${config.walletContractAddress}`);
2294
2575
  console.log(`PolicyEngine: ${policyAddress}`);
2295
- console.log(`Owner: ${ownerAddress}`);
2296
- const telegramOpts = config.telegramBotToken && config.telegramChatId
2297
- ? { botToken: config.telegramBotToken, chatId: config.telegramChatId, threadId: config.telegramThreadId }
2298
- : undefined;
2299
- const { txHash } = await (0, walletconnect_1.requestPhoneWalletSignature)(config.walletConnectProjectId, chainId, () => ({
2300
- to: config.walletContractAddress,
2301
- data: executeInterface.encodeFunctionData("executeContractCall", [{
2302
- target: policyAddress,
2303
- data: registerCalldata,
2304
- value: 0n,
2305
- minReturnValue: 0n,
2306
- maxApprovalAmount: 0n,
2307
- approvalToken: ethers_1.ethers.ZeroAddress,
2308
- }]),
2576
+ console.log(`Owner: ${resolvedOwnerAddress}`);
2577
+ const txData = {
2578
+ to: policyAddress,
2579
+ data: registerCalldata,
2309
2580
  value: "0x0",
2310
- }), `Approve: register wallet on PolicyEngine`, telegramOpts, config);
2581
+ };
2582
+ const { client, session, account } = await requestWalletConnectApprovalSession(config, {
2583
+ actionType: "policy_update",
2584
+ signerMode: "owner_wallet",
2585
+ chainId,
2586
+ walletAddress: config.walletContractAddress,
2587
+ txs: [txData],
2588
+ ui: {
2589
+ title: "Register wallet on PolicyEngine",
2590
+ summary: "Approve: register wallet on PolicyEngine",
2591
+ risk: "high",
2592
+ },
2593
+ metadata: {
2594
+ category: "policy",
2595
+ sourceRuntime: "cli",
2596
+ hardware: !!opts.hardware,
2597
+ },
2598
+ });
2599
+ if (account.toLowerCase() !== resolvedOwnerAddress.toLowerCase()) {
2600
+ console.error(`Connected wallet ${account} is not the wallet owner ${resolvedOwnerAddress}.`);
2601
+ process.exit(1);
2602
+ }
2603
+ const txHash = await (0, walletconnect_1.sendTransactionWithSession)(client, session, account, chainId, txData);
2311
2604
  await provider.waitForTransaction(txHash);
2312
2605
  console.log("\n" + colors_1.c.success + colors_1.c.white(" Wallet registered on PolicyEngine"));
2313
2606
  console.log(" " + colors_1.c.dim("Tx:") + " " + colors_1.c.white(txHash));
@@ -2356,15 +2649,33 @@ function registerWalletCommands(program) {
2356
2649
  console.log(` Pending address: ${pendingRegistry}`);
2357
2650
  console.log(` Timelock: ${timelockStatus}`);
2358
2651
  console.log(`\nCancelling pending registry upgrade to: ${pendingRegistry}`);
2359
- const telegramOpts = config.telegramBotToken && config.telegramChatId
2360
- ? { botToken: config.telegramBotToken, chatId: config.telegramChatId, threadId: config.telegramThreadId }
2361
- : undefined;
2362
2652
  const walletInterface = new ethers_1.ethers.Interface(abis_1.ARC402_WALLET_REGISTRY_ABI);
2363
- const { txHash } = await (0, walletconnect_1.requestPhoneWalletSignature)(config.walletConnectProjectId, chainId, () => ({
2653
+ const txData = {
2364
2654
  to: config.walletContractAddress,
2365
2655
  data: walletInterface.encodeFunctionData("cancelRegistryUpdate", []),
2366
2656
  value: "0x0",
2367
- }), "Approve registry upgrade cancellation on ARC402Wallet", telegramOpts, config);
2657
+ };
2658
+ const execution = await requestWalletGovernanceExecution(config, {
2659
+ actionType: "custom_tx",
2660
+ signerMode: "owner_wallet",
2661
+ chainId,
2662
+ walletAddress: config.walletContractAddress,
2663
+ txs: [txData],
2664
+ ui: {
2665
+ title: "Cancel registry upgrade",
2666
+ summary: "Approve registry upgrade cancellation on ARC402Wallet",
2667
+ risk: "high",
2668
+ },
2669
+ metadata: { sourceRuntime: "cli" },
2670
+ });
2671
+ if (execution.kind === "passkey") {
2672
+ console.log("\n" + colors_1.c.success + colors_1.c.white(" Registry upgrade cancelled"));
2673
+ console.log(" " + colors_1.c.dim("Tx:") + " " + colors_1.c.white(execution.txHash));
2674
+ console.log(" " + colors_1.c.dim("UserOp:") + " " + colors_1.c.white(execution.userOpHash));
2675
+ return;
2676
+ }
2677
+ const { client, session, account } = execution.session;
2678
+ const txHash = await (0, walletconnect_1.sendTransactionWithSession)(client, session, account, chainId, txData);
2368
2679
  console.log("\n" + colors_1.c.success + colors_1.c.white(" Registry upgrade cancelled"));
2369
2680
  console.log(" " + colors_1.c.dim("Tx:") + " " + colors_1.c.white(txHash));
2370
2681
  });
@@ -2505,11 +2816,6 @@ function registerWalletCommands(program) {
2505
2816
  return;
2506
2817
  }
2507
2818
  // ── Step 6: connect WalletConnect once, send all transactions ─────────
2508
- const telegramOpts = config.telegramBotToken && config.telegramChatId
2509
- ? { botToken: config.telegramBotToken, chatId: config.telegramChatId, threadId: config.telegramThreadId }
2510
- : undefined;
2511
- console.log("\nConnecting wallet...");
2512
- const { client, session, account } = await (0, walletconnect_1.connectPhoneWallet)(config.walletConnectProjectId, chainId, config, { telegramOpts, prompt: "Approve governance setup transactions on ARC402Wallet" });
2513
2819
  const provider = new ethers_1.ethers.JsonRpcProvider(config.rpcUrl);
2514
2820
  const ownerInterface = new ethers_1.ethers.Interface(abis_1.ARC402_WALLET_OWNER_ABI);
2515
2821
  const guardianInterface = new ethers_1.ethers.Interface(abis_1.ARC402_WALLET_GUARDIAN_ABI);
@@ -2531,6 +2837,33 @@ function registerWalletCommands(program) {
2531
2837
  govAlreadyDefiEnabled = await policyGovContract.defiAccessEnabled(config.walletContractAddress);
2532
2838
  }
2533
2839
  catch { /* assume not enabled */ }
2840
+ const expectedApprovalCount = (govAlreadyRegistered ? 0 : 1) +
2841
+ (govAlreadyDefiEnabled ? 0 : 1) +
2842
+ 1 +
2843
+ (guardianWallet ? 1 : 0) +
2844
+ categories.length;
2845
+ console.log("\nConnecting wallet...");
2846
+ const { client, session, account } = await requestWalletConnectApprovalSession(config, {
2847
+ actionType: "policy_update",
2848
+ signerMode: "owner_wallet",
2849
+ chainId,
2850
+ walletAddress: config.walletContractAddress,
2851
+ txs: Array.from({ length: expectedApprovalCount }, () => ({
2852
+ to: config.walletContractAddress,
2853
+ data: "0x",
2854
+ value: "0x0",
2855
+ })),
2856
+ ui: {
2857
+ title: "Governance setup",
2858
+ summary: "Approve governance setup transactions on ARC402Wallet",
2859
+ risk: "high",
2860
+ },
2861
+ metadata: {
2862
+ category: "policy",
2863
+ expectedApprovalCount,
2864
+ sourceRuntime: "cli",
2865
+ },
2866
+ });
2534
2867
  if (!govAlreadyRegistered) {
2535
2868
  const registerCalldata = govInterface.encodeFunctionData("registerWallet", [config.walletContractAddress, account]);
2536
2869
  calls.push({
@@ -2665,19 +2998,37 @@ function registerWalletCommands(program) {
2665
2998
  }
2666
2999
  console.log(`\nWallet: ${config.walletContractAddress}`);
2667
3000
  console.log(`Machine key: ${checksumKey}`);
2668
- const telegramOpts = config.telegramBotToken && config.telegramChatId
2669
- ? { botToken: config.telegramBotToken, chatId: config.telegramChatId, threadId: config.telegramThreadId }
2670
- : undefined;
2671
3001
  const walletInterface = new ethers_1.ethers.Interface(machineKeyAbi);
2672
3002
  const txData = {
2673
3003
  to: config.walletContractAddress,
2674
3004
  data: walletInterface.encodeFunctionData("authorizeMachineKey", [checksumKey]),
2675
3005
  value: "0x0",
2676
3006
  };
2677
- const { client, session, account } = await (0, walletconnect_1.connectPhoneWallet)(config.walletConnectProjectId, chainId, config, {
2678
- telegramOpts,
2679
- prompt: `Authorize machine key ${checksumKey} on ARC402Wallet — allows autonomous protocol ops`,
3007
+ const execution = await requestWalletGovernanceExecution(config, {
3008
+ actionType: "custom_tx",
3009
+ signerMode: "owner_wallet",
3010
+ chainId,
3011
+ walletAddress: config.walletContractAddress,
3012
+ txs: [txData],
3013
+ ui: {
3014
+ title: "Authorize machine key",
3015
+ summary: `Authorize machine key ${checksumKey} on ARC402Wallet — allows autonomous protocol ops`,
3016
+ risk: "high",
3017
+ },
3018
+ metadata: { sourceRuntime: "cli" },
2680
3019
  });
3020
+ if (execution.kind === "passkey") {
3021
+ const confirmed = await walletContract.authorizedMachineKeys(checksumKey);
3022
+ console.log("\n" + colors_1.c.success + colors_1.c.white(` Machine key authorized: ${confirmed ? "YES" : "NO"}`));
3023
+ (0, tree_1.renderTree)([
3024
+ { label: "Wallet", value: config.walletContractAddress ?? "" },
3025
+ { label: "Machine key", value: checksumKey },
3026
+ { label: "Tx", value: execution.txHash },
3027
+ { label: "UserOp", value: execution.userOpHash, last: true },
3028
+ ]);
3029
+ return;
3030
+ }
3031
+ const { client, session, account } = execution.session;
2681
3032
  console.log("\n" + colors_1.c.success + colors_1.c.white(` Connected: ${account}`));
2682
3033
  console.log(colors_1.c.dim("Sending authorizeMachineKey transaction..."));
2683
3034
  const hash = await (0, walletconnect_1.sendTransactionWithSession)(client, session, account, chainId, txData);
@@ -2738,18 +3089,40 @@ function registerWalletCommands(program) {
2738
3089
  }
2739
3090
  console.log(`\nWallet: ${config.walletContractAddress}`);
2740
3091
  console.log(`Revoking: ${checksumKey}`);
2741
- const telegramOpts = config.telegramBotToken && config.telegramChatId
2742
- ? { botToken: config.telegramBotToken, chatId: config.telegramChatId, threadId: config.telegramThreadId }
2743
- : undefined;
2744
3092
  const walletInterface = new ethers_1.ethers.Interface(abis_1.ARC402_WALLET_MACHINE_KEY_ABI);
2745
- const { client, session, account } = await (0, walletconnect_1.connectPhoneWallet)(config.walletConnectProjectId, chainId, config, { telegramOpts, prompt: `Revoke machine key ${checksumKey} on ARC402Wallet` });
2746
- console.log("\n" + colors_1.c.success + colors_1.c.white(` Connected: ${account}`));
2747
- console.log(colors_1.c.dim("Sending revokeMachineKey transaction..."));
2748
- const hash = await (0, walletconnect_1.sendTransactionWithSession)(client, session, account, chainId, {
3093
+ const txData = {
2749
3094
  to: config.walletContractAddress,
2750
3095
  data: walletInterface.encodeFunctionData("revokeMachineKey", [checksumKey]),
2751
3096
  value: "0x0",
3097
+ };
3098
+ const execution = await requestWalletGovernanceExecution(config, {
3099
+ actionType: "custom_tx",
3100
+ signerMode: "owner_wallet",
3101
+ chainId,
3102
+ walletAddress: config.walletContractAddress,
3103
+ txs: [txData],
3104
+ ui: {
3105
+ title: "Revoke machine key",
3106
+ summary: `Revoke machine key ${checksumKey} on ARC402Wallet`,
3107
+ risk: "high",
3108
+ },
3109
+ metadata: { sourceRuntime: "cli" },
2752
3110
  });
3111
+ if (execution.kind === "passkey") {
3112
+ const stillAuthorized = await walletContract.authorizedMachineKeys(checksumKey);
3113
+ console.log("\n" + colors_1.c.success + colors_1.c.white(` Machine key revoked: ${stillAuthorized ? "NO (still authorized — check tx)" : "YES"}`));
3114
+ (0, tree_1.renderTree)([
3115
+ { label: "Wallet", value: config.walletContractAddress ?? "" },
3116
+ { label: "Machine key", value: checksumKey },
3117
+ { label: "Tx", value: execution.txHash },
3118
+ { label: "UserOp", value: execution.userOpHash, last: true },
3119
+ ]);
3120
+ return;
3121
+ }
3122
+ const { client, session, account } = execution.session;
3123
+ console.log("\n" + colors_1.c.success + colors_1.c.white(` Connected: ${account}`));
3124
+ console.log(colors_1.c.dim("Sending revokeMachineKey transaction..."));
3125
+ const hash = await (0, walletconnect_1.sendTransactionWithSession)(client, session, account, chainId, txData);
2753
3126
  console.log("\n" + colors_1.c.dim("Transaction submitted:") + " " + colors_1.c.white(hash));
2754
3127
  console.log(colors_1.c.dim("Waiting for confirmation..."));
2755
3128
  const receipt = await provider.waitForTransaction(hash, 1, 60000);
@@ -3248,7 +3621,7 @@ function registerWalletCommands(program) {
3248
3621
  console.error("walletConnectProjectId not set. Run `arc402 config set walletConnectProjectId <id>`.");
3249
3622
  process.exit(1);
3250
3623
  }
3251
- const migrationRegistryAddress = config.migrationRegistryAddress ?? "0x4821D8A590eD4DbEf114fCA3C2d9311e81D576DF";
3624
+ const migrationRegistryAddress = (0, config_1.getCanonicalNetworkAddress)(config, "migrationRegistryAddress");
3252
3625
  let oldWallet;
3253
3626
  let newWallet;
3254
3627
  try {
@@ -3274,10 +3647,20 @@ function registerWalletCommands(program) {
3274
3647
  "function registerMigration(address oldWallet, address newWallet) external"
3275
3648
  ]);
3276
3649
  const callData = mrIface.encodeFunctionData("registerMigration", [oldWallet, newWallet]);
3277
- const telegramOpts = config.telegramBotToken && config.telegramChatId
3278
- ? { botToken: config.telegramBotToken, chatId: config.telegramChatId, threadId: config.telegramThreadId }
3279
- : undefined;
3280
- const { txHash } = await (0, walletconnect_1.requestPhoneWalletSignature)(config.walletConnectProjectId, chainId, () => ({ to: migrationRegistryAddress, data: callData, value: "0x0" }), `Approve: migrate wallet identity ${oldWallet.slice(0, 10)}... → ${newWallet.slice(0, 10)}...`, telegramOpts, config);
3650
+ const txData = { to: migrationRegistryAddress, data: callData, value: "0x0" };
3651
+ const { client, session, account } = await requestWalletConnectApprovalSession(config, {
3652
+ actionType: "custom_tx",
3653
+ signerMode: "owner_wallet",
3654
+ chainId,
3655
+ txs: [txData],
3656
+ ui: {
3657
+ title: "Register wallet migration",
3658
+ summary: `Approve: migrate wallet identity ${oldWallet.slice(0, 10)}... → ${newWallet.slice(0, 10)}...`,
3659
+ risk: "high",
3660
+ },
3661
+ metadata: { sourceRuntime: "cli" },
3662
+ });
3663
+ const txHash = await (0, walletconnect_1.sendTransactionWithSession)(client, session, account, chainId, txData);
3281
3664
  await provider.waitForTransaction(txHash);
3282
3665
  if (opts.json) {
3283
3666
  console.log(JSON.stringify({ ok: true, txHash, from: oldWallet, to: newWallet }));
@@ -3501,18 +3884,29 @@ function registerWalletCommands(program) {
3501
3884
  console.log(`\nWallet: ${config.walletContractAddress}`);
3502
3885
  console.log(`pubKeyX: ${pubKeyX}`);
3503
3886
  console.log(`pubKeyY: ${pubKeyY}`);
3504
- const telegramOpts = config.telegramBotToken && config.telegramChatId
3505
- ? { botToken: config.telegramBotToken, chatId: config.telegramChatId, threadId: config.telegramThreadId }
3506
- : undefined;
3507
3887
  const txData = {
3508
3888
  to: config.walletContractAddress,
3509
3889
  data: walletInterface.encodeFunctionData("setPasskey", [pubKeyX, pubKeyY]),
3510
3890
  value: "0x0",
3511
3891
  };
3512
- const { client, session, account } = await (0, walletconnect_1.connectPhoneWallet)(config.walletConnectProjectId, chainId, config, {
3513
- telegramOpts,
3514
- prompt: `Activate passkey (Face ID) on ARC402Wallet — enables P256 governance signing`,
3515
- });
3892
+ const approval = await (0, broker_1.requestOwnerApproval)({
3893
+ actionType: "custom_tx",
3894
+ signerMode: "owner_wallet",
3895
+ chainId,
3896
+ walletAddress: config.walletContractAddress,
3897
+ txs: [txData],
3898
+ ui: {
3899
+ title: "Activate passkey",
3900
+ summary: "Activate passkey (Face ID) on ARC402Wallet — enables P256 governance signing",
3901
+ risk: "high",
3902
+ },
3903
+ metadata: { sourceRuntime: "cli" },
3904
+ }, config);
3905
+ if (approval.status !== "approved" || !approval.session || approval.session.kind !== "walletconnect") {
3906
+ console.error(colors_1.c.failure + " " + colors_1.c.red(approval.error ?? "Approval transport failed."));
3907
+ process.exit(1);
3908
+ }
3909
+ const { client, session, account } = approval.session;
3516
3910
  console.log("\n" + colors_1.c.success + colors_1.c.white(` Connected: ${account}`));
3517
3911
  console.log(colors_1.c.dim("Sending setPasskey transaction..."));
3518
3912
  const hash = await (0, walletconnect_1.sendTransactionWithSession)(client, session, account, chainId, txData);