@aomi-labs/client 0.1.5 → 0.1.7

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) {
@@ -1514,12 +1557,12 @@ var ClientSession = class extends TypedEventEmitter {
1514
1557
  }
1515
1558
  }
1516
1559
  assertUserStateAligned(actualUserState) {
1517
- if (!this.userState) {
1560
+ if (!this.userState || !actualUserState) {
1518
1561
  return;
1519
1562
  }
1520
- if (!actualUserState || !isSubsetMatch(this.userState, actualUserState)) {
1563
+ if (!isSubsetMatch(this.userState, actualUserState)) {
1521
1564
  const expected = JSON.stringify(sortJson(this.userState));
1522
- const actual = JSON.stringify(sortJson(actualUserState != null ? actualUserState : null));
1565
+ const actual = JSON.stringify(sortJson(actualUserState));
1523
1566
  throw new Error(
1524
1567
  `Backend user_state mismatch. expected subset=${expected} actual=${actual}`
1525
1568
  );
@@ -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,647 @@ 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
+ } = options;
2607
+ if (!calls || localPrivateKey) {
2608
+ return null;
2609
+ }
2610
+ const config = __spreadProps(__spreadValues({}, accountAbstractionConfig), {
2611
+ provider: "alchemy"
2612
+ });
2613
+ const chainConfig = getAAChainConfig(config, calls, chainsById);
2614
+ if (!chainConfig) {
2615
+ if (throwOnMissingConfig) {
2616
+ const chainIds = Array.from(new Set(calls.map((c) => c.chainId)));
2617
+ throw new Error(
2618
+ `AA is not configured for chain ${chainIds[0]}, or batching is disabled for that chain.`
2619
+ );
2620
+ }
2621
+ return null;
2622
+ }
2623
+ const apiKey = readEnv(ALCHEMY_API_KEY_ENVS, { publicOnly });
2624
+ if (!apiKey) {
2625
+ if (throwOnMissingConfig) {
2626
+ throw new Error("Alchemy AA requires ALCHEMY_API_KEY.");
2627
+ }
2628
+ return null;
2629
+ }
2630
+ const chain = chainsById[chainConfig.chainId];
2631
+ if (!chain) {
2632
+ return null;
2633
+ }
2634
+ const gasPolicyId = readGasPolicyEnv(
2635
+ chainConfig.chainId,
2636
+ chainSlugById,
2637
+ ALCHEMY_GAS_POLICY_ENVS,
2638
+ { publicOnly }
2639
+ );
2640
+ if (chainConfig.sponsorship === "required" && !gasPolicyId) {
2641
+ if (throwOnMissingConfig) {
2642
+ throw new Error(
2643
+ `Alchemy gas policy required for chain ${chainConfig.chainId} but not configured.`
2644
+ );
2645
+ }
2646
+ return null;
2647
+ }
2648
+ if (modeOverride && !chainConfig.supportedModes.includes(modeOverride)) {
2649
+ if (throwOnMissingConfig) {
2650
+ throw new Error(
2651
+ `AA mode "${modeOverride}" is not supported on chain ${chainConfig.chainId}.`
2652
+ );
2653
+ }
2654
+ return null;
2655
+ }
2656
+ const resolvedChainConfig = modeOverride ? __spreadProps(__spreadValues({}, chainConfig), { defaultMode: modeOverride }) : chainConfig;
2657
+ const plan = buildAAExecutionPlan(config, resolvedChainConfig);
2658
+ return {
2659
+ chainConfig: resolvedChainConfig,
2660
+ plan,
2661
+ apiKey,
2662
+ chain,
2663
+ rpcUrl: getPreferredRpcUrl2(chain),
2664
+ gasPolicyId,
2665
+ mode: resolvedChainConfig.defaultMode
2666
+ };
2667
+ }
2668
+ function resolvePimlicoConfig(options) {
2669
+ const {
2670
+ calls,
2671
+ localPrivateKey,
2672
+ accountAbstractionConfig = DEFAULT_AA_CONFIG,
2673
+ chainsById,
2674
+ rpcUrl,
2675
+ modeOverride,
2676
+ publicOnly = false,
2677
+ throwOnMissingConfig = false
2678
+ } = options;
2679
+ if (!calls || localPrivateKey) {
2680
+ return null;
2681
+ }
2682
+ const config = __spreadProps(__spreadValues({}, accountAbstractionConfig), {
2683
+ provider: "pimlico"
2684
+ });
2685
+ const chainConfig = getAAChainConfig(config, calls, chainsById);
2686
+ if (!chainConfig) {
2687
+ if (throwOnMissingConfig) {
2688
+ const chainIds = Array.from(new Set(calls.map((c) => c.chainId)));
2689
+ throw new Error(
2690
+ `AA is not configured for chain ${chainIds[0]}, or batching is disabled for that chain.`
2691
+ );
2692
+ }
2693
+ return null;
2694
+ }
2695
+ const apiKey = readEnv(PIMLICO_API_KEY_ENVS, { publicOnly });
2696
+ if (!apiKey) {
2697
+ if (throwOnMissingConfig) {
2698
+ throw new Error("Pimlico AA requires PIMLICO_API_KEY.");
2699
+ }
2700
+ return null;
2701
+ }
2702
+ const chain = chainsById[chainConfig.chainId];
2703
+ if (!chain) {
2704
+ return null;
2705
+ }
2706
+ if (modeOverride && !chainConfig.supportedModes.includes(modeOverride)) {
2707
+ if (throwOnMissingConfig) {
2708
+ throw new Error(
2709
+ `AA mode "${modeOverride}" is not supported on chain ${chainConfig.chainId}.`
2710
+ );
2711
+ }
2712
+ return null;
2713
+ }
2714
+ const resolvedChainConfig = modeOverride ? __spreadProps(__spreadValues({}, chainConfig), { defaultMode: modeOverride }) : chainConfig;
2715
+ const plan = buildAAExecutionPlan(config, resolvedChainConfig);
2716
+ return {
2717
+ chainConfig: resolvedChainConfig,
2718
+ plan,
2719
+ apiKey,
2720
+ chain,
2721
+ rpcUrl,
2722
+ mode: resolvedChainConfig.defaultMode
2723
+ };
2724
+ }
2725
+
2726
+ // src/aa/adapt.ts
2727
+ function adaptSmartAccount(account) {
2728
+ return {
2729
+ provider: account.provider,
2730
+ mode: account.mode,
2731
+ AAAddress: account.smartAccountAddress,
2732
+ delegationAddress: account.delegationAddress,
2733
+ sendTransaction: async (call) => {
2734
+ const receipt = await account.sendTransaction(call);
2735
+ return { transactionHash: receipt.transactionHash };
2736
+ },
2737
+ sendBatchTransaction: async (calls) => {
2738
+ const receipt = await account.sendBatchTransaction(calls);
2739
+ return { transactionHash: receipt.transactionHash };
2740
+ }
2741
+ };
2742
+ }
2743
+ function isAlchemySponsorshipLimitError(error) {
2744
+ const message = error instanceof Error ? error.message : String(error != null ? error : "");
2745
+ const normalized = message.toLowerCase();
2746
+ 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");
2747
+ }
2748
+
2749
+ // src/aa/create.ts
2750
+ import { createAlchemySmartAccount } from "@getpara/aa-alchemy";
2751
+ import { createPimlicoSmartAccount } from "@getpara/aa-pimlico";
2752
+ import { privateKeyToAccount } from "viem/accounts";
2753
+ async function createAAProviderState(options) {
2754
+ if (options.provider === "alchemy") {
2755
+ return createAlchemyAAState(options);
2756
+ }
2757
+ return createPimlicoAAState(options);
2758
+ }
2759
+ async function createAlchemyAAState(options) {
2760
+ var _a3, _b;
2761
+ const {
2762
+ chain,
2763
+ privateKey,
2764
+ rpcUrl,
2765
+ callList,
2766
+ mode,
2767
+ sponsored = true
2768
+ } = options;
2769
+ const resolved = resolveAlchemyConfig({
2770
+ calls: callList,
2771
+ chainsById: { [chain.id]: chain },
2772
+ modeOverride: mode,
2773
+ throwOnMissingConfig: true,
2774
+ getPreferredRpcUrl: () => rpcUrl
2775
+ });
2776
+ if (!resolved) {
2777
+ throw new Error("Alchemy AA config resolution failed.");
2778
+ }
2779
+ const apiKey = (_a3 = options.apiKey) != null ? _a3 : resolved.apiKey;
2780
+ const gasPolicyId = sponsored ? (_b = options.gasPolicyId) != null ? _b : readEnv(ALCHEMY_GAS_POLICY_ENVS) : void 0;
2781
+ const plan = __spreadProps(__spreadValues({}, resolved.plan), {
2782
+ sponsorship: gasPolicyId ? resolved.plan.sponsorship : "disabled",
2783
+ fallbackToEoa: false
2784
+ });
2785
+ const signer = privateKeyToAccount(privateKey);
2786
+ try {
2787
+ const smartAccount = await createAlchemySmartAccount({
2788
+ para: void 0,
2789
+ signer,
2790
+ apiKey,
2791
+ gasPolicyId,
2792
+ chain,
2793
+ rpcUrl,
2794
+ mode: plan.mode
2795
+ });
2796
+ if (!smartAccount) {
2797
+ return {
2798
+ plan,
2799
+ AA: null,
2800
+ isPending: false,
2801
+ error: new Error("Alchemy AA account could not be initialized.")
2802
+ };
2803
+ }
2804
+ return {
2805
+ plan,
2806
+ AA: adaptSmartAccount(smartAccount),
2807
+ isPending: false,
2808
+ error: null
2809
+ };
2810
+ } catch (error) {
2811
+ return {
2812
+ plan,
2813
+ AA: null,
2814
+ isPending: false,
2815
+ error: error instanceof Error ? error : new Error(String(error))
2816
+ };
2817
+ }
2818
+ }
2819
+ async function createPimlicoAAState(options) {
2820
+ var _a3;
2821
+ const {
2822
+ chain,
2823
+ privateKey,
2824
+ rpcUrl,
2825
+ callList,
2826
+ mode
2827
+ } = options;
2828
+ const resolved = resolvePimlicoConfig({
2829
+ calls: callList,
2830
+ chainsById: { [chain.id]: chain },
2831
+ rpcUrl,
2832
+ modeOverride: mode,
2833
+ throwOnMissingConfig: true
2834
+ });
2835
+ if (!resolved) {
2836
+ throw new Error("Pimlico AA config resolution failed.");
2837
+ }
2838
+ const apiKey = (_a3 = options.apiKey) != null ? _a3 : resolved.apiKey;
2839
+ const plan = __spreadProps(__spreadValues({}, resolved.plan), {
2840
+ fallbackToEoa: false
2841
+ });
2842
+ const signer = privateKeyToAccount(privateKey);
2843
+ try {
2844
+ const smartAccount = await createPimlicoSmartAccount({
2845
+ para: void 0,
2846
+ signer,
2847
+ apiKey,
2848
+ chain,
2849
+ rpcUrl,
2850
+ mode: plan.mode
2851
+ });
2852
+ if (!smartAccount) {
2853
+ return {
2854
+ plan,
2855
+ AA: null,
2856
+ isPending: false,
2857
+ error: new Error("Pimlico AA account could not be initialized.")
2858
+ };
2859
+ }
2860
+ return {
2861
+ plan,
2862
+ AA: adaptSmartAccount(smartAccount),
2863
+ isPending: false,
2864
+ error: null
2865
+ };
2866
+ } catch (error) {
2867
+ return {
2868
+ plan,
2869
+ AA: null,
2870
+ isPending: false,
2871
+ error: error instanceof Error ? error : new Error(String(error))
2872
+ };
2873
+ }
2874
+ }
2875
+
2876
+ // src/cli/execution.ts
2877
+ function resolveAAProvider(config) {
2878
+ var _a3;
2879
+ const provider = (_a3 = config.aaProvider) != null ? _a3 : resolveDefaultProvider();
2880
+ if (!isProviderConfigured(provider)) {
2881
+ const envName = provider === "alchemy" ? "ALCHEMY_API_KEY" : "PIMLICO_API_KEY";
2882
+ throw new Error(
2883
+ `AA provider "${provider}" is selected but ${envName} is not configured.`
2884
+ );
2885
+ }
2886
+ return provider;
2887
+ }
2888
+ function resolveCliExecutionDecision(params) {
2889
+ const { config, chain, callList } = params;
2890
+ if (config.execution === "eoa") {
2891
+ return { execution: "eoa" };
2892
+ }
2893
+ const provider = resolveAAProvider(config);
2894
+ const resolveOpts = {
2895
+ calls: callList,
2896
+ chainsById: { [chain.id]: chain },
2897
+ modeOverride: config.aaMode,
2898
+ throwOnMissingConfig: true
2899
+ };
2900
+ const resolved = provider === "alchemy" ? resolveAlchemyConfig(resolveOpts) : resolvePimlicoConfig(resolveOpts);
2901
+ if (!resolved) {
2902
+ throw new Error(`AA config resolution failed for provider "${provider}".`);
2903
+ }
2904
+ return {
2905
+ execution: "aa",
2906
+ provider,
2907
+ aaMode: resolved.plan.mode
2908
+ };
2909
+ }
2910
+ async function createCliProviderState(params) {
2911
+ const { decision, chain, privateKey, rpcUrl, callList, sponsored } = params;
2912
+ if (decision.execution === "eoa") {
2913
+ return DISABLED_PROVIDER_STATE;
2914
+ }
2915
+ return createAAProviderState({
2916
+ provider: decision.provider,
2917
+ chain,
2918
+ privateKey,
2919
+ rpcUrl,
2920
+ callList,
2921
+ mode: decision.aaMode,
2922
+ sponsored
2923
+ });
2924
+ }
2925
+ function describeExecutionDecision(decision) {
2926
+ if (decision.execution === "eoa") {
2927
+ return "eoa";
2928
+ }
2929
+ return `aa (${decision.provider}, ${decision.aaMode})`;
2930
+ }
2931
+
2932
+ // src/cli/commands/wallet.ts
2188
2933
  function txCommand() {
2189
- var _a3, _b, _c;
2934
+ var _a3, _b;
2190
2935
  const state = readState();
2191
2936
  if (!state) {
2192
2937
  console.log("No active session");
@@ -2210,17 +2955,7 @@ function txCommand() {
2210
2955
  if (pending.length > 0) console.log();
2211
2956
  console.log(`Signed (${signed.length}):`);
2212
2957
  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(" "));
2958
+ console.log(formatSignedTxLine(tx, " \u2705"));
2224
2959
  }
2225
2960
  }
2226
2961
  printDataFileLocation();
@@ -2236,6 +2971,13 @@ Run \`aomi tx\` to see available IDs.`
2236
2971
  }
2237
2972
  return pendingTx;
2238
2973
  }
2974
+ function requirePendingTxs(state, txIds) {
2975
+ const uniqueIds = Array.from(new Set(txIds));
2976
+ if (uniqueIds.length !== txIds.length) {
2977
+ fatal("Duplicate transaction IDs are not allowed in a single `aomi sign` call.");
2978
+ }
2979
+ return uniqueIds.map((txId) => requirePendingTx(state, txId));
2980
+ }
2239
2981
  function rewriteSessionState(runtime, state) {
2240
2982
  let changed = false;
2241
2983
  if (runtime.config.baseUrl !== state.baseUrl) {
@@ -2279,12 +3021,147 @@ async function persistResolvedSignerState(session, state, address, chainId) {
2279
3021
  session.resolveWallet(address, chainId);
2280
3022
  await session.syncUserState();
2281
3023
  }
3024
+ function resolveChain(targetChainId, rpcUrl) {
3025
+ var _a3;
3026
+ return (_a3 = Object.values(viemChains).find(
3027
+ (candidate) => typeof candidate === "object" && candidate !== null && "id" in candidate && candidate.id === targetChainId
3028
+ )) != null ? _a3 : {
3029
+ id: targetChainId,
3030
+ name: `Chain ${targetChainId}`,
3031
+ nativeCurrency: {
3032
+ name: "ETH",
3033
+ symbol: "ETH",
3034
+ decimals: 18
3035
+ },
3036
+ rpcUrls: {
3037
+ default: {
3038
+ http: rpcUrl ? [rpcUrl] : []
3039
+ }
3040
+ }
3041
+ };
3042
+ }
3043
+ function getPreferredRpcUrl(chain, override) {
3044
+ var _a3, _b, _c;
3045
+ 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 : "";
3046
+ }
3047
+ async function promptForEoaFallback() {
3048
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
3049
+ return false;
3050
+ }
3051
+ const rl = createInterface({
3052
+ input: process.stdin,
3053
+ output: process.stdout
3054
+ });
3055
+ try {
3056
+ const answer = await rl.question(
3057
+ "Account abstraction not available, use EOA? [yes/no] "
3058
+ );
3059
+ const normalized = answer.trim().toLowerCase();
3060
+ return normalized === "y" || normalized === "yes";
3061
+ } finally {
3062
+ rl.close();
3063
+ }
3064
+ }
3065
+ async function executeCliTransaction(params) {
3066
+ const { privateKey, currentChainId, chainsById, rpcUrl, providerState, callList } = params;
3067
+ const unsupportedWalletMethod = async () => {
3068
+ throw new Error("wallet_client_path_unavailable_in_cli_private_key_mode");
3069
+ };
3070
+ return executeWalletCalls({
3071
+ callList,
3072
+ currentChainId,
3073
+ capabilities: void 0,
3074
+ localPrivateKey: privateKey,
3075
+ providerState,
3076
+ sendCallsSyncAsync: unsupportedWalletMethod,
3077
+ sendTransactionAsync: unsupportedWalletMethod,
3078
+ switchChainAsync: async () => void 0,
3079
+ chainsById,
3080
+ getPreferredRpcUrl: (resolvedChain) => getPreferredRpcUrl(resolvedChain, rpcUrl)
3081
+ });
3082
+ }
3083
+ async function executeTransactionWithFallback(params) {
3084
+ const { decision, privateKey, currentChainId, chainsById, primaryChain, rpcUrl, callList } = params;
3085
+ const runExecution = async (providerState2) => executeCliTransaction({
3086
+ privateKey,
3087
+ currentChainId,
3088
+ chainsById,
3089
+ rpcUrl,
3090
+ providerState: providerState2,
3091
+ callList
3092
+ });
3093
+ if (decision.execution === "eoa") {
3094
+ const providerState2 = await createCliProviderState({
3095
+ decision,
3096
+ chain: primaryChain,
3097
+ privateKey,
3098
+ rpcUrl: rpcUrl != null ? rpcUrl : "",
3099
+ callList
3100
+ });
3101
+ return {
3102
+ execution: await runExecution(providerState2),
3103
+ finalDecision: decision
3104
+ };
3105
+ }
3106
+ let providerState = await createCliProviderState({
3107
+ decision,
3108
+ chain: primaryChain,
3109
+ privateKey,
3110
+ rpcUrl: rpcUrl != null ? rpcUrl : "",
3111
+ callList,
3112
+ sponsored: true
3113
+ });
3114
+ try {
3115
+ return {
3116
+ execution: await runExecution(providerState),
3117
+ finalDecision: decision
3118
+ };
3119
+ } catch (error) {
3120
+ const shouldRetryUnsponsored = decision.provider === "alchemy" && isAlchemySponsorshipLimitError(error);
3121
+ if (shouldRetryUnsponsored) {
3122
+ console.log("AA sponsorship unavailable. Retrying AA with user-funded gas...");
3123
+ providerState = await createCliProviderState({
3124
+ decision,
3125
+ chain: primaryChain,
3126
+ privateKey,
3127
+ rpcUrl: rpcUrl != null ? rpcUrl : "",
3128
+ callList,
3129
+ sponsored: false
3130
+ });
3131
+ try {
3132
+ return {
3133
+ execution: await runExecution(providerState),
3134
+ finalDecision: decision
3135
+ };
3136
+ } catch (retryError) {
3137
+ error = retryError;
3138
+ }
3139
+ }
3140
+ const useEoa = await promptForEoaFallback();
3141
+ if (!useEoa) {
3142
+ throw error;
3143
+ }
3144
+ const eoaDecision = { execution: "eoa" };
3145
+ console.log("Retrying with EOA execution...");
3146
+ const eoaProviderState = await createCliProviderState({
3147
+ decision: eoaDecision,
3148
+ chain: primaryChain,
3149
+ privateKey,
3150
+ rpcUrl: rpcUrl != null ? rpcUrl : "",
3151
+ callList
3152
+ });
3153
+ return {
3154
+ execution: await runExecution(eoaProviderState),
3155
+ finalDecision: eoaDecision
3156
+ };
3157
+ }
3158
+ }
2282
3159
  async function signCommand(runtime) {
2283
- var _a3, _b, _c, _d;
2284
- const txId = runtime.parsed.positional[0];
2285
- if (!txId) {
3160
+ var _a3, _b;
3161
+ const txIds = runtime.parsed.positional;
3162
+ if (txIds.length === 0) {
2286
3163
  fatal(
2287
- "Usage: aomi sign <tx-id>\nRun `aomi tx` to see pending transaction IDs."
3164
+ "Usage: aomi sign <tx-id> [<tx-id> ...]\nRun `aomi tx` to see pending transaction IDs."
2288
3165
  );
2289
3166
  }
2290
3167
  const privateKey = runtime.config.privateKey;
@@ -2303,10 +3180,10 @@ async function signCommand(runtime) {
2303
3180
  fatal("No active session. Run `aomi chat` first.");
2304
3181
  }
2305
3182
  rewriteSessionState(runtime, state);
2306
- const pendingTx = requirePendingTx(state, txId);
3183
+ const pendingTxs = requirePendingTxs(state, txIds);
2307
3184
  const session = createSessionFromState(state);
2308
3185
  try {
2309
- const account = privateKeyToAccount(privateKey);
3186
+ const account = privateKeyToAccount2(privateKey);
2310
3187
  if (state.publicKey && account.address.toLowerCase() !== state.publicKey.toLowerCase()) {
2311
3188
  console.log(
2312
3189
  `\u26A0\uFE0F Signer ${account.address} differs from session public key ${state.publicKey}`
@@ -2314,62 +3191,95 @@ async function signCommand(runtime) {
2314
3191
  console.log(" Updating session to match the signing key...");
2315
3192
  }
2316
3193
  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)
3194
+ const resolvedChainIds = pendingTxs.map((tx) => {
3195
+ var _a4, _b2;
3196
+ return (_b2 = (_a4 = tx.chainId) != null ? _a4 : state.chainId) != null ? _b2 : 1;
2338
3197
  });
3198
+ const primaryChainId = resolvedChainIds[0];
3199
+ const chain = resolveChain(primaryChainId, rpcUrl);
3200
+ const resolvedRpcUrl = getPreferredRpcUrl(chain, rpcUrl);
3201
+ const chainsById = Object.fromEntries(
3202
+ Array.from(new Set(resolvedChainIds)).map((chainId) => [
3203
+ chainId,
3204
+ resolveChain(chainId, rpcUrl)
3205
+ ])
3206
+ );
2339
3207
  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)}...`);
3208
+ console.log(`IDs: ${pendingTxs.map((tx) => tx.id).join(", ")}`);
3209
+ let signedRecords = [];
3210
+ let backendNotifications = [];
3211
+ if (pendingTxs.every((tx) => tx.kind === "transaction")) {
3212
+ console.log(`Kind: transaction${pendingTxs.length > 1 ? " (batch)" : ""}`);
3213
+ for (const tx of pendingTxs) {
3214
+ console.log(`Tx: ${tx.id} -> ${tx.to}`);
3215
+ if (tx.value) console.log(`Value: ${tx.value}`);
3216
+ if ((_a3 = tx.chainId) != null ? _a3 : state.chainId) console.log(`Chain: ${(_b = tx.chainId) != null ? _b : state.chainId}`);
3217
+ if (tx.data) {
3218
+ console.log(`Data: ${tx.data.slice(0, 40)}...`);
3219
+ }
2350
3220
  }
2351
3221
  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
3222
+ const callList = pendingTxs.flatMap(
3223
+ (tx, index) => pendingTxToCallList(__spreadProps(__spreadValues({}, tx), {
3224
+ chainId: resolvedChainIds[index]
3225
+ }))
3226
+ );
3227
+ if (callList.length > 1 && rpcUrl && new Set(callList.map((call) => call.chainId)).size > 1) {
3228
+ fatal("A single `--rpc-url` override cannot be used for a mixed-chain multi-sign request.");
3229
+ }
3230
+ const decision = resolveCliExecutionDecision({
3231
+ config: runtime.config,
3232
+ chain,
3233
+ callList
2356
3234
  });
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 = {
3235
+ console.log(`Exec: ${describeExecutionDecision(decision)}`);
3236
+ const { execution, finalDecision } = await executeTransactionWithFallback({
3237
+ decision,
3238
+ privateKey,
3239
+ currentChainId: primaryChainId,
3240
+ chainsById,
3241
+ primaryChain: chain,
3242
+ rpcUrl,
3243
+ callList
3244
+ });
3245
+ console.log(`\u2705 Sent! Hash: ${execution.txHash}`);
3246
+ if (execution.txHashes.length > 1) {
3247
+ console.log(`Count: ${execution.txHashes.length}`);
3248
+ }
3249
+ if (execution.sponsored) {
3250
+ console.log("Gas: sponsored");
3251
+ }
3252
+ if (execution.AAAddress) {
3253
+ console.log(`AA: ${execution.AAAddress}`);
3254
+ }
3255
+ if (execution.delegationAddress) {
3256
+ console.log(`Deleg: ${execution.delegationAddress}`);
3257
+ }
3258
+ signedRecords = pendingTxs.map(
3259
+ (tx, index) => toSignedTransactionRecord(
3260
+ tx,
3261
+ execution,
3262
+ account.address,
3263
+ resolvedChainIds[index],
3264
+ Date.now(),
3265
+ finalDecision.execution === "aa" ? finalDecision.provider : void 0,
3266
+ finalDecision.execution === "aa" ? finalDecision.aaMode : void 0
3267
+ )
3268
+ );
3269
+ backendNotifications = pendingTxs.map(() => ({
2369
3270
  type: "wallet:tx_complete",
2370
- payload: { txHash: hash, status: "success" }
2371
- };
3271
+ payload: { txHash: execution.txHash, status: "success" }
3272
+ }));
2372
3273
  } else {
3274
+ if (pendingTxs.length > 1) {
3275
+ fatal("Batch signing is only supported for transaction requests, not EIP-712 requests.");
3276
+ }
3277
+ const pendingTx = pendingTxs[0];
3278
+ const walletClient = createWalletClient({
3279
+ account,
3280
+ chain,
3281
+ transport: http(resolvedRpcUrl)
3282
+ });
2373
3283
  const typedData = pendingTx.payload.typed_data;
2374
3284
  if (!typedData) {
2375
3285
  fatal("EIP-712 request is missing typed_data payload.");
@@ -2391,36 +3301,42 @@ async function signCommand(runtime) {
2391
3301
  message
2392
3302
  });
2393
3303
  console.log(`\u2705 Signed! Signature: ${signature.slice(0, 20)}...`);
2394
- signedRecord = {
2395
- id: txId,
3304
+ signedRecords = [{
3305
+ id: pendingTx.id,
2396
3306
  kind: "eip712_sign",
2397
3307
  signature,
2398
3308
  from: account.address,
2399
3309
  description: pendingTx.description,
2400
3310
  timestamp: Date.now()
2401
- };
2402
- backendNotification = {
3311
+ }];
3312
+ backendNotifications = [{
2403
3313
  type: "wallet_eip712_response",
2404
3314
  payload: {
2405
3315
  status: "success",
2406
3316
  signature,
2407
3317
  description: pendingTx.description
2408
3318
  }
2409
- };
3319
+ }];
2410
3320
  }
2411
3321
  await persistResolvedSignerState(
2412
3322
  session,
2413
3323
  state,
2414
3324
  account.address,
2415
- targetChainId
3325
+ primaryChainId
2416
3326
  );
2417
- removePendingTx(state, txId);
3327
+ for (const txId of txIds) {
3328
+ removePendingTx(state, txId);
3329
+ }
2418
3330
  const freshState = readState();
2419
- addSignedTx(freshState, signedRecord);
2420
- await session.client.sendSystemMessage(
2421
- state.sessionId,
2422
- JSON.stringify(backendNotification)
2423
- );
3331
+ for (const signedRecord of signedRecords) {
3332
+ addSignedTx(freshState, signedRecord);
3333
+ }
3334
+ for (const backendNotification of backendNotifications) {
3335
+ await session.client.sendSystemMessage(
3336
+ state.sessionId,
3337
+ JSON.stringify(backendNotification)
3338
+ );
3339
+ }
2424
3340
  console.log("Backend notified.");
2425
3341
  } catch (err) {
2426
3342
  if (err instanceof CliExit) throw err;
@@ -2450,7 +3366,8 @@ Usage:
2450
3366
  Delete a local session file (session-id or session-N)
2451
3367
  aomi log Show full conversation history with tool results
2452
3368
  aomi tx List pending and signed transactions
2453
- aomi sign <tx-id> Sign and submit a pending transaction
3369
+ aomi sign <tx-id> [<tx-id> ...] [--aa | --eoa] [--aa-provider <name>] [--aa-mode <mode>]
3370
+ Sign and submit a pending transaction
2454
3371
  aomi status Show current session state
2455
3372
  aomi events List system events
2456
3373
  aomi close Close the current session
@@ -2465,12 +3382,28 @@ Options:
2465
3382
  --rpc-url <url> RPC URL for transaction submission
2466
3383
  --verbose, -v Show tool calls and streaming output (for chat)
2467
3384
 
3385
+ Sign options:
3386
+ aomi sign <tx-id> --aa
3387
+ Require account-abstraction execution (default)
3388
+ aomi sign <tx-id> --eoa
3389
+ Force plain EOA execution
3390
+ aomi sign <tx-id> --aa-provider <name>
3391
+ AA provider: alchemy | pimlico
3392
+ aomi sign <tx-id> --aa-mode <mode>
3393
+ AA mode: 4337 | 7702
3394
+
2468
3395
  Environment (overridden by flags):
2469
3396
  AOMI_BASE_URL Backend URL
2470
3397
  AOMI_API_KEY API key
2471
3398
  AOMI_APP App
2472
3399
  AOMI_MODEL Model rig
2473
3400
  AOMI_PUBLIC_KEY Wallet address
3401
+ AOMI_AA_PROVIDER AA provider: alchemy | pimlico
3402
+ AOMI_AA_MODE AA mode: 4337 | 7702
3403
+ ALCHEMY_API_KEY Alchemy AA API key
3404
+ ALCHEMY_GAS_POLICY_ID
3405
+ Optional Alchemy gas sponsorship policy ID
3406
+ PIMLICO_API_KEY Pimlico AA API key
2474
3407
  PRIVATE_KEY Hex private key for signing
2475
3408
  CHAIN_RPC_URL RPC URL for transaction submission
2476
3409
  `.trim());