@spectratools/aborean-cli 0.6.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.js +1668 -652
  2. package/package.json +3 -2
package/dist/cli.js CHANGED
@@ -5,12 +5,12 @@ import { readFileSync } from "fs";
5
5
  import { dirname, resolve } from "path";
6
6
  import { fileURLToPath } from "url";
7
7
  import { checksumAddress as checksumAddress6 } from "@spectratools/cli-shared";
8
- import { Cli as Cli8, z as z8 } from "incur";
8
+ import { Cli as Cli8, z as z9 } from "incur";
9
9
  import { formatUnits as formatUnits4 } from "viem";
10
10
 
11
11
  // src/commands/cl.ts
12
- import { checksumAddress, isAddress } from "@spectratools/cli-shared";
13
- import { Cli, z } from "incur";
12
+ import { checksumAddress as checksumAddress2, isAddress } from "@spectratools/cli-shared";
13
+ import { Cli, z as z2 } from "incur";
14
14
  import { formatUnits, parseUnits } from "viem";
15
15
 
16
16
  // src/contracts/abis/CLFactory.abi.json
@@ -560,6 +560,47 @@ var RewardsDistributor_abi_default = [
560
560
  }
561
561
  ];
562
562
 
563
+ // src/contracts/abis/SwapRouter.abi.json
564
+ var SwapRouter_abi_default = [
565
+ {
566
+ type: "function",
567
+ name: "exactInputSingle",
568
+ inputs: [
569
+ {
570
+ name: "params",
571
+ type: "tuple",
572
+ internalType: "struct ISwapRouter.ExactInputSingleParams",
573
+ components: [
574
+ { name: "tokenIn", type: "address", internalType: "address" },
575
+ { name: "tokenOut", type: "address", internalType: "address" },
576
+ { name: "tickSpacing", type: "int24", internalType: "int24" },
577
+ { name: "recipient", type: "address", internalType: "address" },
578
+ { name: "deadline", type: "uint256", internalType: "uint256" },
579
+ { name: "amountIn", type: "uint256", internalType: "uint256" },
580
+ { name: "amountOutMinimum", type: "uint256", internalType: "uint256" },
581
+ { name: "sqrtPriceLimitX96", type: "uint160", internalType: "uint160" }
582
+ ]
583
+ }
584
+ ],
585
+ outputs: [{ name: "amountOut", type: "uint256", internalType: "uint256" }],
586
+ stateMutability: "payable"
587
+ },
588
+ {
589
+ type: "function",
590
+ name: "factory",
591
+ inputs: [],
592
+ outputs: [{ name: "", type: "address", internalType: "address" }],
593
+ stateMutability: "view"
594
+ },
595
+ {
596
+ type: "function",
597
+ name: "WETH9",
598
+ inputs: [],
599
+ outputs: [{ name: "", type: "address", internalType: "address" }],
600
+ stateMutability: "view"
601
+ }
602
+ ];
603
+
563
604
  // src/contracts/abis/V2Pool.abi.json
564
605
  var V2Pool_abi_default = [
565
606
  {
@@ -947,6 +988,7 @@ var nonfungiblePositionManagerAbi = NonfungiblePositionManager_abi_default;
947
988
  var poolFactoryAbi = PoolFactory_abi_default;
948
989
  var quoterV2Abi = QuoterV2_abi_default;
949
990
  var rewardsDistributorAbi = RewardsDistributor_abi_default;
991
+ var swapRouterAbi = SwapRouter_abi_default;
950
992
  var v2PoolAbi = V2Pool_abi_default;
951
993
  var v2RouterAbi = V2Router_abi_default;
952
994
  var voterAbi = Voter_abi_default;
@@ -1032,7 +1074,12 @@ var ABOREAN_ADDRESSES = {
1032
1074
  };
1033
1075
 
1034
1076
  // src/contracts/client.ts
1035
- import { http, createPublicClient, defineChain } from "viem";
1077
+ import {
1078
+ http,
1079
+ createPublicClient,
1080
+ createWalletClient,
1081
+ defineChain
1082
+ } from "viem";
1036
1083
  var abstractMainnet = defineChain({
1037
1084
  id: 2741,
1038
1085
  name: "Abstract Mainnet",
@@ -1053,63 +1100,174 @@ function createAboreanPublicClient(rpcUrl) {
1053
1100
  transport: http(rpcUrl ?? process.env.ABSTRACT_RPC_URL ?? "https://api.mainnet.abs.xyz")
1054
1101
  });
1055
1102
  }
1103
+ function createAboreanWalletClient(account, rpcUrl) {
1104
+ return createWalletClient({
1105
+ account,
1106
+ chain: abstractMainnet,
1107
+ transport: http(rpcUrl ?? process.env.ABSTRACT_RPC_URL ?? "https://api.mainnet.abs.xyz")
1108
+ });
1109
+ }
1110
+
1111
+ // src/commands/_write-utils.ts
1112
+ import {
1113
+ createPrivateKeySigner,
1114
+ executeTx
1115
+ } from "@spectratools/tx-shared";
1116
+ import { z } from "incur";
1117
+
1118
+ // src/commands/_common.ts
1119
+ import { checksumAddress, weiToEth } from "@spectratools/cli-shared";
1120
+ var ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
1121
+ function toChecksum(address) {
1122
+ try {
1123
+ return checksumAddress(address);
1124
+ } catch {
1125
+ return address;
1126
+ }
1127
+ }
1128
+ function asNum(value) {
1129
+ return Number(value);
1130
+ }
1131
+ function relTime(unixSeconds) {
1132
+ const ts = typeof unixSeconds === "bigint" ? Number(unixSeconds) : unixSeconds;
1133
+ if (!Number.isFinite(ts) || ts <= 0) return "n/a";
1134
+ const delta = ts - Math.floor(Date.now() / 1e3);
1135
+ const abs = Math.abs(delta);
1136
+ const hours = Math.floor(abs / 3600);
1137
+ const minutes = Math.floor(abs % 3600 / 60);
1138
+ const label = hours > 0 ? `${hours}h ${minutes}m` : `${minutes}m`;
1139
+ return delta >= 0 ? `in ${label}` : `${label} ago`;
1140
+ }
1141
+ function clampPositive(seconds) {
1142
+ return seconds > 0 ? seconds : 0;
1143
+ }
1144
+ function jsonSafe(value) {
1145
+ if (typeof value === "bigint") return value.toString();
1146
+ if (Array.isArray(value)) return value.map((item) => jsonSafe(item));
1147
+ if (value && typeof value === "object") {
1148
+ return Object.fromEntries(
1149
+ Object.entries(value).map(([key, entry]) => [
1150
+ key,
1151
+ jsonSafe(entry)
1152
+ ])
1153
+ );
1154
+ }
1155
+ return value;
1156
+ }
1157
+
1158
+ // src/commands/_write-utils.ts
1159
+ var PRIVATE_KEY_MISSING_MESSAGE = "Missing PRIVATE_KEY env var. Set PRIVATE_KEY to a 0x-prefixed 32-byte hex private key.";
1160
+ var writeEnv = z.object({
1161
+ ABSTRACT_RPC_URL: z.string().optional().describe("Abstract RPC URL override"),
1162
+ PRIVATE_KEY: z.string().min(1, PRIVATE_KEY_MISSING_MESSAGE).describe("Private key (0x-prefixed 32-byte hex) for signing transactions")
1163
+ });
1164
+ var writeOptions = z.object({
1165
+ "dry-run": z.boolean().default(false).describe("Simulate the transaction without broadcasting"),
1166
+ "gas-limit": z.string().optional().describe("Gas limit override (in gas units)"),
1167
+ "max-fee": z.string().optional().describe("Max fee per gas override in wei (EIP-1559)"),
1168
+ nonce: z.number().optional().describe("Nonce override")
1169
+ });
1170
+ function resolveAccount(env8) {
1171
+ const privateKey = env8.PRIVATE_KEY?.trim();
1172
+ if (!privateKey) {
1173
+ throw new Error(PRIVATE_KEY_MISSING_MESSAGE);
1174
+ }
1175
+ const signer = createPrivateKeySigner(privateKey);
1176
+ return signer.account;
1177
+ }
1178
+ function formatTxResult(result) {
1179
+ if (result.status === "dry-run") {
1180
+ return {
1181
+ dryRun: true,
1182
+ estimatedGas: result.estimatedGas.toString(),
1183
+ simulationResult: jsonSafe(result.simulationResult),
1184
+ ...result.privyPolicy !== void 0 ? { privyPolicy: jsonSafe(result.privyPolicy) } : {}
1185
+ };
1186
+ }
1187
+ return {
1188
+ txHash: result.hash,
1189
+ blockNumber: Number(result.blockNumber),
1190
+ gasUsed: result.gasUsed.toString()
1191
+ };
1192
+ }
1193
+ async function aboreanWriteTx(opts) {
1194
+ const { env: env8, options, address, abi, functionName, args, value } = opts;
1195
+ const account = resolveAccount(env8);
1196
+ const publicClient = createAboreanPublicClient(env8.ABSTRACT_RPC_URL);
1197
+ const walletClient = createAboreanWalletClient(account, env8.ABSTRACT_RPC_URL);
1198
+ const result = await executeTx({
1199
+ publicClient,
1200
+ walletClient,
1201
+ account,
1202
+ address,
1203
+ abi,
1204
+ functionName,
1205
+ ...args !== void 0 ? { args } : {},
1206
+ ...value !== void 0 ? { value } : {},
1207
+ dryRun: options["dry-run"],
1208
+ ...options["gas-limit"] ? { gasLimit: BigInt(options["gas-limit"]) } : {},
1209
+ ...options["max-fee"] ? { maxFeePerGas: BigInt(options["max-fee"]) } : {},
1210
+ ...options.nonce !== void 0 ? { nonce: options.nonce } : {}
1211
+ });
1212
+ return formatTxResult(result);
1213
+ }
1056
1214
 
1057
1215
  // src/commands/cl.ts
1058
1216
  var Q96 = 2n ** 96n;
1059
1217
  var MULTICALL_BATCH_SIZE = 100;
1060
- var env = z.object({
1061
- ABSTRACT_RPC_URL: z.string().optional().describe("Abstract RPC URL override")
1218
+ var env = z2.object({
1219
+ ABSTRACT_RPC_URL: z2.string().optional().describe("Abstract RPC URL override")
1062
1220
  });
1063
- var tokenSchema = z.object({
1064
- address: z.string(),
1065
- symbol: z.string(),
1066
- decimals: z.number()
1221
+ var tokenSchema = z2.object({
1222
+ address: z2.string(),
1223
+ symbol: z2.string(),
1224
+ decimals: z2.number()
1067
1225
  });
1068
- var poolRowSchema = z.object({
1069
- pool: z.string(),
1070
- pair: z.string(),
1226
+ var poolRowSchema = z2.object({
1227
+ pool: z2.string(),
1228
+ pair: z2.string(),
1071
1229
  token0: tokenSchema,
1072
1230
  token1: tokenSchema,
1073
- fee: z.number(),
1074
- feePercent: z.number(),
1075
- tickSpacing: z.number(),
1076
- liquidity: z.string(),
1077
- currentTick: z.number(),
1078
- sqrtPriceX96: z.string(),
1079
- activeLiquidityEstimate: z.object({
1080
- token0: z.string(),
1081
- token1: z.string(),
1082
- totalInToken0: z.number().nullable(),
1083
- totalInToken1: z.number().nullable()
1231
+ fee: z2.number(),
1232
+ feePercent: z2.number(),
1233
+ tickSpacing: z2.number(),
1234
+ liquidity: z2.string(),
1235
+ currentTick: z2.number(),
1236
+ sqrtPriceX96: z2.string(),
1237
+ activeLiquidityEstimate: z2.object({
1238
+ token0: z2.string(),
1239
+ token1: z2.string(),
1240
+ totalInToken0: z2.number().nullable(),
1241
+ totalInToken1: z2.number().nullable()
1084
1242
  }),
1085
- price: z.object({
1086
- token1PerToken0: z.number().nullable(),
1087
- token0PerToken1: z.number().nullable()
1243
+ price: z2.object({
1244
+ token1PerToken0: z2.number().nullable(),
1245
+ token0PerToken1: z2.number().nullable()
1088
1246
  })
1089
1247
  });
1090
- var quoteOutputSchema = z.object({
1091
- pool: z.string(),
1092
- selectedFee: z.number(),
1093
- selectedTickSpacing: z.number(),
1248
+ var quoteOutputSchema = z2.object({
1249
+ pool: z2.string(),
1250
+ selectedFee: z2.number(),
1251
+ selectedTickSpacing: z2.number(),
1094
1252
  tokenIn: tokenSchema,
1095
1253
  tokenOut: tokenSchema,
1096
- amountIn: z.object({
1097
- raw: z.string(),
1098
- decimal: z.string()
1254
+ amountIn: z2.object({
1255
+ raw: z2.string(),
1256
+ decimal: z2.string()
1099
1257
  }),
1100
- amountOut: z.object({
1101
- raw: z.string(),
1102
- decimal: z.string()
1258
+ amountOut: z2.object({
1259
+ raw: z2.string(),
1260
+ decimal: z2.string()
1103
1261
  }),
1104
- execution: z.object({
1105
- sqrtPriceX96After: z.string(),
1106
- initializedTicksCrossed: z.number(),
1107
- gasEstimate: z.string()
1262
+ execution: z2.object({
1263
+ sqrtPriceX96After: z2.string(),
1264
+ initializedTicksCrossed: z2.number(),
1265
+ gasEstimate: z2.string()
1108
1266
  }),
1109
- prices: z.object({
1110
- poolMidPriceOutPerIn: z.number().nullable(),
1111
- quotePriceOutPerIn: z.number().nullable(),
1112
- priceImpactPct: z.number().nullable()
1267
+ prices: z2.object({
1268
+ poolMidPriceOutPerIn: z2.number().nullable(),
1269
+ quotePriceOutPerIn: z2.number().nullable(),
1270
+ priceImpactPct: z2.number().nullable()
1113
1271
  })
1114
1272
  });
1115
1273
  var erc20MetadataAbi = [
@@ -1137,7 +1295,7 @@ function finiteOrNull(value) {
1137
1295
  function toTokenMetaFallback(address) {
1138
1296
  return {
1139
1297
  address,
1140
- symbol: shortAddress(checksumAddress(address)),
1298
+ symbol: shortAddress(checksumAddress2(address)),
1141
1299
  decimals: 18
1142
1300
  };
1143
1301
  }
@@ -1232,7 +1390,7 @@ async function readTokenMetadata(client, tokenAddresses) {
1232
1390
  const symbol = symbolResult && symbolResult.status === "success" && typeof symbolResult.result === "string" ? symbolResult.result : fallback.symbol;
1233
1391
  const decimals = decimalsResult && decimalsResult.status === "success" && typeof decimalsResult.result === "number" ? decimalsResult.result : fallback.decimals;
1234
1392
  out.set(address, {
1235
- address: checksumAddress(address),
1393
+ address: checksumAddress2(address),
1236
1394
  symbol,
1237
1395
  decimals
1238
1396
  });
@@ -1316,7 +1474,7 @@ function toPoolRow(pool, tokenMeta) {
1316
1474
  prices
1317
1475
  );
1318
1476
  return {
1319
- pool: checksumAddress(pool.pool),
1477
+ pool: checksumAddress2(pool.pool),
1320
1478
  pair: `${token0.symbol}/${token1.symbol}`,
1321
1479
  token0,
1322
1480
  token1,
@@ -1339,9 +1497,9 @@ var cl = Cli.create("cl", {
1339
1497
  cl.command("pools", {
1340
1498
  description: "List Slipstream pools with current state, prices, and active liquidity estimate.",
1341
1499
  env,
1342
- output: z.object({
1343
- count: z.number(),
1344
- pools: z.array(poolRowSchema)
1500
+ output: z2.object({
1501
+ count: z2.number(),
1502
+ pools: z2.array(poolRowSchema)
1345
1503
  }),
1346
1504
  async run(c) {
1347
1505
  const client = createAboreanPublicClient(c.env.ABSTRACT_RPC_URL);
@@ -1352,19 +1510,41 @@ cl.command("pools", {
1352
1510
  poolStates.flatMap((pool) => [pool.token0, pool.token1])
1353
1511
  );
1354
1512
  const rows = poolStates.map((pool) => toPoolRow(pool, tokenMeta));
1355
- return c.ok({
1356
- count: rows.length,
1357
- pools: rows
1358
- });
1513
+ const firstPool = rows[0];
1514
+ return c.ok(
1515
+ {
1516
+ count: rows.length,
1517
+ pools: rows
1518
+ },
1519
+ c.format === "json" || c.format === "jsonl" ? void 0 : {
1520
+ cta: {
1521
+ description: "Explore CL pools:",
1522
+ commands: [
1523
+ ...firstPool ? [
1524
+ {
1525
+ command: "cl pool",
1526
+ args: { pool: firstPool.pool },
1527
+ description: `Inspect ${firstPool.pair}`
1528
+ }
1529
+ ] : [],
1530
+ {
1531
+ command: "cl positions",
1532
+ args: { owner: "<address>" },
1533
+ description: "List CL positions for an address"
1534
+ }
1535
+ ]
1536
+ }
1537
+ }
1538
+ );
1359
1539
  }
1360
1540
  });
1361
1541
  cl.command("pool", {
1362
1542
  description: "Get detailed state for a Slipstream pool address.",
1363
- args: z.object({
1364
- pool: z.string().describe("Pool address")
1543
+ args: z2.object({
1544
+ pool: z2.string().describe("Pool address")
1365
1545
  }),
1366
1546
  env,
1367
- output: z.object({
1547
+ output: z2.object({
1368
1548
  pool: poolRowSchema
1369
1549
  }),
1370
1550
  async run(c) {
@@ -1375,35 +1555,59 @@ cl.command("pool", {
1375
1555
  });
1376
1556
  }
1377
1557
  const client = createAboreanPublicClient(c.env.ABSTRACT_RPC_URL);
1378
- const checksummedPool = checksumAddress(c.args.pool);
1558
+ const checksummedPool = checksumAddress2(c.args.pool);
1379
1559
  const [poolState] = await readPoolStates(client, [checksummedPool]);
1380
1560
  const tokenMeta = await readTokenMetadata(client, [poolState.token0, poolState.token1]);
1381
- return c.ok({
1382
- pool: toPoolRow(poolState, tokenMeta)
1383
- });
1561
+ const row = toPoolRow(poolState, tokenMeta);
1562
+ return c.ok(
1563
+ {
1564
+ pool: row
1565
+ },
1566
+ c.format === "json" || c.format === "jsonl" ? void 0 : {
1567
+ cta: {
1568
+ description: "Next steps:",
1569
+ commands: [
1570
+ {
1571
+ command: "cl quote",
1572
+ args: {
1573
+ tokenIn: row.token0.address,
1574
+ tokenOut: row.token1.address,
1575
+ amountIn: "1"
1576
+ },
1577
+ description: `Quote a ${row.token0.symbol} \u2192 ${row.token1.symbol} swap`
1578
+ },
1579
+ {
1580
+ command: "cl positions",
1581
+ args: { owner: "<address>" },
1582
+ description: "List positions in this pool"
1583
+ }
1584
+ ]
1585
+ }
1586
+ }
1587
+ );
1384
1588
  }
1385
1589
  });
1386
1590
  cl.command("positions", {
1387
1591
  description: "List concentrated liquidity NFT positions for an owner.",
1388
- args: z.object({
1389
- owner: z.string().describe("Owner wallet address")
1592
+ args: z2.object({
1593
+ owner: z2.string().describe("Owner wallet address")
1390
1594
  }),
1391
1595
  env,
1392
- output: z.object({
1393
- owner: z.string(),
1394
- count: z.number(),
1395
- positions: z.array(
1396
- z.object({
1397
- tokenId: z.string(),
1398
- pair: z.string(),
1596
+ output: z2.object({
1597
+ owner: z2.string(),
1598
+ count: z2.number(),
1599
+ positions: z2.array(
1600
+ z2.object({
1601
+ tokenId: z2.string(),
1602
+ pair: z2.string(),
1399
1603
  token0: tokenSchema,
1400
1604
  token1: tokenSchema,
1401
- tickSpacing: z.number(),
1402
- tickLower: z.number(),
1403
- tickUpper: z.number(),
1404
- liquidity: z.string(),
1405
- tokensOwed0: z.object({ raw: z.string(), decimal: z.string() }),
1406
- tokensOwed1: z.object({ raw: z.string(), decimal: z.string() })
1605
+ tickSpacing: z2.number(),
1606
+ tickLower: z2.number(),
1607
+ tickUpper: z2.number(),
1608
+ liquidity: z2.string(),
1609
+ tokensOwed0: z2.object({ raw: z2.string(), decimal: z2.string() }),
1610
+ tokensOwed1: z2.object({ raw: z2.string(), decimal: z2.string() })
1407
1611
  })
1408
1612
  )
1409
1613
  }),
@@ -1415,7 +1619,7 @@ cl.command("positions", {
1415
1619
  });
1416
1620
  }
1417
1621
  const client = createAboreanPublicClient(c.env.ABSTRACT_RPC_URL);
1418
- const owner = checksumAddress(c.args.owner);
1622
+ const owner = checksumAddress2(c.args.owner);
1419
1623
  const balance = await client.readContract({
1420
1624
  abi: nonfungiblePositionManagerAbi,
1421
1625
  address: ABOREAN_CL_ADDRESSES.nonfungiblePositionManager,
@@ -1466,22 +1670,46 @@ cl.command("positions", {
1466
1670
  }
1467
1671
  };
1468
1672
  });
1469
- return c.ok({
1470
- owner,
1471
- count: positions.length,
1472
- positions
1473
- });
1673
+ const firstPosition = positions[0];
1674
+ return c.ok(
1675
+ {
1676
+ owner,
1677
+ count: positions.length,
1678
+ positions
1679
+ },
1680
+ c.format === "json" || c.format === "jsonl" ? void 0 : {
1681
+ cta: {
1682
+ description: "Related commands:",
1683
+ commands: firstPosition ? [
1684
+ {
1685
+ command: "cl pool",
1686
+ args: { pool: "<poolAddress>" },
1687
+ description: `Inspect pool for ${firstPosition.pair}`
1688
+ },
1689
+ {
1690
+ command: "cl quote",
1691
+ args: {
1692
+ tokenIn: firstPosition.token0.address,
1693
+ tokenOut: firstPosition.token1.address,
1694
+ amountIn: "1"
1695
+ },
1696
+ description: `Quote a ${firstPosition.token0.symbol} \u2192 ${firstPosition.token1.symbol} swap`
1697
+ }
1698
+ ] : []
1699
+ }
1700
+ }
1701
+ );
1474
1702
  }
1475
1703
  });
1476
1704
  cl.command("quote", {
1477
1705
  description: "Quote a single-hop Slipstream swap via QuoterV2.",
1478
- args: z.object({
1479
- tokenIn: z.string().describe("Input token address"),
1480
- tokenOut: z.string().describe("Output token address"),
1481
- amountIn: z.string().describe("Input amount in human-readable decimal units")
1706
+ args: z2.object({
1707
+ tokenIn: z2.string().describe("Input token address"),
1708
+ tokenOut: z2.string().describe("Output token address"),
1709
+ amountIn: z2.string().describe("Input amount in human-readable decimal units")
1482
1710
  }),
1483
- options: z.object({
1484
- fee: z.coerce.number().int().positive().optional().describe("Optional fee tier filter")
1711
+ options: z2.object({
1712
+ fee: z2.coerce.number().int().positive().optional().describe("Optional fee tier filter")
1485
1713
  }),
1486
1714
  env,
1487
1715
  output: quoteOutputSchema,
@@ -1494,8 +1722,8 @@ cl.command("quote", {
1494
1722
  });
1495
1723
  }
1496
1724
  const client = createAboreanPublicClient(c.env.ABSTRACT_RPC_URL);
1497
- const inAddress = checksumAddress(tokenIn);
1498
- const outAddress = checksumAddress(tokenOut);
1725
+ const inAddress = checksumAddress2(tokenIn);
1726
+ const outAddress = checksumAddress2(tokenOut);
1499
1727
  const allPools = await listPoolAddresses(client);
1500
1728
  const poolStates = await readPoolStates(client, allPools);
1501
1729
  const pairPools = poolStates.filter((pool) => {
@@ -1560,67 +1788,214 @@ cl.command("quote", {
1560
1788
  const inIsToken0 = normalizeAddress(inAddress) === normalizeAddress(selectedPool.token0);
1561
1789
  const poolMidPriceOutPerIn = inIsToken0 ? poolPrices.token1PerToken0 : poolPrices.token0PerToken1;
1562
1790
  const priceImpactPct = quotePriceOutPerIn === null || poolMidPriceOutPerIn === null || poolMidPriceOutPerIn === 0 ? null : finiteOrNull((poolMidPriceOutPerIn - quotePriceOutPerIn) / poolMidPriceOutPerIn * 100);
1791
+ return c.ok(
1792
+ {
1793
+ pool: checksumAddress2(selectedPool.pool),
1794
+ selectedFee: selectedPool.fee,
1795
+ selectedTickSpacing: selectedPool.tickSpacing,
1796
+ tokenIn: inMeta,
1797
+ tokenOut: outMeta,
1798
+ amountIn: {
1799
+ raw: amountInRaw.toString(),
1800
+ decimal: amountInDecimal
1801
+ },
1802
+ amountOut: {
1803
+ raw: amountOutRaw.toString(),
1804
+ decimal: amountOutDecimal
1805
+ },
1806
+ execution: {
1807
+ sqrtPriceX96After: quote[1].toString(),
1808
+ initializedTicksCrossed: quote[2],
1809
+ gasEstimate: quote[3].toString()
1810
+ },
1811
+ prices: {
1812
+ poolMidPriceOutPerIn,
1813
+ quotePriceOutPerIn,
1814
+ priceImpactPct
1815
+ }
1816
+ },
1817
+ c.format === "json" || c.format === "jsonl" ? void 0 : {
1818
+ cta: {
1819
+ description: "Related commands:",
1820
+ commands: [
1821
+ {
1822
+ command: "cl pool",
1823
+ args: { pool: checksumAddress2(selectedPool.pool) },
1824
+ description: "Inspect the pool used for this quote"
1825
+ },
1826
+ {
1827
+ command: "cl quote",
1828
+ args: {
1829
+ tokenIn: outMeta.address,
1830
+ tokenOut: inMeta.address,
1831
+ amountIn: amountOutDecimal
1832
+ },
1833
+ description: `Reverse quote ${outMeta.symbol} \u2192 ${inMeta.symbol}`
1834
+ }
1835
+ ]
1836
+ }
1837
+ }
1838
+ );
1839
+ }
1840
+ });
1841
+ var DEFAULT_SLIPPAGE_PERCENT = 0.5;
1842
+ var DEFAULT_DEADLINE_SECONDS = 300;
1843
+ var swapOutputSchema = z2.object({
1844
+ pool: z2.string(),
1845
+ tokenIn: tokenSchema,
1846
+ tokenOut: tokenSchema,
1847
+ amountIn: z2.object({ raw: z2.string(), decimal: z2.string() }),
1848
+ quotedAmountOut: z2.object({ raw: z2.string(), decimal: z2.string() }),
1849
+ amountOutMinimum: z2.object({ raw: z2.string(), decimal: z2.string() }),
1850
+ slippagePercent: z2.number(),
1851
+ deadlineSeconds: z2.number(),
1852
+ tx: z2.union([
1853
+ z2.object({
1854
+ txHash: z2.string(),
1855
+ blockNumber: z2.number(),
1856
+ gasUsed: z2.string()
1857
+ }),
1858
+ z2.object({
1859
+ dryRun: z2.literal(true),
1860
+ estimatedGas: z2.string(),
1861
+ simulationResult: z2.unknown()
1862
+ })
1863
+ ])
1864
+ });
1865
+ cl.command("swap", {
1866
+ description: "Execute a single-hop Slipstream swap via the CL SwapRouter.",
1867
+ options: z2.object({
1868
+ "token-in": z2.string().describe("Input token address"),
1869
+ "token-out": z2.string().describe("Output token address"),
1870
+ "amount-in": z2.string().describe("Input amount in wei"),
1871
+ slippage: z2.coerce.number().default(DEFAULT_SLIPPAGE_PERCENT).describe("Slippage tolerance in percent (default: 0.5)"),
1872
+ deadline: z2.coerce.number().int().default(DEFAULT_DEADLINE_SECONDS).describe("Transaction deadline in seconds from now (default: 300)"),
1873
+ ...writeOptions.shape
1874
+ }),
1875
+ env: writeEnv,
1876
+ output: swapOutputSchema,
1877
+ async run(c) {
1878
+ const tokenIn = c.options["token-in"];
1879
+ const tokenOut = c.options["token-out"];
1880
+ const amountInWei = c.options["amount-in"];
1881
+ const slippage = c.options.slippage;
1882
+ const deadlineSeconds = c.options.deadline;
1883
+ if (!isAddress(tokenIn) || !isAddress(tokenOut)) {
1884
+ return c.error({
1885
+ code: "INVALID_ADDRESS",
1886
+ message: "token-in and token-out must both be valid 0x-prefixed 20-byte addresses."
1887
+ });
1888
+ }
1889
+ const inAddress = checksumAddress2(tokenIn);
1890
+ const outAddress = checksumAddress2(tokenOut);
1891
+ let amountInRaw;
1892
+ try {
1893
+ amountInRaw = BigInt(amountInWei);
1894
+ } catch {
1895
+ return c.error({
1896
+ code: "INVALID_AMOUNT",
1897
+ message: `Invalid amount-in: "${amountInWei}". Provide a valid integer in wei.`
1898
+ });
1899
+ }
1900
+ if (amountInRaw <= 0n) {
1901
+ return c.error({
1902
+ code: "INVALID_AMOUNT",
1903
+ message: "amount-in must be a positive integer."
1904
+ });
1905
+ }
1906
+ if (slippage < 0 || slippage > 100) {
1907
+ return c.error({
1908
+ code: "INVALID_SLIPPAGE",
1909
+ message: `Slippage must be between 0 and 100. Got: ${slippage}`
1910
+ });
1911
+ }
1912
+ const client = createAboreanPublicClient(c.env.ABSTRACT_RPC_URL);
1913
+ const allPools = await listPoolAddresses(client);
1914
+ const poolStates = await readPoolStates(client, allPools);
1915
+ const pairPools = poolStates.filter((pool) => {
1916
+ const a = normalizeAddress(pool.token0);
1917
+ const b = normalizeAddress(pool.token1);
1918
+ const tokenInNorm = normalizeAddress(inAddress);
1919
+ const tokenOutNorm = normalizeAddress(outAddress);
1920
+ return a === tokenInNorm && b === tokenOutNorm || a === tokenOutNorm && b === tokenInNorm;
1921
+ });
1922
+ if (pairPools.length === 0) {
1923
+ return c.error({
1924
+ code: "POOL_NOT_FOUND",
1925
+ message: `No Slipstream pool found for pair ${inAddress}/${outAddress}.`
1926
+ });
1927
+ }
1928
+ const selectedPool = [...pairPools].sort((a, b) => {
1929
+ if (a.liquidity === b.liquidity) return 0;
1930
+ return a.liquidity > b.liquidity ? -1 : 1;
1931
+ })[0];
1932
+ const tokenMeta = await readTokenMetadata(client, [inAddress, outAddress]);
1933
+ const inMeta = tokenMeta.get(inAddress) ?? toTokenMetaFallback(inAddress);
1934
+ const outMeta = tokenMeta.get(outAddress) ?? toTokenMetaFallback(outAddress);
1935
+ const quote = await client.readContract({
1936
+ abi: quoterV2Abi,
1937
+ address: ABOREAN_CL_ADDRESSES.quoterV2,
1938
+ functionName: "quoteExactInputSingle",
1939
+ args: [
1940
+ {
1941
+ tokenIn: inAddress,
1942
+ tokenOut: outAddress,
1943
+ amountIn: amountInRaw,
1944
+ tickSpacing: selectedPool.tickSpacing,
1945
+ sqrtPriceLimitX96: 0n
1946
+ }
1947
+ ]
1948
+ });
1949
+ const quotedAmountOut = quote[0];
1950
+ const slippageBps = BigInt(Math.round(slippage * 100));
1951
+ const amountOutMinimum = quotedAmountOut - quotedAmountOut * slippageBps / 10000n;
1952
+ const account = resolveAccount(c.env);
1953
+ const deadlineTimestamp = BigInt(Math.floor(Date.now() / 1e3) + deadlineSeconds);
1954
+ const txResult = await aboreanWriteTx({
1955
+ env: c.env,
1956
+ options: {
1957
+ "dry-run": c.options["dry-run"],
1958
+ "gas-limit": c.options["gas-limit"],
1959
+ "max-fee": c.options["max-fee"],
1960
+ nonce: c.options.nonce
1961
+ },
1962
+ address: ABOREAN_CL_ADDRESSES.swapRouter,
1963
+ abi: swapRouterAbi,
1964
+ functionName: "exactInputSingle",
1965
+ args: [
1966
+ {
1967
+ tokenIn: inAddress,
1968
+ tokenOut: outAddress,
1969
+ tickSpacing: selectedPool.tickSpacing,
1970
+ recipient: account.address,
1971
+ deadline: deadlineTimestamp,
1972
+ amountIn: amountInRaw,
1973
+ amountOutMinimum,
1974
+ sqrtPriceLimitX96: 0n
1975
+ }
1976
+ ]
1977
+ });
1978
+ const amountInDecimal = formatUnits(amountInRaw, inMeta.decimals);
1979
+ const quotedOutDecimal = formatUnits(quotedAmountOut, outMeta.decimals);
1980
+ const minOutDecimal = formatUnits(amountOutMinimum, outMeta.decimals);
1563
1981
  return c.ok({
1564
- pool: checksumAddress(selectedPool.pool),
1565
- selectedFee: selectedPool.fee,
1566
- selectedTickSpacing: selectedPool.tickSpacing,
1982
+ pool: checksumAddress2(selectedPool.pool),
1567
1983
  tokenIn: inMeta,
1568
1984
  tokenOut: outMeta,
1569
- amountIn: {
1570
- raw: amountInRaw.toString(),
1571
- decimal: amountInDecimal
1572
- },
1573
- amountOut: {
1574
- raw: amountOutRaw.toString(),
1575
- decimal: amountOutDecimal
1576
- },
1577
- execution: {
1578
- sqrtPriceX96After: quote[1].toString(),
1579
- initializedTicksCrossed: quote[2],
1580
- gasEstimate: quote[3].toString()
1581
- },
1582
- prices: {
1583
- poolMidPriceOutPerIn,
1584
- quotePriceOutPerIn,
1585
- priceImpactPct
1586
- }
1985
+ amountIn: { raw: amountInRaw.toString(), decimal: amountInDecimal },
1986
+ quotedAmountOut: { raw: quotedAmountOut.toString(), decimal: quotedOutDecimal },
1987
+ amountOutMinimum: { raw: amountOutMinimum.toString(), decimal: minOutDecimal },
1988
+ slippagePercent: slippage,
1989
+ deadlineSeconds,
1990
+ tx: txResult
1587
1991
  });
1588
1992
  }
1589
1993
  });
1590
1994
 
1591
1995
  // src/commands/gauges.ts
1592
- import { Cli as Cli2, z as z2 } from "incur";
1593
-
1594
- // src/commands/_common.ts
1595
- import { checksumAddress as checksumAddress2, weiToEth } from "@spectratools/cli-shared";
1596
- var ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
1597
- function toChecksum(address) {
1598
- try {
1599
- return checksumAddress2(address);
1600
- } catch {
1601
- return address;
1602
- }
1603
- }
1604
- function asNum(value) {
1605
- return Number(value);
1606
- }
1607
- function relTime(unixSeconds) {
1608
- const ts = typeof unixSeconds === "bigint" ? Number(unixSeconds) : unixSeconds;
1609
- if (!Number.isFinite(ts) || ts <= 0) return "n/a";
1610
- const delta = ts - Math.floor(Date.now() / 1e3);
1611
- const abs = Math.abs(delta);
1612
- const hours = Math.floor(abs / 3600);
1613
- const minutes = Math.floor(abs % 3600 / 60);
1614
- const label = hours > 0 ? `${hours}h ${minutes}m` : `${minutes}m`;
1615
- return delta >= 0 ? `in ${label}` : `${label} ago`;
1616
- }
1617
- function clampPositive(seconds) {
1618
- return seconds > 0 ? seconds : 0;
1619
- }
1620
-
1621
- // src/commands/gauges.ts
1622
- var env2 = z2.object({
1623
- ABSTRACT_RPC_URL: z2.string().optional().describe("Abstract RPC URL override")
1996
+ import { Cli as Cli2, z as z3 } from "incur";
1997
+ var env2 = z3.object({
1998
+ ABSTRACT_RPC_URL: z3.string().optional().describe("Abstract RPC URL override")
1624
1999
  });
1625
2000
  async function discoverGaugePools(client) {
1626
2001
  const [v2PoolCount, clPoolCount] = await Promise.all([
@@ -1676,20 +2051,20 @@ var gauges = Cli2.create("gauges", {
1676
2051
  gauges.command("list", {
1677
2052
  description: "List active gauges with pool, emissions, and staking stats.",
1678
2053
  env: env2,
1679
- output: z2.object({
1680
- gauges: z2.array(
1681
- z2.object({
1682
- pool: z2.string(),
1683
- gauge: z2.string(),
1684
- rewardToken: z2.string(),
1685
- rewardRate: z2.string(),
1686
- totalStaked: z2.string(),
1687
- claimableEmissions: z2.string(),
1688
- periodFinish: z2.number(),
1689
- periodFinishRelative: z2.string()
2054
+ output: z3.object({
2055
+ gauges: z3.array(
2056
+ z3.object({
2057
+ pool: z3.string(),
2058
+ gauge: z3.string(),
2059
+ rewardToken: z3.string(),
2060
+ rewardRate: z3.string(),
2061
+ totalStaked: z3.string(),
2062
+ claimableEmissions: z3.string(),
2063
+ periodFinish: z3.number(),
2064
+ periodFinishRelative: z3.string()
1690
2065
  })
1691
2066
  ),
1692
- count: z2.number()
2067
+ count: z3.number()
1693
2068
  }),
1694
2069
  examples: [{ description: "List all active gauges and current emissions state" }],
1695
2070
  async run(c) {
@@ -1747,35 +2122,50 @@ gauges.command("list", {
1747
2122
  periodFinishRelative: relTime(periodFinish)
1748
2123
  };
1749
2124
  });
1750
- return c.ok({
1751
- gauges: items,
1752
- count: items.length
1753
- });
2125
+ const firstGauge = items[0];
2126
+ return c.ok(
2127
+ {
2128
+ gauges: items,
2129
+ count: items.length
2130
+ },
2131
+ c.format === "json" || c.format === "jsonl" ? void 0 : {
2132
+ cta: {
2133
+ description: "Explore gauges:",
2134
+ commands: firstGauge ? [
2135
+ {
2136
+ command: "gauges info",
2137
+ args: { gauge: firstGauge.gauge },
2138
+ description: "Inspect top gauge details"
2139
+ }
2140
+ ] : []
2141
+ }
2142
+ }
2143
+ );
1754
2144
  }
1755
2145
  });
1756
2146
  gauges.command("info", {
1757
2147
  description: "Get detailed state for one gauge address.",
1758
- args: z2.object({
1759
- gauge: z2.string().describe("Gauge contract address")
2148
+ args: z3.object({
2149
+ gauge: z3.string().describe("Gauge contract address")
1760
2150
  }),
1761
2151
  env: env2,
1762
- output: z2.object({
1763
- gauge: z2.string(),
1764
- pool: z2.string(),
1765
- isAlive: z2.boolean(),
1766
- stakingToken: z2.string(),
1767
- rewardToken: z2.string(),
1768
- totalStaked: z2.string(),
1769
- rewardRate: z2.string(),
1770
- rewardPerTokenStored: z2.string(),
1771
- fees0: z2.string(),
1772
- fees1: z2.string(),
1773
- left: z2.string(),
1774
- periodFinish: z2.number(),
1775
- periodFinishRelative: z2.string(),
1776
- lastUpdateTime: z2.number(),
1777
- bribeContract: z2.string(),
1778
- feeContract: z2.string()
2152
+ output: z3.object({
2153
+ gauge: z3.string(),
2154
+ pool: z3.string(),
2155
+ isAlive: z3.boolean(),
2156
+ stakingToken: z3.string(),
2157
+ rewardToken: z3.string(),
2158
+ totalStaked: z3.string(),
2159
+ rewardRate: z3.string(),
2160
+ rewardPerTokenStored: z3.string(),
2161
+ fees0: z3.string(),
2162
+ fees1: z3.string(),
2163
+ left: z3.string(),
2164
+ periodFinish: z3.number(),
2165
+ periodFinishRelative: z3.string(),
2166
+ lastUpdateTime: z3.number(),
2167
+ bribeContract: z3.string(),
2168
+ feeContract: z3.string()
1779
2169
  }),
1780
2170
  examples: [
1781
2171
  {
@@ -1877,44 +2267,66 @@ gauges.command("info", {
1877
2267
  functionName: "left"
1878
2268
  })
1879
2269
  ]);
1880
- return c.ok({
1881
- gauge: toChecksum(gauge),
1882
- pool: toChecksum(pool),
1883
- isAlive,
1884
- stakingToken: toChecksum(stakingToken),
1885
- rewardToken: toChecksum(rewardToken),
1886
- totalStaked: totalStaked.toString(),
1887
- rewardRate: rewardRate.toString(),
1888
- rewardPerTokenStored: rewardPerTokenStored.toString(),
1889
- fees0: fees0.toString(),
1890
- fees1: fees1.toString(),
1891
- left: left.toString(),
1892
- periodFinish: asNum(periodFinish),
1893
- periodFinishRelative: relTime(periodFinish),
1894
- lastUpdateTime: asNum(lastUpdateTime),
1895
- bribeContract: toChecksum(bribeContract),
1896
- feeContract: toChecksum(feeContract)
1897
- });
2270
+ return c.ok(
2271
+ {
2272
+ gauge: toChecksum(gauge),
2273
+ pool: toChecksum(pool),
2274
+ isAlive,
2275
+ stakingToken: toChecksum(stakingToken),
2276
+ rewardToken: toChecksum(rewardToken),
2277
+ totalStaked: totalStaked.toString(),
2278
+ rewardRate: rewardRate.toString(),
2279
+ rewardPerTokenStored: rewardPerTokenStored.toString(),
2280
+ fees0: fees0.toString(),
2281
+ fees1: fees1.toString(),
2282
+ left: left.toString(),
2283
+ periodFinish: asNum(periodFinish),
2284
+ periodFinishRelative: relTime(periodFinish),
2285
+ lastUpdateTime: asNum(lastUpdateTime),
2286
+ bribeContract: toChecksum(bribeContract),
2287
+ feeContract: toChecksum(feeContract)
2288
+ },
2289
+ c.format === "json" || c.format === "jsonl" ? void 0 : {
2290
+ cta: {
2291
+ description: "Related commands:",
2292
+ commands: [
2293
+ {
2294
+ command: "ve stats",
2295
+ description: "View veABX global stats"
2296
+ },
2297
+ {
2298
+ command: "voter weights",
2299
+ description: "Check pool voting weight distribution"
2300
+ },
2301
+ {
2302
+ command: "voter bribes",
2303
+ args: { pool: toChecksum(pool) },
2304
+ description: "View bribe rewards for this pool"
2305
+ }
2306
+ ]
2307
+ }
2308
+ }
2309
+ );
1898
2310
  }
1899
2311
  });
1900
2312
  gauges.command("staked", {
1901
2313
  description: "Show one address staking positions across all gauges.",
1902
- args: z2.object({
1903
- address: z2.string().describe("Wallet address to inspect")
2314
+ args: z3.object({
2315
+ address: z3.string().describe("Wallet address to inspect")
1904
2316
  }),
1905
2317
  env: env2,
1906
- output: z2.object({
1907
- address: z2.string(),
1908
- positions: z2.array(
1909
- z2.object({
1910
- pool: z2.string(),
1911
- gauge: z2.string(),
1912
- rewardToken: z2.string(),
1913
- staked: z2.string(),
1914
- earned: z2.string()
2318
+ output: z3.object({
2319
+ address: z3.string(),
2320
+ positions: z3.array(
2321
+ z3.object({
2322
+ pool: z3.string(),
2323
+ gauge: z3.string(),
2324
+ rewardToken: z3.string(),
2325
+ staked: z3.string(),
2326
+ earned: z3.string()
1915
2327
  })
1916
2328
  ),
1917
- count: z2.number()
2329
+ count: z3.number()
1918
2330
  }),
1919
2331
  examples: [
1920
2332
  {
@@ -1973,21 +2385,43 @@ gauges.command("staked", {
1973
2385
  staked: position.staked.toString(),
1974
2386
  earned: position.earned.toString()
1975
2387
  }));
1976
- return c.ok({
1977
- address: toChecksum(c.args.address),
1978
- positions,
1979
- count: positions.length
1980
- });
2388
+ const firstPosition = positions[0];
2389
+ return c.ok(
2390
+ {
2391
+ address: toChecksum(c.args.address),
2392
+ positions,
2393
+ count: positions.length
2394
+ },
2395
+ c.format === "json" || c.format === "jsonl" ? void 0 : {
2396
+ cta: {
2397
+ description: "Related commands:",
2398
+ commands: [
2399
+ ...firstPosition ? [
2400
+ {
2401
+ command: "gauges info",
2402
+ args: { gauge: firstPosition.gauge },
2403
+ description: "Inspect gauge details"
2404
+ }
2405
+ ] : [],
2406
+ {
2407
+ command: "ve locks",
2408
+ args: { address: toChecksum(c.args.address) },
2409
+ description: "View veNFT locks for this address"
2410
+ }
2411
+ ]
2412
+ }
2413
+ }
2414
+ );
1981
2415
  }
1982
2416
  });
1983
2417
 
1984
2418
  // src/commands/lending.ts
1985
2419
  import { checksumAddress as checksumAddress3, isAddress as isAddress2 } from "@spectratools/cli-shared";
1986
- import { Cli as Cli3, z as z3 } from "incur";
2420
+ import { Cli as Cli3, z as z4 } from "incur";
1987
2421
  import { formatUnits as formatUnits2 } from "viem";
1988
2422
  var MORPHO_DEPLOY_BLOCK = 13947713n;
1989
- var env3 = z3.object({
1990
- ABSTRACT_RPC_URL: z3.string().optional().describe("Abstract RPC URL override")
2423
+ var env3 = z4.object({
2424
+ ABSTRACT_RPC_URL: z4.string().optional().describe("Abstract RPC URL override")
1991
2425
  });
1992
2426
  var morphoAbi = [
1993
2427
  {
@@ -2068,27 +2502,27 @@ var erc20MetadataAbi2 = [
2068
2502
  outputs: [{ type: "uint8" }]
2069
2503
  }
2070
2504
  ];
2071
- var tokenMetaSchema = z3.object({
2072
- address: z3.string(),
2073
- symbol: z3.string(),
2074
- decimals: z3.number()
2505
+ var tokenMetaSchema = z4.object({
2506
+ address: z4.string(),
2507
+ symbol: z4.string(),
2508
+ decimals: z4.number()
2075
2509
  });
2076
- var lendingMarketRowSchema = z3.object({
2077
- marketId: z3.string(),
2510
+ var lendingMarketRowSchema = z4.object({
2511
+ marketId: z4.string(),
2078
2512
  loanToken: tokenMetaSchema,
2079
2513
  collateralToken: tokenMetaSchema,
2080
- oracle: z3.string(),
2081
- irm: z3.string(),
2082
- lltvBps: z3.number(),
2083
- lltvPercent: z3.number(),
2084
- totalSupplyAssets: z3.string(),
2085
- totalBorrowAssets: z3.string(),
2086
- totalSupplyShares: z3.string(),
2087
- totalBorrowShares: z3.string(),
2088
- availableLiquidityAssets: z3.string(),
2089
- utilization: z3.number().nullable(),
2090
- feeWad: z3.string(),
2091
- lastUpdate: z3.number()
2514
+ oracle: z4.string(),
2515
+ irm: z4.string(),
2516
+ lltvBps: z4.number(),
2517
+ lltvPercent: z4.number(),
2518
+ totalSupplyAssets: z4.string(),
2519
+ totalBorrowAssets: z4.string(),
2520
+ totalSupplyShares: z4.string(),
2521
+ totalBorrowShares: z4.string(),
2522
+ availableLiquidityAssets: z4.string(),
2523
+ utilization: z4.number().nullable(),
2524
+ feeWad: z4.string(),
2525
+ lastUpdate: z4.number()
2092
2526
  });
2093
2527
  function isMarketId(value) {
2094
2528
  return /^0x[0-9a-fA-F]{64}$/.test(value);
@@ -2324,21 +2758,21 @@ var lending = Cli3.create("lending", {
2324
2758
  });
2325
2759
  lending.command("markets", {
2326
2760
  description: "List Morpho markets discovered from CreateMarket events.",
2327
- args: z3.object({
2328
- limit: z3.coerce.number().int().positive().max(200).default(25).describe("Max markets to return")
2761
+ args: z4.object({
2762
+ limit: z4.coerce.number().int().positive().max(200).default(25).describe("Max markets to return")
2329
2763
  }),
2330
2764
  env: env3,
2331
- output: z3.object({
2332
- morpho: z3.string(),
2333
- marketCount: z3.number(),
2334
- markets: z3.array(lendingMarketRowSchema),
2335
- totalsByLoanToken: z3.array(
2336
- z3.object({
2337
- token: z3.string(),
2338
- symbol: z3.string(),
2339
- decimals: z3.number(),
2340
- totalSupplyAssets: z3.string(),
2341
- totalBorrowAssets: z3.string()
2765
+ output: z4.object({
2766
+ morpho: z4.string(),
2767
+ marketCount: z4.number(),
2768
+ markets: z4.array(lendingMarketRowSchema),
2769
+ totalsByLoanToken: z4.array(
2770
+ z4.object({
2771
+ token: z4.string(),
2772
+ symbol: z4.string(),
2773
+ decimals: z4.number(),
2774
+ totalSupplyAssets: z4.string(),
2775
+ totalBorrowAssets: z4.string()
2342
2776
  })
2343
2777
  )
2344
2778
  }),
@@ -2354,18 +2788,39 @@ lending.command("markets", {
2354
2788
  const rows = markets.map((market) => toMarketRow(market.marketId, market.params, market.state, tokenMeta)).sort(
2355
2789
  (a, b) => BigInt(a.totalSupplyAssets) > BigInt(b.totalSupplyAssets) ? -1 : BigInt(a.totalSupplyAssets) < BigInt(b.totalSupplyAssets) ? 1 : 0
2356
2790
  ).slice(0, c.args.limit);
2357
- return c.ok({
2358
- morpho: toChecksum(ABOREAN_LENDING_ADDRESSES.morphoBlue),
2359
- marketCount: marketIds.length,
2360
- markets: rows,
2361
- totalsByLoanToken: summarizeByLoanToken(markets, tokenMeta)
2362
- });
2791
+ const firstMarket = rows[0];
2792
+ return c.ok(
2793
+ {
2794
+ morpho: toChecksum(ABOREAN_LENDING_ADDRESSES.morphoBlue),
2795
+ marketCount: marketIds.length,
2796
+ markets: rows,
2797
+ totalsByLoanToken: summarizeByLoanToken(markets, tokenMeta)
2798
+ },
2799
+ c.format === "json" || c.format === "jsonl" ? void 0 : {
2800
+ cta: {
2801
+ description: "Explore lending:",
2802
+ commands: [
2803
+ ...firstMarket ? [
2804
+ {
2805
+ command: "lending market",
2806
+ args: { marketId: firstMarket.marketId },
2807
+ description: `Inspect ${firstMarket.loanToken.symbol}/${firstMarket.collateralToken.symbol} market`
2808
+ }
2809
+ ] : [],
2810
+ {
2811
+ command: "pools list",
2812
+ description: "View V2 AMM pools"
2813
+ }
2814
+ ]
2815
+ }
2816
+ }
2817
+ );
2363
2818
  }
2364
2819
  });
2365
2820
  lending.command("market", {
2366
2821
  description: "Get details for one Morpho market id (bytes32).",
2367
- args: z3.object({
2368
- marketId: z3.string().describe("Morpho market id (bytes32 hex)")
2822
+ args: z4.object({
2823
+ marketId: z4.string().describe("Morpho market id (bytes32 hex)")
2369
2824
  }),
2370
2825
  env: env3,
2371
2826
  output: lendingMarketRowSchema,
@@ -2401,34 +2856,53 @@ lending.command("market", {
2401
2856
  const params = normalizeMarketParams(paramsRaw);
2402
2857
  const state = normalizeMarketState(stateRaw);
2403
2858
  const tokenMeta = await readTokenMetadata2(client, [params.loanToken, params.collateralToken]);
2404
- return c.ok(toMarketRow(marketId, params, state, tokenMeta));
2859
+ const row = toMarketRow(marketId, params, state, tokenMeta);
2860
+ return c.ok(
2861
+ row,
2862
+ c.format === "json" || c.format === "jsonl" ? void 0 : {
2863
+ cta: {
2864
+ description: "Related commands:",
2865
+ commands: [
2866
+ {
2867
+ command: "lending position",
2868
+ args: { marketId, user: "<address>" },
2869
+ description: "Inspect a user position in this market"
2870
+ },
2871
+ {
2872
+ command: "lending markets",
2873
+ description: "List all Morpho markets"
2874
+ }
2875
+ ]
2876
+ }
2877
+ }
2878
+ );
2405
2879
  }
2406
2880
  });
2407
2881
  lending.command("position", {
2408
2882
  description: "Inspect one user position in a Morpho market.",
2409
- args: z3.object({
2410
- marketId: z3.string().describe("Morpho market id (bytes32 hex)"),
2411
- user: z3.string().describe("Position owner address")
2883
+ args: z4.object({
2884
+ marketId: z4.string().describe("Morpho market id (bytes32 hex)"),
2885
+ user: z4.string().describe("Position owner address")
2412
2886
  }),
2413
2887
  env: env3,
2414
- output: z3.object({
2415
- marketId: z3.string(),
2416
- user: z3.string(),
2888
+ output: z4.object({
2889
+ marketId: z4.string(),
2890
+ user: z4.string(),
2417
2891
  loanToken: tokenMetaSchema,
2418
2892
  collateralToken: tokenMetaSchema,
2419
- supplyShares: z3.string(),
2420
- supplyAssetsEstimate: z3.object({
2421
- raw: z3.string(),
2422
- decimal: z3.string()
2893
+ supplyShares: z4.string(),
2894
+ supplyAssetsEstimate: z4.object({
2895
+ raw: z4.string(),
2896
+ decimal: z4.string()
2423
2897
  }),
2424
- borrowShares: z3.string(),
2425
- borrowAssetsEstimate: z3.object({
2426
- raw: z3.string(),
2427
- decimal: z3.string()
2898
+ borrowShares: z4.string(),
2899
+ borrowAssetsEstimate: z4.object({
2900
+ raw: z4.string(),
2901
+ decimal: z4.string()
2428
2902
  }),
2429
- collateralAssets: z3.object({
2430
- raw: z3.string(),
2431
- decimal: z3.string()
2903
+ collateralAssets: z4.object({
2904
+ raw: z4.string(),
2905
+ decimal: z4.string()
2432
2906
  })
2433
2907
  }),
2434
2908
  examples: [
@@ -2508,71 +2982,137 @@ lending.command("position", {
2508
2982
  state.totalBorrowShares,
2509
2983
  state.totalBorrowAssets
2510
2984
  );
2511
- return c.ok({
2512
- marketId,
2513
- user: toChecksum(user),
2514
- loanToken: {
2515
- address: toChecksum(loanMeta.address),
2516
- symbol: loanMeta.symbol,
2517
- decimals: loanMeta.decimals
2518
- },
2519
- collateralToken: {
2520
- address: toChecksum(collateralMeta.address),
2521
- symbol: collateralMeta.symbol,
2522
- decimals: collateralMeta.decimals
2523
- },
2524
- supplyShares: position.supplyShares.toString(),
2525
- supplyAssetsEstimate: {
2526
- raw: supplyAssetsEstimate.toString(),
2527
- decimal: formatUnits2(supplyAssetsEstimate, loanMeta.decimals)
2528
- },
2529
- borrowShares: position.borrowShares.toString(),
2530
- borrowAssetsEstimate: {
2531
- raw: borrowAssetsEstimate.toString(),
2532
- decimal: formatUnits2(borrowAssetsEstimate, loanMeta.decimals)
2985
+ return c.ok(
2986
+ {
2987
+ marketId,
2988
+ user: toChecksum(user),
2989
+ loanToken: {
2990
+ address: toChecksum(loanMeta.address),
2991
+ symbol: loanMeta.symbol,
2992
+ decimals: loanMeta.decimals
2993
+ },
2994
+ collateralToken: {
2995
+ address: toChecksum(collateralMeta.address),
2996
+ symbol: collateralMeta.symbol,
2997
+ decimals: collateralMeta.decimals
2998
+ },
2999
+ supplyShares: position.supplyShares.toString(),
3000
+ supplyAssetsEstimate: {
3001
+ raw: supplyAssetsEstimate.toString(),
3002
+ decimal: formatUnits2(supplyAssetsEstimate, loanMeta.decimals)
3003
+ },
3004
+ borrowShares: position.borrowShares.toString(),
3005
+ borrowAssetsEstimate: {
3006
+ raw: borrowAssetsEstimate.toString(),
3007
+ decimal: formatUnits2(borrowAssetsEstimate, loanMeta.decimals)
3008
+ },
3009
+ collateralAssets: {
3010
+ raw: position.collateral.toString(),
3011
+ decimal: formatUnits2(BigInt(position.collateral), collateralMeta.decimals)
3012
+ }
2533
3013
  },
2534
- collateralAssets: {
2535
- raw: position.collateral.toString(),
2536
- decimal: formatUnits2(BigInt(position.collateral), collateralMeta.decimals)
3014
+ c.format === "json" || c.format === "jsonl" ? void 0 : {
3015
+ cta: {
3016
+ description: "Related commands:",
3017
+ commands: [
3018
+ {
3019
+ command: "lending market",
3020
+ args: { marketId },
3021
+ description: "View market details"
3022
+ },
3023
+ {
3024
+ command: "lending markets",
3025
+ description: "List all Morpho markets"
3026
+ }
3027
+ ]
3028
+ }
2537
3029
  }
2538
- });
3030
+ );
2539
3031
  }
2540
3032
  });
2541
3033
 
2542
3034
  // src/commands/pools.ts
2543
3035
  import { checksumAddress as checksumAddress4, isAddress as isAddress3 } from "@spectratools/cli-shared";
2544
- import { Cli as Cli4, z as z4 } from "incur";
3036
+ import { Cli as Cli4, z as z5 } from "incur";
2545
3037
  import { formatUnits as formatUnits3, parseUnits as parseUnits2 } from "viem";
2546
3038
  var MULTICALL_BATCH_SIZE2 = 100;
2547
- var env4 = z4.object({
2548
- ABSTRACT_RPC_URL: z4.string().optional().describe("Abstract RPC URL override")
3039
+ var DEFAULT_SLIPPAGE_PERCENT2 = 0.5;
3040
+ var DEFAULT_DEADLINE_SECONDS2 = 300;
3041
+ var erc20ApproveAbi = [
3042
+ {
3043
+ type: "function",
3044
+ name: "approve",
3045
+ stateMutability: "nonpayable",
3046
+ inputs: [
3047
+ { name: "spender", type: "address" },
3048
+ { name: "amount", type: "uint256" }
3049
+ ],
3050
+ outputs: [{ name: "", type: "bool" }]
3051
+ },
3052
+ {
3053
+ type: "function",
3054
+ name: "allowance",
3055
+ stateMutability: "view",
3056
+ inputs: [
3057
+ { name: "owner", type: "address" },
3058
+ { name: "account", type: "address" }
3059
+ ],
3060
+ outputs: [{ name: "", type: "uint256" }]
3061
+ }
3062
+ ];
3063
+ var v2RouterSwapAbi = [
3064
+ {
3065
+ type: "function",
3066
+ name: "swapExactTokensForTokens",
3067
+ stateMutability: "nonpayable",
3068
+ inputs: [
3069
+ { name: "amountIn", type: "uint256" },
3070
+ { name: "amountOutMin", type: "uint256" },
3071
+ {
3072
+ name: "routes",
3073
+ type: "tuple[]",
3074
+ components: [
3075
+ { name: "from", type: "address" },
3076
+ { name: "to", type: "address" },
3077
+ { name: "stable", type: "bool" },
3078
+ { name: "factory", type: "address" }
3079
+ ]
3080
+ },
3081
+ { name: "to", type: "address" },
3082
+ { name: "deadline", type: "uint256" }
3083
+ ],
3084
+ outputs: [{ name: "amounts", type: "uint256[]" }]
3085
+ }
3086
+ ];
3087
+ var env4 = z5.object({
3088
+ ABSTRACT_RPC_URL: z5.string().optional().describe("Abstract RPC URL override")
2549
3089
  });
2550
- var tokenSchema2 = z4.object({
2551
- address: z4.string(),
2552
- symbol: z4.string(),
2553
- decimals: z4.number()
3090
+ var tokenSchema2 = z5.object({
3091
+ address: z5.string(),
3092
+ symbol: z5.string(),
3093
+ decimals: z5.number()
2554
3094
  });
2555
- var amountSchema = z4.object({
2556
- raw: z4.string(),
2557
- decimal: z4.string()
3095
+ var amountSchema = z5.object({
3096
+ raw: z5.string(),
3097
+ decimal: z5.string()
2558
3098
  });
2559
- var feeSchema = z4.object({
2560
- feeBps: z4.number(),
2561
- feePercent: z4.number()
3099
+ var feeSchema = z5.object({
3100
+ feeBps: z5.number(),
3101
+ feePercent: z5.number()
2562
3102
  }).nullable();
2563
- var poolSummarySchema = z4.object({
2564
- pool: z4.string(),
2565
- pair: z4.string(),
2566
- stable: z4.boolean(),
2567
- poolType: z4.enum(["stable", "volatile"]),
3103
+ var poolSummarySchema = z5.object({
3104
+ pool: z5.string(),
3105
+ pair: z5.string(),
3106
+ stable: z5.boolean(),
3107
+ poolType: z5.enum(["stable", "volatile"]),
2568
3108
  token0: tokenSchema2,
2569
3109
  token1: tokenSchema2,
2570
- reserves: z4.object({
3110
+ reserves: z5.object({
2571
3111
  token0: amountSchema,
2572
3112
  token1: amountSchema,
2573
- blockTimestampLast: z4.number()
3113
+ blockTimestampLast: z5.number()
2574
3114
  }),
2575
- totalSupply: z4.string(),
3115
+ totalSupply: z5.string(),
2576
3116
  fee: feeSchema
2577
3117
  });
2578
3118
  var erc20MetadataAbi3 = [
@@ -2777,17 +3317,17 @@ var pools = Cli4.create("pools", {
2777
3317
  });
2778
3318
  pools.command("list", {
2779
3319
  description: "List V2 pools with token pairs, reserves, and stable/volatile type.",
2780
- options: z4.object({
2781
- offset: z4.coerce.number().int().nonnegative().default(0).describe("Pool index offset"),
2782
- limit: z4.coerce.number().int().positive().max(500).default(50).describe("Maximum pools to return (max 500)")
3320
+ options: z5.object({
3321
+ offset: z5.coerce.number().int().nonnegative().default(0).describe("Pool index offset"),
3322
+ limit: z5.coerce.number().int().positive().max(500).default(50).describe("Maximum pools to return (max 500)")
2783
3323
  }),
2784
3324
  env: env4,
2785
- output: z4.object({
2786
- total: z4.number(),
2787
- offset: z4.number(),
2788
- limit: z4.number(),
2789
- count: z4.number(),
2790
- pools: z4.array(poolSummarySchema)
3325
+ output: z5.object({
3326
+ total: z5.number(),
3327
+ offset: z5.number(),
3328
+ limit: z5.number(),
3329
+ count: z5.number(),
3330
+ pools: z5.array(poolSummarySchema)
2791
3331
  }),
2792
3332
  async run(c) {
2793
3333
  const client = createAboreanPublicClient(c.env.ABSTRACT_RPC_URL);
@@ -2827,25 +3367,52 @@ pools.command("list", {
2827
3367
  client,
2828
3368
  poolStates.map((pool) => ({ pool: pool.pool, stable: pool.stable }))
2829
3369
  );
2830
- return c.ok({
2831
- total,
2832
- offset,
2833
- limit: c.options.limit,
2834
- count: poolStates.length,
2835
- pools: poolStates.map((pool, index) => toPoolSummary(pool, tokenMeta, fees[index] ?? null))
2836
- });
3370
+ const summaries = poolStates.map(
3371
+ (pool, index) => toPoolSummary(pool, tokenMeta, fees[index] ?? null)
3372
+ );
3373
+ const firstPool = summaries[0];
3374
+ return c.ok(
3375
+ {
3376
+ total,
3377
+ offset,
3378
+ limit: c.options.limit,
3379
+ count: summaries.length,
3380
+ pools: summaries
3381
+ },
3382
+ c.format === "json" || c.format === "jsonl" ? void 0 : {
3383
+ cta: {
3384
+ description: "Explore pools:",
3385
+ commands: [
3386
+ ...firstPool ? [
3387
+ {
3388
+ command: "pools pool",
3389
+ args: { address: firstPool.pool },
3390
+ description: `Inspect ${firstPool.pair}`
3391
+ }
3392
+ ] : [],
3393
+ ...firstPool ? [
3394
+ {
3395
+ command: "pools fees",
3396
+ args: { pool: firstPool.pool },
3397
+ description: `Check fees for ${firstPool.pair}`
3398
+ }
3399
+ ] : []
3400
+ ]
3401
+ }
3402
+ }
3403
+ );
2837
3404
  }
2838
3405
  });
2839
3406
  pools.command("pool", {
2840
3407
  description: "Get detailed state for one V2 pool.",
2841
- args: z4.object({
2842
- address: z4.string().describe("Pool address")
3408
+ args: z5.object({
3409
+ address: z5.string().describe("Pool address")
2843
3410
  }),
2844
3411
  env: env4,
2845
- output: z4.object({
3412
+ output: z5.object({
2846
3413
  pool: poolSummarySchema.extend({
2847
- poolFees: z4.string(),
2848
- factory: z4.string()
3414
+ poolFees: z5.string(),
3415
+ factory: z5.string()
2849
3416
  })
2850
3417
  }),
2851
3418
  async run(c) {
@@ -2896,47 +3463,71 @@ pools.command("pool", {
2896
3463
  ]);
2897
3464
  const tokenMeta = await readTokenMetadata3(client, [token0, token1]);
2898
3465
  const [fee] = await readPoolFees(client, [{ pool: poolAddress, stable }]);
2899
- return c.ok({
2900
- pool: {
2901
- ...toPoolSummary(
2902
- {
2903
- pool: poolAddress,
2904
- token0,
2905
- token1,
2906
- stable,
2907
- reserve0: reserves[0],
2908
- reserve1: reserves[1],
2909
- blockTimestampLast: reserves[2],
2910
- totalSupply
2911
- },
2912
- tokenMeta,
2913
- fee ?? null
2914
- ),
2915
- poolFees: checksumAddress4(poolFees),
2916
- factory: checksumAddress4(factory)
3466
+ const summary = toPoolSummary(
3467
+ {
3468
+ pool: poolAddress,
3469
+ token0,
3470
+ token1,
3471
+ stable,
3472
+ reserve0: reserves[0],
3473
+ reserve1: reserves[1],
3474
+ blockTimestampLast: reserves[2],
3475
+ totalSupply
3476
+ },
3477
+ tokenMeta,
3478
+ fee ?? null
3479
+ );
3480
+ return c.ok(
3481
+ {
3482
+ pool: {
3483
+ ...summary,
3484
+ poolFees: checksumAddress4(poolFees),
3485
+ factory: checksumAddress4(factory)
3486
+ }
3487
+ },
3488
+ c.format === "json" || c.format === "jsonl" ? void 0 : {
3489
+ cta: {
3490
+ description: "Next steps:",
3491
+ commands: [
3492
+ {
3493
+ command: "pools quote",
3494
+ args: {
3495
+ tokenIn: summary.token0.address,
3496
+ tokenOut: summary.token1.address,
3497
+ amountIn: "1"
3498
+ },
3499
+ description: `Quote a ${summary.token0.symbol} \u2192 ${summary.token1.symbol} swap`
3500
+ },
3501
+ {
3502
+ command: "pools fees",
3503
+ args: { pool: checksumAddress4(poolAddress) },
3504
+ description: "Check fee configuration"
3505
+ }
3506
+ ]
3507
+ }
2917
3508
  }
2918
- });
3509
+ );
2919
3510
  }
2920
3511
  });
2921
3512
  pools.command("quote", {
2922
3513
  description: "Quote a single-hop V2 swap between tokenIn and tokenOut.",
2923
- args: z4.object({
2924
- tokenIn: z4.string().describe("Input token address"),
2925
- tokenOut: z4.string().describe("Output token address"),
2926
- amountIn: z4.string().describe("Input amount in human-readable decimal units")
3514
+ args: z5.object({
3515
+ tokenIn: z5.string().describe("Input token address"),
3516
+ tokenOut: z5.string().describe("Output token address"),
3517
+ amountIn: z5.string().describe("Input amount in human-readable decimal units")
2927
3518
  }),
2928
- options: z4.object({
2929
- stable: z4.boolean().default(false).describe("Use stable pool route (default: volatile)")
3519
+ options: z5.object({
3520
+ stable: z5.boolean().default(false).describe("Use stable pool route (default: volatile)")
2930
3521
  }),
2931
3522
  env: env4,
2932
- output: z4.object({
2933
- pool: z4.string(),
2934
- stable: z4.boolean(),
3523
+ output: z5.object({
3524
+ pool: z5.string(),
3525
+ stable: z5.boolean(),
2935
3526
  tokenIn: tokenSchema2,
2936
3527
  tokenOut: tokenSchema2,
2937
3528
  amountIn: amountSchema,
2938
3529
  amountOut: amountSchema,
2939
- priceOutPerIn: z4.number().nullable()
3530
+ priceOutPerIn: z5.number().nullable()
2940
3531
  }),
2941
3532
  async run(c) {
2942
3533
  if (!isAddress3(c.args.tokenIn) || !isAddress3(c.args.tokenOut)) {
@@ -2994,33 +3585,56 @@ pools.command("quote", {
2994
3585
  const amountInDecimal = formatUnits3(amountInRaw, inMeta.decimals);
2995
3586
  const amountOutDecimal = formatUnits3(amountOutRaw, outMeta.decimals);
2996
3587
  const ratio = Number(amountOutDecimal) / Number(amountInDecimal);
2997
- return c.ok({
2998
- pool: checksumAddress4(poolAddress),
2999
- stable: c.options.stable,
3000
- tokenIn: inMeta,
3001
- tokenOut: outMeta,
3002
- amountIn: {
3003
- raw: amountInRaw.toString(),
3004
- decimal: amountInDecimal
3005
- },
3006
- amountOut: {
3007
- raw: amountOutRaw.toString(),
3008
- decimal: amountOutDecimal
3588
+ return c.ok(
3589
+ {
3590
+ pool: checksumAddress4(poolAddress),
3591
+ stable: c.options.stable,
3592
+ tokenIn: inMeta,
3593
+ tokenOut: outMeta,
3594
+ amountIn: {
3595
+ raw: amountInRaw.toString(),
3596
+ decimal: amountInDecimal
3597
+ },
3598
+ amountOut: {
3599
+ raw: amountOutRaw.toString(),
3600
+ decimal: amountOutDecimal
3601
+ },
3602
+ priceOutPerIn: Number(amountInDecimal) === 0 ? null : finiteOrNull3(ratio)
3009
3603
  },
3010
- priceOutPerIn: Number(amountInDecimal) === 0 ? null : finiteOrNull3(ratio)
3011
- });
3604
+ c.format === "json" || c.format === "jsonl" ? void 0 : {
3605
+ cta: {
3606
+ description: "Related commands:",
3607
+ commands: [
3608
+ {
3609
+ command: "pools pool",
3610
+ args: { address: checksumAddress4(poolAddress) },
3611
+ description: "Inspect the pool used for this quote"
3612
+ },
3613
+ {
3614
+ command: "pools quote",
3615
+ args: {
3616
+ tokenIn: outMeta.address,
3617
+ tokenOut: inMeta.address,
3618
+ amountIn: amountOutDecimal
3619
+ },
3620
+ description: `Reverse quote ${outMeta.symbol} \u2192 ${inMeta.symbol}`
3621
+ }
3622
+ ]
3623
+ }
3624
+ }
3625
+ );
3012
3626
  }
3013
3627
  });
3014
3628
  pools.command("fees", {
3015
3629
  description: "Read V2 fee configuration for a pool address.",
3016
- args: z4.object({
3017
- pool: z4.string().describe("Pool address")
3630
+ args: z5.object({
3631
+ pool: z5.string().describe("Pool address")
3018
3632
  }),
3019
3633
  env: env4,
3020
- output: z4.object({
3021
- pool: z4.string(),
3022
- pair: z4.string(),
3023
- stable: z4.boolean(),
3634
+ output: z5.object({
3635
+ pool: z5.string(),
3636
+ pair: z5.string(),
3637
+ stable: z5.boolean(),
3024
3638
  activeFee: feeSchema,
3025
3639
  stableFee: feeSchema,
3026
3640
  volatileFee: feeSchema
@@ -3060,22 +3674,206 @@ pools.command("fees", {
3060
3674
  ]);
3061
3675
  const stableFee = toFeeInfo(stableFeeRaw ?? null);
3062
3676
  const volatileFee = toFeeInfo(volatileFeeRaw ?? null);
3677
+ return c.ok(
3678
+ {
3679
+ pool: checksumAddress4(pool),
3680
+ pair: `${token0Meta.symbol}/${token1Meta.symbol}`,
3681
+ stable,
3682
+ activeFee: stable ? stableFee : volatileFee,
3683
+ stableFee,
3684
+ volatileFee
3685
+ },
3686
+ c.format === "json" || c.format === "jsonl" ? void 0 : {
3687
+ cta: {
3688
+ description: "Related commands:",
3689
+ commands: [
3690
+ {
3691
+ command: "pools pool",
3692
+ args: { address: checksumAddress4(pool) },
3693
+ description: "View full pool state"
3694
+ },
3695
+ {
3696
+ command: "pools quote",
3697
+ args: {
3698
+ tokenIn: token0Meta.address,
3699
+ tokenOut: token1Meta.address,
3700
+ amountIn: "1"
3701
+ },
3702
+ description: `Quote a ${token0Meta.symbol} \u2192 ${token1Meta.symbol} swap`
3703
+ }
3704
+ ]
3705
+ }
3706
+ }
3707
+ );
3708
+ }
3709
+ });
3710
+ pools.command("swap", {
3711
+ description: "Execute a single-hop V2 AMM swap. Quotes the expected output, applies slippage tolerance, approves the router if needed, and broadcasts the swap transaction.",
3712
+ options: z5.object({
3713
+ "token-in": z5.string().describe("Input token address"),
3714
+ "token-out": z5.string().describe("Output token address"),
3715
+ "amount-in": z5.string().describe("Input amount in wei"),
3716
+ slippage: z5.coerce.number().min(0).max(100).default(DEFAULT_SLIPPAGE_PERCENT2).describe("Slippage tolerance in percent (default: 0.5)"),
3717
+ deadline: z5.coerce.number().int().positive().default(DEFAULT_DEADLINE_SECONDS2).describe("Transaction deadline in seconds from now (default: 300)"),
3718
+ stable: z5.boolean().default(false).describe("Use stable pool route (default: volatile)")
3719
+ }).merge(writeOptions),
3720
+ env: writeEnv,
3721
+ output: z5.object({
3722
+ pool: z5.string(),
3723
+ stable: z5.boolean(),
3724
+ tokenIn: tokenSchema2,
3725
+ tokenOut: tokenSchema2,
3726
+ amountIn: amountSchema,
3727
+ expectedAmountOut: amountSchema,
3728
+ minAmountOut: amountSchema,
3729
+ slippagePercent: z5.number(),
3730
+ effectivePrice: z5.number().nullable(),
3731
+ tx: z5.union([
3732
+ z5.object({
3733
+ txHash: z5.string(),
3734
+ blockNumber: z5.number(),
3735
+ gasUsed: z5.string()
3736
+ }),
3737
+ z5.object({
3738
+ dryRun: z5.literal(true),
3739
+ estimatedGas: z5.string(),
3740
+ simulationResult: z5.unknown()
3741
+ })
3742
+ ])
3743
+ }),
3744
+ async run(c) {
3745
+ const tokenInRaw = c.options["token-in"];
3746
+ const tokenOutRaw = c.options["token-out"];
3747
+ if (!isAddress3(tokenInRaw) || !isAddress3(tokenOutRaw)) {
3748
+ return c.error({
3749
+ code: "INVALID_ADDRESS",
3750
+ message: "token-in and token-out must both be valid 0x-prefixed 20-byte addresses."
3751
+ });
3752
+ }
3753
+ const tokenIn = toAddress(tokenInRaw);
3754
+ const tokenOut = toAddress(tokenOutRaw);
3755
+ const amountInRaw = BigInt(c.options["amount-in"]);
3756
+ if (amountInRaw <= 0n) {
3757
+ return c.error({
3758
+ code: "INVALID_AMOUNT",
3759
+ message: "amount-in must be a positive integer in wei."
3760
+ });
3761
+ }
3762
+ const client = createAboreanPublicClient(c.env.ABSTRACT_RPC_URL);
3763
+ const tokenMeta = await readTokenMetadata3(client, [tokenIn, tokenOut]);
3764
+ const inMeta = tokenMeta.get(tokenIn.toLowerCase()) ?? fallbackTokenMeta(tokenIn);
3765
+ const outMeta = tokenMeta.get(tokenOut.toLowerCase()) ?? fallbackTokenMeta(tokenOut);
3766
+ const poolAddress = await client.readContract({
3767
+ abi: poolFactoryAbi,
3768
+ address: ABOREAN_V2_ADDRESSES.poolFactory,
3769
+ functionName: "getPool",
3770
+ args: [tokenIn, tokenOut, c.options.stable]
3771
+ });
3772
+ if (poolAddress.toLowerCase() === ZERO_ADDRESS.toLowerCase()) {
3773
+ return c.error({
3774
+ code: "POOL_NOT_FOUND",
3775
+ message: `No ${c.options.stable ? "stable" : "volatile"} V2 pool found for pair ${checksumAddress4(tokenIn)}/${checksumAddress4(tokenOut)}.`
3776
+ });
3777
+ }
3778
+ const amounts = await client.readContract({
3779
+ abi: v2RouterAbi,
3780
+ address: ABOREAN_V2_ADDRESSES.router,
3781
+ functionName: "getAmountsOut",
3782
+ args: [
3783
+ amountInRaw,
3784
+ [
3785
+ {
3786
+ from: tokenIn,
3787
+ to: tokenOut,
3788
+ stable: c.options.stable,
3789
+ factory: ABOREAN_V2_ADDRESSES.poolFactory
3790
+ }
3791
+ ]
3792
+ ]
3793
+ });
3794
+ const expectedAmountOut = amounts[amounts.length - 1] ?? 0n;
3795
+ if (expectedAmountOut === 0n) {
3796
+ return c.error({
3797
+ code: "ZERO_QUOTE",
3798
+ message: "Router returned zero output amount. The pool may have insufficient liquidity."
3799
+ });
3800
+ }
3801
+ const slippageBps = Math.round(c.options.slippage * 100);
3802
+ const minAmountOut = expectedAmountOut * BigInt(1e4 - slippageBps) / 10000n;
3803
+ const deadlineTimestamp = BigInt(Math.floor(Date.now() / 1e3) + c.options.deadline);
3804
+ const account = resolveAccount(c.env);
3805
+ if (!c.options["dry-run"]) {
3806
+ const currentAllowance = await client.readContract({
3807
+ abi: erc20ApproveAbi,
3808
+ address: tokenIn,
3809
+ functionName: "allowance",
3810
+ args: [account.address, ABOREAN_V2_ADDRESSES.router]
3811
+ });
3812
+ if (currentAllowance < amountInRaw) {
3813
+ await aboreanWriteTx({
3814
+ env: c.env,
3815
+ options: { ...c.options, "dry-run": false },
3816
+ address: tokenIn,
3817
+ abi: erc20ApproveAbi,
3818
+ functionName: "approve",
3819
+ args: [ABOREAN_V2_ADDRESSES.router, amountInRaw]
3820
+ });
3821
+ }
3822
+ }
3823
+ const tx = await aboreanWriteTx({
3824
+ env: c.env,
3825
+ options: c.options,
3826
+ address: ABOREAN_V2_ADDRESSES.router,
3827
+ abi: v2RouterSwapAbi,
3828
+ functionName: "swapExactTokensForTokens",
3829
+ args: [
3830
+ amountInRaw,
3831
+ minAmountOut,
3832
+ [
3833
+ {
3834
+ from: tokenIn,
3835
+ to: tokenOut,
3836
+ stable: c.options.stable,
3837
+ factory: ABOREAN_V2_ADDRESSES.poolFactory
3838
+ }
3839
+ ],
3840
+ account.address,
3841
+ deadlineTimestamp
3842
+ ]
3843
+ });
3844
+ const amountInDecimal = formatUnits3(amountInRaw, inMeta.decimals);
3845
+ const expectedOutDecimal = formatUnits3(expectedAmountOut, outMeta.decimals);
3846
+ const minOutDecimal = formatUnits3(minAmountOut, outMeta.decimals);
3847
+ const ratio = Number(expectedOutDecimal) / Number(amountInDecimal);
3063
3848
  return c.ok({
3064
- pool: checksumAddress4(pool),
3065
- pair: `${token0Meta.symbol}/${token1Meta.symbol}`,
3066
- stable,
3067
- activeFee: stable ? stableFee : volatileFee,
3068
- stableFee,
3069
- volatileFee
3849
+ pool: checksumAddress4(poolAddress),
3850
+ stable: c.options.stable,
3851
+ tokenIn: inMeta,
3852
+ tokenOut: outMeta,
3853
+ amountIn: {
3854
+ raw: amountInRaw.toString(),
3855
+ decimal: amountInDecimal
3856
+ },
3857
+ expectedAmountOut: {
3858
+ raw: expectedAmountOut.toString(),
3859
+ decimal: expectedOutDecimal
3860
+ },
3861
+ minAmountOut: {
3862
+ raw: minAmountOut.toString(),
3863
+ decimal: minOutDecimal
3864
+ },
3865
+ slippagePercent: c.options.slippage,
3866
+ effectivePrice: Number(amountInDecimal) === 0 ? null : finiteOrNull3(ratio),
3867
+ tx
3070
3868
  });
3071
3869
  }
3072
3870
  });
3073
3871
 
3074
3872
  // src/commands/vaults.ts
3075
3873
  import { checksumAddress as checksumAddress5, isAddress as isAddress4 } from "@spectratools/cli-shared";
3076
- import { Cli as Cli5, z as z5 } from "incur";
3077
- var env5 = z5.object({
3078
- ABSTRACT_RPC_URL: z5.string().optional().describe("Abstract RPC URL override")
3874
+ import { Cli as Cli5, z as z6 } from "incur";
3875
+ var env5 = z6.object({
3876
+ ABSTRACT_RPC_URL: z6.string().optional().describe("Abstract RPC URL override")
3079
3877
  });
3080
3878
  var relayAbi = [
3081
3879
  {
@@ -3139,22 +3937,22 @@ var votingEscrowLiteAbi = [
3139
3937
  outputs: [{ type: "uint256" }]
3140
3938
  }
3141
3939
  ];
3142
- var relayRowSchema = z5.object({
3143
- label: z5.string(),
3144
- relay: z5.string(),
3145
- name: z5.string(),
3146
- factoryType: z5.enum(["autoCompounder", "autoConverter"]),
3147
- managedTokenId: z5.string(),
3148
- managedVotingPower: z5.string(),
3149
- relayToken: z5.object({
3150
- address: z5.string(),
3151
- symbol: z5.string(),
3152
- decimals: z5.number()
3940
+ var relayRowSchema = z6.object({
3941
+ label: z6.string(),
3942
+ relay: z6.string(),
3943
+ name: z6.string(),
3944
+ factoryType: z6.enum(["autoCompounder", "autoConverter"]),
3945
+ managedTokenId: z6.string(),
3946
+ managedVotingPower: z6.string(),
3947
+ relayToken: z6.object({
3948
+ address: z6.string(),
3949
+ symbol: z6.string(),
3950
+ decimals: z6.number()
3153
3951
  }),
3154
- relayTokenBalance: z5.string(),
3155
- keeperLastRun: z5.number(),
3156
- keeperLastRunRelative: z5.string(),
3157
- secondsSinceKeeperRun: z5.number()
3952
+ relayTokenBalance: z6.string(),
3953
+ keeperLastRun: z6.number(),
3954
+ keeperLastRunRelative: z6.string(),
3955
+ secondsSinceKeeperRun: z6.number()
3158
3956
  });
3159
3957
  var KNOWN_RELAYS = [
3160
3958
  {
@@ -3280,17 +4078,17 @@ var vaults = Cli5.create("vaults", {
3280
4078
  vaults.command("list", {
3281
4079
  description: "List known Aborean relay vaults with keeper and veNFT state.",
3282
4080
  env: env5,
3283
- output: z5.object({
3284
- relayCount: z5.number(),
3285
- relays: z5.array(relayRowSchema),
3286
- totals: z5.object({
3287
- managedVotingPower: z5.string(),
3288
- relayTokenBalances: z5.array(
3289
- z5.object({
3290
- token: z5.string(),
3291
- symbol: z5.string(),
3292
- decimals: z5.number(),
3293
- balance: z5.string()
4081
+ output: z6.object({
4082
+ relayCount: z6.number(),
4083
+ relays: z6.array(relayRowSchema),
4084
+ totals: z6.object({
4085
+ managedVotingPower: z6.string(),
4086
+ relayTokenBalances: z6.array(
4087
+ z6.object({
4088
+ token: z6.string(),
4089
+ symbol: z6.string(),
4090
+ decimals: z6.number(),
4091
+ balance: z6.string()
3294
4092
  })
3295
4093
  )
3296
4094
  })
@@ -3299,13 +4097,38 @@ vaults.command("list", {
3299
4097
  async run(c) {
3300
4098
  const client = createAboreanPublicClient(c.env.ABSTRACT_RPC_URL);
3301
4099
  const snapshot = await readVaultSummary(client);
3302
- return c.ok(snapshot);
4100
+ const firstRelay = snapshot.relays[0];
4101
+ return c.ok(
4102
+ snapshot,
4103
+ c.format === "json" || c.format === "jsonl" ? void 0 : {
4104
+ cta: {
4105
+ description: "Related commands:",
4106
+ commands: [
4107
+ ...firstRelay ? [
4108
+ {
4109
+ command: "vaults relay",
4110
+ args: { relay: firstRelay.relay },
4111
+ description: `Inspect ${firstRelay.label}`
4112
+ }
4113
+ ] : [],
4114
+ {
4115
+ command: "lending markets",
4116
+ description: "View Morpho lending markets"
4117
+ },
4118
+ {
4119
+ command: "ve stats",
4120
+ description: "View veABX global stats"
4121
+ }
4122
+ ]
4123
+ }
4124
+ }
4125
+ );
3303
4126
  }
3304
4127
  });
3305
4128
  vaults.command("relay", {
3306
4129
  description: "Inspect one relay vault by address.",
3307
- args: z5.object({
3308
- relay: z5.string().describe("Relay vault contract address")
4130
+ args: z6.object({
4131
+ relay: z6.string().describe("Relay vault contract address")
3309
4132
  }),
3310
4133
  env: env5,
3311
4134
  output: relayRowSchema,
@@ -3334,14 +4157,32 @@ vaults.command("relay", {
3334
4157
  }
3335
4158
  const client = createAboreanPublicClient(c.env.ABSTRACT_RPC_URL);
3336
4159
  const snapshot = await readRelaySnapshot(client, known);
3337
- return c.ok(snapshot);
4160
+ return c.ok(
4161
+ snapshot,
4162
+ c.format === "json" || c.format === "jsonl" ? void 0 : {
4163
+ cta: {
4164
+ description: "Related commands:",
4165
+ commands: [
4166
+ {
4167
+ command: "vaults list",
4168
+ description: "List all relay vaults"
4169
+ },
4170
+ {
4171
+ command: "ve lock",
4172
+ args: { tokenId: Number(snapshot.managedTokenId) },
4173
+ description: `Inspect managed veNFT #${snapshot.managedTokenId}`
4174
+ }
4175
+ ]
4176
+ }
4177
+ }
4178
+ );
3338
4179
  }
3339
4180
  });
3340
4181
 
3341
4182
  // src/commands/ve.ts
3342
- import { Cli as Cli6, z as z6 } from "incur";
3343
- var env6 = z6.object({
3344
- ABSTRACT_RPC_URL: z6.string().optional().describe("Abstract RPC URL override")
4183
+ import { Cli as Cli6, z as z7 } from "incur";
4184
+ var env6 = z7.object({
4185
+ ABSTRACT_RPC_URL: z7.string().optional().describe("Abstract RPC URL override")
3345
4186
  });
3346
4187
  var ve = Cli6.create("ve", {
3347
4188
  description: "Inspect Aborean VotingEscrow (veABX) global and per-NFT lock state."
@@ -3349,16 +4190,16 @@ var ve = Cli6.create("ve", {
3349
4190
  ve.command("stats", {
3350
4191
  description: "Get global VotingEscrow supply, locks, and decay checkpoint data.",
3351
4192
  env: env6,
3352
- output: z6.object({
3353
- token: z6.string(),
3354
- totalVotingPower: z6.string(),
3355
- totalLocked: z6.string(),
3356
- permanentLocked: z6.string(),
3357
- epoch: z6.number(),
3358
- decayBias: z6.string(),
3359
- decaySlope: z6.string(),
3360
- lastCheckpointTimestamp: z6.number(),
3361
- lastCheckpointBlock: z6.number()
4193
+ output: z7.object({
4194
+ token: z7.string(),
4195
+ totalVotingPower: z7.string(),
4196
+ totalLocked: z7.string(),
4197
+ permanentLocked: z7.string(),
4198
+ epoch: z7.number(),
4199
+ decayBias: z7.string(),
4200
+ decaySlope: z7.string(),
4201
+ lastCheckpointTimestamp: z7.number(),
4202
+ lastCheckpointBlock: z7.number()
3362
4203
  }),
3363
4204
  examples: [{ description: "Show global veABX state and decay metrics" }],
3364
4205
  async run(c) {
@@ -3396,32 +4237,50 @@ ve.command("stats", {
3396
4237
  functionName: "pointHistory",
3397
4238
  args: [epoch]
3398
4239
  });
3399
- return c.ok({
3400
- token: toChecksum(token),
3401
- totalVotingPower: totalVotingPower.toString(),
3402
- totalLocked: totalLocked.toString(),
3403
- permanentLocked: permanentLocked.toString(),
3404
- epoch: asNum(epoch),
3405
- decayBias: point.bias.toString(),
3406
- decaySlope: point.slope.toString(),
3407
- lastCheckpointTimestamp: asNum(point.ts),
3408
- lastCheckpointBlock: asNum(point.blk)
3409
- });
4240
+ return c.ok(
4241
+ {
4242
+ token: toChecksum(token),
4243
+ totalVotingPower: totalVotingPower.toString(),
4244
+ totalLocked: totalLocked.toString(),
4245
+ permanentLocked: permanentLocked.toString(),
4246
+ epoch: asNum(epoch),
4247
+ decayBias: point.bias.toString(),
4248
+ decaySlope: point.slope.toString(),
4249
+ lastCheckpointTimestamp: asNum(point.ts),
4250
+ lastCheckpointBlock: asNum(point.blk)
4251
+ },
4252
+ c.format === "json" || c.format === "jsonl" ? void 0 : {
4253
+ cta: {
4254
+ description: "Explore veABX:",
4255
+ commands: [
4256
+ {
4257
+ command: "ve locks",
4258
+ args: { address: "<owner>" },
4259
+ description: "List veNFT locks for an address"
4260
+ },
4261
+ {
4262
+ command: "voter epoch",
4263
+ description: "View current emissions epoch timing"
4264
+ }
4265
+ ]
4266
+ }
4267
+ }
4268
+ );
3410
4269
  }
3411
4270
  });
3412
4271
  ve.command("lock", {
3413
4272
  description: "Get lock details and voting power for one veNFT token id.",
3414
- args: z6.object({
3415
- tokenId: z6.coerce.number().int().nonnegative().describe("veNFT token id")
4273
+ args: z7.object({
4274
+ tokenId: z7.coerce.number().int().nonnegative().describe("veNFT token id")
3416
4275
  }),
3417
4276
  env: env6,
3418
- output: z6.object({
3419
- tokenId: z6.number(),
3420
- owner: z6.string(),
3421
- amount: z6.string(),
3422
- unlockTime: z6.number(),
3423
- isPermanent: z6.boolean(),
3424
- votingPower: z6.string()
4277
+ output: z7.object({
4278
+ tokenId: z7.number(),
4279
+ owner: z7.string(),
4280
+ amount: z7.string(),
4281
+ unlockTime: z7.number(),
4282
+ isPermanent: z7.boolean(),
4283
+ votingPower: z7.string()
3425
4284
  }),
3426
4285
  examples: [{ args: { tokenId: 1 }, description: "Inspect lock details for veNFT #1" }],
3427
4286
  async run(c) {
@@ -3447,34 +4306,53 @@ ve.command("lock", {
3447
4306
  args: [tokenId]
3448
4307
  })
3449
4308
  ]);
3450
- return c.ok({
3451
- tokenId: c.args.tokenId,
3452
- owner: toChecksum(owner),
3453
- amount: locked.amount.toString(),
3454
- unlockTime: asNum(locked.end),
3455
- isPermanent: locked.isPermanent,
3456
- votingPower: votingPower.toString()
3457
- });
4309
+ return c.ok(
4310
+ {
4311
+ tokenId: c.args.tokenId,
4312
+ owner: toChecksum(owner),
4313
+ amount: locked.amount.toString(),
4314
+ unlockTime: asNum(locked.end),
4315
+ isPermanent: locked.isPermanent,
4316
+ votingPower: votingPower.toString()
4317
+ },
4318
+ c.format === "json" || c.format === "jsonl" ? void 0 : {
4319
+ cta: {
4320
+ description: "Related commands:",
4321
+ commands: [
4322
+ {
4323
+ command: "ve voting-power",
4324
+ args: { tokenId: c.args.tokenId },
4325
+ description: "Check current voting power"
4326
+ },
4327
+ {
4328
+ command: "voter rewards",
4329
+ args: { tokenId: c.args.tokenId },
4330
+ description: "Check claimable rewards for this veNFT"
4331
+ }
4332
+ ]
4333
+ }
4334
+ }
4335
+ );
3458
4336
  }
3459
4337
  });
3460
4338
  ve.command("locks", {
3461
4339
  description: "List all veNFT locks owned by an address.",
3462
- args: z6.object({
3463
- address: z6.string().describe("Owner address")
4340
+ args: z7.object({
4341
+ address: z7.string().describe("Owner address")
3464
4342
  }),
3465
4343
  env: env6,
3466
- output: z6.object({
3467
- address: z6.string(),
3468
- locks: z6.array(
3469
- z6.object({
3470
- tokenId: z6.string(),
3471
- amount: z6.string(),
3472
- unlockTime: z6.number(),
3473
- isPermanent: z6.boolean(),
3474
- votingPower: z6.string()
4344
+ output: z7.object({
4345
+ address: z7.string(),
4346
+ locks: z7.array(
4347
+ z7.object({
4348
+ tokenId: z7.string(),
4349
+ amount: z7.string(),
4350
+ unlockTime: z7.number(),
4351
+ isPermanent: z7.boolean(),
4352
+ votingPower: z7.string()
3475
4353
  })
3476
4354
  ),
3477
- count: z6.number()
4355
+ count: z7.number()
3478
4356
  }),
3479
4357
  examples: [
3480
4358
  {
@@ -3537,22 +4415,42 @@ ve.command("locks", {
3537
4415
  votingPower: votingPower.toString()
3538
4416
  };
3539
4417
  });
3540
- return c.ok({
3541
- address: toChecksum(c.args.address),
3542
- locks,
3543
- count: locks.length
3544
- });
4418
+ const firstLock = locks[0];
4419
+ return c.ok(
4420
+ {
4421
+ address: toChecksum(c.args.address),
4422
+ locks,
4423
+ count: locks.length
4424
+ },
4425
+ c.format === "json" || c.format === "jsonl" ? void 0 : {
4426
+ cta: {
4427
+ description: "Explore locks:",
4428
+ commands: firstLock ? [
4429
+ {
4430
+ command: "ve lock",
4431
+ args: { tokenId: Number(firstLock.tokenId) },
4432
+ description: `Inspect veNFT #${firstLock.tokenId}`
4433
+ },
4434
+ {
4435
+ command: "voter rewards",
4436
+ args: { tokenId: Number(firstLock.tokenId) },
4437
+ description: `Check rewards for veNFT #${firstLock.tokenId}`
4438
+ }
4439
+ ] : []
4440
+ }
4441
+ }
4442
+ );
3545
4443
  }
3546
4444
  });
3547
4445
  ve.command("voting-power", {
3548
4446
  description: "Get current voting power for one veNFT token id.",
3549
- args: z6.object({
3550
- tokenId: z6.coerce.number().int().nonnegative().describe("veNFT token id")
4447
+ args: z7.object({
4448
+ tokenId: z7.coerce.number().int().nonnegative().describe("veNFT token id")
3551
4449
  }),
3552
4450
  env: env6,
3553
- output: z6.object({
3554
- tokenId: z6.number(),
3555
- votingPower: z6.string()
4451
+ output: z7.object({
4452
+ tokenId: z7.number(),
4453
+ votingPower: z7.string()
3556
4454
  }),
3557
4455
  examples: [{ args: { tokenId: 1 }, description: "Get current voting power for veNFT #1" }],
3558
4456
  async run(c) {
@@ -3563,17 +4461,36 @@ ve.command("voting-power", {
3563
4461
  functionName: "balanceOfNFT",
3564
4462
  args: [BigInt(c.args.tokenId)]
3565
4463
  });
3566
- return c.ok({
3567
- tokenId: c.args.tokenId,
3568
- votingPower: votingPower.toString()
3569
- });
4464
+ return c.ok(
4465
+ {
4466
+ tokenId: c.args.tokenId,
4467
+ votingPower: votingPower.toString()
4468
+ },
4469
+ c.format === "json" || c.format === "jsonl" ? void 0 : {
4470
+ cta: {
4471
+ description: "Related commands:",
4472
+ commands: [
4473
+ {
4474
+ command: "ve lock",
4475
+ args: { tokenId: c.args.tokenId },
4476
+ description: "View full lock details"
4477
+ },
4478
+ {
4479
+ command: "voter rewards",
4480
+ args: { tokenId: c.args.tokenId },
4481
+ description: "Check claimable rewards"
4482
+ }
4483
+ ]
4484
+ }
4485
+ }
4486
+ );
3570
4487
  }
3571
4488
  });
3572
4489
 
3573
4490
  // src/commands/voter.ts
3574
- import { Cli as Cli7, z as z7 } from "incur";
3575
- var env7 = z7.object({
3576
- ABSTRACT_RPC_URL: z7.string().optional().describe("Abstract RPC URL override")
4491
+ import { Cli as Cli7, z as z8 } from "incur";
4492
+ var env7 = z8.object({
4493
+ ABSTRACT_RPC_URL: z8.string().optional().describe("Abstract RPC URL override")
3577
4494
  });
3578
4495
  async function discoverPools(client) {
3579
4496
  const [v2PoolCount, clPoolCount] = await Promise.all([
@@ -3618,14 +4535,14 @@ var voter = Cli7.create("voter", {
3618
4535
  voter.command("epoch", {
3619
4536
  description: "Show current emissions epoch timing from Minter.",
3620
4537
  env: env7,
3621
- output: z7.object({
3622
- activePeriod: z7.number(),
3623
- epochEnd: z7.number(),
3624
- secondsRemaining: z7.number(),
3625
- timeRemaining: z7.string(),
3626
- weekSeconds: z7.number(),
3627
- epochCount: z7.number(),
3628
- weeklyEmission: z7.string()
4538
+ output: z8.object({
4539
+ activePeriod: z8.number(),
4540
+ epochEnd: z8.number(),
4541
+ secondsRemaining: z8.number(),
4542
+ timeRemaining: z8.string(),
4543
+ weekSeconds: z8.number(),
4544
+ epochCount: z8.number(),
4545
+ weeklyEmission: z8.string()
3629
4546
  }),
3630
4547
  examples: [{ description: "Inspect current voter epoch boundaries" }],
3631
4548
  async run(c) {
@@ -3654,30 +4571,47 @@ voter.command("epoch", {
3654
4571
  ]);
3655
4572
  const now = Math.floor(Date.now() / 1e3);
3656
4573
  const epochEnd = asNum(activePeriod + weekSeconds);
3657
- return c.ok({
3658
- activePeriod: asNum(activePeriod),
3659
- epochEnd,
3660
- secondsRemaining: clampPositive(epochEnd - now),
3661
- timeRemaining: relTime(activePeriod + weekSeconds),
3662
- weekSeconds: asNum(weekSeconds),
3663
- epochCount: asNum(epochCount),
3664
- weeklyEmission: weeklyEmission.toString()
3665
- });
4574
+ return c.ok(
4575
+ {
4576
+ activePeriod: asNum(activePeriod),
4577
+ epochEnd,
4578
+ secondsRemaining: clampPositive(epochEnd - now),
4579
+ timeRemaining: relTime(activePeriod + weekSeconds),
4580
+ weekSeconds: asNum(weekSeconds),
4581
+ epochCount: asNum(epochCount),
4582
+ weeklyEmission: weeklyEmission.toString()
4583
+ },
4584
+ c.format === "json" || c.format === "jsonl" ? void 0 : {
4585
+ cta: {
4586
+ description: "Related commands:",
4587
+ commands: [
4588
+ {
4589
+ command: "voter weights",
4590
+ description: "View pool voting weight distribution"
4591
+ },
4592
+ {
4593
+ command: "gauges list",
4594
+ description: "List active gauges"
4595
+ }
4596
+ ]
4597
+ }
4598
+ }
4599
+ );
3666
4600
  }
3667
4601
  });
3668
4602
  voter.command("weights", {
3669
4603
  description: "Show current pool voting weight distribution.",
3670
4604
  env: env7,
3671
- output: z7.object({
3672
- totalWeight: z7.string(),
3673
- pools: z7.array(
3674
- z7.object({
3675
- pool: z7.string(),
3676
- gauge: z7.string(),
3677
- weight: z7.string()
4605
+ output: z8.object({
4606
+ totalWeight: z8.string(),
4607
+ pools: z8.array(
4608
+ z8.object({
4609
+ pool: z8.string(),
4610
+ gauge: z8.string(),
4611
+ weight: z8.string()
3678
4612
  })
3679
4613
  ),
3680
- count: z7.number()
4614
+ count: z8.number()
3681
4615
  }),
3682
4616
  examples: [{ description: "List all pools with non-zero voting weight" }],
3683
4617
  async run(c) {
@@ -3726,28 +4660,49 @@ voter.command("weights", {
3726
4660
  gauge: toChecksum(entry.gauge),
3727
4661
  weight: entry.weight.toString()
3728
4662
  }));
3729
- return c.ok({
3730
- totalWeight: totalWeight.toString(),
3731
- pools: entries,
3732
- count: entries.length
3733
- });
4663
+ const firstEntry = entries[0];
4664
+ return c.ok(
4665
+ {
4666
+ totalWeight: totalWeight.toString(),
4667
+ pools: entries,
4668
+ count: entries.length
4669
+ },
4670
+ c.format === "json" || c.format === "jsonl" ? void 0 : {
4671
+ cta: {
4672
+ description: "Related commands:",
4673
+ commands: [
4674
+ {
4675
+ command: "voter epoch",
4676
+ description: "View current epoch timing"
4677
+ },
4678
+ ...firstEntry ? [
4679
+ {
4680
+ command: "voter bribes",
4681
+ args: { pool: firstEntry.pool },
4682
+ description: "View bribe rewards for top-weighted pool"
4683
+ }
4684
+ ] : []
4685
+ ]
4686
+ }
4687
+ }
4688
+ );
3734
4689
  }
3735
4690
  });
3736
4691
  voter.command("rewards", {
3737
4692
  description: "Show claimable rebase rewards and voting context for a veNFT.",
3738
- args: z7.object({
3739
- tokenId: z7.coerce.number().int().nonnegative().describe("veNFT token id")
4693
+ args: z8.object({
4694
+ tokenId: z8.coerce.number().int().nonnegative().describe("veNFT token id")
3740
4695
  }),
3741
4696
  env: env7,
3742
- output: z7.object({
3743
- tokenId: z7.number(),
3744
- rewardToken: z7.string(),
3745
- claimableRebase: z7.string(),
3746
- timeCursor: z7.number(),
3747
- lastTokenTime: z7.number(),
3748
- distributorStartTime: z7.number(),
3749
- usedWeight: z7.string(),
3750
- lastVoted: z7.number()
4697
+ output: z8.object({
4698
+ tokenId: z8.number(),
4699
+ rewardToken: z8.string(),
4700
+ claimableRebase: z8.string(),
4701
+ timeCursor: z8.number(),
4702
+ lastTokenTime: z8.number(),
4703
+ distributorStartTime: z8.number(),
4704
+ usedWeight: z8.string(),
4705
+ lastVoted: z8.number()
3751
4706
  }),
3752
4707
  examples: [{ args: { tokenId: 1 }, description: "Check claimable voter/distributor rewards" }],
3753
4708
  async run(c) {
@@ -3802,36 +4757,54 @@ voter.command("rewards", {
3802
4757
  args: [tokenId]
3803
4758
  })
3804
4759
  ]);
3805
- return c.ok({
3806
- tokenId: c.args.tokenId,
3807
- rewardToken: toChecksum(rewardToken),
3808
- claimableRebase: claimableRebase.toString(),
3809
- timeCursor: asNum(timeCursor),
3810
- lastTokenTime: asNum(lastTokenTime),
3811
- distributorStartTime: asNum(distributorStartTime),
3812
- usedWeight: usedWeight.toString(),
3813
- lastVoted: asNum(lastVoted)
3814
- });
4760
+ return c.ok(
4761
+ {
4762
+ tokenId: c.args.tokenId,
4763
+ rewardToken: toChecksum(rewardToken),
4764
+ claimableRebase: claimableRebase.toString(),
4765
+ timeCursor: asNum(timeCursor),
4766
+ lastTokenTime: asNum(lastTokenTime),
4767
+ distributorStartTime: asNum(distributorStartTime),
4768
+ usedWeight: usedWeight.toString(),
4769
+ lastVoted: asNum(lastVoted)
4770
+ },
4771
+ c.format === "json" || c.format === "jsonl" ? void 0 : {
4772
+ cta: {
4773
+ description: "Related commands:",
4774
+ commands: [
4775
+ {
4776
+ command: "ve lock",
4777
+ args: { tokenId: c.args.tokenId },
4778
+ description: "View lock details for this veNFT"
4779
+ },
4780
+ {
4781
+ command: "voter weights",
4782
+ description: "Check pool voting weight distribution"
4783
+ }
4784
+ ]
4785
+ }
4786
+ }
4787
+ );
3815
4788
  }
3816
4789
  });
3817
4790
  voter.command("bribes", {
3818
4791
  description: "Show active bribe reward tokens and current-epoch amounts for a pool.",
3819
- args: z7.object({
3820
- pool: z7.string().describe("Pool address")
4792
+ args: z8.object({
4793
+ pool: z8.string().describe("Pool address")
3821
4794
  }),
3822
4795
  env: env7,
3823
- output: z7.object({
3824
- pool: z7.string(),
3825
- gauge: z7.string(),
3826
- bribeContract: z7.string(),
3827
- epochStart: z7.number(),
3828
- rewardTokens: z7.array(
3829
- z7.object({
3830
- token: z7.string(),
3831
- epochAmount: z7.string()
4796
+ output: z8.object({
4797
+ pool: z8.string(),
4798
+ gauge: z8.string(),
4799
+ bribeContract: z8.string(),
4800
+ epochStart: z8.number(),
4801
+ rewardTokens: z8.array(
4802
+ z8.object({
4803
+ token: z8.string(),
4804
+ epochAmount: z8.string()
3832
4805
  })
3833
4806
  ),
3834
- count: z7.number()
4807
+ count: z8.number()
3835
4808
  }),
3836
4809
  examples: [
3837
4810
  {
@@ -3916,14 +4889,32 @@ voter.command("bribes", {
3916
4889
  token: toChecksum(token),
3917
4890
  epochAmount: epochAmounts[index].toString()
3918
4891
  }));
3919
- return c.ok({
3920
- pool: toChecksum(c.args.pool),
3921
- gauge: toChecksum(gauge),
3922
- bribeContract: toChecksum(bribeContract),
3923
- epochStart: asNum(epochStart),
3924
- rewardTokens: items,
3925
- count: items.length
3926
- });
4892
+ return c.ok(
4893
+ {
4894
+ pool: toChecksum(c.args.pool),
4895
+ gauge: toChecksum(gauge),
4896
+ bribeContract: toChecksum(bribeContract),
4897
+ epochStart: asNum(epochStart),
4898
+ rewardTokens: items,
4899
+ count: items.length
4900
+ },
4901
+ c.format === "json" || c.format === "jsonl" ? void 0 : {
4902
+ cta: {
4903
+ description: "Related commands:",
4904
+ commands: [
4905
+ {
4906
+ command: "gauges info",
4907
+ args: { gauge: toChecksum(gauge) },
4908
+ description: "Inspect gauge details"
4909
+ },
4910
+ {
4911
+ command: "voter epoch",
4912
+ description: "View current epoch timing"
4913
+ }
4914
+ ]
4915
+ }
4916
+ }
4917
+ );
3927
4918
  }
3928
4919
  });
3929
4920
 
@@ -4068,8 +5059,8 @@ cli.command(voter);
4068
5059
  cli.command(cl);
4069
5060
  cli.command(vaults);
4070
5061
  cli.command(lending);
4071
- var rootEnv = z8.object({
4072
- ABSTRACT_RPC_URL: z8.string().optional().describe("Abstract RPC URL override")
5062
+ var rootEnv = z9.object({
5063
+ ABSTRACT_RPC_URL: z9.string().optional().describe("Abstract RPC URL override")
4073
5064
  });
4074
5065
  var erc20MetadataAbi4 = [
4075
5066
  {
@@ -4274,65 +5265,65 @@ async function readTopV2PoolsSnapshot(client, limit) {
4274
5265
  cli.command("status", {
4275
5266
  description: "Cross-protocol Aborean snapshot (TVL estimates, epoch, top pools, ve lock, vaults, Morpho lending).",
4276
5267
  env: rootEnv,
4277
- output: z8.object({
4278
- v2PoolCount: z8.number().describe("Number of V2 AMM pools"),
4279
- clPoolCount: z8.number().describe("Number of Slipstream (CL) pools"),
4280
- gaugeCount: z8.number().describe("Number of pools with gauges"),
4281
- totalVotingWeight: z8.string().describe("Total voting weight (wei)"),
4282
- veABXTotalSupply: z8.string().describe("Total veABX supply (wei)"),
4283
- veABXLockedSupply: z8.string().describe("Total ABX locked in VotingEscrow (wei)"),
4284
- epoch: z8.object({
4285
- activePeriod: z8.number(),
4286
- epochEnd: z8.number(),
4287
- secondsRemaining: z8.number(),
4288
- epochCount: z8.number(),
4289
- weeklyEmission: z8.string()
5268
+ output: z9.object({
5269
+ v2PoolCount: z9.number().describe("Number of V2 AMM pools"),
5270
+ clPoolCount: z9.number().describe("Number of Slipstream (CL) pools"),
5271
+ gaugeCount: z9.number().describe("Number of pools with gauges"),
5272
+ totalVotingWeight: z9.string().describe("Total voting weight (wei)"),
5273
+ veABXTotalSupply: z9.string().describe("Total veABX supply (wei)"),
5274
+ veABXLockedSupply: z9.string().describe("Total ABX locked in VotingEscrow (wei)"),
5275
+ epoch: z9.object({
5276
+ activePeriod: z9.number(),
5277
+ epochEnd: z9.number(),
5278
+ secondsRemaining: z9.number(),
5279
+ epochCount: z9.number(),
5280
+ weeklyEmission: z9.string()
4290
5281
  }),
4291
- topPools: z8.array(
4292
- z8.object({
4293
- pool: z8.string(),
4294
- pair: z8.string(),
4295
- poolType: z8.enum(["stable", "volatile"]),
4296
- token0: z8.object({
4297
- address: z8.string(),
4298
- symbol: z8.string(),
4299
- decimals: z8.number()
5282
+ topPools: z9.array(
5283
+ z9.object({
5284
+ pool: z9.string(),
5285
+ pair: z9.string(),
5286
+ poolType: z9.enum(["stable", "volatile"]),
5287
+ token0: z9.object({
5288
+ address: z9.string(),
5289
+ symbol: z9.string(),
5290
+ decimals: z9.number()
4300
5291
  }),
4301
- token1: z8.object({
4302
- address: z8.string(),
4303
- symbol: z8.string(),
4304
- decimals: z8.number()
5292
+ token1: z9.object({
5293
+ address: z9.string(),
5294
+ symbol: z9.string(),
5295
+ decimals: z9.number()
4305
5296
  }),
4306
- reserves: z8.object({
4307
- token0: z8.string(),
4308
- token1: z8.string()
5297
+ reserves: z9.object({
5298
+ token0: z9.string(),
5299
+ token1: z9.string()
4309
5300
  }),
4310
- tvlEstimateUnits: z8.number()
5301
+ tvlEstimateUnits: z9.number()
4311
5302
  })
4312
5303
  ),
4313
- tvl: z8.object({
4314
- v2ReserveUnitEstimate: z8.number(),
4315
- vaultManagedVotingPower: z8.string()
5304
+ tvl: z9.object({
5305
+ v2ReserveUnitEstimate: z9.number(),
5306
+ vaultManagedVotingPower: z9.string()
4316
5307
  }),
4317
- vaults: z8.object({
4318
- relayCount: z8.number(),
4319
- managedVotingPower: z8.string(),
4320
- note: z8.string().nullable()
5308
+ vaults: z9.object({
5309
+ relayCount: z9.number(),
5310
+ managedVotingPower: z9.string(),
5311
+ note: z9.string().nullable()
4321
5312
  }),
4322
- lending: z8.object({
4323
- available: z8.boolean(),
4324
- morpho: z8.string(),
4325
- marketCount: z8.number(),
4326
- supplyByLoanToken: z8.array(
4327
- z8.object({
4328
- token: z8.string(),
4329
- symbol: z8.string(),
4330
- decimals: z8.number(),
4331
- totalSupplyAssets: z8.string(),
4332
- totalBorrowAssets: z8.string()
5313
+ lending: z9.object({
5314
+ available: z9.boolean(),
5315
+ morpho: z9.string(),
5316
+ marketCount: z9.number(),
5317
+ supplyByLoanToken: z9.array(
5318
+ z9.object({
5319
+ token: z9.string(),
5320
+ symbol: z9.string(),
5321
+ decimals: z9.number(),
5322
+ totalSupplyAssets: z9.string(),
5323
+ totalBorrowAssets: z9.string()
4333
5324
  })
4334
5325
  ),
4335
- note: z8.string().nullable()
5326
+ note: z9.string().nullable()
4336
5327
  })
4337
5328
  }),
4338
5329
  examples: [{ description: "Fetch the current Aborean protocol status" }],
@@ -4430,38 +5421,63 @@ cli.command("status", {
4430
5421
  }
4431
5422
  const now = Math.floor(Date.now() / 1e3);
4432
5423
  const epochEnd = Number(activePeriod) + Number(weekSeconds);
4433
- return c.ok({
4434
- v2PoolCount: Number(v2PoolCount),
4435
- clPoolCount: Number(clPoolCount),
4436
- gaugeCount: Number(gaugeCount),
4437
- totalVotingWeight: String(totalVotingWeight),
4438
- veABXTotalSupply: String(veABXTotalSupply),
4439
- veABXLockedSupply: String(veABXLockedSupply),
4440
- epoch: {
4441
- activePeriod: Number(activePeriod),
4442
- epochEnd,
4443
- secondsRemaining: Math.max(0, epochEnd - now),
4444
- epochCount: Number(epochCount),
4445
- weeklyEmission: String(weeklyEmission)
4446
- },
4447
- topPools: v2PoolSnapshot.topPools,
4448
- tvl: {
4449
- v2ReserveUnitEstimate: v2PoolSnapshot.reserveUnitTvl,
4450
- vaultManagedVotingPower
4451
- },
4452
- vaults: {
4453
- relayCount: vaultRelayCount,
4454
- managedVotingPower: vaultManagedVotingPower,
4455
- note: vaultNote
5424
+ return c.ok(
5425
+ {
5426
+ v2PoolCount: Number(v2PoolCount),
5427
+ clPoolCount: Number(clPoolCount),
5428
+ gaugeCount: Number(gaugeCount),
5429
+ totalVotingWeight: String(totalVotingWeight),
5430
+ veABXTotalSupply: String(veABXTotalSupply),
5431
+ veABXLockedSupply: String(veABXLockedSupply),
5432
+ epoch: {
5433
+ activePeriod: Number(activePeriod),
5434
+ epochEnd,
5435
+ secondsRemaining: Math.max(0, epochEnd - now),
5436
+ epochCount: Number(epochCount),
5437
+ weeklyEmission: String(weeklyEmission)
5438
+ },
5439
+ topPools: v2PoolSnapshot.topPools,
5440
+ tvl: {
5441
+ v2ReserveUnitEstimate: v2PoolSnapshot.reserveUnitTvl,
5442
+ vaultManagedVotingPower
5443
+ },
5444
+ vaults: {
5445
+ relayCount: vaultRelayCount,
5446
+ managedVotingPower: vaultManagedVotingPower,
5447
+ note: vaultNote
5448
+ },
5449
+ lending: {
5450
+ available: lendingAvailable,
5451
+ morpho: lendingMorpho,
5452
+ marketCount: lendingMarketCount,
5453
+ supplyByLoanToken: lendingSupplyByLoanToken,
5454
+ note: lendingNote
5455
+ }
4456
5456
  },
4457
- lending: {
4458
- available: lendingAvailable,
4459
- morpho: lendingMorpho,
4460
- marketCount: lendingMarketCount,
4461
- supplyByLoanToken: lendingSupplyByLoanToken,
4462
- note: lendingNote
5457
+ c.format === "json" || c.format === "jsonl" ? void 0 : {
5458
+ cta: {
5459
+ description: "Drill down:",
5460
+ commands: [
5461
+ {
5462
+ command: "pools list",
5463
+ description: "List V2 AMM pools"
5464
+ },
5465
+ {
5466
+ command: "cl pools",
5467
+ description: "List Slipstream CL pools"
5468
+ },
5469
+ {
5470
+ command: "gauges list",
5471
+ description: "List active gauges"
5472
+ },
5473
+ {
5474
+ command: "ve stats",
5475
+ description: "View veABX global stats"
5476
+ }
5477
+ ]
5478
+ }
4463
5479
  }
4464
- });
5480
+ );
4465
5481
  }
4466
5482
  });
4467
5483
  applyFriendlyErrorHandling(cli);