@hypurrquant/defi-cli 0.2.0 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.js CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  // src/cli.ts
4
4
  import { Command } from "commander";
5
+ import { createRequire } from "module";
5
6
 
6
7
  // src/executor.ts
7
8
  import { createPublicClient as createPublicClient2, createWalletClient, http as http2 } from "viem";
@@ -863,6 +864,10 @@ async function multicallRead(rpcUrl, calls) {
863
864
  });
864
865
  return decoded.map((r) => r.success ? r.returnData : null);
865
866
  }
867
+ function decodeU256(data) {
868
+ if (!data || data.length < 66) return 0n;
869
+ return BigInt(data.slice(0, 66));
870
+ }
866
871
  var ChainConfig = class {
867
872
  name;
868
873
  chain_id;
@@ -6120,7 +6125,7 @@ var POOL_ABI4 = parseAbi27([
6120
6125
  var ORACLE_ABI5 = parseAbi27([
6121
6126
  "function getAssetPrice(address asset) external view returns (uint256)"
6122
6127
  ]);
6123
- function decodeU256(data, wordOffset = 0) {
6128
+ function decodeU2562(data, wordOffset = 0) {
6124
6129
  if (!data || data.length < 2 + (wordOffset + 1) * 64) return 0n;
6125
6130
  const hex = data.slice(2 + wordOffset * 64, 2 + wordOffset * 64 + 64);
6126
6131
  return BigInt("0x" + hex);
@@ -6200,7 +6205,7 @@ function registerPortfolio(parent, getOpts) {
6200
6205
  let nativePriceUsd = 0;
6201
6206
  if (oracleAddr) {
6202
6207
  const priceData = results[results.length - 1] ?? null;
6203
- nativePriceUsd = Number(decodeU256(priceData)) / 1e8;
6208
+ nativePriceUsd = Number(decodeU2562(priceData)) / 1e8;
6204
6209
  }
6205
6210
  let totalValueUsd = 0;
6206
6211
  let idx = 0;
@@ -6214,7 +6219,7 @@ function registerPortfolio(parent, getOpts) {
6214
6219
  }
6215
6220
  if (entry.address === "0x0000000000000000000000000000000000000000") continue;
6216
6221
  if (idx >= results.length) break;
6217
- const balance = decodeU256(results[idx] ?? null);
6222
+ const balance = decodeU2562(results[idx] ?? null);
6218
6223
  if (balance > 0n) {
6219
6224
  const decimals = entry.decimals;
6220
6225
  const balF64 = Number(balance) / 10 ** decimals;
@@ -6234,9 +6239,9 @@ function registerPortfolio(parent, getOpts) {
6234
6239
  if (idx >= results.length) break;
6235
6240
  const data = results[idx] ?? null;
6236
6241
  if (data && data.length >= 2 + 192 * 2) {
6237
- const collateral = Number(decodeU256(data, 0)) / 1e8;
6238
- const debt = Number(decodeU256(data, 1)) / 1e8;
6239
- const hfRaw = decodeU256(data, 5);
6242
+ const collateral = Number(decodeU2562(data, 0)) / 1e8;
6243
+ const debt = Number(decodeU2562(data, 1)) / 1e8;
6244
+ const hfRaw = decodeU2562(data, 5);
6240
6245
  let hf = null;
6241
6246
  if (hfRaw <= BigInt("0xffffffffffffffffffffffffffffffff")) {
6242
6247
  const v = Number(hfRaw) / 1e18;
@@ -7078,7 +7083,7 @@ function estimateTokenValue(symbol, balance, nativePrice) {
7078
7083
  if (["WETH", "ETH", "METH", "CBETH", "WSTETH"].includes(s)) return balance * 2350;
7079
7084
  return balance * nativePrice;
7080
7085
  }
7081
- function decodeU2562(data, offset = 0) {
7086
+ function decodeU2563(data, offset = 0) {
7082
7087
  if (!data || data.length < 2 + (offset + 32) * 2) return 0n;
7083
7088
  const hex = data.slice(2 + offset * 64, 2 + offset * 64 + 64);
7084
7089
  return BigInt("0x" + hex);
@@ -7116,7 +7121,7 @@ async function scanSingleChain(chainName, rpc, user, tokens, lendingPools, oracl
7116
7121
  } catch {
7117
7122
  return null;
7118
7123
  }
7119
- const nativePrice = oracleAddr ? Number(decodeU2562(results[results.length - 1])) / 1e8 : 0;
7124
+ const nativePrice = oracleAddr ? Number(decodeU2563(results[results.length - 1])) / 1e8 : 0;
7120
7125
  const tokenBalances = [];
7121
7126
  const lendingPositions = [];
7122
7127
  let chainValue = 0;
@@ -7126,7 +7131,7 @@ async function scanSingleChain(chainName, rpc, user, tokens, lendingPools, oracl
7126
7131
  const ct = callTypes[i];
7127
7132
  const data = results[i] ?? null;
7128
7133
  if (ct.kind === "token") {
7129
- const balance = decodeU2562(data);
7134
+ const balance = decodeU2563(data);
7130
7135
  if (balance > 0n) {
7131
7136
  const balF64 = Number(balance) / 10 ** ct.decimals;
7132
7137
  const valueUsd = estimateTokenValue(ct.symbol, balF64, nativePrice);
@@ -7143,9 +7148,9 @@ async function scanSingleChain(chainName, rpc, user, tokens, lendingPools, oracl
7143
7148
  if (data && data.length >= 2 + 192 * 2) {
7144
7149
  const priceDecimals = ct.iface === "aave_v2" ? 18 : 8;
7145
7150
  const divisor = 10 ** priceDecimals;
7146
- const collateral = Number(decodeU2562(data, 0)) / divisor;
7147
- const debt = Number(decodeU2562(data, 1)) / divisor;
7148
- const hfRaw = decodeU2562(data, 5);
7151
+ const collateral = Number(decodeU2563(data, 0)) / divisor;
7152
+ const debt = Number(decodeU2563(data, 1)) / divisor;
7153
+ const hfRaw = decodeU2563(data, 5);
7149
7154
  let hf = null;
7150
7155
  if (hfRaw <= BigInt("0xffffffffffffffffffffffffffffffff")) {
7151
7156
  const v = Number(hfRaw) / 1e18;
@@ -7491,7 +7496,7 @@ function round24(x) {
7491
7496
  function round43(x) {
7492
7497
  return Math.round(x * 1e4) / 1e4;
7493
7498
  }
7494
- function decodeU2563(data, wordOffset = 0) {
7499
+ function decodeU2564(data, wordOffset = 0) {
7495
7500
  if (!data || data.length < 2 + (wordOffset + 1) * 64) return 0n;
7496
7501
  const hex = data.slice(2 + wordOffset * 64, 2 + wordOffset * 64 + 64);
7497
7502
  return BigInt("0x" + hex);
@@ -7616,9 +7621,9 @@ function registerWhales(parent, getOpts) {
7616
7621
  if (data && data.length >= 2 + 192 * 2) {
7617
7622
  const dec = iface === "aave_v2" ? 18 : 8;
7618
7623
  const divisor = 10 ** dec;
7619
- const collateral = Number(decodeU2563(data, 0)) / divisor;
7620
- const debt = Number(decodeU2563(data, 1)) / divisor;
7621
- const hfRaw = decodeU2563(data, 5);
7624
+ const collateral = Number(decodeU2564(data, 0)) / divisor;
7625
+ const debt = Number(decodeU2564(data, 1)) / divisor;
7626
+ const hfRaw = decodeU2564(data, 5);
7622
7627
  let hf = null;
7623
7628
  if (hfRaw <= BigInt("0xffffffffffffffffffffffffffffffff")) {
7624
7629
  const v = Number(hfRaw) / 1e18;
@@ -8021,11 +8026,11 @@ function registerBridge(parent, getOpts) {
8021
8026
  const amountUsdc = Number(BigInt(opts.amount)) / 1e6;
8022
8027
  const { fee, maxFeeSubunits } = await getCctpFeeEstimate(srcDomain, dstDomain, amountUsdc);
8023
8028
  const recipientPadded = `0x${"0".repeat(24)}${recipient.replace("0x", "").toLowerCase()}`;
8024
- const { encodeFunctionData: encodeFunctionData28, parseAbi: parseAbi31 } = await import("viem");
8025
- const tokenMessengerAbi = parseAbi31([
8029
+ const { encodeFunctionData: encodeFunctionData29, parseAbi: parseAbi33 } = await import("viem");
8030
+ const tokenMessengerAbi = parseAbi33([
8026
8031
  "function depositForBurn(uint256 amount, uint32 destinationDomain, bytes32 mintRecipient, address burnToken, bytes32 destinationCaller, uint256 maxFee, uint32 minFinalityThreshold) external returns (uint64 nonce)"
8027
8032
  ]);
8028
- const data = encodeFunctionData28({
8033
+ const data = encodeFunctionData29({
8029
8034
  abi: tokenMessengerAbi,
8030
8035
  functionName: "depositForBurn",
8031
8036
  args: [
@@ -8219,6 +8224,8 @@ function registerFarm(parent, getOpts, makeExecutor2) {
8219
8224
  }
8220
8225
 
8221
8226
  // src/cli.ts
8227
+ var _require = createRequire(import.meta.url);
8228
+ var _pkg = _require("../package.json");
8222
8229
  var BANNER = `
8223
8230
  \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557
8224
8231
  \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2551
@@ -8232,7 +8239,7 @@ var BANNER = `
8232
8239
  Scan exploits, swap tokens, bridge assets, track whales,
8233
8240
  compare yields \u2014 all from your terminal.
8234
8241
  `;
8235
- var program = new Command().name("defi").description("DeFi CLI \u2014 Multi-chain DeFi toolkit").version("0.1.0").addHelpText("before", BANNER).option("--json", "Output as JSON").option("--ndjson", "Output as newline-delimited JSON").option("--fields <fields>", "Select specific output fields (comma-separated)").option("--chain <chain>", "Target chain", "hyperevm").option("--dry-run", "Dry-run mode (default, no broadcast)", true).option("--broadcast", "Actually broadcast the transaction");
8242
+ var program = new Command().name("defi").description("DeFi CLI \u2014 Multi-chain DeFi toolkit").version(_pkg.version).addHelpText("before", BANNER).option("--json", "Output as JSON").option("--ndjson", "Output as newline-delimited JSON").option("--fields <fields>", "Select specific output fields (comma-separated)").option("--chain <chain>", "Target chain", "hyperevm").option("--dry-run", "Dry-run mode (default, no broadcast)", true).option("--broadcast", "Actually broadcast the transaction");
8236
8243
  function getOutputMode() {
8237
8244
  const opts = program.opts();
8238
8245
  return parseOutputMode(opts);
@@ -8274,9 +8281,214 @@ program.command("agent").description("Agent mode: read JSON commands from stdin
8274
8281
  process.exit(1);
8275
8282
  });
8276
8283
 
8284
+ // src/landing.ts
8285
+ import pc2 from "picocolors";
8286
+ import { encodeFunctionData as encodeFunctionData28, parseAbi as parseAbi31, formatUnits } from "viem";
8287
+ var HYPEREVM_DISPLAY = ["HYPE", "WHYPE", "USDC", "USDT0", "USDe", "kHYPE", "wstHYPE"];
8288
+ var MANTLE_DISPLAY = ["MNT", "WMNT", "USDC", "USDT", "WETH", "mETH"];
8289
+ var balanceOfAbi = parseAbi31([
8290
+ "function balanceOf(address account) view returns (uint256)"
8291
+ ]);
8292
+ var getEthBalanceAbi = parseAbi31([
8293
+ "function getEthBalance(address addr) view returns (uint256)"
8294
+ ]);
8295
+ async function fetchBalances(rpcUrl, wallet, tokens) {
8296
+ const calls = tokens.map((t) => {
8297
+ const isNative = t.tags?.includes("native") || t.address === "0x0000000000000000000000000000000000000000";
8298
+ if (isNative) {
8299
+ return [
8300
+ MULTICALL3_ADDRESS,
8301
+ encodeFunctionData28({
8302
+ abi: getEthBalanceAbi,
8303
+ functionName: "getEthBalance",
8304
+ args: [wallet]
8305
+ })
8306
+ ];
8307
+ }
8308
+ return [
8309
+ t.address,
8310
+ encodeFunctionData28({
8311
+ abi: balanceOfAbi,
8312
+ functionName: "balanceOf",
8313
+ args: [wallet]
8314
+ })
8315
+ ];
8316
+ });
8317
+ let results;
8318
+ try {
8319
+ results = await multicallRead(rpcUrl, calls);
8320
+ } catch {
8321
+ results = tokens.map(() => null);
8322
+ }
8323
+ return tokens.map((t, i) => {
8324
+ const raw = decodeU256(results[i]);
8325
+ const formatted = formatUnits(raw, t.decimals);
8326
+ const num = parseFloat(formatted);
8327
+ const display = num === 0 ? "0.00" : num >= 1e3 ? num.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 }) : num.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 6 });
8328
+ return { symbol: t.symbol, balance: display, decimals: t.decimals };
8329
+ });
8330
+ }
8331
+ function shortenAddress(addr) {
8332
+ if (addr.length < 12) return addr;
8333
+ return `${addr.slice(0, 6)}...${addr.slice(-4)}`;
8334
+ }
8335
+ function padRight(s, len) {
8336
+ return s.length >= len ? s : s + " ".repeat(len - s.length);
8337
+ }
8338
+ function padLeft(s, len) {
8339
+ return s.length >= len ? s : " ".repeat(len - s.length) + s;
8340
+ }
8341
+ function formatBalanceLine(sym, bal) {
8342
+ const symPad = padRight(sym, 10);
8343
+ const balPad = padLeft(bal, 12);
8344
+ return ` ${symPad}${balPad}`;
8345
+ }
8346
+ async function showLandingPage(isJson) {
8347
+ const registry = Registry.loadEmbedded();
8348
+ const wallet = process.env.DEFI_WALLET_ADDRESS;
8349
+ if (isJson) {
8350
+ if (!wallet) {
8351
+ console.log(JSON.stringify({ error: "DEFI_WALLET_ADDRESS not set" }, null, 2));
8352
+ return;
8353
+ }
8354
+ const heChain2 = registry.getChain("hyperevm");
8355
+ const mantleChain2 = registry.getChain("mantle");
8356
+ const heTokens2 = (registry.tokens.get("hyperevm") ?? []).filter((t) => HYPEREVM_DISPLAY.includes(t.symbol));
8357
+ const mantleTokens2 = (registry.tokens.get("mantle") ?? []).filter((t) => MANTLE_DISPLAY.includes(t.symbol));
8358
+ const heSorted2 = HYPEREVM_DISPLAY.map((s) => heTokens2.find((t) => t.symbol === s)).filter(Boolean);
8359
+ const mantleSorted2 = MANTLE_DISPLAY.map((s) => mantleTokens2.find((t) => t.symbol === s)).filter(Boolean);
8360
+ const [heBalances2, mantleBalances2] = await Promise.all([
8361
+ fetchBalances(heChain2.effectiveRpcUrl(), wallet, heSorted2),
8362
+ fetchBalances(mantleChain2.effectiveRpcUrl(), wallet, mantleSorted2)
8363
+ ]);
8364
+ console.log(JSON.stringify({
8365
+ wallet,
8366
+ chains: {
8367
+ hyperevm: { name: heChain2.name, balances: heBalances2 },
8368
+ mantle: { name: mantleChain2.name, balances: mantleBalances2 }
8369
+ }
8370
+ }, null, 2));
8371
+ return;
8372
+ }
8373
+ const { createRequire: createRequire2 } = await import("module");
8374
+ const _require2 = createRequire2(import.meta.url);
8375
+ const pkg = _require2("../package.json");
8376
+ const version = pkg.version;
8377
+ if (!wallet) {
8378
+ console.log("");
8379
+ console.log(pc2.bold(pc2.cyan(" DeFi CLI v" + version)));
8380
+ console.log("");
8381
+ console.log(pc2.yellow(" Wallet not configured."));
8382
+ console.log(" Set DEFI_WALLET_ADDRESS to see your balances:");
8383
+ console.log("");
8384
+ console.log(pc2.dim(" export DEFI_WALLET_ADDRESS=0x..."));
8385
+ console.log("");
8386
+ console.log(" Commands:");
8387
+ console.log(pc2.dim(" defi status Protocol overview"));
8388
+ console.log(pc2.dim(" defi lending rates Compare lending APYs"));
8389
+ console.log(pc2.dim(" defi dex quote Get swap quotes"));
8390
+ console.log(pc2.dim(" defi portfolio View all positions"));
8391
+ console.log(pc2.dim(" defi scan Exploit detection"));
8392
+ console.log(pc2.dim(" defi --help Full command list"));
8393
+ console.log("");
8394
+ return;
8395
+ }
8396
+ const heChain = registry.getChain("hyperevm");
8397
+ const mantleChain = registry.getChain("mantle");
8398
+ const heTokens = (registry.tokens.get("hyperevm") ?? []).filter((t) => HYPEREVM_DISPLAY.includes(t.symbol));
8399
+ const mantleTokens = (registry.tokens.get("mantle") ?? []).filter((t) => MANTLE_DISPLAY.includes(t.symbol));
8400
+ const heSorted = HYPEREVM_DISPLAY.map((s) => heTokens.find((t) => t.symbol === s)).filter(Boolean);
8401
+ const mantleSorted = MANTLE_DISPLAY.map((s) => mantleTokens.find((t) => t.symbol === s)).filter(Boolean);
8402
+ const [heBalances, mantleBalances] = await Promise.all([
8403
+ fetchBalances(heChain.effectiveRpcUrl(), wallet, heSorted).catch(
8404
+ () => heSorted.map((t) => ({ symbol: t.symbol, balance: "?", decimals: t.decimals }))
8405
+ ),
8406
+ fetchBalances(mantleChain.effectiveRpcUrl(), wallet, mantleSorted).catch(
8407
+ () => mantleSorted.map((t) => ({ symbol: t.symbol, balance: "?", decimals: t.decimals }))
8408
+ )
8409
+ ]);
8410
+ const colWidth = 38;
8411
+ const divider = "\u2500".repeat(colWidth - 2);
8412
+ console.log("");
8413
+ console.log(
8414
+ pc2.bold(pc2.cyan(" DeFi CLI v" + version)) + pc2.dim(" \u2014 ") + pc2.bold(heChain.name) + pc2.dim(" \xB7 ") + pc2.bold(mantleChain.name)
8415
+ );
8416
+ console.log("");
8417
+ console.log(" Wallet: " + pc2.yellow(shortenAddress(wallet)));
8418
+ console.log("");
8419
+ const heHeader = padRight(
8420
+ " " + pc2.bold(heChain.name),
8421
+ colWidth + 10
8422
+ /* account for ANSI */
8423
+ );
8424
+ const mantleHeader = pc2.bold(mantleChain.name);
8425
+ console.log(heHeader + " " + mantleHeader);
8426
+ const heDivider = padRight(" " + pc2.dim(divider), colWidth + 10);
8427
+ const mantleDivider = pc2.dim(divider);
8428
+ console.log(heDivider + " " + mantleDivider);
8429
+ const maxRows = Math.max(heBalances.length, mantleBalances.length);
8430
+ for (let i = 0; i < maxRows; i++) {
8431
+ const heEntry = heBalances[i];
8432
+ const mantleEntry = mantleBalances[i];
8433
+ const heText = heEntry ? formatBalanceLine(heEntry.symbol, heEntry.balance) : "";
8434
+ const mantleText = mantleEntry ? formatBalanceLine(mantleEntry.symbol, mantleEntry.balance) : "";
8435
+ const heColored = heEntry ? heEntry.balance === "0.00" || heEntry.balance === "?" ? pc2.dim(heText) : heText : "";
8436
+ const mantleColored = mantleEntry ? mantleEntry.balance === "0.00" || mantleEntry.balance === "?" ? pc2.dim(mantleText) : mantleText : "";
8437
+ const visibleLen = heText.length;
8438
+ const padNeeded = colWidth - visibleLen;
8439
+ const paddedHe = heColored + (padNeeded > 0 ? " ".repeat(padNeeded) : "");
8440
+ console.log(paddedHe + " " + mantleColored);
8441
+ }
8442
+ console.log("");
8443
+ console.log(" " + pc2.bold("Commands:"));
8444
+ console.log(" " + pc2.cyan("defi status") + " Protocol overview");
8445
+ console.log(" " + pc2.cyan("defi lending rates") + " Compare lending APYs");
8446
+ console.log(" " + pc2.cyan("defi dex quote") + " Get swap quotes");
8447
+ console.log(" " + pc2.cyan("defi portfolio") + " View all positions");
8448
+ console.log(" " + pc2.cyan("defi scan") + " Exploit detection");
8449
+ console.log(" " + pc2.cyan("defi --help") + " Full command list");
8450
+ console.log("");
8451
+ }
8452
+
8277
8453
  // src/main.ts
8278
8454
  async function main() {
8279
8455
  try {
8456
+ const rawArgs = process.argv.slice(2);
8457
+ const knownSubcommands = /* @__PURE__ */ new Set([
8458
+ "status",
8459
+ "schema",
8460
+ "dex",
8461
+ "gauge",
8462
+ "lending",
8463
+ "cdp",
8464
+ "staking",
8465
+ "vault",
8466
+ "yield",
8467
+ "portfolio",
8468
+ "monitor",
8469
+ "alert",
8470
+ "scan",
8471
+ "arb",
8472
+ "positions",
8473
+ "price",
8474
+ "wallet",
8475
+ "token",
8476
+ "whales",
8477
+ "compare",
8478
+ "swap",
8479
+ "bridge",
8480
+ "nft",
8481
+ "farm",
8482
+ "agent"
8483
+ ]);
8484
+ const hasSubcommand = rawArgs.some((a) => !a.startsWith("-") && knownSubcommands.has(a));
8485
+ const isJson = rawArgs.includes("--json") || rawArgs.includes("--ndjson");
8486
+ const isHelp = rawArgs.includes("--help") || rawArgs.includes("-h");
8487
+ const isVersion = rawArgs.includes("--version") || rawArgs.includes("-V");
8488
+ if (!isHelp && !isVersion && (rawArgs.length === 0 || !hasSubcommand)) {
8489
+ await showLandingPage(isJson);
8490
+ return;
8491
+ }
8280
8492
  await program.parseAsync(process.argv);
8281
8493
  } catch (error) {
8282
8494
  const isJsonMode = process.argv.includes("--json") || process.argv.includes("--ndjson");