arc402-cli 1.3.15 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2705,187 +2705,6 @@ function registerWalletCommands(program) {
2705
2705
  console.log(" " + colors_1.c.dim("Wallet:") + " " + colors_1.c.white(walletAddr));
2706
2706
  }
2707
2707
  });
2708
- // ─── force-close-context ───────────────────────────────────────────────────
2709
- //
2710
- // Workaround for wallets where wallet.closeContext() reverts because the deployed
2711
- // PolicyEngine doesn't have closeContext(). Instead of calling wallet.closeContext()
2712
- // (which internally calls pe.closeContext() and reverts), this command:
2713
- // 1. Calls PE.closeContext(wallet, contextId) directly via owner WalletConnect
2714
- // (PE accepts calls from wallet owner via walletOwners mapping)
2715
- // 2. Then calls wallet.closeContext() — which should now succeed since PE call
2716
- // was already made, but if it still reverts due to PE not having the function,
2717
- // we directly zero the context via a raw storage slot write (not possible on mainnet)
2718
- //
2719
- // Fallback: call PE.closeContext directly as owner EOA (not via wallet) — PE accepts
2720
- // msg.sender == walletOwners[wallet].
2721
- wallet.command("force-close-context")
2722
- .description("Force-close stale context when wallet.closeContext() reverts (PE missing closeContext). Owner signs via WalletConnect.")
2723
- .option("--json")
2724
- .action(async (opts) => {
2725
- const config = (0, config_1.loadConfig)();
2726
- const walletAddr = config.walletContractAddress;
2727
- if (!walletAddr) {
2728
- console.error("walletContractAddress not set in config.");
2729
- process.exit(1);
2730
- }
2731
- if (!config.walletConnectProjectId) {
2732
- console.error("walletConnectProjectId not set in config.");
2733
- process.exit(1);
2734
- }
2735
- const provider = new ethers_1.ethers.JsonRpcProvider(config.rpcUrl);
2736
- // Check context is actually open
2737
- const walletAbi = ["function contextOpen() view returns (bool)", "function activeContextId() view returns (bytes32)"];
2738
- const walletReadOnly = new ethers_1.ethers.Contract(walletAddr, walletAbi, provider);
2739
- const isOpen = await walletReadOnly.contextOpen();
2740
- if (!isOpen) {
2741
- console.log("Context is already closed — nothing to do.");
2742
- return;
2743
- }
2744
- const contextId = await walletReadOnly.activeContextId();
2745
- console.log(`Open context found: ${contextId}`);
2746
- // Step 1: Call PE.closeContext(wallet, contextId) directly as owner via WalletConnect
2747
- // PE accepts: msg.sender == wallet OR msg.sender == walletOwners[wallet]
2748
- const policyEngineAddress = config.policyEngineAddress ?? "0xAA5Ef3489C929bFB3BFf5D5FE15aa62d3763c847";
2749
- const chainId = config.network === "base-mainnet" ? 8453 : 84532;
2750
- const peIface = new ethers_1.ethers.Interface(["function closeContext(address wallet, bytes32 contextId) external"]);
2751
- const peCalldata = peIface.encodeFunctionData("closeContext", [walletAddr, contextId]);
2752
- const telegramOpts = config.telegramBotToken && config.telegramChatId
2753
- ? { botToken: config.telegramBotToken, chatId: config.telegramChatId, threadId: config.telegramThreadId }
2754
- : undefined;
2755
- console.log("Step 1: Calling PE.closeContext as owner (WalletConnect)...");
2756
- let peTxHash;
2757
- try {
2758
- const result = await (0, walletconnect_1.requestPhoneWalletSignature)(config.walletConnectProjectId, chainId, () => ({ to: policyEngineAddress, data: peCalldata, value: "0x0" }), `Force-close wallet context — clear stale open context on ${walletAddr.slice(0, 10)}...`, telegramOpts, config);
2759
- peTxHash = result.txHash;
2760
- await provider.waitForTransaction(peTxHash);
2761
- console.log(" " + colors_1.c.success + colors_1.c.white(" PE.closeContext called"));
2762
- }
2763
- catch (e) {
2764
- console.log(" " + colors_1.c.warning + colors_1.c.white(` PE.closeContext failed (may not have this function): ${e instanceof Error ? e.message.slice(0, 80) : String(e)}`));
2765
- console.log(colors_1.c.dim(" Proceeding to wallet.closeContext anyway..."));
2766
- }
2767
- // Step 2: Call wallet.closeContext via executeContractCall (owner WalletConnect)
2768
- console.log("Step 2: Calling wallet.closeContext via executeContractCall (WalletConnect)...");
2769
- const walletIface = new ethers_1.ethers.Interface([
2770
- "function executeContractCall((address target, bytes data, uint256 value, uint256 minReturnValue, uint256 maxApprovalAmount, address approvalToken)) external returns (bytes)",
2771
- "function closeContext() external",
2772
- ]);
2773
- // Encode closeContext() call on the wallet itself — but wallet blocks self-targeting
2774
- // Instead, we already called PE directly. Now just call wallet.closeContext() directly
2775
- // from the owner EOA via WalletConnect (not via executeContractCall since target==wallet is blocked)
2776
- const closeCtxCalldata = walletIface.encodeFunctionData("closeContext", []);
2777
- try {
2778
- const result2 = await (0, walletconnect_1.requestPhoneWalletSignature)(config.walletConnectProjectId, chainId, () => ({ to: walletAddr, data: closeCtxCalldata, value: "0x0" }), `Close wallet context — final step`, telegramOpts, config);
2779
- await provider.waitForTransaction(result2.txHash);
2780
- console.log(" " + colors_1.c.success + colors_1.c.white(" Context closed"));
2781
- if (opts.json)
2782
- console.log(JSON.stringify({ ok: true, peTxHash, closeTxHash: result2.txHash, contextId }));
2783
- }
2784
- catch (e) {
2785
- console.error(" " + colors_1.c.failure + colors_1.c.white(` wallet.closeContext failed: ${e instanceof Error ? e.message.slice(0, 100) : String(e)}`));
2786
- console.error(colors_1.c.dim(" The stale context is still open. PolicyEngine redeployment required for full fix."));
2787
- if (opts.json)
2788
- console.log(JSON.stringify({ ok: false, peTxHash, contextId, error: String(e) }));
2789
- process.exit(1);
2790
- }
2791
- });
2792
- // ─── re-register-pe ────────────────────────────────────────────────────────
2793
- //
2794
- // After a PE redeploy, existing wallets need to re-register on the new PE.
2795
- // PE.registerWallet() requires msg.sender == wallet, so it goes via executeContractCall.
2796
- // Also sets the general spend limit (required for drain flow).
2797
- // Two WalletConnect approvals: registerWallet + setCategoryLimitFor.
2798
- wallet.command("re-register-pe")
2799
- .description("Re-register wallet on new PolicyEngine after PE redeploy (two WalletConnect approvals)")
2800
- .option("--spend-limit <eth>", "Per-tx spend limit for 'general' category in ETH", "0.01")
2801
- .option("--json")
2802
- .action(async (opts) => {
2803
- const config = (0, config_1.loadConfig)();
2804
- const walletAddr = config.walletContractAddress;
2805
- if (!walletAddr) {
2806
- console.error("walletContractAddress not set in config.");
2807
- process.exit(1);
2808
- }
2809
- if (!config.walletConnectProjectId) {
2810
- console.error("walletConnectProjectId not set in config.");
2811
- process.exit(1);
2812
- }
2813
- const provider = new ethers_1.ethers.JsonRpcProvider(config.rpcUrl);
2814
- const chainId = config.network === "base-mainnet" ? 8453 : 84532;
2815
- // Resolve current PE from registry
2816
- const registryAbi = ["function getContracts() external view returns (tuple(address policyEngine, address trustRegistry, address intentAttestation, address serviceAgreement, address sessionChannels, address agentRegistry, address reputationOracle, address settlementCoordinator, address vouchingRegistry, address migrationRegistry))"];
2817
- const registryAddress = "0xcc0D8731ccCf6CFfF4e66F6d68cA86330Ea8B622";
2818
- const registry = new ethers_1.ethers.Contract(registryAddress, registryAbi, provider);
2819
- let policyEngineAddress;
2820
- try {
2821
- const contracts = await registry.getContracts();
2822
- policyEngineAddress = contracts.policyEngine;
2823
- }
2824
- catch {
2825
- policyEngineAddress = config.policyEngineAddress ?? "0x0743ab6a7280b416D3b75c7e5457390906312139";
2826
- }
2827
- console.log(`Policy Engine: ${policyEngineAddress}`);
2828
- // Check if already registered
2829
- const peAbi = [
2830
- "function walletOwners(address) external view returns (address)",
2831
- "function registerWallet(address wallet, address owner) external",
2832
- "function setCategoryLimitFor(address wallet, string category, uint256 limitPerTx) external",
2833
- ];
2834
- const peReadOnly = new ethers_1.ethers.Contract(policyEngineAddress, peAbi, provider);
2835
- const registeredOwner = await peReadOnly.walletOwners(walletAddr);
2836
- const ownerAddress = config.ownerAddress ?? registeredOwner;
2837
- if (!ownerAddress || ownerAddress === ethers_1.ethers.ZeroAddress) {
2838
- console.error("ownerAddress not set in config. Run `arc402 config set ownerAddress <address>`.");
2839
- process.exit(1);
2840
- }
2841
- const walletIface = new ethers_1.ethers.Interface([
2842
- "function executeContractCall((address target, bytes data, uint256 value, uint256 minReturnValue, uint256 maxApprovalAmount, address approvalToken)) external returns (bytes)",
2843
- ]);
2844
- const peIface = new ethers_1.ethers.Interface(peAbi);
2845
- const telegramOpts = config.telegramBotToken && config.telegramChatId
2846
- ? { botToken: config.telegramBotToken, chatId: config.telegramChatId, threadId: config.telegramThreadId }
2847
- : undefined;
2848
- const spendLimitWei = ethers_1.ethers.parseEther(opts.spendLimit);
2849
- // Step 1: registerWallet (only if not already registered)
2850
- if (registeredOwner === ethers_1.ethers.ZeroAddress) {
2851
- console.log("Step 1: Registering wallet on new PE...");
2852
- const innerRegister = peIface.encodeFunctionData("registerWallet", [walletAddr, ownerAddress]);
2853
- const callDataRegister = walletIface.encodeFunctionData("executeContractCall", [{
2854
- target: policyEngineAddress,
2855
- data: innerRegister,
2856
- value: 0n,
2857
- minReturnValue: 0n,
2858
- maxApprovalAmount: 0n,
2859
- approvalToken: ethers_1.ethers.ZeroAddress,
2860
- }]);
2861
- const { txHash: tx1 } = await (0, walletconnect_1.requestPhoneWalletSignature)(config.walletConnectProjectId, chainId, () => ({ to: walletAddr, data: callDataRegister, value: "0x0" }), `Register wallet on new PolicyEngine`, telegramOpts, config);
2862
- await provider.waitForTransaction(tx1);
2863
- console.log(" " + colors_1.c.success + colors_1.c.white(" Wallet registered on new PE"));
2864
- }
2865
- else {
2866
- console.log(" " + colors_1.c.success + colors_1.c.white(` Already registered (owner: ${registeredOwner})`));
2867
- }
2868
- // Step 2: setCategoryLimitFor general
2869
- console.log(`Step 2: Setting 'general' spend limit to ${opts.spendLimit} ETH...`);
2870
- const innerLimit = peIface.encodeFunctionData("setCategoryLimitFor", [walletAddr, "general", spendLimitWei]);
2871
- const callDataLimit = walletIface.encodeFunctionData("executeContractCall", [{
2872
- target: policyEngineAddress,
2873
- data: innerLimit,
2874
- value: 0n,
2875
- minReturnValue: 0n,
2876
- maxApprovalAmount: 0n,
2877
- approvalToken: ethers_1.ethers.ZeroAddress,
2878
- }]);
2879
- const { txHash: tx2 } = await (0, walletconnect_1.requestPhoneWalletSignature)(config.walletConnectProjectId, chainId, () => ({ to: walletAddr, data: callDataLimit, value: "0x0" }), `Set 'general' spend limit: ${opts.spendLimit} ETH/tx`, telegramOpts, config);
2880
- await provider.waitForTransaction(tx2);
2881
- console.log(" " + colors_1.c.success + colors_1.c.white(" Spend limit set"));
2882
- if (opts.json) {
2883
- console.log(JSON.stringify({ ok: true, policyEngine: policyEngineAddress, wallet: walletAddr, spendLimit: opts.spendLimit }));
2884
- }
2885
- else {
2886
- console.log("\n" + colors_1.c.success + colors_1.c.white(" Wallet re-registered on new PE. You can now run `arc402 wallet drain`."));
2887
- }
2888
- });
2889
2708
  // ─── drain ─────────────────────────────────────────────────────────────────
2890
2709
  //
2891
2710
  // P1 + BUG-DRAIN-06: full autonomous drain via machine key.