arc402-cli 1.8.8 → 1.8.10

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 (182) 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/daemon.d.ts.map +1 -1
  61. package/dist/commands/daemon.js +54 -20
  62. package/dist/commands/daemon.js.map +1 -1
  63. package/dist/commands/deliver.js +1 -1
  64. package/dist/commands/deliver.js.map +1 -1
  65. package/dist/commands/endpoint.d.ts.map +1 -1
  66. package/dist/commands/endpoint.js +78 -15
  67. package/dist/commands/endpoint.js.map +1 -1
  68. package/dist/commands/hire.d.ts.map +1 -1
  69. package/dist/commands/hire.js +3 -4
  70. package/dist/commands/hire.js.map +1 -1
  71. package/dist/commands/job.d.ts.map +1 -1
  72. package/dist/commands/job.js +1 -1
  73. package/dist/commands/job.js.map +1 -1
  74. package/dist/commands/openshell.d.ts.map +1 -1
  75. package/dist/commands/openshell.js +79 -45
  76. package/dist/commands/openshell.js.map +1 -1
  77. package/dist/commands/setup.d.ts.map +1 -1
  78. package/dist/commands/setup.js +4 -8
  79. package/dist/commands/setup.js.map +1 -1
  80. package/dist/commands/wallet.d.ts.map +1 -1
  81. package/dist/commands/wallet.js +582 -206
  82. package/dist/commands/wallet.js.map +1 -1
  83. package/dist/commands/workroom.js +4 -4
  84. package/dist/commands/workroom.js.map +1 -1
  85. package/dist/config.d.ts +20 -0
  86. package/dist/config.d.ts.map +1 -1
  87. package/dist/config.js +77 -36
  88. package/dist/config.js.map +1 -1
  89. package/dist/daemon/index.d.ts.map +1 -1
  90. package/dist/daemon/index.js +59 -0
  91. package/dist/daemon/index.js.map +1 -1
  92. package/dist/daemon/passkey-approvals.d.ts +24 -0
  93. package/dist/daemon/passkey-approvals.d.ts.map +1 -0
  94. package/dist/daemon/passkey-approvals.js +31 -0
  95. package/dist/daemon/passkey-approvals.js.map +1 -0
  96. package/dist/endpoint-notify.d.ts +1 -2
  97. package/dist/endpoint-notify.d.ts.map +1 -1
  98. package/dist/endpoint-notify.js +1 -3
  99. package/dist/endpoint-notify.js.map +1 -1
  100. package/dist/index.js +5 -11
  101. package/dist/index.js.map +1 -1
  102. package/dist/openshell-runtime.d.ts.map +1 -1
  103. package/dist/openshell-runtime.js +8 -15
  104. package/dist/openshell-runtime.js.map +1 -1
  105. package/dist/program.d.ts.map +1 -1
  106. package/dist/program.js +2 -0
  107. package/dist/program.js.map +1 -1
  108. package/dist/tui-esm/App.js +136 -0
  109. package/dist/tui-esm/App.js.map +1 -0
  110. package/dist/tui-esm/Footer.js +13 -0
  111. package/dist/tui-esm/Footer.js.map +1 -0
  112. package/dist/tui-esm/Header.js +19 -0
  113. package/dist/tui-esm/Header.js.map +1 -0
  114. package/dist/tui-esm/InputLine.js +121 -0
  115. package/dist/tui-esm/InputLine.js.map +1 -0
  116. package/dist/tui-esm/Viewport.js +39 -0
  117. package/dist/tui-esm/Viewport.js.map +1 -0
  118. package/dist/tui-esm/WalletConnectPairing.js +61 -0
  119. package/dist/tui-esm/WalletConnectPairing.js.map +1 -0
  120. package/dist/tui-esm/command-catalog.js +20 -0
  121. package/dist/tui-esm/command-catalog.js.map +1 -0
  122. package/dist/tui-esm/command-renderers.js +180 -0
  123. package/dist/tui-esm/command-renderers.js.map +1 -0
  124. package/dist/tui-esm/commerce-format.js +19 -0
  125. package/dist/tui-esm/commerce-format.js.map +1 -0
  126. package/dist/tui-esm/components/Button.js +21 -0
  127. package/dist/tui-esm/components/Button.js.map +1 -0
  128. package/dist/tui-esm/components/CeremonyView.js +10 -0
  129. package/dist/tui-esm/components/CeremonyView.js.map +1 -0
  130. package/dist/tui-esm/components/CompletionDropdown.js +23 -0
  131. package/dist/tui-esm/components/CompletionDropdown.js.map +1 -0
  132. package/dist/tui-esm/components/ConfirmPrompt.js +10 -0
  133. package/dist/tui-esm/components/ConfirmPrompt.js.map +1 -0
  134. package/dist/tui-esm/components/CustomTextInput.js +99 -0
  135. package/dist/tui-esm/components/CustomTextInput.js.map +1 -0
  136. package/dist/tui-esm/components/InteractiveTable.js +61 -0
  137. package/dist/tui-esm/components/InteractiveTable.js.map +1 -0
  138. package/dist/tui-esm/components/StepSpinner.js +32 -0
  139. package/dist/tui-esm/components/StepSpinner.js.map +1 -0
  140. package/dist/tui-esm/components/Toast.js +29 -0
  141. package/dist/tui-esm/components/Toast.js.map +1 -0
  142. package/dist/tui-esm/components/commerce/AgreementList.js +25 -0
  143. package/dist/tui-esm/components/commerce/AgreementList.js.map +1 -0
  144. package/dist/tui-esm/components/commerce/ComputeCard.js +9 -0
  145. package/dist/tui-esm/components/commerce/ComputeCard.js.map +1 -0
  146. package/dist/tui-esm/components/commerce/DiscoverList.js +14 -0
  147. package/dist/tui-esm/components/commerce/DiscoverList.js.map +1 -0
  148. package/dist/tui-esm/components/commerce/HireCard.js +11 -0
  149. package/dist/tui-esm/components/commerce/HireCard.js.map +1 -0
  150. package/dist/tui-esm/components/commerce/RoundsList.js +9 -0
  151. package/dist/tui-esm/components/commerce/RoundsList.js.map +1 -0
  152. package/dist/tui-esm/components/commerce/SquadCard.js +10 -0
  153. package/dist/tui-esm/components/commerce/SquadCard.js.map +1 -0
  154. package/dist/tui-esm/components/commerce/StatusCard.js +10 -0
  155. package/dist/tui-esm/components/commerce/StatusCard.js.map +1 -0
  156. package/dist/tui-esm/components/commerce/SubscribeCard.js +10 -0
  157. package/dist/tui-esm/components/commerce/SubscribeCard.js.map +1 -0
  158. package/dist/tui-esm/components/commerce/WorkroomCard.js +10 -0
  159. package/dist/tui-esm/components/commerce/WorkroomCard.js.map +1 -0
  160. package/dist/tui-esm/components/commerce/common.js +71 -0
  161. package/dist/tui-esm/components/commerce/common.js.map +1 -0
  162. package/dist/tui-esm/components/commerce/index.js +27 -0
  163. package/dist/tui-esm/components/commerce/index.js.map +1 -0
  164. package/dist/tui-esm/index.js +55 -0
  165. package/dist/tui-esm/index.js.map +1 -0
  166. package/dist/tui-esm/kernel.js +734 -0
  167. package/dist/tui-esm/kernel.js.map +1 -0
  168. package/dist/tui-esm/render-inline.js +32 -0
  169. package/dist/tui-esm/render-inline.js.map +1 -0
  170. package/dist/tui-esm/useChat.js +91 -0
  171. package/dist/tui-esm/useChat.js.map +1 -0
  172. package/dist/tui-esm/useCommand.js +142 -0
  173. package/dist/tui-esm/useCommand.js.map +1 -0
  174. package/dist/tui-esm/useNotifications.js +17 -0
  175. package/dist/tui-esm/useNotifications.js.map +1 -0
  176. package/dist/tui-esm/useScroll.js +46 -0
  177. package/dist/tui-esm/useScroll.js.map +1 -0
  178. package/dist/tui-esm/useTerminalSize.js +32 -0
  179. package/dist/tui-esm/useTerminalSize.js.map +1 -0
  180. package/hermes/plugins/dist/arc402_hermes-1.0.0-py3-none-any.whl +0 -0
  181. package/hermes/plugins/dist/arc402_hermes-1.0.0.tar.gz +0 -0
  182. package/package.json +2 -2
@@ -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) {
@@ -182,7 +233,6 @@ const ONBOARDING_CATEGORIES = [
182
233
  */
183
234
  async function runWalletOnboardingCeremony(walletAddress, ownerAddress, config, provider, sendTx) {
184
235
  const policyAddress = resolvePolicyEngineAddress(config);
185
- const executeIface = new ethers_1.ethers.Interface(abis_1.ARC402_WALLET_EXECUTE_ABI);
186
236
  const govIface = new ethers_1.ethers.Interface(abis_1.POLICY_ENGINE_GOVERNANCE_ABI);
187
237
  const limitsIface = new ethers_1.ethers.Interface(abis_1.POLICY_ENGINE_LIMITS_ABI);
188
238
  const policyGov = new ethers_1.ethers.Contract(policyAddress, abis_1.POLICY_ENGINE_GOVERNANCE_ABI, provider);
@@ -205,15 +255,8 @@ async function runWalletOnboardingCeremony(walletAddress, ownerAddress, config,
205
255
  if (!alreadyRegistered) {
206
256
  const registerCalldata = govIface.encodeFunctionData("registerWallet", [walletAddress, ownerAddress]);
207
257
  await sendTx({
208
- to: walletAddress,
209
- data: executeIface.encodeFunctionData("executeContractCall", [{
210
- target: policyAddress,
211
- data: registerCalldata,
212
- value: 0n,
213
- minReturnValue: 0n,
214
- maxApprovalAmount: 0n,
215
- approvalToken: ethers_1.ethers.ZeroAddress,
216
- }]),
258
+ to: policyAddress,
259
+ data: registerCalldata,
217
260
  value: "0x0",
218
261
  }, "registerWallet on PolicyEngine");
219
262
  }
@@ -261,11 +304,8 @@ async function runWalletOnboardingCeremony(walletAddress, ownerAddress, config,
261
304
  */
262
305
  async function runCompleteOnboardingCeremony(walletAddress, ownerAddress, config, provider, sendTx) {
263
306
  const policyAddress = resolvePolicyEngineAddress(config);
264
- const agentRegistryAddress = config.agentRegistryV2Address ??
265
- config_1.NETWORK_DEFAULTS[config.network]?.agentRegistryV2Address;
266
- const handshakeAddress = config.handshakeAddress ??
267
- config_1.NETWORK_DEFAULTS[config.network]?.handshakeAddress ??
268
- "0x4F5A38Bb746d7E5d49d8fd26CA6beD141Ec2DDb3";
307
+ const agentRegistryAddress = (0, config_1.getCanonicalAgentRegistryAddress)(config);
308
+ const handshakeAddress = (0, config_1.getCanonicalNetworkAddress)(config, "handshakeAddress");
269
309
  // ── Step 2: Machine Key ────────────────────────────────────────────────────
270
310
  console.log("\n" + colors_1.c.dim("── Step 2: Machine Key ────────────────────────────────────────"));
271
311
  let machineKeyAddress;
@@ -493,11 +533,11 @@ async function runCompleteOnboardingCeremony(walletAddress, ownerAddress, config
493
533
  // Whitelist all contracts an agent will need for the full ARC-402 flow.
494
534
  const protocolContracts = [
495
535
  { address: agentRegistryAddress ?? "", name: "AgentRegistry" },
496
- { address: config.serviceAgreementAddress ?? config_1.NETWORK_DEFAULTS[config.network]?.serviceAgreementAddress ?? "0xC98B402CAB9156da68A87a69E3B4bf167A3CCcF6", name: "ServiceAgreement" },
497
- { address: config.computeAgreementAddress ?? config_1.NETWORK_DEFAULTS[config.network]?.computeAgreementAddress ?? "0xf898A8A2cF9900A588B174d9f96349BBA95e57F3", name: "ComputeAgreement" },
498
- { address: config.subscriptionAgreementAddress ?? config_1.NETWORK_DEFAULTS[config.network]?.subscriptionAgreementAddress ?? "0x809c1D997Eab3531Eb2d01FCD5120Ac786D850D6", name: "SubscriptionAgreement" },
499
- { address: config.handshakeAddress ?? config_1.NETWORK_DEFAULTS[config.network]?.handshakeAddress ?? "0x4F5A38Bb746d7E5d49d8fd26CA6beD141Ec2DDb3", name: "Handshake" },
500
- { 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" },
501
541
  ].filter(c => c.address && c.address !== "");
502
542
  for (const contract of protocolContracts) {
503
543
  const isWhitelisted = await peContract.isContractWhitelisted(walletAddress, contract.address).catch(() => false);
@@ -656,10 +696,10 @@ async function runCompleteOnboardingCeremony(walletAddress, ownerAddress, config
656
696
  function printOpenShellHint() {
657
697
  const r = (0, child_process_1.spawnSync)("which", ["openshell"], { encoding: "utf-8" });
658
698
  if (r.status === 0 && r.stdout.trim()) {
659
- console.log("\nOpenShell detected. Run: arc402 openshell init");
699
+ console.log("\nWorkroom substrate detected. Main path: arc402 workroom init && arc402 workroom start");
660
700
  }
661
701
  else {
662
- console.log("\nOptional: install OpenShell for sandboxed execution: arc402 openshell install");
702
+ console.log("\nOptional internal substrate install: arc402 openshell install");
663
703
  }
664
704
  }
665
705
  function registerWalletCommands(program) {
@@ -1009,7 +1049,7 @@ function registerWalletCommands(program) {
1009
1049
  console.log(colors_1.c.dim(` ◈ --force: cleared wallet config. Factory reset to ${config.walletFactoryAddress} (V6).`));
1010
1050
  }
1011
1051
  if (opts.dryRun) {
1012
- 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)";
1013
1053
  const chainId = config.network === "base-mainnet" ? 8453 : 84532;
1014
1054
  console.log();
1015
1055
  console.log(" " + colors_1.c.dim("── Dry run: wallet deploy ──────────────────────────────────────"));
@@ -1145,6 +1185,7 @@ function registerWalletCommands(program) {
1145
1185
  console.error("Could not find WalletDeployed/WalletCreated event in receipt. Check the transaction on-chain.");
1146
1186
  process.exit(1);
1147
1187
  }
1188
+ await assertAddressHasCode(provider, walletAddress, "Deployed wallet");
1148
1189
  config.walletContractAddress = walletAddress;
1149
1190
  config.ownerAddress = account;
1150
1191
  (0, config_1.saveConfig)(config);
@@ -1346,13 +1387,11 @@ function registerWalletCommands(program) {
1346
1387
  process.exit(1);
1347
1388
  }
1348
1389
  // Resolve protocol contracts to whitelist
1349
- const handshakeAddress = config.handshakeAddress ??
1350
- config_1.NETWORK_DEFAULTS[config.network]?.handshakeAddress ??
1351
- "0x4F5A38Bb746d7E5d49d8fd26CA6beD141Ec2DDb3";
1390
+ const handshakeAddress = config_1.NETWORK_DEFAULTS[config.network]?.handshakeAddress ?? "";
1352
1391
  const protocolContracts = [
1353
- { address: config.serviceAgreementAddress ?? config_1.NETWORK_DEFAULTS[config.network]?.serviceAgreementAddress ?? "", name: "ServiceAgreement" },
1354
- { address: config.computeAgreementAddress ?? config_1.NETWORK_DEFAULTS[config.network]?.computeAgreementAddress ?? "", name: "ComputeAgreement" },
1355
- { 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" },
1356
1395
  { address: handshakeAddress, name: "Handshake" },
1357
1396
  ].filter(pc => pc.address && pc.address !== "");
1358
1397
  // Pre-check on-chain state to count pending txs and decide what to skip
@@ -1426,23 +1465,31 @@ function registerWalletCommands(program) {
1426
1465
  return;
1427
1466
  }
1428
1467
  console.log(" " + colors_1.c.dim(`◈ ${pendingTxCount} transaction(s) pending — your phone will need to approve each.`));
1429
- // Telegram notification before first tx
1430
- const telegramOpts = config.telegramBotToken && config.telegramChatId
1431
- ? { botToken: config.telegramBotToken, chatId: config.telegramChatId, threadId: config.telegramThreadId }
1432
- : undefined;
1433
- // Connect WalletConnect ONCE — reuse session for all txs
1434
- 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;
1435
1490
  const networkName = chainId === 8453 ? "Base" : "Base Sepolia";
1436
1491
  const shortAddr = `${account.slice(0, 6)}...${account.slice(-5)}`;
1437
1492
  console.log("\n" + colors_1.c.success + colors_1.c.white(` Connected: ${shortAddr} on ${networkName}`));
1438
- if (telegramOpts) {
1439
- await (0, telegram_notify_1.sendTelegramMessage)({
1440
- botToken: telegramOpts.botToken,
1441
- chatId: telegramOpts.chatId,
1442
- threadId: telegramOpts.threadId,
1443
- text: `◈ arc402 wallet onboard: ${pendingTxCount} txs pending for ${shortAddr}`,
1444
- });
1445
- }
1446
1493
  const sendTx = async (call, description) => {
1447
1494
  const txSpin = (0, spinner_1.startSpinner)(description);
1448
1495
  const hash = await (0, walletconnect_1.sendTransactionWithSession)(client, session, account, chainId, call);
@@ -1450,24 +1497,22 @@ function registerWalletCommands(program) {
1450
1497
  txSpin.succeed(description);
1451
1498
  return hash;
1452
1499
  };
1453
- const executeIface = new ethers_1.ethers.Interface(abis_1.ARC402_WALLET_EXECUTE_ABI);
1454
1500
  const govIface = new ethers_1.ethers.Interface(abis_1.POLICY_ENGINE_GOVERNANCE_ABI);
1455
1501
  const limitsIface = new ethers_1.ethers.Interface(abis_1.POLICY_ENGINE_LIMITS_ABI);
1456
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
+ }
1457
1509
  console.log("\n" + colors_1.c.dim("── Onboarding ceremony ────────────────────────────────────────"));
1458
1510
  // 1. registerWallet
1459
1511
  if (needsRegister) {
1460
- const registerCalldata = govIface.encodeFunctionData("registerWallet", [walletAddress, account]);
1512
+ const registerCalldata = govIface.encodeFunctionData("registerWallet", [walletAddress, walletOwner]);
1461
1513
  await sendTx({
1462
- to: walletAddress,
1463
- data: executeIface.encodeFunctionData("executeContractCall", [{
1464
- target: policyAddress,
1465
- data: registerCalldata,
1466
- value: 0n,
1467
- minReturnValue: 0n,
1468
- maxApprovalAmount: 0n,
1469
- approvalToken: ethers_1.ethers.ZeroAddress,
1470
- }]),
1514
+ to: policyAddress,
1515
+ data: registerCalldata,
1471
1516
  value: "0x0",
1472
1517
  }, "registerWallet on PolicyEngine");
1473
1518
  }
@@ -1592,15 +1637,33 @@ function registerWalletCommands(program) {
1592
1637
  process.exit(1);
1593
1638
  }
1594
1639
  const provider = new ethers_1.ethers.JsonRpcProvider(config.rpcUrl);
1595
- 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, {
1596
1663
  to: policyAddress,
1597
1664
  data: policyInterface.encodeFunctionData("setCategoryLimitFor", [walletAddr, opts.category, amount]),
1598
1665
  value: "0x0",
1599
- }), `Approve spend limit: ${opts.category} → ${opts.amount} ETH`, config.telegramBotToken && config.telegramChatId ? {
1600
- botToken: config.telegramBotToken,
1601
- chatId: config.telegramChatId,
1602
- threadId: config.telegramThreadId,
1603
- } : undefined, config);
1666
+ });
1604
1667
  console.log(`\nTransaction submitted: ${txHash}`);
1605
1668
  await provider.waitForTransaction(txHash);
1606
1669
  console.log(`Spend limit for ${opts.category} set to ${opts.amount} ETH`);
@@ -1643,15 +1706,33 @@ function registerWalletCommands(program) {
1643
1706
  const policyInterface = new ethers_1.ethers.Interface(abis_1.POLICY_ENGINE_LIMITS_ABI);
1644
1707
  if (config.walletConnectProjectId) {
1645
1708
  const provider = new ethers_1.ethers.JsonRpcProvider(config.rpcUrl);
1646
- 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, {
1647
1732
  to: policyAddress,
1648
1733
  data: policyInterface.encodeFunctionData("setDailyLimitFor", [walletAddr, opts.category, amount]),
1649
1734
  value: "0x0",
1650
- }), `Approve daily limit: ${opts.category} → ${opts.amount} ETH`, config.telegramBotToken && config.telegramChatId ? {
1651
- botToken: config.telegramBotToken,
1652
- chatId: config.telegramChatId,
1653
- threadId: config.telegramThreadId,
1654
- } : undefined, config);
1735
+ });
1655
1736
  await provider.waitForTransaction(txHash);
1656
1737
  console.log(`Daily limit for ${opts.category} set to ${opts.amount} ETH (12/24h rolling window)`);
1657
1738
  }
@@ -1692,24 +1773,30 @@ function registerWalletCommands(program) {
1692
1773
  ];
1693
1774
  const policyInterface = new ethers_1.ethers.Interface(abis_1.POLICY_ENGINE_LIMITS_ABI);
1694
1775
  const provider = new ethers_1.ethers.JsonRpcProvider(config.rpcUrl);
1695
- const telegramOpts = config.telegramBotToken && config.telegramChatId ? {
1696
- botToken: config.telegramBotToken,
1697
- chatId: config.telegramChatId,
1698
- threadId: config.telegramThreadId,
1699
- } : undefined;
1700
1776
  console.log(colors_1.c.white("\n◈ Spend limit init — 1 connection, 5 approvals\n"));
1701
1777
  console.log(colors_1.c.dim(" Connect once — MetaMask will prompt 5 times in sequence."));
1702
- // Connect once, reuse session for all 5 txs
1703
- 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;
1704
1796
  for (const cat of categories) {
1705
1797
  const amount = ethers_1.ethers.parseEther(cat.amount);
1706
1798
  const data = policyInterface.encodeFunctionData("setCategoryLimitFor", [walletAddr, cat.name, amount]);
1707
1799
  console.log(colors_1.c.dim(`\n Setting ${cat.name} → ${cat.amount} ETH...`));
1708
- // Send Telegram notification for each
1709
- if (telegramOpts) {
1710
- const { sendTelegramMessage: sendTg } = await Promise.resolve().then(() => __importStar(require("../telegram-notify.js")));
1711
- await sendTg({ ...telegramOpts, text: `Approve spend limit: ${cat.name} → ${cat.amount} ETH` });
1712
- }
1713
1800
  const txHash = await (0, walletconnect_1.sendTransactionWithSession)(client, session, account, chainId, {
1714
1801
  to: policyAddress,
1715
1802
  data,
@@ -1754,14 +1841,33 @@ function registerWalletCommands(program) {
1754
1841
  console.log(`\nWallet: ${config.walletContractAddress}`);
1755
1842
  console.log(`Current policy: ${currentPolicy}`);
1756
1843
  console.log(`New policy: ${policyIdHex}`);
1757
- const telegramOpts = config.telegramBotToken && config.telegramChatId
1758
- ? { botToken: config.telegramBotToken, chatId: config.telegramChatId, threadId: config.telegramThreadId }
1759
- : undefined;
1760
- 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, {
1761
1867
  to: config.walletContractAddress,
1762
1868
  data: ownerInterface.encodeFunctionData("updatePolicy", [policyIdHex]),
1763
1869
  value: "0x0",
1764
- }), `Approve: update policy to ${policyIdHex}`, telegramOpts, config);
1870
+ });
1765
1871
  await provider.waitForTransaction(txHash);
1766
1872
  console.log("\n" + colors_1.c.success + colors_1.c.white(" Active policy updated"));
1767
1873
  console.log(" " + colors_1.c.dim("Tx:") + " " + colors_1.c.white(txHash));
@@ -1832,10 +1938,35 @@ function registerWalletCommands(program) {
1832
1938
  const chainId = config.network === "base-mainnet" ? 8453 : 84532;
1833
1939
  const provider = new ethers_1.ethers.JsonRpcProvider(config.rpcUrl);
1834
1940
  const walletInterface = new ethers_1.ethers.Interface(abis_1.ARC402_WALLET_GUARDIAN_ABI);
1835
- const telegramOpts = config.telegramBotToken && config.telegramChatId
1836
- ? { botToken: config.telegramBotToken, chatId: config.telegramChatId, threadId: config.telegramThreadId }
1837
- : undefined;
1838
- 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;
1839
1970
  const networkName = chainId === 8453 ? "Base" : "Base Sepolia";
1840
1971
  const shortAddr = `${account.slice(0, 6)}...${account.slice(-5)}`;
1841
1972
  console.log("\n" + colors_1.c.success + colors_1.c.white(` Connected: ${shortAddr} on ${networkName}`));
@@ -1893,10 +2024,37 @@ function registerWalletCommands(program) {
1893
2024
  const walletInterface = new ethers_1.ethers.Interface(abis_1.ARC402_WALLET_GUARDIAN_ABI);
1894
2025
  console.log(`\nGuardian address: ${guardianWallet.address}`);
1895
2026
  console.log(`Wallet contract: ${config.walletContractAddress}`);
1896
- const telegramOpts = config.telegramBotToken && config.telegramChatId
1897
- ? { botToken: config.telegramBotToken, chatId: config.telegramChatId, threadId: config.telegramThreadId }
1898
- : undefined;
1899
- 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;
1900
2058
  const networkName = chainId === 8453 ? "Base" : "Base Sepolia";
1901
2059
  const shortAddr = `${account.slice(0, 6)}...${account.slice(-5)}`;
1902
2060
  console.log("\n" + colors_1.c.success + colors_1.c.white(` Connected: ${shortAddr} on ${networkName}`));
@@ -1992,10 +2150,36 @@ function registerWalletCommands(program) {
1992
2150
  console.log(` Value: 0x0`);
1993
2151
  return;
1994
2152
  }
1995
- const telegramOpts = config.telegramBotToken && config.telegramChatId
1996
- ? { botToken: config.telegramBotToken, chatId: config.telegramChatId, threadId: config.telegramThreadId }
1997
- : undefined;
1998
- 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;
1999
2183
  const networkName = chainId === 8453 ? "Base" : "Base Sepolia";
2000
2184
  const shortAddr = `${account.slice(0, 6)}...${account.slice(-5)}`;
2001
2185
  console.log("\n" + colors_1.c.success + colors_1.c.white(` Connected: ${shortAddr} on ${networkName}`));
@@ -2061,15 +2245,39 @@ function registerWalletCommands(program) {
2061
2245
  }
2062
2246
  console.log(`Pending registry: ${pendingRegistry}`);
2063
2247
  console.log("Timelock elapsed — proceeding with executeRegistryUpdate()");
2064
- const telegramOpts = config.telegramBotToken && config.telegramChatId
2065
- ? { botToken: config.telegramBotToken, chatId: config.telegramChatId, threadId: config.telegramThreadId }
2066
- : undefined;
2067
2248
  const walletInterface = new ethers_1.ethers.Interface(abis_1.ARC402_WALLET_REGISTRY_ABI);
2068
- const { txHash } = await (0, walletconnect_1.requestPhoneWalletSignature)(config.walletConnectProjectId, chainId, () => ({
2249
+ const txData = {
2069
2250
  to: config.walletContractAddress,
2070
2251
  data: walletInterface.encodeFunctionData("executeRegistryUpdate", []),
2071
2252
  value: "0x0",
2072
- }), "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);
2073
2281
  // Wait for tx to confirm, then read back the active registry (J6-02)
2074
2282
  await provider.waitForTransaction(txHash);
2075
2283
  let confirmedRegistry = pendingRegistry;
@@ -2136,18 +2344,33 @@ function registerWalletCommands(program) {
2136
2344
  console.log(`\nWallet: ${config.walletContractAddress}`);
2137
2345
  console.log(`PolicyEngine: ${policyAddress}`);
2138
2346
  console.log(`Whitelisting: ${checksumTarget}`);
2139
- const telegramOpts = config.telegramBotToken && config.telegramChatId
2140
- ? { botToken: config.telegramBotToken, chatId: config.telegramChatId, threadId: config.telegramThreadId }
2141
- : undefined;
2142
2347
  const policyIface = new ethers_1.ethers.Interface(peAbi);
2143
- const { txHash } = await (0, walletconnect_1.requestPhoneWalletSignature)(config.walletConnectProjectId, chainId, () => ({
2348
+ const txData = {
2144
2349
  to: policyAddress,
2145
2350
  data: policyIface.encodeFunctionData("whitelistContract", [
2146
2351
  config.walletContractAddress,
2147
2352
  checksumTarget,
2148
2353
  ]),
2149
2354
  value: "0x0",
2150
- }), `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);
2151
2374
  await provider.waitForTransaction(txHash);
2152
2375
  if (opts.json) {
2153
2376
  console.log(JSON.stringify({ ok: true, txHash, wallet: config.walletContractAddress, target: checksumTarget }));
@@ -2194,14 +2417,33 @@ function registerWalletCommands(program) {
2194
2417
  console.log(`\nWallet: ${config.walletContractAddress}`);
2195
2418
  console.log(`Current interceptor: ${currentInterceptor}`);
2196
2419
  console.log(`New interceptor: ${checksumAddress}`);
2197
- const telegramOpts = config.telegramBotToken && config.telegramChatId
2198
- ? { botToken: config.telegramBotToken, chatId: config.telegramChatId, threadId: config.telegramThreadId }
2199
- : undefined;
2200
- const { txHash } = await (0, walletconnect_1.requestPhoneWalletSignature)(config.walletConnectProjectId, chainId, () => ({
2420
+ const txData = {
2201
2421
  to: config.walletContractAddress,
2202
2422
  data: ownerInterface.encodeFunctionData("setAuthorizedInterceptor", [checksumAddress]),
2203
2423
  value: "0x0",
2204
- }), `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);
2205
2447
  await provider.waitForTransaction(txHash);
2206
2448
  console.log("\n" + colors_1.c.success + colors_1.c.white(" X402 interceptor updated"));
2207
2449
  console.log(" " + colors_1.c.dim("Tx:") + " " + colors_1.c.white(txHash));
@@ -2245,14 +2487,36 @@ function registerWalletCommands(program) {
2245
2487
  console.log(`\nWallet: ${config.walletContractAddress}`);
2246
2488
  console.log(`Current limit: ${currentLimit}`);
2247
2489
  console.log(`New limit: ${limitEth} ETH (max ETH per rolling window)`);
2248
- const telegramOpts = config.telegramBotToken && config.telegramChatId
2249
- ? { botToken: config.telegramBotToken, chatId: config.telegramChatId, threadId: config.telegramThreadId }
2250
- : undefined;
2251
- const { txHash } = await (0, walletconnect_1.requestPhoneWalletSignature)(config.walletConnectProjectId, chainId, () => ({
2490
+ const txData = {
2252
2491
  to: config.walletContractAddress,
2253
2492
  data: ownerInterface.encodeFunctionData("setVelocityLimit", [limitWei]),
2254
2493
  value: "0x0",
2255
- }), `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);
2256
2520
  await provider.waitForTransaction(txHash);
2257
2521
  console.log("\n" + colors_1.c.success + colors_1.c.white(" Velocity limit updated"));
2258
2522
  console.log(" " + colors_1.c.dim("Tx:") + " " + colors_1.c.white(txHash));
@@ -2260,9 +2524,8 @@ function registerWalletCommands(program) {
2260
2524
  });
2261
2525
  // ─── register-policy ───────────────────────────────────────────────────────
2262
2526
  //
2263
- // Calls registerWallet(walletAddress, ownerAddress) on PolicyEngine via
2264
- // executeContractCall on the ARC402Wallet. PolicyEngine requires msg.sender == wallet,
2265
- // 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.
2266
2529
  wallet.command("register-policy")
2267
2530
  .description("Register this wallet on PolicyEngine (required before spend limits can be set)")
2268
2531
  .option("--hardware", "Hardware wallet mode: show raw wc: URI only")
@@ -2299,33 +2562,45 @@ function registerWalletCommands(program) {
2299
2562
  }
2300
2563
  }
2301
2564
  }
2565
+ const resolvedOwnerAddress = ownerAddress;
2302
2566
  // Encode registerWallet(wallet, owner) calldata — called on PolicyEngine
2303
2567
  const policyInterface = new ethers_1.ethers.Interface([
2304
2568
  "function registerWallet(address wallet, address owner) external",
2305
2569
  ]);
2306
2570
  const registerCalldata = policyInterface.encodeFunctionData("registerWallet", [
2307
2571
  config.walletContractAddress,
2308
- ownerAddress,
2572
+ resolvedOwnerAddress,
2309
2573
  ]);
2310
- const executeInterface = new ethers_1.ethers.Interface(abis_1.ARC402_WALLET_EXECUTE_ABI);
2311
2574
  console.log(`\nWallet: ${config.walletContractAddress}`);
2312
2575
  console.log(`PolicyEngine: ${policyAddress}`);
2313
- console.log(`Owner: ${ownerAddress}`);
2314
- const telegramOpts = config.telegramBotToken && config.telegramChatId
2315
- ? { botToken: config.telegramBotToken, chatId: config.telegramChatId, threadId: config.telegramThreadId }
2316
- : undefined;
2317
- const { txHash } = await (0, walletconnect_1.requestPhoneWalletSignature)(config.walletConnectProjectId, chainId, () => ({
2318
- to: config.walletContractAddress,
2319
- data: executeInterface.encodeFunctionData("executeContractCall", [{
2320
- target: policyAddress,
2321
- data: registerCalldata,
2322
- value: 0n,
2323
- minReturnValue: 0n,
2324
- maxApprovalAmount: 0n,
2325
- approvalToken: ethers_1.ethers.ZeroAddress,
2326
- }]),
2576
+ console.log(`Owner: ${resolvedOwnerAddress}`);
2577
+ const txData = {
2578
+ to: policyAddress,
2579
+ data: registerCalldata,
2327
2580
  value: "0x0",
2328
- }), `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);
2329
2604
  await provider.waitForTransaction(txHash);
2330
2605
  console.log("\n" + colors_1.c.success + colors_1.c.white(" Wallet registered on PolicyEngine"));
2331
2606
  console.log(" " + colors_1.c.dim("Tx:") + " " + colors_1.c.white(txHash));
@@ -2374,15 +2649,33 @@ function registerWalletCommands(program) {
2374
2649
  console.log(` Pending address: ${pendingRegistry}`);
2375
2650
  console.log(` Timelock: ${timelockStatus}`);
2376
2651
  console.log(`\nCancelling pending registry upgrade to: ${pendingRegistry}`);
2377
- const telegramOpts = config.telegramBotToken && config.telegramChatId
2378
- ? { botToken: config.telegramBotToken, chatId: config.telegramChatId, threadId: config.telegramThreadId }
2379
- : undefined;
2380
2652
  const walletInterface = new ethers_1.ethers.Interface(abis_1.ARC402_WALLET_REGISTRY_ABI);
2381
- const { txHash } = await (0, walletconnect_1.requestPhoneWalletSignature)(config.walletConnectProjectId, chainId, () => ({
2653
+ const txData = {
2382
2654
  to: config.walletContractAddress,
2383
2655
  data: walletInterface.encodeFunctionData("cancelRegistryUpdate", []),
2384
2656
  value: "0x0",
2385
- }), "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);
2386
2679
  console.log("\n" + colors_1.c.success + colors_1.c.white(" Registry upgrade cancelled"));
2387
2680
  console.log(" " + colors_1.c.dim("Tx:") + " " + colors_1.c.white(txHash));
2388
2681
  });
@@ -2523,11 +2816,6 @@ function registerWalletCommands(program) {
2523
2816
  return;
2524
2817
  }
2525
2818
  // ── Step 6: connect WalletConnect once, send all transactions ─────────
2526
- const telegramOpts = config.telegramBotToken && config.telegramChatId
2527
- ? { botToken: config.telegramBotToken, chatId: config.telegramChatId, threadId: config.telegramThreadId }
2528
- : undefined;
2529
- console.log("\nConnecting wallet...");
2530
- const { client, session, account } = await (0, walletconnect_1.connectPhoneWallet)(config.walletConnectProjectId, chainId, config, { telegramOpts, prompt: "Approve governance setup transactions on ARC402Wallet" });
2531
2819
  const provider = new ethers_1.ethers.JsonRpcProvider(config.rpcUrl);
2532
2820
  const ownerInterface = new ethers_1.ethers.Interface(abis_1.ARC402_WALLET_OWNER_ABI);
2533
2821
  const guardianInterface = new ethers_1.ethers.Interface(abis_1.ARC402_WALLET_GUARDIAN_ABI);
@@ -2549,6 +2837,33 @@ function registerWalletCommands(program) {
2549
2837
  govAlreadyDefiEnabled = await policyGovContract.defiAccessEnabled(config.walletContractAddress);
2550
2838
  }
2551
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
+ });
2552
2867
  if (!govAlreadyRegistered) {
2553
2868
  const registerCalldata = govInterface.encodeFunctionData("registerWallet", [config.walletContractAddress, account]);
2554
2869
  calls.push({
@@ -2683,19 +2998,37 @@ function registerWalletCommands(program) {
2683
2998
  }
2684
2999
  console.log(`\nWallet: ${config.walletContractAddress}`);
2685
3000
  console.log(`Machine key: ${checksumKey}`);
2686
- const telegramOpts = config.telegramBotToken && config.telegramChatId
2687
- ? { botToken: config.telegramBotToken, chatId: config.telegramChatId, threadId: config.telegramThreadId }
2688
- : undefined;
2689
3001
  const walletInterface = new ethers_1.ethers.Interface(machineKeyAbi);
2690
3002
  const txData = {
2691
3003
  to: config.walletContractAddress,
2692
3004
  data: walletInterface.encodeFunctionData("authorizeMachineKey", [checksumKey]),
2693
3005
  value: "0x0",
2694
3006
  };
2695
- const { client, session, account } = await (0, walletconnect_1.connectPhoneWallet)(config.walletConnectProjectId, chainId, config, {
2696
- telegramOpts,
2697
- 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" },
2698
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;
2699
3032
  console.log("\n" + colors_1.c.success + colors_1.c.white(` Connected: ${account}`));
2700
3033
  console.log(colors_1.c.dim("Sending authorizeMachineKey transaction..."));
2701
3034
  const hash = await (0, walletconnect_1.sendTransactionWithSession)(client, session, account, chainId, txData);
@@ -2756,18 +3089,40 @@ function registerWalletCommands(program) {
2756
3089
  }
2757
3090
  console.log(`\nWallet: ${config.walletContractAddress}`);
2758
3091
  console.log(`Revoking: ${checksumKey}`);
2759
- const telegramOpts = config.telegramBotToken && config.telegramChatId
2760
- ? { botToken: config.telegramBotToken, chatId: config.telegramChatId, threadId: config.telegramThreadId }
2761
- : undefined;
2762
3092
  const walletInterface = new ethers_1.ethers.Interface(abis_1.ARC402_WALLET_MACHINE_KEY_ABI);
2763
- const { client, session, account } = await (0, walletconnect_1.connectPhoneWallet)(config.walletConnectProjectId, chainId, config, { telegramOpts, prompt: `Revoke machine key ${checksumKey} on ARC402Wallet` });
2764
- console.log("\n" + colors_1.c.success + colors_1.c.white(` Connected: ${account}`));
2765
- console.log(colors_1.c.dim("Sending revokeMachineKey transaction..."));
2766
- const hash = await (0, walletconnect_1.sendTransactionWithSession)(client, session, account, chainId, {
3093
+ const txData = {
2767
3094
  to: config.walletContractAddress,
2768
3095
  data: walletInterface.encodeFunctionData("revokeMachineKey", [checksumKey]),
2769
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" },
2770
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);
2771
3126
  console.log("\n" + colors_1.c.dim("Transaction submitted:") + " " + colors_1.c.white(hash));
2772
3127
  console.log(colors_1.c.dim("Waiting for confirmation..."));
2773
3128
  const receipt = await provider.waitForTransaction(hash, 1, 60000);
@@ -3266,7 +3621,7 @@ function registerWalletCommands(program) {
3266
3621
  console.error("walletConnectProjectId not set. Run `arc402 config set walletConnectProjectId <id>`.");
3267
3622
  process.exit(1);
3268
3623
  }
3269
- const migrationRegistryAddress = config.migrationRegistryAddress ?? "0x4821D8A590eD4DbEf114fCA3C2d9311e81D576DF";
3624
+ const migrationRegistryAddress = (0, config_1.getCanonicalNetworkAddress)(config, "migrationRegistryAddress");
3270
3625
  let oldWallet;
3271
3626
  let newWallet;
3272
3627
  try {
@@ -3292,10 +3647,20 @@ function registerWalletCommands(program) {
3292
3647
  "function registerMigration(address oldWallet, address newWallet) external"
3293
3648
  ]);
3294
3649
  const callData = mrIface.encodeFunctionData("registerMigration", [oldWallet, newWallet]);
3295
- const telegramOpts = config.telegramBotToken && config.telegramChatId
3296
- ? { botToken: config.telegramBotToken, chatId: config.telegramChatId, threadId: config.telegramThreadId }
3297
- : undefined;
3298
- 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);
3299
3664
  await provider.waitForTransaction(txHash);
3300
3665
  if (opts.json) {
3301
3666
  console.log(JSON.stringify({ ok: true, txHash, from: oldWallet, to: newWallet }));
@@ -3519,18 +3884,29 @@ function registerWalletCommands(program) {
3519
3884
  console.log(`\nWallet: ${config.walletContractAddress}`);
3520
3885
  console.log(`pubKeyX: ${pubKeyX}`);
3521
3886
  console.log(`pubKeyY: ${pubKeyY}`);
3522
- const telegramOpts = config.telegramBotToken && config.telegramChatId
3523
- ? { botToken: config.telegramBotToken, chatId: config.telegramChatId, threadId: config.telegramThreadId }
3524
- : undefined;
3525
3887
  const txData = {
3526
3888
  to: config.walletContractAddress,
3527
3889
  data: walletInterface.encodeFunctionData("setPasskey", [pubKeyX, pubKeyY]),
3528
3890
  value: "0x0",
3529
3891
  };
3530
- const { client, session, account } = await (0, walletconnect_1.connectPhoneWallet)(config.walletConnectProjectId, chainId, config, {
3531
- telegramOpts,
3532
- prompt: `Activate passkey (Face ID) on ARC402Wallet — enables P256 governance signing`,
3533
- });
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;
3534
3910
  console.log("\n" + colors_1.c.success + colors_1.c.white(` Connected: ${account}`));
3535
3911
  console.log(colors_1.c.dim("Sending setPasskey transaction..."));
3536
3912
  const hash = await (0, walletconnect_1.sendTransactionWithSession)(client, session, account, chainId, txData);