@hypurrquant/defi-cli 0.1.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;
@@ -1012,9 +1016,11 @@ var DEFAULT_PRIORITY_FEE_WEI = 100000000n;
1012
1016
  var Executor = class _Executor {
1013
1017
  dryRun;
1014
1018
  rpcUrl;
1015
- constructor(broadcast, rpcUrl) {
1019
+ explorerUrl;
1020
+ constructor(broadcast, rpcUrl, explorerUrl) {
1016
1021
  this.dryRun = !broadcast;
1017
1022
  this.rpcUrl = rpcUrl;
1023
+ this.explorerUrl = explorerUrl;
1018
1024
  }
1019
1025
  /** Apply 20% buffer to a gas estimate */
1020
1026
  static applyGasBuffer(gas) {
@@ -1149,7 +1155,10 @@ var Executor = class _Executor {
1149
1155
  maxFeePerGas: maxFeePerGas > 0n ? maxFeePerGas : void 0,
1150
1156
  maxPriorityFeePerGas: maxPriorityFeePerGas > 0n ? maxPriorityFeePerGas : void 0
1151
1157
  });
1158
+ const txUrl = this.explorerUrl ? `${this.explorerUrl}/tx/${txHash}` : void 0;
1152
1159
  process.stderr.write(`Transaction sent: ${txHash}
1160
+ `);
1161
+ if (txUrl) process.stderr.write(`Explorer: ${txUrl}
1153
1162
  `);
1154
1163
  process.stderr.write("Waiting for confirmation...\n");
1155
1164
  const receipt = await publicClient.waitForTransactionReceipt({ hash: txHash });
@@ -1165,6 +1174,7 @@ var Executor = class _Executor {
1165
1174
  block_number: receipt.blockNumber?.toString(),
1166
1175
  gas_limit: gasLimit.toString(),
1167
1176
  gas_used: receipt.gasUsed?.toString(),
1177
+ explorer_url: txUrl,
1168
1178
  mode: "broadcast"
1169
1179
  }
1170
1180
  };
@@ -6114,7 +6124,7 @@ var POOL_ABI4 = parseAbi27([
6114
6124
  var ORACLE_ABI5 = parseAbi27([
6115
6125
  "function getAssetPrice(address asset) external view returns (uint256)"
6116
6126
  ]);
6117
- function decodeU256(data, wordOffset = 0) {
6127
+ function decodeU2562(data, wordOffset = 0) {
6118
6128
  if (!data || data.length < 2 + (wordOffset + 1) * 64) return 0n;
6119
6129
  const hex = data.slice(2 + wordOffset * 64, 2 + wordOffset * 64 + 64);
6120
6130
  return BigInt("0x" + hex);
@@ -6194,7 +6204,7 @@ function registerPortfolio(parent, getOpts) {
6194
6204
  let nativePriceUsd = 0;
6195
6205
  if (oracleAddr) {
6196
6206
  const priceData = results[results.length - 1] ?? null;
6197
- nativePriceUsd = Number(decodeU256(priceData)) / 1e8;
6207
+ nativePriceUsd = Number(decodeU2562(priceData)) / 1e8;
6198
6208
  }
6199
6209
  let totalValueUsd = 0;
6200
6210
  let idx = 0;
@@ -6208,7 +6218,7 @@ function registerPortfolio(parent, getOpts) {
6208
6218
  }
6209
6219
  if (entry.address === "0x0000000000000000000000000000000000000000") continue;
6210
6220
  if (idx >= results.length) break;
6211
- const balance = decodeU256(results[idx] ?? null);
6221
+ const balance = decodeU2562(results[idx] ?? null);
6212
6222
  if (balance > 0n) {
6213
6223
  const decimals = entry.decimals;
6214
6224
  const balF64 = Number(balance) / 10 ** decimals;
@@ -6228,9 +6238,9 @@ function registerPortfolio(parent, getOpts) {
6228
6238
  if (idx >= results.length) break;
6229
6239
  const data = results[idx] ?? null;
6230
6240
  if (data && data.length >= 2 + 192 * 2) {
6231
- const collateral = Number(decodeU256(data, 0)) / 1e8;
6232
- const debt = Number(decodeU256(data, 1)) / 1e8;
6233
- 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);
6234
6244
  let hf = null;
6235
6245
  if (hfRaw <= BigInt("0xffffffffffffffffffffffffffffffff")) {
6236
6246
  const v = Number(hfRaw) / 1e18;
@@ -7072,7 +7082,7 @@ function estimateTokenValue(symbol, balance, nativePrice) {
7072
7082
  if (["WETH", "ETH", "METH", "CBETH", "WSTETH"].includes(s)) return balance * 2350;
7073
7083
  return balance * nativePrice;
7074
7084
  }
7075
- function decodeU2562(data, offset = 0) {
7085
+ function decodeU2563(data, offset = 0) {
7076
7086
  if (!data || data.length < 2 + (offset + 32) * 2) return 0n;
7077
7087
  const hex = data.slice(2 + offset * 64, 2 + offset * 64 + 64);
7078
7088
  return BigInt("0x" + hex);
@@ -7110,7 +7120,7 @@ async function scanSingleChain(chainName, rpc, user, tokens, lendingPools, oracl
7110
7120
  } catch {
7111
7121
  return null;
7112
7122
  }
7113
- const nativePrice = oracleAddr ? Number(decodeU2562(results[results.length - 1])) / 1e8 : 0;
7123
+ const nativePrice = oracleAddr ? Number(decodeU2563(results[results.length - 1])) / 1e8 : 0;
7114
7124
  const tokenBalances = [];
7115
7125
  const lendingPositions = [];
7116
7126
  let chainValue = 0;
@@ -7120,7 +7130,7 @@ async function scanSingleChain(chainName, rpc, user, tokens, lendingPools, oracl
7120
7130
  const ct = callTypes[i];
7121
7131
  const data = results[i] ?? null;
7122
7132
  if (ct.kind === "token") {
7123
- const balance = decodeU2562(data);
7133
+ const balance = decodeU2563(data);
7124
7134
  if (balance > 0n) {
7125
7135
  const balF64 = Number(balance) / 10 ** ct.decimals;
7126
7136
  const valueUsd = estimateTokenValue(ct.symbol, balF64, nativePrice);
@@ -7137,9 +7147,9 @@ async function scanSingleChain(chainName, rpc, user, tokens, lendingPools, oracl
7137
7147
  if (data && data.length >= 2 + 192 * 2) {
7138
7148
  const priceDecimals = ct.iface === "aave_v2" ? 18 : 8;
7139
7149
  const divisor = 10 ** priceDecimals;
7140
- const collateral = Number(decodeU2562(data, 0)) / divisor;
7141
- const debt = Number(decodeU2562(data, 1)) / divisor;
7142
- 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);
7143
7153
  let hf = null;
7144
7154
  if (hfRaw <= BigInt("0xffffffffffffffffffffffffffffffff")) {
7145
7155
  const v = Number(hfRaw) / 1e18;
@@ -7485,7 +7495,7 @@ function round24(x) {
7485
7495
  function round43(x) {
7486
7496
  return Math.round(x * 1e4) / 1e4;
7487
7497
  }
7488
- function decodeU2563(data, wordOffset = 0) {
7498
+ function decodeU2564(data, wordOffset = 0) {
7489
7499
  if (!data || data.length < 2 + (wordOffset + 1) * 64) return 0n;
7490
7500
  const hex = data.slice(2 + wordOffset * 64, 2 + wordOffset * 64 + 64);
7491
7501
  return BigInt("0x" + hex);
@@ -7610,9 +7620,9 @@ function registerWhales(parent, getOpts) {
7610
7620
  if (data && data.length >= 2 + 192 * 2) {
7611
7621
  const dec = iface === "aave_v2" ? 18 : 8;
7612
7622
  const divisor = 10 ** dec;
7613
- const collateral = Number(decodeU2563(data, 0)) / divisor;
7614
- const debt = Number(decodeU2563(data, 1)) / divisor;
7615
- 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);
7616
7626
  let hf = null;
7617
7627
  if (hfRaw <= BigInt("0xffffffffffffffffffffffffffffffff")) {
7618
7628
  const v = Number(hfRaw) / 1e18;
@@ -8015,11 +8025,11 @@ function registerBridge(parent, getOpts) {
8015
8025
  const amountUsdc = Number(BigInt(opts.amount)) / 1e6;
8016
8026
  const { fee, maxFeeSubunits } = await getCctpFeeEstimate(srcDomain, dstDomain, amountUsdc);
8017
8027
  const recipientPadded = `0x${"0".repeat(24)}${recipient.replace("0x", "").toLowerCase()}`;
8018
- const { encodeFunctionData: encodeFunctionData28, parseAbi: parseAbi31 } = await import("viem");
8019
- const tokenMessengerAbi = parseAbi31([
8028
+ const { encodeFunctionData: encodeFunctionData29, parseAbi: parseAbi33 } = await import("viem");
8029
+ const tokenMessengerAbi = parseAbi33([
8020
8030
  "function depositForBurn(uint256 amount, uint32 destinationDomain, bytes32 mintRecipient, address burnToken, bytes32 destinationCaller, uint256 maxFee, uint32 minFinalityThreshold) external returns (uint64 nonce)"
8021
8031
  ]);
8022
- const data = encodeFunctionData28({
8032
+ const data = encodeFunctionData29({
8023
8033
  abi: tokenMessengerAbi,
8024
8034
  functionName: "depositForBurn",
8025
8035
  args: [
@@ -8235,7 +8245,7 @@ function makeExecutor() {
8235
8245
  const opts = program.opts();
8236
8246
  const registry = Registry.loadEmbedded();
8237
8247
  const chain = registry.getChain(opts.chain ?? "hyperevm");
8238
- return new Executor(!!opts.broadcast, chain.effectiveRpcUrl());
8248
+ return new Executor(!!opts.broadcast, chain.effectiveRpcUrl(), chain.explorer_url);
8239
8249
  }
8240
8250
  registerStatus(program, getOutputMode);
8241
8251
  registerSchema(program, getOutputMode);
@@ -8268,9 +8278,210 @@ program.command("agent").description("Agent mode: read JSON commands from stdin
8268
8278
  process.exit(1);
8269
8279
  });
8270
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
+
8271
8447
  // src/main.ts
8272
8448
  async function main() {
8273
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
+ }
8274
8485
  await program.parseAsync(process.argv);
8275
8486
  } catch (error) {
8276
8487
  const isJsonMode = process.argv.includes("--json") || process.argv.includes("--ndjson");