@aomi-labs/client 0.1.6 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -67,6 +67,30 @@ ${list}`);
67
67
  }
68
68
  return n;
69
69
  }
70
+ function resolveExecutionMode(flags) {
71
+ const flagAA = flags["aa"] === "true";
72
+ const flagEoa = flags["eoa"] === "true";
73
+ if (flagAA && flagEoa) {
74
+ fatal("Choose only one of `--aa` or `--eoa`.");
75
+ }
76
+ if (flagAA) return "aa";
77
+ if (flagEoa) return "eoa";
78
+ return "aa";
79
+ }
80
+ function parseAAProvider(value) {
81
+ if (value === void 0) return void 0;
82
+ if (value === "alchemy" || value === "pimlico") {
83
+ return value;
84
+ }
85
+ fatal("Unsupported AA provider. Use `alchemy` or `pimlico`.");
86
+ }
87
+ function parseAAMode(value) {
88
+ if (value === void 0) return void 0;
89
+ if (value === "4337" || value === "7702") {
90
+ return value;
91
+ }
92
+ fatal("Unsupported AA mode. Use `4337` or `7702`.");
93
+ }
70
94
  function parseArgs(argv) {
71
95
  const raw = argv.slice(2);
72
96
  const command = raw[0] && !raw[0].startsWith("--") ? raw[0] : void 0;
@@ -96,16 +120,35 @@ function parseArgs(argv) {
96
120
  return { command, positional, flags };
97
121
  }
98
122
  function getConfig(parsed) {
99
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j;
123
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
124
+ const usesSignFlags = parsed.flags["aa"] === "true" || parsed.flags["eoa"] === "true" || parsed.flags["aa-provider"] !== void 0 || parsed.flags["aa-mode"] !== void 0;
125
+ if (usesSignFlags && parsed.command !== "sign") {
126
+ fatal(
127
+ "AA/EOA execution flags are only supported on `aomi sign <tx-id>`."
128
+ );
129
+ }
130
+ const execution = resolveExecutionMode(parsed.flags);
131
+ const aaProvider = parseAAProvider(
132
+ (_a3 = parsed.flags["aa-provider"]) != null ? _a3 : process.env.AOMI_AA_PROVIDER
133
+ );
134
+ const aaMode = parseAAMode(
135
+ (_b = parsed.flags["aa-mode"]) != null ? _b : process.env.AOMI_AA_MODE
136
+ );
137
+ if (execution === "eoa" && (aaProvider || aaMode)) {
138
+ fatal("`--aa-provider` and `--aa-mode` cannot be used with `--eoa`.");
139
+ }
100
140
  return {
101
- baseUrl: (_b = (_a3 = parsed.flags["backend-url"]) != null ? _a3 : process.env.AOMI_BASE_URL) != null ? _b : "https://api.aomi.dev",
102
- apiKey: (_c = parsed.flags["api-key"]) != null ? _c : process.env.AOMI_API_KEY,
103
- app: (_e = (_d = parsed.flags["app"]) != null ? _d : process.env.AOMI_APP) != null ? _e : "default",
104
- model: (_f = parsed.flags["model"]) != null ? _f : process.env.AOMI_MODEL,
105
- publicKey: (_g = parsed.flags["public-key"]) != null ? _g : process.env.AOMI_PUBLIC_KEY,
106
- privateKey: (_h = parsed.flags["private-key"]) != null ? _h : process.env.PRIVATE_KEY,
107
- chainRpcUrl: (_i = parsed.flags["rpc-url"]) != null ? _i : process.env.CHAIN_RPC_URL,
108
- chain: parseChainId((_j = parsed.flags["chain"]) != null ? _j : process.env.AOMI_CHAIN_ID)
141
+ baseUrl: (_d = (_c = parsed.flags["backend-url"]) != null ? _c : process.env.AOMI_BASE_URL) != null ? _d : "https://api.aomi.dev",
142
+ apiKey: (_e = parsed.flags["api-key"]) != null ? _e : process.env.AOMI_API_KEY,
143
+ app: (_g = (_f = parsed.flags["app"]) != null ? _f : process.env.AOMI_APP) != null ? _g : "default",
144
+ model: (_h = parsed.flags["model"]) != null ? _h : process.env.AOMI_MODEL,
145
+ publicKey: (_i = parsed.flags["public-key"]) != null ? _i : process.env.AOMI_PUBLIC_KEY,
146
+ privateKey: (_j = parsed.flags["private-key"]) != null ? _j : process.env.PRIVATE_KEY,
147
+ chainRpcUrl: (_k = parsed.flags["rpc-url"]) != null ? _k : process.env.CHAIN_RPC_URL,
148
+ chain: parseChainId((_l = parsed.flags["chain"]) != null ? _l : process.env.AOMI_CHAIN_ID),
149
+ execution,
150
+ aaProvider,
151
+ aaMode
109
152
  };
110
153
  }
111
154
  function createRuntime(argv) {
@@ -1623,6 +1666,40 @@ function walletRequestToPendingTx(request) {
1623
1666
  payload: request.payload
1624
1667
  };
1625
1668
  }
1669
+ function pendingTxToCallList(tx) {
1670
+ var _a3, _b;
1671
+ if (tx.kind !== "transaction" || !tx.to) {
1672
+ throw new Error("pending_transaction_missing_call_data");
1673
+ }
1674
+ return [
1675
+ {
1676
+ to: tx.to,
1677
+ value: (_a3 = tx.value) != null ? _a3 : "0",
1678
+ data: tx.data,
1679
+ chainId: (_b = tx.chainId) != null ? _b : 1
1680
+ }
1681
+ ];
1682
+ }
1683
+ function toSignedTransactionRecord(tx, execution, from, chainId, timestamp, aaProvider, aaMode) {
1684
+ return {
1685
+ id: tx.id,
1686
+ kind: "transaction",
1687
+ txHash: execution.txHash,
1688
+ txHashes: execution.txHashes,
1689
+ executionKind: execution.executionKind,
1690
+ aaProvider,
1691
+ aaMode,
1692
+ batched: execution.batched,
1693
+ sponsored: execution.sponsored,
1694
+ AAAddress: execution.AAAddress,
1695
+ delegationAddress: execution.delegationAddress,
1696
+ from,
1697
+ to: tx.to,
1698
+ value: tx.value,
1699
+ chainId,
1700
+ timestamp
1701
+ };
1702
+ }
1626
1703
  function formatTxLine(tx, prefix) {
1627
1704
  var _a3;
1628
1705
  const parts = [`${prefix} ${tx.id}`];
@@ -1638,6 +1715,29 @@ function formatTxLine(tx, prefix) {
1638
1715
  parts.push(`(${new Date(tx.timestamp).toLocaleTimeString()})`);
1639
1716
  return parts.join(" ");
1640
1717
  }
1718
+ function formatSignedTxLine(tx, prefix) {
1719
+ var _a3;
1720
+ const parts = [`${prefix} ${tx.id}`];
1721
+ if (tx.kind === "eip712_sign") {
1722
+ parts.push(`sig: ${(_a3 = tx.signature) == null ? void 0 : _a3.slice(0, 20)}...`);
1723
+ if (tx.description) parts.push(tx.description);
1724
+ } else {
1725
+ parts.push(`hash: ${tx.txHash}`);
1726
+ if (tx.executionKind) parts.push(`exec: ${tx.executionKind}`);
1727
+ if (tx.aaProvider) parts.push(`provider: ${tx.aaProvider}`);
1728
+ if (tx.aaMode) parts.push(`mode: ${tx.aaMode}`);
1729
+ if (tx.txHashes && tx.txHashes.length > 1) {
1730
+ parts.push(`txs: ${tx.txHashes.length}`);
1731
+ }
1732
+ if (tx.sponsored) parts.push("sponsored");
1733
+ if (tx.AAAddress) parts.push(`aa: ${tx.AAAddress}`);
1734
+ if (tx.delegationAddress) parts.push(`delegation: ${tx.delegationAddress}`);
1735
+ if (tx.to) parts.push(`to: ${tx.to}`);
1736
+ if (tx.value) parts.push(`value: ${tx.value}`);
1737
+ }
1738
+ parts.push(`(${new Date(tx.timestamp).toLocaleTimeString()})`);
1739
+ return parts.join(" ");
1740
+ }
1641
1741
 
1642
1742
  // src/cli/commands/chat.ts
1643
1743
  async function chatCommand(runtime) {
@@ -1914,17 +2014,25 @@ function toPendingTxMetadata(tx) {
1914
2014
  };
1915
2015
  }
1916
2016
  function toSignedTxMetadata(tx) {
1917
- var _a3, _b, _c, _d, _e, _f, _g;
2017
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
1918
2018
  return {
1919
2019
  id: tx.id,
1920
2020
  kind: tx.kind,
1921
2021
  txHash: (_a3 = tx.txHash) != null ? _a3 : null,
1922
- signature: (_b = tx.signature) != null ? _b : null,
1923
- from: (_c = tx.from) != null ? _c : null,
1924
- to: (_d = tx.to) != null ? _d : null,
1925
- value: (_e = tx.value) != null ? _e : null,
1926
- chainId: (_f = tx.chainId) != null ? _f : null,
1927
- description: (_g = tx.description) != null ? _g : null,
2022
+ txHashes: (_b = tx.txHashes) != null ? _b : null,
2023
+ executionKind: (_c = tx.executionKind) != null ? _c : null,
2024
+ aaProvider: (_d = tx.aaProvider) != null ? _d : null,
2025
+ aaMode: (_e = tx.aaMode) != null ? _e : null,
2026
+ batched: (_f = tx.batched) != null ? _f : null,
2027
+ sponsored: (_g = tx.sponsored) != null ? _g : null,
2028
+ AAAddress: (_h = tx.AAAddress) != null ? _h : null,
2029
+ delegationAddress: (_i = tx.delegationAddress) != null ? _i : null,
2030
+ signature: (_j = tx.signature) != null ? _j : null,
2031
+ from: (_k = tx.from) != null ? _k : null,
2032
+ to: (_l = tx.to) != null ? _l : null,
2033
+ value: (_m = tx.value) != null ? _m : null,
2034
+ chainId: (_n = tx.chainId) != null ? _n : null,
2035
+ description: (_o = tx.description) != null ? _o : null,
1928
2036
  timestamp: new Date(tx.timestamp).toISOString()
1929
2037
  };
1930
2038
  }
@@ -2183,10 +2291,704 @@ function sessionCommand(runtime) {
2183
2291
 
2184
2292
  // src/cli/commands/wallet.ts
2185
2293
  import { createWalletClient, http } from "viem";
2186
- import { privateKeyToAccount } from "viem/accounts";
2294
+ import { createInterface } from "readline/promises";
2295
+ import { privateKeyToAccount as privateKeyToAccount2 } from "viem/accounts";
2187
2296
  import * as viemChains from "viem/chains";
2297
+
2298
+ // src/aa/types.ts
2299
+ function getAAChainConfig(config, calls, chainsById) {
2300
+ if (!config.enabled || calls.length === 0) {
2301
+ return null;
2302
+ }
2303
+ const chainIds = Array.from(new Set(calls.map((call) => call.chainId)));
2304
+ if (chainIds.length !== 1) {
2305
+ return null;
2306
+ }
2307
+ const chainId = chainIds[0];
2308
+ if (!chainsById[chainId]) {
2309
+ return null;
2310
+ }
2311
+ const chainConfig = config.chains.find((item) => item.chainId === chainId);
2312
+ if (!(chainConfig == null ? void 0 : chainConfig.enabled)) {
2313
+ return null;
2314
+ }
2315
+ if (calls.length > 1 && !chainConfig.allowBatching) {
2316
+ return null;
2317
+ }
2318
+ return chainConfig;
2319
+ }
2320
+ function buildAAExecutionPlan(config, chainConfig) {
2321
+ const mode = chainConfig.supportedModes.includes(chainConfig.defaultMode) ? chainConfig.defaultMode : chainConfig.supportedModes[0];
2322
+ if (!mode) {
2323
+ throw new Error(`No smart account mode configured for chain ${chainConfig.chainId}`);
2324
+ }
2325
+ return {
2326
+ provider: config.provider,
2327
+ chainId: chainConfig.chainId,
2328
+ mode,
2329
+ batchingEnabled: chainConfig.allowBatching,
2330
+ sponsorship: chainConfig.sponsorship,
2331
+ fallbackToEoa: config.fallbackToEoa
2332
+ };
2333
+ }
2334
+ function mapCall(call) {
2335
+ return {
2336
+ to: call.to,
2337
+ value: BigInt(call.value),
2338
+ data: call.data ? call.data : void 0
2339
+ };
2340
+ }
2341
+ var DEFAULT_AA_CONFIG = {
2342
+ enabled: true,
2343
+ provider: "alchemy",
2344
+ fallbackToEoa: true,
2345
+ chains: [
2346
+ {
2347
+ chainId: 1,
2348
+ enabled: true,
2349
+ defaultMode: "7702",
2350
+ supportedModes: ["4337", "7702"],
2351
+ allowBatching: true,
2352
+ sponsorship: "optional"
2353
+ },
2354
+ {
2355
+ chainId: 137,
2356
+ enabled: true,
2357
+ defaultMode: "4337",
2358
+ supportedModes: ["4337", "7702"],
2359
+ allowBatching: true,
2360
+ sponsorship: "optional"
2361
+ },
2362
+ {
2363
+ chainId: 42161,
2364
+ enabled: true,
2365
+ defaultMode: "4337",
2366
+ supportedModes: ["4337", "7702"],
2367
+ allowBatching: true,
2368
+ sponsorship: "optional"
2369
+ },
2370
+ {
2371
+ chainId: 10,
2372
+ enabled: true,
2373
+ defaultMode: "4337",
2374
+ supportedModes: ["4337", "7702"],
2375
+ allowBatching: true,
2376
+ sponsorship: "optional"
2377
+ },
2378
+ {
2379
+ chainId: 8453,
2380
+ enabled: true,
2381
+ defaultMode: "4337",
2382
+ supportedModes: ["4337", "7702"],
2383
+ allowBatching: true,
2384
+ sponsorship: "optional"
2385
+ }
2386
+ ]
2387
+ };
2388
+ var DISABLED_PROVIDER_STATE = {
2389
+ plan: null,
2390
+ AA: void 0,
2391
+ isPending: false,
2392
+ error: null
2393
+ };
2394
+ async function executeWalletCalls(params) {
2395
+ const {
2396
+ callList,
2397
+ currentChainId,
2398
+ capabilities,
2399
+ localPrivateKey,
2400
+ providerState,
2401
+ sendCallsSyncAsync,
2402
+ sendTransactionAsync,
2403
+ switchChainAsync,
2404
+ chainsById,
2405
+ getPreferredRpcUrl: getPreferredRpcUrl2
2406
+ } = params;
2407
+ if (providerState.plan && providerState.AA) {
2408
+ return executeViaAA(callList, providerState);
2409
+ }
2410
+ if (providerState.plan && providerState.error && !providerState.plan.fallbackToEoa) {
2411
+ throw providerState.error;
2412
+ }
2413
+ return executeViaEoa({
2414
+ callList,
2415
+ currentChainId,
2416
+ capabilities,
2417
+ localPrivateKey,
2418
+ sendCallsSyncAsync,
2419
+ sendTransactionAsync,
2420
+ switchChainAsync,
2421
+ chainsById,
2422
+ getPreferredRpcUrl: getPreferredRpcUrl2
2423
+ });
2424
+ }
2425
+ async function executeViaAA(callList, providerState) {
2426
+ var _a3;
2427
+ const AA = providerState.AA;
2428
+ const plan = providerState.plan;
2429
+ if (!AA || !plan) {
2430
+ throw (_a3 = providerState.error) != null ? _a3 : new Error("smart_account_unavailable");
2431
+ }
2432
+ const callsPayload = callList.map(mapCall);
2433
+ const receipt = callList.length > 1 ? await AA.sendBatchTransaction(callsPayload) : await AA.sendTransaction(callsPayload[0]);
2434
+ const txHash = receipt.transactionHash;
2435
+ const providerPrefix = AA.provider.toLowerCase();
2436
+ return {
2437
+ txHash,
2438
+ txHashes: [txHash],
2439
+ executionKind: `${providerPrefix}_${AA.mode}`,
2440
+ batched: callList.length > 1,
2441
+ sponsored: plan.sponsorship !== "disabled",
2442
+ AAAddress: AA.AAAddress,
2443
+ delegationAddress: AA.mode === "7702" ? AA.delegationAddress : void 0
2444
+ };
2445
+ }
2446
+ async function executeViaEoa({
2447
+ callList,
2448
+ currentChainId,
2449
+ capabilities,
2450
+ localPrivateKey,
2451
+ sendCallsSyncAsync,
2452
+ sendTransactionAsync,
2453
+ switchChainAsync,
2454
+ chainsById,
2455
+ getPreferredRpcUrl: getPreferredRpcUrl2
2456
+ }) {
2457
+ var _a3, _b;
2458
+ const { createPublicClient, createWalletClient: createWalletClient2, http: http2 } = await import("viem");
2459
+ const { privateKeyToAccount: privateKeyToAccount3 } = await import("viem/accounts");
2460
+ const hashes = [];
2461
+ if (localPrivateKey) {
2462
+ for (const call of callList) {
2463
+ const chain = chainsById[call.chainId];
2464
+ if (!chain) {
2465
+ throw new Error(`Unsupported chain ${call.chainId}`);
2466
+ }
2467
+ const rpcUrl = getPreferredRpcUrl2(chain);
2468
+ if (!rpcUrl) {
2469
+ throw new Error(`No RPC for chain ${call.chainId}`);
2470
+ }
2471
+ const account = privateKeyToAccount3(localPrivateKey);
2472
+ const walletClient = createWalletClient2({
2473
+ account,
2474
+ chain,
2475
+ transport: http2(rpcUrl)
2476
+ });
2477
+ const hash = await walletClient.sendTransaction({
2478
+ account,
2479
+ to: call.to,
2480
+ value: BigInt(call.value),
2481
+ data: call.data ? call.data : void 0
2482
+ });
2483
+ const publicClient = createPublicClient({
2484
+ chain,
2485
+ transport: http2(rpcUrl)
2486
+ });
2487
+ await publicClient.waitForTransactionReceipt({ hash });
2488
+ hashes.push(hash);
2489
+ }
2490
+ return {
2491
+ txHash: hashes[hashes.length - 1],
2492
+ txHashes: hashes,
2493
+ executionKind: "eoa",
2494
+ batched: hashes.length > 1,
2495
+ sponsored: false
2496
+ };
2497
+ }
2498
+ const chainIds = Array.from(new Set(callList.map((call) => call.chainId)));
2499
+ if (chainIds.length > 1) {
2500
+ throw new Error("mixed_chain_bundle_not_supported");
2501
+ }
2502
+ const chainId = chainIds[0];
2503
+ if (currentChainId !== chainId) {
2504
+ await switchChainAsync({ chainId });
2505
+ }
2506
+ const chainCaps = capabilities == null ? void 0 : capabilities[`eip155:${chainId}`];
2507
+ const atomicStatus = (_a3 = chainCaps == null ? void 0 : chainCaps.atomic) == null ? void 0 : _a3.status;
2508
+ const canUseSendCalls = atomicStatus === "supported" || atomicStatus === "ready";
2509
+ if (canUseSendCalls) {
2510
+ const batchResult = await sendCallsSyncAsync({
2511
+ calls: callList.map(mapCall),
2512
+ capabilities: {
2513
+ atomic: {
2514
+ required: true
2515
+ }
2516
+ }
2517
+ });
2518
+ const receipts = (_b = batchResult.receipts) != null ? _b : [];
2519
+ for (const receipt of receipts) {
2520
+ if (receipt.transactionHash) {
2521
+ hashes.push(receipt.transactionHash);
2522
+ }
2523
+ }
2524
+ } else {
2525
+ for (const call of callList) {
2526
+ const hash = await sendTransactionAsync({
2527
+ chainId: call.chainId,
2528
+ to: call.to,
2529
+ value: BigInt(call.value),
2530
+ data: call.data ? call.data : void 0
2531
+ });
2532
+ hashes.push(hash);
2533
+ }
2534
+ }
2535
+ return {
2536
+ txHash: hashes[hashes.length - 1],
2537
+ txHashes: hashes,
2538
+ executionKind: "eoa",
2539
+ batched: hashes.length > 1,
2540
+ sponsored: false
2541
+ };
2542
+ }
2543
+
2544
+ // src/aa/env.ts
2545
+ var ALCHEMY_API_KEY_ENVS = [
2546
+ "ALCHEMY_API_KEY",
2547
+ "NEXT_PUBLIC_ALCHEMY_API_KEY"
2548
+ ];
2549
+ var ALCHEMY_GAS_POLICY_ENVS = [
2550
+ "ALCHEMY_GAS_POLICY_ID",
2551
+ "NEXT_PUBLIC_ALCHEMY_GAS_POLICY_ID"
2552
+ ];
2553
+ var PIMLICO_API_KEY_ENVS = [
2554
+ "PIMLICO_API_KEY",
2555
+ "NEXT_PUBLIC_PIMLICO_API_KEY"
2556
+ ];
2557
+ function readEnv(candidates, options = {}) {
2558
+ var _a3;
2559
+ const { publicOnly = false } = options;
2560
+ for (const name of candidates) {
2561
+ if (publicOnly && !name.startsWith("NEXT_PUBLIC_")) {
2562
+ continue;
2563
+ }
2564
+ const value = (_a3 = process.env[name]) == null ? void 0 : _a3.trim();
2565
+ if (value) return value;
2566
+ }
2567
+ return void 0;
2568
+ }
2569
+ function readGasPolicyEnv(chainId, chainSlugById, baseCandidates, options = {}) {
2570
+ const slug = chainSlugById[chainId];
2571
+ if (slug) {
2572
+ const chainSpecific = baseCandidates.map(
2573
+ (base) => `${base}_${slug.toUpperCase()}`
2574
+ );
2575
+ const found = readEnv(chainSpecific, options);
2576
+ if (found) return found;
2577
+ }
2578
+ return readEnv(baseCandidates, options);
2579
+ }
2580
+ function isProviderConfigured(provider, options = {}) {
2581
+ return provider === "alchemy" ? Boolean(readEnv(ALCHEMY_API_KEY_ENVS, options)) : Boolean(readEnv(PIMLICO_API_KEY_ENVS, options));
2582
+ }
2583
+ function resolveDefaultProvider(options = {}) {
2584
+ if (isProviderConfigured("alchemy", options)) return "alchemy";
2585
+ if (isProviderConfigured("pimlico", options)) return "pimlico";
2586
+ throw new Error(
2587
+ "AA requires provider credentials. Set ALCHEMY_API_KEY or PIMLICO_API_KEY, or use --eoa."
2588
+ );
2589
+ }
2590
+
2591
+ // src/aa/resolve.ts
2592
+ function resolveAlchemyConfig(options) {
2593
+ const {
2594
+ calls,
2595
+ localPrivateKey,
2596
+ accountAbstractionConfig = DEFAULT_AA_CONFIG,
2597
+ chainsById,
2598
+ chainSlugById = {},
2599
+ getPreferredRpcUrl: getPreferredRpcUrl2 = (chain2) => {
2600
+ var _a3;
2601
+ return (_a3 = chain2.rpcUrls.default.http[0]) != null ? _a3 : "";
2602
+ },
2603
+ modeOverride,
2604
+ publicOnly = false,
2605
+ throwOnMissingConfig = false,
2606
+ apiKey: preResolvedApiKey,
2607
+ gasPolicyId: preResolvedGasPolicyId
2608
+ } = options;
2609
+ if (!calls || localPrivateKey) {
2610
+ return null;
2611
+ }
2612
+ const config = __spreadProps(__spreadValues({}, accountAbstractionConfig), {
2613
+ provider: "alchemy"
2614
+ });
2615
+ const chainConfig = getAAChainConfig(config, calls, chainsById);
2616
+ if (!chainConfig) {
2617
+ if (throwOnMissingConfig) {
2618
+ const chainIds = Array.from(new Set(calls.map((c) => c.chainId)));
2619
+ throw new Error(
2620
+ `AA is not configured for chain ${chainIds[0]}, or batching is disabled for that chain.`
2621
+ );
2622
+ }
2623
+ return null;
2624
+ }
2625
+ const apiKey = preResolvedApiKey != null ? preResolvedApiKey : readEnv(ALCHEMY_API_KEY_ENVS, { publicOnly });
2626
+ if (!apiKey) {
2627
+ if (throwOnMissingConfig) {
2628
+ throw new Error("Alchemy AA requires ALCHEMY_API_KEY.");
2629
+ }
2630
+ return null;
2631
+ }
2632
+ const chain = chainsById[chainConfig.chainId];
2633
+ if (!chain) {
2634
+ return null;
2635
+ }
2636
+ const gasPolicyId = preResolvedGasPolicyId != null ? preResolvedGasPolicyId : readGasPolicyEnv(
2637
+ chainConfig.chainId,
2638
+ chainSlugById,
2639
+ ALCHEMY_GAS_POLICY_ENVS,
2640
+ { publicOnly }
2641
+ );
2642
+ if (chainConfig.sponsorship === "required" && !gasPolicyId) {
2643
+ if (throwOnMissingConfig) {
2644
+ throw new Error(
2645
+ `Alchemy gas policy required for chain ${chainConfig.chainId} but not configured.`
2646
+ );
2647
+ }
2648
+ return null;
2649
+ }
2650
+ if (modeOverride && !chainConfig.supportedModes.includes(modeOverride)) {
2651
+ if (throwOnMissingConfig) {
2652
+ throw new Error(
2653
+ `AA mode "${modeOverride}" is not supported on chain ${chainConfig.chainId}.`
2654
+ );
2655
+ }
2656
+ return null;
2657
+ }
2658
+ const resolvedChainConfig = modeOverride ? __spreadProps(__spreadValues({}, chainConfig), { defaultMode: modeOverride }) : chainConfig;
2659
+ const plan = buildAAExecutionPlan(config, resolvedChainConfig);
2660
+ return {
2661
+ chainConfig: resolvedChainConfig,
2662
+ plan,
2663
+ apiKey,
2664
+ chain,
2665
+ rpcUrl: getPreferredRpcUrl2(chain),
2666
+ gasPolicyId,
2667
+ mode: resolvedChainConfig.defaultMode
2668
+ };
2669
+ }
2670
+ function resolvePimlicoConfig(options) {
2671
+ const {
2672
+ calls,
2673
+ localPrivateKey,
2674
+ accountAbstractionConfig = DEFAULT_AA_CONFIG,
2675
+ chainsById,
2676
+ rpcUrl,
2677
+ modeOverride,
2678
+ publicOnly = false,
2679
+ throwOnMissingConfig = false,
2680
+ apiKey: preResolvedApiKey
2681
+ } = options;
2682
+ if (!calls || localPrivateKey) {
2683
+ return null;
2684
+ }
2685
+ const config = __spreadProps(__spreadValues({}, accountAbstractionConfig), {
2686
+ provider: "pimlico"
2687
+ });
2688
+ const chainConfig = getAAChainConfig(config, calls, chainsById);
2689
+ if (!chainConfig) {
2690
+ if (throwOnMissingConfig) {
2691
+ const chainIds = Array.from(new Set(calls.map((c) => c.chainId)));
2692
+ throw new Error(
2693
+ `AA is not configured for chain ${chainIds[0]}, or batching is disabled for that chain.`
2694
+ );
2695
+ }
2696
+ return null;
2697
+ }
2698
+ const apiKey = preResolvedApiKey != null ? preResolvedApiKey : readEnv(PIMLICO_API_KEY_ENVS, { publicOnly });
2699
+ if (!apiKey) {
2700
+ if (throwOnMissingConfig) {
2701
+ throw new Error("Pimlico AA requires PIMLICO_API_KEY.");
2702
+ }
2703
+ return null;
2704
+ }
2705
+ const chain = chainsById[chainConfig.chainId];
2706
+ if (!chain) {
2707
+ return null;
2708
+ }
2709
+ if (modeOverride && !chainConfig.supportedModes.includes(modeOverride)) {
2710
+ if (throwOnMissingConfig) {
2711
+ throw new Error(
2712
+ `AA mode "${modeOverride}" is not supported on chain ${chainConfig.chainId}.`
2713
+ );
2714
+ }
2715
+ return null;
2716
+ }
2717
+ const resolvedChainConfig = modeOverride ? __spreadProps(__spreadValues({}, chainConfig), { defaultMode: modeOverride }) : chainConfig;
2718
+ const plan = buildAAExecutionPlan(config, resolvedChainConfig);
2719
+ return {
2720
+ chainConfig: resolvedChainConfig,
2721
+ plan,
2722
+ apiKey,
2723
+ chain,
2724
+ rpcUrl,
2725
+ mode: resolvedChainConfig.defaultMode
2726
+ };
2727
+ }
2728
+
2729
+ // src/aa/adapt.ts
2730
+ function adaptSmartAccount(account) {
2731
+ return {
2732
+ provider: account.provider,
2733
+ mode: account.mode,
2734
+ AAAddress: account.smartAccountAddress,
2735
+ delegationAddress: account.delegationAddress,
2736
+ sendTransaction: async (call) => {
2737
+ const receipt = await account.sendTransaction(call);
2738
+ return { transactionHash: receipt.transactionHash };
2739
+ },
2740
+ sendBatchTransaction: async (calls) => {
2741
+ const receipt = await account.sendBatchTransaction(calls);
2742
+ return { transactionHash: receipt.transactionHash };
2743
+ }
2744
+ };
2745
+ }
2746
+ function isAlchemySponsorshipLimitError(error) {
2747
+ const message = error instanceof Error ? error.message : String(error != null ? error : "");
2748
+ const normalized = message.toLowerCase();
2749
+ return normalized.includes("gas sponsorship limit") || normalized.includes("put your team over your gas sponsorship limit") || normalized.includes("buy gas credits in your gas manager dashboard");
2750
+ }
2751
+
2752
+ // src/aa/create.ts
2753
+ import { createAlchemySmartAccount } from "@getpara/aa-alchemy";
2754
+ import { createPimlicoSmartAccount } from "@getpara/aa-pimlico";
2755
+ import { privateKeyToAccount } from "viem/accounts";
2756
+ async function createAAProviderState(options) {
2757
+ if (options.provider === "alchemy") {
2758
+ return createAlchemyAAState({
2759
+ chain: options.chain,
2760
+ owner: options.owner,
2761
+ rpcUrl: options.rpcUrl,
2762
+ callList: options.callList,
2763
+ mode: options.mode,
2764
+ apiKey: options.apiKey,
2765
+ gasPolicyId: options.gasPolicyId,
2766
+ sponsored: options.sponsored
2767
+ });
2768
+ }
2769
+ return createPimlicoAAState({
2770
+ chain: options.chain,
2771
+ owner: options.owner,
2772
+ rpcUrl: options.rpcUrl,
2773
+ callList: options.callList,
2774
+ mode: options.mode,
2775
+ apiKey: options.apiKey
2776
+ });
2777
+ }
2778
+ function getOwnerParams(owner) {
2779
+ if (!owner) {
2780
+ return null;
2781
+ }
2782
+ if ("privateKey" in owner) {
2783
+ return {
2784
+ para: void 0,
2785
+ signer: privateKeyToAccount(owner.privateKey)
2786
+ };
2787
+ }
2788
+ if ("signer" in owner) {
2789
+ return __spreadValues({
2790
+ para: owner.para,
2791
+ signer: owner.signer
2792
+ }, owner.address ? { address: owner.address } : {});
2793
+ }
2794
+ if ("para" in owner) {
2795
+ return __spreadValues({
2796
+ para: owner.para
2797
+ }, owner.address ? { address: owner.address } : {});
2798
+ }
2799
+ return null;
2800
+ }
2801
+ function getMissingOwnerState(plan, provider) {
2802
+ return {
2803
+ plan,
2804
+ AA: null,
2805
+ isPending: false,
2806
+ error: new Error(
2807
+ `${provider} AA account creation requires a signer or Para session.`
2808
+ )
2809
+ };
2810
+ }
2811
+ async function createAlchemyAAState(options) {
2812
+ var _a3, _b;
2813
+ const {
2814
+ chain,
2815
+ owner,
2816
+ rpcUrl,
2817
+ callList,
2818
+ mode,
2819
+ sponsored = true
2820
+ } = options;
2821
+ const resolved = resolveAlchemyConfig({
2822
+ calls: callList,
2823
+ chainsById: { [chain.id]: chain },
2824
+ modeOverride: mode,
2825
+ throwOnMissingConfig: true,
2826
+ getPreferredRpcUrl: () => rpcUrl,
2827
+ apiKey: options.apiKey,
2828
+ gasPolicyId: options.gasPolicyId
2829
+ });
2830
+ if (!resolved) {
2831
+ throw new Error("Alchemy AA config resolution failed.");
2832
+ }
2833
+ const apiKey = (_a3 = options.apiKey) != null ? _a3 : resolved.apiKey;
2834
+ const gasPolicyId = sponsored ? (_b = options.gasPolicyId) != null ? _b : readEnv(ALCHEMY_GAS_POLICY_ENVS) : void 0;
2835
+ const plan = __spreadProps(__spreadValues({}, resolved.plan), {
2836
+ sponsorship: gasPolicyId ? resolved.plan.sponsorship : "disabled",
2837
+ fallbackToEoa: false
2838
+ });
2839
+ const ownerParams = getOwnerParams(owner);
2840
+ if (!ownerParams) {
2841
+ return getMissingOwnerState(plan, "alchemy");
2842
+ }
2843
+ try {
2844
+ const smartAccount = await createAlchemySmartAccount(__spreadProps(__spreadValues({}, ownerParams), {
2845
+ apiKey,
2846
+ gasPolicyId,
2847
+ chain,
2848
+ rpcUrl,
2849
+ mode: plan.mode
2850
+ }));
2851
+ if (!smartAccount) {
2852
+ return {
2853
+ plan,
2854
+ AA: null,
2855
+ isPending: false,
2856
+ error: new Error("Alchemy AA account could not be initialized.")
2857
+ };
2858
+ }
2859
+ return {
2860
+ plan,
2861
+ AA: adaptSmartAccount(smartAccount),
2862
+ isPending: false,
2863
+ error: null
2864
+ };
2865
+ } catch (error) {
2866
+ return {
2867
+ plan,
2868
+ AA: null,
2869
+ isPending: false,
2870
+ error: error instanceof Error ? error : new Error(String(error))
2871
+ };
2872
+ }
2873
+ }
2874
+ async function createPimlicoAAState(options) {
2875
+ var _a3;
2876
+ const {
2877
+ chain,
2878
+ owner,
2879
+ rpcUrl,
2880
+ callList,
2881
+ mode
2882
+ } = options;
2883
+ const resolved = resolvePimlicoConfig({
2884
+ calls: callList,
2885
+ chainsById: { [chain.id]: chain },
2886
+ rpcUrl,
2887
+ modeOverride: mode,
2888
+ throwOnMissingConfig: true,
2889
+ apiKey: options.apiKey
2890
+ });
2891
+ if (!resolved) {
2892
+ throw new Error("Pimlico AA config resolution failed.");
2893
+ }
2894
+ const apiKey = (_a3 = options.apiKey) != null ? _a3 : resolved.apiKey;
2895
+ const plan = __spreadProps(__spreadValues({}, resolved.plan), {
2896
+ fallbackToEoa: false
2897
+ });
2898
+ const ownerParams = getOwnerParams(owner);
2899
+ if (!ownerParams) {
2900
+ return getMissingOwnerState(plan, "pimlico");
2901
+ }
2902
+ try {
2903
+ const smartAccount = await createPimlicoSmartAccount(__spreadProps(__spreadValues({}, ownerParams), {
2904
+ apiKey,
2905
+ chain,
2906
+ rpcUrl,
2907
+ mode: plan.mode
2908
+ }));
2909
+ if (!smartAccount) {
2910
+ return {
2911
+ plan,
2912
+ AA: null,
2913
+ isPending: false,
2914
+ error: new Error("Pimlico AA account could not be initialized.")
2915
+ };
2916
+ }
2917
+ return {
2918
+ plan,
2919
+ AA: adaptSmartAccount(smartAccount),
2920
+ isPending: false,
2921
+ error: null
2922
+ };
2923
+ } catch (error) {
2924
+ return {
2925
+ plan,
2926
+ AA: null,
2927
+ isPending: false,
2928
+ error: error instanceof Error ? error : new Error(String(error))
2929
+ };
2930
+ }
2931
+ }
2932
+
2933
+ // src/cli/execution.ts
2934
+ function resolveAAProvider(config) {
2935
+ var _a3;
2936
+ const provider = (_a3 = config.aaProvider) != null ? _a3 : resolveDefaultProvider();
2937
+ if (!isProviderConfigured(provider)) {
2938
+ const envName = provider === "alchemy" ? "ALCHEMY_API_KEY" : "PIMLICO_API_KEY";
2939
+ throw new Error(
2940
+ `AA provider "${provider}" is selected but ${envName} is not configured.`
2941
+ );
2942
+ }
2943
+ return provider;
2944
+ }
2945
+ function resolveCliExecutionDecision(params) {
2946
+ const { config, chain, callList } = params;
2947
+ if (config.execution === "eoa") {
2948
+ return { execution: "eoa" };
2949
+ }
2950
+ const provider = resolveAAProvider(config);
2951
+ const resolveOpts = {
2952
+ calls: callList,
2953
+ chainsById: { [chain.id]: chain },
2954
+ modeOverride: config.aaMode,
2955
+ throwOnMissingConfig: true
2956
+ };
2957
+ const resolved = provider === "alchemy" ? resolveAlchemyConfig(resolveOpts) : resolvePimlicoConfig(resolveOpts);
2958
+ if (!resolved) {
2959
+ throw new Error(`AA config resolution failed for provider "${provider}".`);
2960
+ }
2961
+ return {
2962
+ execution: "aa",
2963
+ provider,
2964
+ aaMode: resolved.plan.mode
2965
+ };
2966
+ }
2967
+ async function createCliProviderState(params) {
2968
+ const { decision, chain, privateKey, rpcUrl, callList, sponsored } = params;
2969
+ if (decision.execution === "eoa") {
2970
+ return DISABLED_PROVIDER_STATE;
2971
+ }
2972
+ return createAAProviderState({
2973
+ provider: decision.provider,
2974
+ chain,
2975
+ owner: { privateKey },
2976
+ rpcUrl,
2977
+ callList,
2978
+ mode: decision.aaMode,
2979
+ sponsored
2980
+ });
2981
+ }
2982
+ function describeExecutionDecision(decision) {
2983
+ if (decision.execution === "eoa") {
2984
+ return "eoa";
2985
+ }
2986
+ return `aa (${decision.provider}, ${decision.aaMode})`;
2987
+ }
2988
+
2989
+ // src/cli/commands/wallet.ts
2188
2990
  function txCommand() {
2189
- var _a3, _b, _c;
2991
+ var _a3, _b;
2190
2992
  const state = readState();
2191
2993
  if (!state) {
2192
2994
  console.log("No active session");
@@ -2210,17 +3012,7 @@ function txCommand() {
2210
3012
  if (pending.length > 0) console.log();
2211
3013
  console.log(`Signed (${signed.length}):`);
2212
3014
  for (const tx of signed) {
2213
- const parts = [` \u2705 ${tx.id}`];
2214
- if (tx.kind === "eip712_sign") {
2215
- parts.push(`sig: ${(_c = tx.signature) == null ? void 0 : _c.slice(0, 20)}...`);
2216
- if (tx.description) parts.push(tx.description);
2217
- } else {
2218
- parts.push(`hash: ${tx.txHash}`);
2219
- if (tx.to) parts.push(`to: ${tx.to}`);
2220
- if (tx.value) parts.push(`value: ${tx.value}`);
2221
- }
2222
- parts.push(`(${new Date(tx.timestamp).toLocaleTimeString()})`);
2223
- console.log(parts.join(" "));
3015
+ console.log(formatSignedTxLine(tx, " \u2705"));
2224
3016
  }
2225
3017
  }
2226
3018
  printDataFileLocation();
@@ -2236,6 +3028,13 @@ Run \`aomi tx\` to see available IDs.`
2236
3028
  }
2237
3029
  return pendingTx;
2238
3030
  }
3031
+ function requirePendingTxs(state, txIds) {
3032
+ const uniqueIds = Array.from(new Set(txIds));
3033
+ if (uniqueIds.length !== txIds.length) {
3034
+ fatal("Duplicate transaction IDs are not allowed in a single `aomi sign` call.");
3035
+ }
3036
+ return uniqueIds.map((txId) => requirePendingTx(state, txId));
3037
+ }
2239
3038
  function rewriteSessionState(runtime, state) {
2240
3039
  let changed = false;
2241
3040
  if (runtime.config.baseUrl !== state.baseUrl) {
@@ -2279,12 +3078,147 @@ async function persistResolvedSignerState(session, state, address, chainId) {
2279
3078
  session.resolveWallet(address, chainId);
2280
3079
  await session.syncUserState();
2281
3080
  }
3081
+ function resolveChain(targetChainId, rpcUrl) {
3082
+ var _a3;
3083
+ return (_a3 = Object.values(viemChains).find(
3084
+ (candidate) => typeof candidate === "object" && candidate !== null && "id" in candidate && candidate.id === targetChainId
3085
+ )) != null ? _a3 : {
3086
+ id: targetChainId,
3087
+ name: `Chain ${targetChainId}`,
3088
+ nativeCurrency: {
3089
+ name: "ETH",
3090
+ symbol: "ETH",
3091
+ decimals: 18
3092
+ },
3093
+ rpcUrls: {
3094
+ default: {
3095
+ http: rpcUrl ? [rpcUrl] : []
3096
+ }
3097
+ }
3098
+ };
3099
+ }
3100
+ function getPreferredRpcUrl(chain, override) {
3101
+ var _a3, _b, _c;
3102
+ return (_c = (_b = override != null ? override : chain.rpcUrls.default.http[0]) != null ? _b : (_a3 = chain.rpcUrls.public) == null ? void 0 : _a3.http[0]) != null ? _c : "";
3103
+ }
3104
+ async function promptForEoaFallback() {
3105
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
3106
+ return false;
3107
+ }
3108
+ const rl = createInterface({
3109
+ input: process.stdin,
3110
+ output: process.stdout
3111
+ });
3112
+ try {
3113
+ const answer = await rl.question(
3114
+ "Account abstraction not available, use EOA? [yes/no] "
3115
+ );
3116
+ const normalized = answer.trim().toLowerCase();
3117
+ return normalized === "y" || normalized === "yes";
3118
+ } finally {
3119
+ rl.close();
3120
+ }
3121
+ }
3122
+ async function executeCliTransaction(params) {
3123
+ const { privateKey, currentChainId, chainsById, rpcUrl, providerState, callList } = params;
3124
+ const unsupportedWalletMethod = async () => {
3125
+ throw new Error("wallet_client_path_unavailable_in_cli_private_key_mode");
3126
+ };
3127
+ return executeWalletCalls({
3128
+ callList,
3129
+ currentChainId,
3130
+ capabilities: void 0,
3131
+ localPrivateKey: privateKey,
3132
+ providerState,
3133
+ sendCallsSyncAsync: unsupportedWalletMethod,
3134
+ sendTransactionAsync: unsupportedWalletMethod,
3135
+ switchChainAsync: async () => void 0,
3136
+ chainsById,
3137
+ getPreferredRpcUrl: (resolvedChain) => getPreferredRpcUrl(resolvedChain, rpcUrl)
3138
+ });
3139
+ }
3140
+ async function executeTransactionWithFallback(params) {
3141
+ const { decision, privateKey, currentChainId, chainsById, primaryChain, rpcUrl, callList } = params;
3142
+ const runExecution = async (providerState2) => executeCliTransaction({
3143
+ privateKey,
3144
+ currentChainId,
3145
+ chainsById,
3146
+ rpcUrl,
3147
+ providerState: providerState2,
3148
+ callList
3149
+ });
3150
+ if (decision.execution === "eoa") {
3151
+ const providerState2 = await createCliProviderState({
3152
+ decision,
3153
+ chain: primaryChain,
3154
+ privateKey,
3155
+ rpcUrl: rpcUrl != null ? rpcUrl : "",
3156
+ callList
3157
+ });
3158
+ return {
3159
+ execution: await runExecution(providerState2),
3160
+ finalDecision: decision
3161
+ };
3162
+ }
3163
+ let providerState = await createCliProviderState({
3164
+ decision,
3165
+ chain: primaryChain,
3166
+ privateKey,
3167
+ rpcUrl: rpcUrl != null ? rpcUrl : "",
3168
+ callList,
3169
+ sponsored: true
3170
+ });
3171
+ try {
3172
+ return {
3173
+ execution: await runExecution(providerState),
3174
+ finalDecision: decision
3175
+ };
3176
+ } catch (error) {
3177
+ const shouldRetryUnsponsored = decision.provider === "alchemy" && isAlchemySponsorshipLimitError(error);
3178
+ if (shouldRetryUnsponsored) {
3179
+ console.log("AA sponsorship unavailable. Retrying AA with user-funded gas...");
3180
+ providerState = await createCliProviderState({
3181
+ decision,
3182
+ chain: primaryChain,
3183
+ privateKey,
3184
+ rpcUrl: rpcUrl != null ? rpcUrl : "",
3185
+ callList,
3186
+ sponsored: false
3187
+ });
3188
+ try {
3189
+ return {
3190
+ execution: await runExecution(providerState),
3191
+ finalDecision: decision
3192
+ };
3193
+ } catch (retryError) {
3194
+ error = retryError;
3195
+ }
3196
+ }
3197
+ const useEoa = await promptForEoaFallback();
3198
+ if (!useEoa) {
3199
+ throw error;
3200
+ }
3201
+ const eoaDecision = { execution: "eoa" };
3202
+ console.log("Retrying with EOA execution...");
3203
+ const eoaProviderState = await createCliProviderState({
3204
+ decision: eoaDecision,
3205
+ chain: primaryChain,
3206
+ privateKey,
3207
+ rpcUrl: rpcUrl != null ? rpcUrl : "",
3208
+ callList
3209
+ });
3210
+ return {
3211
+ execution: await runExecution(eoaProviderState),
3212
+ finalDecision: eoaDecision
3213
+ };
3214
+ }
3215
+ }
2282
3216
  async function signCommand(runtime) {
2283
- var _a3, _b, _c, _d;
2284
- const txId = runtime.parsed.positional[0];
2285
- if (!txId) {
3217
+ var _a3, _b;
3218
+ const txIds = runtime.parsed.positional;
3219
+ if (txIds.length === 0) {
2286
3220
  fatal(
2287
- "Usage: aomi sign <tx-id>\nRun `aomi tx` to see pending transaction IDs."
3221
+ "Usage: aomi sign <tx-id> [<tx-id> ...]\nRun `aomi tx` to see pending transaction IDs."
2288
3222
  );
2289
3223
  }
2290
3224
  const privateKey = runtime.config.privateKey;
@@ -2303,10 +3237,10 @@ async function signCommand(runtime) {
2303
3237
  fatal("No active session. Run `aomi chat` first.");
2304
3238
  }
2305
3239
  rewriteSessionState(runtime, state);
2306
- const pendingTx = requirePendingTx(state, txId);
3240
+ const pendingTxs = requirePendingTxs(state, txIds);
2307
3241
  const session = createSessionFromState(state);
2308
3242
  try {
2309
- const account = privateKeyToAccount(privateKey);
3243
+ const account = privateKeyToAccount2(privateKey);
2310
3244
  if (state.publicKey && account.address.toLowerCase() !== state.publicKey.toLowerCase()) {
2311
3245
  console.log(
2312
3246
  `\u26A0\uFE0F Signer ${account.address} differs from session public key ${state.publicKey}`
@@ -2314,62 +3248,95 @@ async function signCommand(runtime) {
2314
3248
  console.log(" Updating session to match the signing key...");
2315
3249
  }
2316
3250
  const rpcUrl = runtime.config.chainRpcUrl;
2317
- const targetChainId = (_b = (_a3 = pendingTx.chainId) != null ? _a3 : state.chainId) != null ? _b : 1;
2318
- const chain = (_c = Object.values(viemChains).find(
2319
- (candidate) => typeof candidate === "object" && candidate !== null && "id" in candidate && candidate.id === targetChainId
2320
- )) != null ? _c : {
2321
- id: targetChainId,
2322
- name: `Chain ${targetChainId}`,
2323
- nativeCurrency: {
2324
- name: "ETH",
2325
- symbol: "ETH",
2326
- decimals: 18
2327
- },
2328
- rpcUrls: {
2329
- default: {
2330
- http: [rpcUrl != null ? rpcUrl : ""]
2331
- }
2332
- }
2333
- };
2334
- const walletClient = createWalletClient({
2335
- account,
2336
- chain,
2337
- transport: http(rpcUrl)
3251
+ const resolvedChainIds = pendingTxs.map((tx) => {
3252
+ var _a4, _b2;
3253
+ return (_b2 = (_a4 = tx.chainId) != null ? _a4 : state.chainId) != null ? _b2 : 1;
2338
3254
  });
3255
+ const primaryChainId = resolvedChainIds[0];
3256
+ const chain = resolveChain(primaryChainId, rpcUrl);
3257
+ const resolvedRpcUrl = getPreferredRpcUrl(chain, rpcUrl);
3258
+ const chainsById = Object.fromEntries(
3259
+ Array.from(new Set(resolvedChainIds)).map((chainId) => [
3260
+ chainId,
3261
+ resolveChain(chainId, rpcUrl)
3262
+ ])
3263
+ );
2339
3264
  console.log(`Signer: ${account.address}`);
2340
- console.log(`ID: ${pendingTx.id}`);
2341
- console.log(`Kind: ${pendingTx.kind}`);
2342
- let signedRecord;
2343
- let backendNotification;
2344
- if (pendingTx.kind === "transaction") {
2345
- console.log(`To: ${pendingTx.to}`);
2346
- if (pendingTx.value) console.log(`Value: ${pendingTx.value}`);
2347
- if (pendingTx.chainId) console.log(`Chain: ${pendingTx.chainId}`);
2348
- if (pendingTx.data) {
2349
- console.log(`Data: ${pendingTx.data.slice(0, 40)}...`);
3265
+ console.log(`IDs: ${pendingTxs.map((tx) => tx.id).join(", ")}`);
3266
+ let signedRecords = [];
3267
+ let backendNotifications = [];
3268
+ if (pendingTxs.every((tx) => tx.kind === "transaction")) {
3269
+ console.log(`Kind: transaction${pendingTxs.length > 1 ? " (batch)" : ""}`);
3270
+ for (const tx of pendingTxs) {
3271
+ console.log(`Tx: ${tx.id} -> ${tx.to}`);
3272
+ if (tx.value) console.log(`Value: ${tx.value}`);
3273
+ if ((_a3 = tx.chainId) != null ? _a3 : state.chainId) console.log(`Chain: ${(_b = tx.chainId) != null ? _b : state.chainId}`);
3274
+ if (tx.data) {
3275
+ console.log(`Data: ${tx.data.slice(0, 40)}...`);
3276
+ }
2350
3277
  }
2351
3278
  console.log();
2352
- const hash = await walletClient.sendTransaction({
2353
- to: pendingTx.to,
2354
- value: pendingTx.value ? BigInt(pendingTx.value) : /* @__PURE__ */ BigInt("0"),
2355
- data: (_d = pendingTx.data) != null ? _d : void 0
3279
+ const callList = pendingTxs.flatMap(
3280
+ (tx, index) => pendingTxToCallList(__spreadProps(__spreadValues({}, tx), {
3281
+ chainId: resolvedChainIds[index]
3282
+ }))
3283
+ );
3284
+ if (callList.length > 1 && rpcUrl && new Set(callList.map((call) => call.chainId)).size > 1) {
3285
+ fatal("A single `--rpc-url` override cannot be used for a mixed-chain multi-sign request.");
3286
+ }
3287
+ const decision = resolveCliExecutionDecision({
3288
+ config: runtime.config,
3289
+ chain,
3290
+ callList
2356
3291
  });
2357
- console.log(`\u2705 Sent! Hash: ${hash}`);
2358
- signedRecord = {
2359
- id: txId,
2360
- kind: "transaction",
2361
- txHash: hash,
2362
- from: account.address,
2363
- to: pendingTx.to,
2364
- value: pendingTx.value,
2365
- chainId: pendingTx.chainId,
2366
- timestamp: Date.now()
2367
- };
2368
- backendNotification = {
3292
+ console.log(`Exec: ${describeExecutionDecision(decision)}`);
3293
+ const { execution, finalDecision } = await executeTransactionWithFallback({
3294
+ decision,
3295
+ privateKey,
3296
+ currentChainId: primaryChainId,
3297
+ chainsById,
3298
+ primaryChain: chain,
3299
+ rpcUrl,
3300
+ callList
3301
+ });
3302
+ console.log(`\u2705 Sent! Hash: ${execution.txHash}`);
3303
+ if (execution.txHashes.length > 1) {
3304
+ console.log(`Count: ${execution.txHashes.length}`);
3305
+ }
3306
+ if (execution.sponsored) {
3307
+ console.log("Gas: sponsored");
3308
+ }
3309
+ if (execution.AAAddress) {
3310
+ console.log(`AA: ${execution.AAAddress}`);
3311
+ }
3312
+ if (execution.delegationAddress) {
3313
+ console.log(`Deleg: ${execution.delegationAddress}`);
3314
+ }
3315
+ signedRecords = pendingTxs.map(
3316
+ (tx, index) => toSignedTransactionRecord(
3317
+ tx,
3318
+ execution,
3319
+ account.address,
3320
+ resolvedChainIds[index],
3321
+ Date.now(),
3322
+ finalDecision.execution === "aa" ? finalDecision.provider : void 0,
3323
+ finalDecision.execution === "aa" ? finalDecision.aaMode : void 0
3324
+ )
3325
+ );
3326
+ backendNotifications = pendingTxs.map(() => ({
2369
3327
  type: "wallet:tx_complete",
2370
- payload: { txHash: hash, status: "success" }
2371
- };
3328
+ payload: { txHash: execution.txHash, status: "success" }
3329
+ }));
2372
3330
  } else {
3331
+ if (pendingTxs.length > 1) {
3332
+ fatal("Batch signing is only supported for transaction requests, not EIP-712 requests.");
3333
+ }
3334
+ const pendingTx = pendingTxs[0];
3335
+ const walletClient = createWalletClient({
3336
+ account,
3337
+ chain,
3338
+ transport: http(resolvedRpcUrl)
3339
+ });
2373
3340
  const typedData = pendingTx.payload.typed_data;
2374
3341
  if (!typedData) {
2375
3342
  fatal("EIP-712 request is missing typed_data payload.");
@@ -2391,36 +3358,42 @@ async function signCommand(runtime) {
2391
3358
  message
2392
3359
  });
2393
3360
  console.log(`\u2705 Signed! Signature: ${signature.slice(0, 20)}...`);
2394
- signedRecord = {
2395
- id: txId,
3361
+ signedRecords = [{
3362
+ id: pendingTx.id,
2396
3363
  kind: "eip712_sign",
2397
3364
  signature,
2398
3365
  from: account.address,
2399
3366
  description: pendingTx.description,
2400
3367
  timestamp: Date.now()
2401
- };
2402
- backendNotification = {
3368
+ }];
3369
+ backendNotifications = [{
2403
3370
  type: "wallet_eip712_response",
2404
3371
  payload: {
2405
3372
  status: "success",
2406
3373
  signature,
2407
3374
  description: pendingTx.description
2408
3375
  }
2409
- };
3376
+ }];
2410
3377
  }
2411
3378
  await persistResolvedSignerState(
2412
3379
  session,
2413
3380
  state,
2414
3381
  account.address,
2415
- targetChainId
3382
+ primaryChainId
2416
3383
  );
2417
- removePendingTx(state, txId);
3384
+ for (const txId of txIds) {
3385
+ removePendingTx(state, txId);
3386
+ }
2418
3387
  const freshState = readState();
2419
- addSignedTx(freshState, signedRecord);
2420
- await session.client.sendSystemMessage(
2421
- state.sessionId,
2422
- JSON.stringify(backendNotification)
2423
- );
3388
+ for (const signedRecord of signedRecords) {
3389
+ addSignedTx(freshState, signedRecord);
3390
+ }
3391
+ for (const backendNotification of backendNotifications) {
3392
+ await session.client.sendSystemMessage(
3393
+ state.sessionId,
3394
+ JSON.stringify(backendNotification)
3395
+ );
3396
+ }
2424
3397
  console.log("Backend notified.");
2425
3398
  } catch (err) {
2426
3399
  if (err instanceof CliExit) throw err;
@@ -2450,7 +3423,8 @@ Usage:
2450
3423
  Delete a local session file (session-id or session-N)
2451
3424
  aomi log Show full conversation history with tool results
2452
3425
  aomi tx List pending and signed transactions
2453
- aomi sign <tx-id> Sign and submit a pending transaction
3426
+ aomi sign <tx-id> [<tx-id> ...] [--aa | --eoa] [--aa-provider <name>] [--aa-mode <mode>]
3427
+ Sign and submit a pending transaction
2454
3428
  aomi status Show current session state
2455
3429
  aomi events List system events
2456
3430
  aomi close Close the current session
@@ -2465,12 +3439,28 @@ Options:
2465
3439
  --rpc-url <url> RPC URL for transaction submission
2466
3440
  --verbose, -v Show tool calls and streaming output (for chat)
2467
3441
 
3442
+ Sign options:
3443
+ aomi sign <tx-id> --aa
3444
+ Require account-abstraction execution (default)
3445
+ aomi sign <tx-id> --eoa
3446
+ Force plain EOA execution
3447
+ aomi sign <tx-id> --aa-provider <name>
3448
+ AA provider: alchemy | pimlico
3449
+ aomi sign <tx-id> --aa-mode <mode>
3450
+ AA mode: 4337 | 7702
3451
+
2468
3452
  Environment (overridden by flags):
2469
3453
  AOMI_BASE_URL Backend URL
2470
3454
  AOMI_API_KEY API key
2471
3455
  AOMI_APP App
2472
3456
  AOMI_MODEL Model rig
2473
3457
  AOMI_PUBLIC_KEY Wallet address
3458
+ AOMI_AA_PROVIDER AA provider: alchemy | pimlico
3459
+ AOMI_AA_MODE AA mode: 4337 | 7702
3460
+ ALCHEMY_API_KEY Alchemy AA API key
3461
+ ALCHEMY_GAS_POLICY_ID
3462
+ Optional Alchemy gas sponsorship policy ID
3463
+ PIMLICO_API_KEY Pimlico AA API key
2474
3464
  PRIVATE_KEY Hex private key for signing
2475
3465
  CHAIN_RPC_URL RPC URL for transaction submission
2476
3466
  `.trim());