moneyos 0.6.0 → 0.6.2

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/CHANGELOG.md CHANGED
@@ -2,6 +2,59 @@
2
2
 
3
3
  All notable changes to the repo's current `main` branch are documented here.
4
4
 
5
+ ## 0.6.2 - 2026-04-16
6
+
7
+ Fixes the broken `npm install -g moneyos` path reported in #114.
8
+
9
+ ### Fixed
10
+
11
+ - `npm install -g moneyos` no longer ships with an empty `node_modules/viem/`
12
+ and no longer crashes with `ERR_MODULE_NOT_FOUND` before the CLI can print
13
+ `--help`. Root cause was `@moneyos/gasless` being bundled inside the moneyos
14
+ tarball while declaring `viem` as a peerDependency, which npm mishandles on
15
+ global install. The smart-account package is now published standalone as
16
+ `@moneyos/smart-account` and no longer bundled, so `viem` resolves normally
17
+ as a top-level sibling. See #114 and #118.
18
+
19
+ ### Changed
20
+
21
+ - `@moneyos/gasless` has been renamed to
22
+ [`@moneyos/smart-account`](https://www.npmjs.com/package/@moneyos/smart-account)
23
+ and is now a standalone npm package. The new name reflects the package's
24
+ actual surface: smart-account contracts, EIP-712 intent signing, nonce lanes,
25
+ and the gasless relay client are all included — gasless execution is one
26
+ capability built on those primitives. The `moneyos gasless` CLI command,
27
+ `config.gasless` key, and `MONEYOS_GASLESS_*` env vars all keep their names;
28
+ only the underlying package was renamed.
29
+ - Root `moneyos` tarball shrinks from 180KB to 143KB now that the smart-account
30
+ code lives in its own package alongside `viem` and other deps.
31
+ - `scripts/release-verify.mjs` now includes a `npm install -g` smoke test that
32
+ would have caught the 0.6.0 and 0.6.1 regressions before they shipped.
33
+
34
+ ## 0.6.1 - 2026-04-16
35
+
36
+ Patch release rolling up the gasless swap UX fixes from #101, #102, #108, and #110.
37
+
38
+ ### Fixed
39
+
40
+ - `moneyos balance` now shows both the EOA and smart-account balances when
41
+ gasless mode is enabled and `--address` is not passed, so users can see at a
42
+ glance which wallet actually holds funds (#103, #108).
43
+ - `moneyos swap` performs a client-side balance pre-check on the executor wallet
44
+ before requesting a quote, failing fast with a clear
45
+ `Insufficient <symbol> balance on <address>: need N, have M` error for both
46
+ ERC-20 and native inputs (#106, #108).
47
+ - Gasless swap failures surface the underlying revert reason in the error
48
+ message (e.g. `Gasless swap failed: ERC20: transfer amount exceeds balance
49
+ [simulation_failed]`) instead of the generic
50
+ `Relay accepted intent without txHash` (#105, #110).
51
+ - `@moneyos/smart-account` exports a typed `GaslessRelayError` class that carries
52
+ `revertReason`, `policyCode`, `reason`, and `submissionId` for callers that
53
+ want to react programmatically to relay rejections (#110).
54
+ - Local session executor submits the `approve` + `swap` pair as a single atomic
55
+ batch on batching executors, matching the relay policy that only sponsors
56
+ `[approve, swap]` shapes for ERC-20-in swaps (#101, #102).
57
+
5
58
  ## 0.6.0 - 2026-04-15
6
59
 
7
60
  Gasless execution, local contacts, and a wave of wallet UX cleanup.
@@ -9,7 +62,7 @@ Gasless execution, local contacts, and a wave of wallet UX cleanup.
9
62
  ### Added
10
63
 
11
64
  - Gasless execution mode, opt-in and default-off. `moneyos gasless status|enable|disable` routes write commands through a smart-account executor instead of the owner EOA. The relay sponsors gas; the smart account still has to hold the asset being sent or swapped. Arbitrum One is the v1 target, with baked defaults for the relay URL, sponsor, factory, and derived smart-account address so `moneyos gasless enable` works out of the box.
12
- - Smart-account primitives shipped inside the new `@moneyos/gasless` workspace package: `MoneyOSAccountV1`, `MoneyOSAccountFactoryV1` with deterministic CREATE2 deployment, EIP-712 `IntentV1`, ERC-1271 owner-only validation, and replay-safe signer-scoped nonce lanes. The package is bundled inside the published root `moneyos` tarball and is not published to npm on its own.
65
+ - Smart-account primitives shipped inside the new `@moneyos/smart-account` workspace package: `MoneyOSAccountV1`, `MoneyOSAccountFactoryV1` with deterministic CREATE2 deployment, EIP-712 `IntentV1`, ERC-1271 owner-only validation, and replay-safe signer-scoped nonce lanes. The package is bundled inside the published root `moneyos` tarball and is not published to npm on its own.
13
66
  - Hosted gasless relay at `services/relay/` — Fastify HTTP app with `POST /v1/execute`, `GET /v1/capabilities`, `GET /v1/tx/:id`; SQLite persistence for nonce reservations, submissions, and usage counters; nonce/simulation/treasury/wallet/health gates; submission adapter with `deployAndExecute` for undeployed smart accounts; kill switch; deploy artifacts for macOS launchd, Linux systemd, and Docker.
14
67
  - Local contacts address book. `moneyos contact set|list|remove` stores `name → address` pairs in `~/.moneyos/contacts.json` with 0600 permissions. `moneyos send <amount> <token> <name>` now accepts either a 0x address or a saved contact name, and prints the resolved address before execution so the recipient is always visible.
15
68
  - `moneyos update [tool] [--check]` for updating installed MoneyOS CLI tools from the user tool home.
package/README.md CHANGED
@@ -12,7 +12,7 @@ but the repo is structured so each major surface can evolve independently.
12
12
 
13
13
  - `moneyos`: the root SDK + CLI package for runtime, wallet, balance, and send
14
14
  - `@moneyos/core`: runtime interfaces, shared types, chain/token registries
15
- - `@moneyos/gasless`: smart-account contracts, intent helpers, gasless executor, relay client, and baked Arbitrum defaults
15
+ - `@moneyos/smart-account`: smart-account contracts, intent helpers, gasless executor, relay client, and baked Arbitrum defaults
16
16
  - `@moneyos/swap`: canonical swap package and Odos provider, published on npm
17
17
  - `services/relay`: the hosted relay service that sponsors gas for the gasless path
18
18
 
@@ -268,7 +268,7 @@ Published packages:
268
268
  - `@moneyos/core`
269
269
  - `@moneyos/swap`
270
270
 
271
- `@moneyos/gasless` exists in this repo but is not published to npm yet.
271
+ `@moneyos/smart-account` exists in this repo but is not published to npm yet.
272
272
 
273
273
  Current `moneyos` releases no longer bundle swap into the root SDK or CLI. If
274
274
  you want swap from the root CLI, install `moneyos` and then run
package/dist/cli/index.js CHANGED
@@ -653,7 +653,7 @@ import {
653
653
  GaslessExecutor,
654
654
  RelayClient,
655
655
  moneyOSAccountV1Abi
656
- } from "@moneyos/gasless";
656
+ } from "@moneyos/smart-account";
657
657
  import { createPublicClient, http } from "viem";
658
658
 
659
659
  // packages/core/dist/index.js
@@ -1003,7 +1003,7 @@ function createRequestId() {
1003
1003
  return `${Date.now()}-${Math.random().toString(16).slice(2, 10)}`;
1004
1004
  }
1005
1005
  function createSendRequestFingerprint(request) {
1006
- return JSON.stringify(request.params);
1006
+ return JSON.stringify({ type: request.type, params: request.params });
1007
1007
  }
1008
1008
  function toGaslessExecutionConfig(config) {
1009
1009
  return {
@@ -1142,6 +1142,14 @@ function unwrapSessionSendResponse(response) {
1142
1142
  }
1143
1143
  return response.result;
1144
1144
  }
1145
+ function capabilitiesForMode(mode) {
1146
+ const smartAccount = mode === "smart-account";
1147
+ return {
1148
+ sponsoredGas: smartAccount,
1149
+ batching: false,
1150
+ simulation: smartAccount
1151
+ };
1152
+ }
1145
1153
  async function getSessionStatus(socketPath, tokenPath) {
1146
1154
  try {
1147
1155
  const response = await sendSessionRequest(
@@ -1157,10 +1165,12 @@ async function getSessionStatus(socketPath, tokenPath) {
1157
1165
  return void 0;
1158
1166
  }
1159
1167
  const result = response.result;
1168
+ const mode = result.mode ?? "eoa";
1160
1169
  return {
1161
1170
  address: result.address,
1162
1171
  expiresAt: result.expiresAt,
1163
- mode: result.mode ?? "eoa"
1172
+ mode,
1173
+ capabilities: result.capabilities ?? capabilitiesForMode(mode)
1164
1174
  };
1165
1175
  } catch {
1166
1176
  removeFileIfPresent(socketPath);
@@ -1186,16 +1196,26 @@ async function lockSession(socketPath, tokenPath) {
1186
1196
  return false;
1187
1197
  }
1188
1198
  }
1199
+ function serializeCallForSession(call) {
1200
+ return {
1201
+ to: call.to,
1202
+ chainId: call.chainId,
1203
+ data: call.data,
1204
+ value: call.value?.toString()
1205
+ };
1206
+ }
1189
1207
  var SessionExecutionClient = class {
1190
1208
  mode;
1191
1209
  socketPath;
1192
1210
  tokenPath;
1193
1211
  address;
1212
+ caps;
1194
1213
  constructor(params) {
1195
1214
  this.socketPath = params.socketPath;
1196
1215
  this.tokenPath = params.tokenPath;
1197
1216
  this.address = params.address;
1198
1217
  this.mode = params.mode ?? "eoa";
1218
+ this.caps = params.capabilities ?? capabilitiesForMode(this.mode);
1199
1219
  }
1200
1220
  getAddress() {
1201
1221
  return this.address;
@@ -1211,6 +1231,20 @@ var SessionExecutionClient = class {
1211
1231
  return this.sendWithRequestId(requestId, call);
1212
1232
  }
1213
1233
  }
1234
+ async sendBatch(calls) {
1235
+ if (calls.length === 0) {
1236
+ throw new Error("sendBatch requires at least one call");
1237
+ }
1238
+ const requestId = createRequestId();
1239
+ try {
1240
+ return await this.sendBatchWithRequestId(requestId, calls);
1241
+ } catch (error) {
1242
+ if (!isRetryableSessionTransportError(error)) {
1243
+ throw error;
1244
+ }
1245
+ return this.sendBatchWithRequestId(requestId, calls);
1246
+ }
1247
+ }
1214
1248
  async sendWithRequestId(requestId, call) {
1215
1249
  const response = await sendSessionRequest(
1216
1250
  this.socketPath,
@@ -1218,11 +1252,21 @@ var SessionExecutionClient = class {
1218
1252
  {
1219
1253
  id: requestId,
1220
1254
  type: "send",
1255
+ params: serializeCallForSession(call)
1256
+ },
1257
+ SESSION_SEND_TIMEOUT_MS
1258
+ );
1259
+ return unwrapSessionSendResponse(response);
1260
+ }
1261
+ async sendBatchWithRequestId(requestId, calls) {
1262
+ const response = await sendSessionRequest(
1263
+ this.socketPath,
1264
+ this.tokenPath,
1265
+ {
1266
+ id: requestId,
1267
+ type: "sendBatch",
1221
1268
  params: {
1222
- to: call.to,
1223
- chainId: call.chainId,
1224
- data: call.data,
1225
- value: call.value?.toString()
1269
+ calls: calls.map(serializeCallForSession)
1226
1270
  }
1227
1271
  },
1228
1272
  SESSION_SEND_TIMEOUT_MS
@@ -1230,14 +1274,54 @@ var SessionExecutionClient = class {
1230
1274
  return unwrapSessionSendResponse(response);
1231
1275
  }
1232
1276
  capabilities() {
1233
- const smartAccount = this.mode === "smart-account";
1277
+ return { ...this.caps };
1278
+ }
1279
+ };
1280
+ function deserializeSessionCall(params) {
1281
+ return {
1282
+ to: params.to,
1283
+ chainId: params.chainId,
1284
+ data: params.data,
1285
+ value: params.value !== void 0 ? BigInt(params.value) : void 0
1286
+ };
1287
+ }
1288
+ async function executeSendLikeRequest(executor, request) {
1289
+ try {
1290
+ let result;
1291
+ if (request.type === "send") {
1292
+ result = await executor.send(deserializeSessionCall(request.params));
1293
+ } else {
1294
+ if (typeof executor.sendBatch !== "function") {
1295
+ return {
1296
+ id: request.id,
1297
+ ok: false,
1298
+ error: "Session executor does not support batched execution."
1299
+ };
1300
+ }
1301
+ if (request.params.calls.length === 0) {
1302
+ return {
1303
+ id: request.id,
1304
+ ok: false,
1305
+ error: "sendBatch requires at least one call"
1306
+ };
1307
+ }
1308
+ result = await executor.sendBatch(
1309
+ request.params.calls.map(deserializeSessionCall)
1310
+ );
1311
+ }
1234
1312
  return {
1235
- sponsoredGas: smartAccount,
1236
- batching: false,
1237
- simulation: smartAccount
1313
+ id: request.id,
1314
+ ok: true,
1315
+ result
1316
+ };
1317
+ } catch (error) {
1318
+ return {
1319
+ id: request.id,
1320
+ ok: false,
1321
+ error: error instanceof Error ? error.message : String(error)
1238
1322
  };
1239
1323
  }
1240
- };
1324
+ }
1241
1325
  async function startSessionServer(start, hooks = {}) {
1242
1326
  ensureSecureParent(start.tokenPath);
1243
1327
  ensureSecureParent(start.socketPath);
@@ -1328,7 +1412,8 @@ async function startSessionServer(start, hooks = {}) {
1328
1412
  result: {
1329
1413
  address: executor.getAddress(),
1330
1414
  expiresAt: expiresAt.toISOString(),
1331
- mode: executor.mode
1415
+ mode: executor.mode,
1416
+ capabilities: executor.capabilities()
1332
1417
  }
1333
1418
  });
1334
1419
  return;
@@ -1361,27 +1446,7 @@ async function startSessionServer(start, hooks = {}) {
1361
1446
  respond(await existing.response);
1362
1447
  return;
1363
1448
  }
1364
- const responsePromise = (async () => {
1365
- try {
1366
- const result = await executor.send({
1367
- to: request.params.to,
1368
- chainId: request.params.chainId,
1369
- data: request.params.data,
1370
- value: request.params.value !== void 0 ? BigInt(request.params.value) : void 0
1371
- });
1372
- return {
1373
- id: request.id,
1374
- ok: true,
1375
- result
1376
- };
1377
- } catch (error) {
1378
- return {
1379
- id: request.id,
1380
- ok: false,
1381
- error: error instanceof Error ? error.message : String(error)
1382
- };
1383
- }
1384
- })();
1449
+ const responsePromise = executeSendLikeRequest(executor, request);
1385
1450
  sendResponses.set(request.id, {
1386
1451
  fingerprint,
1387
1452
  response: responsePromise
@@ -1416,6 +1481,7 @@ async function startSessionServer(start, hooks = {}) {
1416
1481
  address: executor.getAddress(),
1417
1482
  expiresAt: expiresAt.toISOString(),
1418
1483
  mode: executor.mode,
1484
+ capabilities: executor.capabilities(),
1419
1485
  close
1420
1486
  };
1421
1487
  }
@@ -1473,7 +1539,8 @@ async function startDetachedSessionDaemon(params) {
1473
1539
  finish(void 0, {
1474
1540
  address: payload.address,
1475
1541
  expiresAt: payload.expiresAt,
1476
- mode: payload.mode
1542
+ mode: payload.mode,
1543
+ capabilities: payload.capabilities ?? capabilitiesForMode(payload.mode)
1477
1544
  });
1478
1545
  } else if (payload?.type === "error") {
1479
1546
  finish(new Error(payload.error));
@@ -1519,7 +1586,8 @@ async function runSessionDaemonProcess() {
1519
1586
  type: "ready",
1520
1587
  address: handle.address,
1521
1588
  expiresAt: handle.expiresAt,
1522
- mode: handle.mode
1589
+ mode: handle.mode,
1590
+ capabilities: handle.capabilities
1523
1591
  });
1524
1592
  process.on("SIGTERM", () => {
1525
1593
  void handle.close().then(() => shutdown());
@@ -1876,7 +1944,8 @@ async function connectLocalSession(options = {}) {
1876
1944
  socketPath,
1877
1945
  tokenPath,
1878
1946
  address: session.address,
1879
- mode: session.mode
1947
+ mode: session.mode,
1948
+ capabilities: session.capabilities
1880
1949
  });
1881
1950
  }
1882
1951
 
@@ -1884,7 +1953,7 @@ async function connectLocalSession(options = {}) {
1884
1953
  import {
1885
1954
  deriveDefaultGaslessAccount,
1886
1955
  getGaslessNetworkDefaults
1887
- } from "@moneyos/gasless";
1956
+ } from "@moneyos/smart-account";
1888
1957
  var GASLESS_ENABLED_ENV = "MONEYOS_GASLESS_ENABLED";
1889
1958
  var GASLESS_RELAY_URL_ENV = "MONEYOS_GASLESS_RELAY_URL";
1890
1959
  var GASLESS_ACCOUNT_ENV = "MONEYOS_GASLESS_ACCOUNT";
@@ -1940,6 +2009,7 @@ async function resolveGaslessExecutionConfig(config, options = {}) {
1940
2009
  }
1941
2010
 
1942
2011
  // src/cli/wallet.ts
2012
+ var GASLESS_MISSING_ENV_ERROR = "Gasless mode is enabled but required environment variables are missing. Set MONEYOS_GASLESS_RELAY_URL, MONEYOS_GASLESS_ACCOUNT, and MONEYOS_GASLESS_SPONSOR.";
1943
2013
  function resolveEnvPrivateKey(explicit) {
1944
2014
  return explicit ?? process.env.MONEYOS_PRIVATE_KEY;
1945
2015
  }
@@ -1979,6 +2049,24 @@ async function loadCliAddress(config, options = {}) {
1979
2049
  }
1980
2050
  throw new Error("No wallet configured. Run `moneyos init`.");
1981
2051
  }
2052
+ async function resolveCliOwnedAddresses(config, options = {}) {
2053
+ const { address } = await loadCliAddress(config, options);
2054
+ if (!isGaslessEnabled(config)) {
2055
+ return { eoa: address };
2056
+ }
2057
+ const gasless = await resolveGaslessExecutionConfig(config, {
2058
+ ownerAddress: address,
2059
+ chainId: options.chainId ?? config.chainId ?? 42161,
2060
+ rpcUrl: config.rpcUrl
2061
+ });
2062
+ if (!gasless) {
2063
+ throw new Error(GASLESS_MISSING_ENV_ERROR);
2064
+ }
2065
+ return {
2066
+ eoa: address,
2067
+ smartAccount: gasless.account
2068
+ };
2069
+ }
1982
2070
  async function buildCliMoneyOSConfig(config, options = {}) {
1983
2071
  const moneyosConfig = {
1984
2072
  chainId: options.chainId ?? config.chainId ?? 42161,
@@ -1998,9 +2086,7 @@ async function buildCliMoneyOSConfig(config, options = {}) {
1998
2086
  });
1999
2087
  if (!gasless) {
2000
2088
  if (gaslessEnabled) {
2001
- throw new Error(
2002
- "Gasless mode is enabled but required environment variables are missing. Set MONEYOS_GASLESS_RELAY_URL, MONEYOS_GASLESS_ACCOUNT, and MONEYOS_GASLESS_SPONSOR."
2003
- );
2089
+ throw new Error(GASLESS_MISSING_ENV_ERROR);
2004
2090
  }
2005
2091
  return {
2006
2092
  ...moneyosConfig,
@@ -2059,6 +2145,28 @@ async function buildCliMoneyOSConfig(config, options = {}) {
2059
2145
  }
2060
2146
 
2061
2147
  // src/cli/commands/balance.ts
2148
+ function shortenAddress(address) {
2149
+ return `${address.slice(0, 6)}\u2026${address.slice(-4)}`;
2150
+ }
2151
+ function formatTargetLabel(target) {
2152
+ return `${target.label} (${shortenAddress(target.address)}):`;
2153
+ }
2154
+ function targetLineWidth(targets) {
2155
+ return Math.max(...targets.map((target) => formatTargetLabel(target).length));
2156
+ }
2157
+ async function resolveBalanceTargets(config, chainId, address) {
2158
+ if (address) {
2159
+ return [{ address }];
2160
+ }
2161
+ const resolved = await resolveCliOwnedAddresses(config, { chainId });
2162
+ if (resolved.smartAccount && resolved.smartAccount.toLowerCase() !== resolved.eoa.toLowerCase()) {
2163
+ return [
2164
+ { label: "EOA", address: resolved.eoa },
2165
+ { label: "Smart account", address: resolved.smartAccount }
2166
+ ];
2167
+ }
2168
+ return [{ address: resolved.eoa }];
2169
+ }
2062
2170
  var balanceCommand = new Command2("balance").description("Check token balance").argument("[token]", "Token symbol (e.g. USDC, ETH, RYZE). Omit with --all.").option("-a, --address <address>", "Address to check (defaults to your own)").option("-c, --chain <chainId>", "Chain ID (default: 42161 Arbitrum)").option("--all", "Show balances for every built-in token on the selected chain").action(async (token, options) => {
2063
2171
  if (options.all && token !== void 0) {
2064
2172
  console.error("Cannot combine a token argument with --all.");
@@ -2071,17 +2179,15 @@ var balanceCommand = new Command2("balance").description("Check token balance").
2071
2179
  return;
2072
2180
  }
2073
2181
  const config = loadConfig();
2074
- const chainId = options.chain ? parseInt(options.chain) : config.chainId;
2075
- let address = options.address;
2182
+ const chainId = options.chain ? parseInt(options.chain) : config.chainId ?? 42161;
2183
+ const address = options.address;
2076
2184
  let moneyos;
2185
+ let targets;
2077
2186
  try {
2078
- if (!address) {
2079
- const resolved = await loadCliAddress(config);
2080
- address = resolved.address;
2081
- }
2187
+ targets = await resolveBalanceTargets(config, chainId, address);
2082
2188
  moneyos = new MoneyOS(
2083
2189
  await buildCliMoneyOSConfig(config, {
2084
- chainId: chainId ?? 42161,
2190
+ chainId,
2085
2191
  requireSigner: false
2086
2192
  })
2087
2193
  );
@@ -2093,40 +2199,62 @@ var balanceCommand = new Command2("balance").description("Check token balance").
2093
2199
  return;
2094
2200
  }
2095
2201
  if (options.all) {
2096
- const resolvedChainId = chainId ?? 42161;
2097
- const candidates = listTokens(resolvedChainId);
2202
+ const candidates = listTokens(chainId);
2098
2203
  if (candidates.length === 0) {
2099
2204
  console.error(
2100
- `No built-in tokens registered on chain ${resolvedChainId}.`
2205
+ `No built-in tokens registered on chain ${chainId}.`
2101
2206
  );
2102
2207
  process.exitCode = 1;
2103
2208
  return;
2104
2209
  }
2105
- const results = await Promise.allSettled(
2106
- candidates.map(
2107
- (t) => moneyos.balance(t.symbol, { address, chainId: resolvedChainId })
2108
- )
2109
- );
2110
- const width = Math.max(...candidates.map((t) => t.symbol.length));
2111
2210
  let anyFailed = false;
2112
- results.forEach((result2, i) => {
2113
- const symbol = candidates[i].symbol.padEnd(width);
2114
- if (result2.status === "fulfilled") {
2115
- console.log(`${symbol} ${result2.value.amount}`);
2116
- } else {
2211
+ for (const [index, target] of targets.entries()) {
2212
+ if (target.label) {
2213
+ if (index > 0) console.log("");
2214
+ console.log(formatTargetLabel(target));
2215
+ }
2216
+ const results2 = await Promise.allSettled(
2217
+ candidates.map(
2218
+ (candidate) => moneyos.balance(candidate.symbol, { address: target.address, chainId })
2219
+ )
2220
+ );
2221
+ const width2 = Math.max(...candidates.map((candidate) => candidate.symbol.length));
2222
+ results2.forEach((result, resultIndex) => {
2223
+ const symbol = candidates[resultIndex].symbol.padEnd(width2);
2224
+ if (result.status === "fulfilled") {
2225
+ console.log(`${symbol} ${result.value.amount}`);
2226
+ return;
2227
+ }
2117
2228
  anyFailed = true;
2118
- const message = result2.reason instanceof Error ? result2.reason.message : String(result2.reason);
2229
+ const message = result.reason instanceof Error ? result.reason.message : String(result.reason);
2119
2230
  console.log(`${symbol} error: ${message}`);
2120
- }
2121
- });
2231
+ });
2232
+ }
2122
2233
  if (anyFailed) process.exitCode = 1;
2123
2234
  return;
2124
2235
  }
2125
- const result = await moneyos.balance(token, {
2126
- address,
2127
- chainId
2236
+ if (targets.length === 1) {
2237
+ const result = await moneyos.balance(token, {
2238
+ address: targets[0].address,
2239
+ chainId
2240
+ });
2241
+ console.log(`${result.amount} ${result.symbol}`);
2242
+ return;
2243
+ }
2244
+ const width = targetLineWidth(targets);
2245
+ const results = await Promise.all(
2246
+ targets.map(
2247
+ (target) => moneyos.balance(token, {
2248
+ address: target.address,
2249
+ chainId
2250
+ })
2251
+ )
2252
+ );
2253
+ results.forEach((result, index) => {
2254
+ console.log(
2255
+ `${formatTargetLabel(targets[index]).padEnd(width)} ${result.symbol} ${result.amount}`
2256
+ );
2128
2257
  });
2129
- console.log(`${result.amount} ${result.symbol}`);
2130
2258
  });
2131
2259
 
2132
2260
  // src/cli/commands/send.ts
@@ -2695,7 +2823,7 @@ contactCommand.command("remove").description("Remove a saved contact").argument(
2695
2823
 
2696
2824
  // src/cli/commands/gasless.ts
2697
2825
  import { Command as Command8 } from "commander";
2698
- import { getGaslessNetworkDefaults as getGaslessNetworkDefaults2 } from "@moneyos/gasless";
2826
+ import { getGaslessNetworkDefaults as getGaslessNetworkDefaults2 } from "@moneyos/smart-account";
2699
2827
  function formatEnabled(enabled) {
2700
2828
  return enabled ? "enabled" : "disabled";
2701
2829
  }