@hypurrquant/defi-cli 0.2.0 → 0.2.2

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