@hypurrquant/defi-cli 1.0.1 → 1.0.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.
Files changed (62) hide show
  1. package/config/chains.toml +54 -0
  2. package/config/protocols/dex/aerodrome_base.toml +15 -0
  3. package/config/protocols/dex/aerodrome_cl.toml +17 -0
  4. package/config/protocols/dex/apeswap_bnb.toml +14 -0
  5. package/config/protocols/dex/babydogeswap_bnb.toml +14 -0
  6. package/config/protocols/dex/bakeryswap_bnb.toml +14 -0
  7. package/config/protocols/dex/biswap_bnb.toml +14 -0
  8. package/config/protocols/dex/bscswap_bnb.toml +14 -0
  9. package/config/protocols/dex/curve_hyperevm.toml +21 -0
  10. package/config/protocols/dex/fstswap_bnb.toml +14 -0
  11. package/config/protocols/dex/hybra.toml +3 -1
  12. package/config/protocols/dex/hyperswap.toml +15 -0
  13. package/config/protocols/dex/kittenswap.toml +4 -1
  14. package/config/protocols/dex/merchantmoe_mantle.toml +1 -0
  15. package/config/protocols/dex/nest.toml +3 -1
  16. package/config/protocols/dex/pancakeswap_v2_bnb.toml +14 -0
  17. package/config/protocols/dex/pancakeswap_v3_bnb.toml +15 -0
  18. package/config/protocols/dex/project_x.toml +4 -2
  19. package/config/protocols/dex/ramses_cl.toml +14 -7
  20. package/config/protocols/dex/ramses_hl.toml +6 -14
  21. package/config/protocols/dex/thena_fusion_bnb.toml +14 -0
  22. package/config/protocols/dex/thena_v1_bnb.toml +13 -0
  23. package/config/protocols/dex/traderjoe_monad.toml +14 -0
  24. package/config/protocols/dex/uniswap_v2_monad.toml +12 -0
  25. package/config/protocols/dex/uniswap_v3_base.toml +14 -0
  26. package/config/protocols/dex/uniswap_v3_bnb.toml +13 -0
  27. package/config/protocols/dex/uniswap_v3_monad.toml +14 -0
  28. package/config/protocols/lending/.omc/state/last-tool-error.json +7 -0
  29. package/config/protocols/lending/aave_v3_base.toml +14 -0
  30. package/config/protocols/lending/aave_v3_bnb.toml +14 -0
  31. package/config/protocols/lending/compound_v3_base.toml +14 -0
  32. package/config/protocols/lending/felix_morpho.toml +2 -1
  33. package/config/protocols/lending/hyperlend.toml +2 -1
  34. package/config/protocols/lending/hypurrfi.toml +2 -1
  35. package/config/protocols/lending/kinza_bnb.toml +15 -0
  36. package/config/protocols/lending/morpho_blue_monad.toml +11 -0
  37. package/config/protocols/lending/venus_bnb.toml +18 -0
  38. package/config/protocols/lending/venus_flux_bnb.toml +22 -0
  39. package/config/protocols/vault/beefy_bnb.toml +12 -0
  40. package/config/tokens/arbitrum.toml +77 -0
  41. package/config/tokens/base.toml +50 -0
  42. package/config/tokens/bnb.toml +50 -0
  43. package/config/tokens/ethereum.toml +107 -0
  44. package/config/tokens/hyperevm.toml +1 -0
  45. package/config/tokens/monad.toml +48 -0
  46. package/dist/index.js +171 -68
  47. package/dist/index.js.map +1 -1
  48. package/dist/main.js +231 -136
  49. package/dist/main.js.map +1 -1
  50. package/dist/mcp-server.js +59 -4
  51. package/dist/mcp-server.js.map +1 -1
  52. package/package.json +3 -1
  53. package/skills/defi-cli/package.json +1 -1
  54. package/config/pools.example.toml +0 -31
  55. package/config/protocols/cdp/felix.toml +0 -21
  56. package/config/protocols/nft/seaport_hyperevm.toml +0 -10
  57. package/config/protocols/vault/felix_vaults.toml +0 -21
  58. package/config/protocols/vault/hyperbeat_hyperevm.toml +0 -29
  59. package/config/protocols/vault/hypersurface_hyperevm.toml +0 -13
  60. package/config/protocols/vault/looping_hyperevm.toml +0 -17
  61. package/config/protocols/vault/upshift.toml +0 -14
  62. package/config/protocols/yield_aggregator/lazy_summer.toml +0 -13
package/dist/main.js CHANGED
@@ -5612,6 +5612,7 @@ var init_dist2 = __esm({
5612
5612
  }
5613
5613
  };
5614
5614
  CTOKEN_ABI = parseAbi17([
5615
+ "function underlying() external view returns (address)",
5615
5616
  "function supplyRatePerBlock() external view returns (uint256)",
5616
5617
  "function borrowRatePerBlock() external view returns (uint256)",
5617
5618
  "function totalSupply() external view returns (uint256)",
@@ -5625,7 +5626,10 @@ var init_dist2 = __esm({
5625
5626
  CompoundV2Adapter = class {
5626
5627
  protocolName;
5627
5628
  defaultVtoken;
5629
+ vTokenCandidates;
5628
5630
  rpcUrl;
5631
+ // Lazy cache: underlying asset address (lowercased) → vToken address
5632
+ vTokenByAsset = null;
5629
5633
  constructor(entry, rpcUrl) {
5630
5634
  this.protocolName = entry.name;
5631
5635
  this.rpcUrl = rpcUrl;
@@ -5633,6 +5637,26 @@ var init_dist2 = __esm({
5633
5637
  const vtoken = contracts["vusdt"] ?? contracts["vusdc"] ?? contracts["vbnb"] ?? contracts["comptroller"];
5634
5638
  if (!vtoken) throw DefiError.contractError("Missing vToken or comptroller address");
5635
5639
  this.defaultVtoken = vtoken;
5640
+ this.vTokenCandidates = Object.entries(contracts).filter(([k]) => /^v[a-z][a-z0-9]*$/i.test(k)).map(([, v]) => v);
5641
+ if (this.vTokenCandidates.length === 0) this.vTokenCandidates = [vtoken];
5642
+ }
5643
+ async resolveVtoken(asset) {
5644
+ if (!this.rpcUrl) return null;
5645
+ if (!this.vTokenByAsset) {
5646
+ const client = createPublicClient13({ transport: http13(this.rpcUrl) });
5647
+ const map = /* @__PURE__ */ new Map();
5648
+ const lookups = await Promise.allSettled(
5649
+ this.vTokenCandidates.map(async (v) => {
5650
+ const u = await client.readContract({ address: v, abi: CTOKEN_ABI, functionName: "underlying" });
5651
+ return [u.toLowerCase(), v];
5652
+ })
5653
+ );
5654
+ for (const r of lookups) {
5655
+ if (r.status === "fulfilled") map.set(r.value[0], r.value[1]);
5656
+ }
5657
+ this.vTokenByAsset = map;
5658
+ }
5659
+ return this.vTokenByAsset.get(asset.toLowerCase()) ?? null;
5636
5660
  }
5637
5661
  name() {
5638
5662
  return this.protocolName;
@@ -5696,15 +5720,27 @@ var init_dist2 = __esm({
5696
5720
  async getRates(asset) {
5697
5721
  if (!this.rpcUrl) throw DefiError.rpcError("No RPC URL configured");
5698
5722
  const client = createPublicClient13({ transport: http13(this.rpcUrl) });
5723
+ const vtoken = await this.resolveVtoken(asset);
5724
+ if (!vtoken) {
5725
+ return {
5726
+ protocol: this.protocolName,
5727
+ asset,
5728
+ supply_apy: 0,
5729
+ borrow_variable_apy: 0,
5730
+ utilization: 0,
5731
+ total_supply: 0n,
5732
+ total_borrow: 0n
5733
+ };
5734
+ }
5699
5735
  const [supplyRate, borrowRate, totalSupply, totalBorrows] = await Promise.all([
5700
- client.readContract({ address: this.defaultVtoken, abi: CTOKEN_ABI, functionName: "supplyRatePerBlock" }).catch((e) => {
5736
+ client.readContract({ address: vtoken, abi: CTOKEN_ABI, functionName: "supplyRatePerBlock" }).catch((e) => {
5701
5737
  throw DefiError.rpcError(`[${this.protocolName}] supplyRatePerBlock failed: ${e}`);
5702
5738
  }),
5703
- client.readContract({ address: this.defaultVtoken, abi: CTOKEN_ABI, functionName: "borrowRatePerBlock" }).catch((e) => {
5739
+ client.readContract({ address: vtoken, abi: CTOKEN_ABI, functionName: "borrowRatePerBlock" }).catch((e) => {
5704
5740
  throw DefiError.rpcError(`[${this.protocolName}] borrowRatePerBlock failed: ${e}`);
5705
5741
  }),
5706
- client.readContract({ address: this.defaultVtoken, abi: CTOKEN_ABI, functionName: "totalSupply" }).catch(() => 0n),
5707
- client.readContract({ address: this.defaultVtoken, abi: CTOKEN_ABI, functionName: "totalBorrows" }).catch(() => 0n)
5742
+ client.readContract({ address: vtoken, abi: CTOKEN_ABI, functionName: "totalSupply" }).catch(() => 0n),
5743
+ client.readContract({ address: vtoken, abi: CTOKEN_ABI, functionName: "totalBorrows" }).catch(() => 0n)
5708
5744
  ]);
5709
5745
  const supplyPerBlock = Number(supplyRate) / 1e18;
5710
5746
  const borrowPerBlock = Number(borrowRate) / 1e18;
@@ -5730,6 +5766,7 @@ var init_dist2 = __esm({
5730
5766
  }
5731
5767
  };
5732
5768
  COMET_ABI = parseAbi18([
5769
+ "function baseToken() external view returns (address)",
5733
5770
  "function getUtilization() external view returns (uint256)",
5734
5771
  "function getSupplyRate(uint256 utilization) external view returns (uint64)",
5735
5772
  "function getBorrowRate(uint256 utilization) external view returns (uint64)",
@@ -5815,6 +5852,24 @@ var init_dist2 = __esm({
5815
5852
  async getRates(asset) {
5816
5853
  if (!this.rpcUrl) throw DefiError.rpcError("No RPC URL configured");
5817
5854
  const client = createPublicClient14({ transport: http14(this.rpcUrl) });
5855
+ const baseToken = await client.readContract({
5856
+ address: this.comet,
5857
+ abi: COMET_ABI,
5858
+ functionName: "baseToken"
5859
+ }).catch((e) => {
5860
+ throw DefiError.rpcError(`[${this.protocolName}] baseToken failed: ${e}`);
5861
+ });
5862
+ if (baseToken.toLowerCase() !== asset.toLowerCase()) {
5863
+ return {
5864
+ protocol: this.protocolName,
5865
+ asset,
5866
+ supply_apy: 0,
5867
+ borrow_variable_apy: 0,
5868
+ utilization: 0,
5869
+ total_supply: 0n,
5870
+ total_borrow: 0n
5871
+ };
5872
+ }
5818
5873
  const utilization = await client.readContract({
5819
5874
  address: this.comet,
5820
5875
  abi: COMET_ABI,
@@ -9418,11 +9473,16 @@ function registerLending(parent, getOpts, makeExecutor2) {
9418
9473
  const rates = await adapter.getRates(asset);
9419
9474
  printOutput(rates, getOpts());
9420
9475
  });
9421
- lending.command("position").description("Show current lending position").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--address <address>", "Wallet address to query").action(async (opts) => {
9476
+ lending.command("position").description("Show current lending position").requiredOption("--protocol <protocol>", "Protocol slug").option("--address <address>", "Wallet address (defaults to DEFI_WALLET_ADDRESS)").action(async (opts) => {
9422
9477
  const ctx = resolveContext(parent, getOpts, opts.protocol);
9423
9478
  if (!ctx) return;
9479
+ const address = opts.address ?? process.env["DEFI_WALLET_ADDRESS"];
9480
+ if (!address) {
9481
+ printOutput({ error: "--address required (or set DEFI_WALLET_ADDRESS)" }, getOpts());
9482
+ return;
9483
+ }
9424
9484
  const adapter = createLending(ctx.protocol, ctx.rpcUrl);
9425
- const position = await adapter.getUserPosition(opts.address);
9485
+ const position = await adapter.getUserPosition(address);
9426
9486
  printOutput(position, getOpts());
9427
9487
  });
9428
9488
  lending.command("supply").description("Supply an asset to a lending protocol").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--asset <token>", "Token symbol or address").requiredOption("--amount <amount>", "Amount to supply in wei").option("--on-behalf-of <address>", "On behalf of address").action(async (opts) => {
@@ -9696,39 +9756,6 @@ async function scanRatesForExecute(registry, asset) {
9696
9756
  }
9697
9757
  function registerYield(parent, getOpts, makeExecutor2) {
9698
9758
  const yieldCmd = parent.command("yield").description("Yield operations: compare, scan, optimize, execute");
9699
- yieldCmd.option("--asset <token>", "Token symbol or address", "USDC").action(async (opts) => {
9700
- try {
9701
- const registry = Registry.loadEmbedded();
9702
- const asset = opts.asset;
9703
- const specifiedChain = parent.opts().chain;
9704
- const chainKeys = specifiedChain ? [specifiedChain.toLowerCase()] : Array.from(registry.chains.keys());
9705
- const allRates = [];
9706
- for (const chainKey of chainKeys) {
9707
- try {
9708
- const chain = registry.getChain(chainKey);
9709
- const rpc = chain.effectiveRpcUrl();
9710
- let assetAddr;
9711
- try {
9712
- assetAddr = resolveAsset(registry, chainKey, asset);
9713
- } catch {
9714
- continue;
9715
- }
9716
- const rates = await collectLendingRates(registry, chainKey, rpc, assetAddr);
9717
- for (const r of rates) {
9718
- if (r.supply_apy > 0) {
9719
- allRates.push({ chain: chain.name, protocol: r.protocol, supply_apy: r.supply_apy, borrow_variable_apy: r.borrow_variable_apy });
9720
- }
9721
- }
9722
- } catch {
9723
- }
9724
- }
9725
- allRates.sort((a, b) => b.supply_apy - a.supply_apy);
9726
- const best = allRates[0] ? `${allRates[0].protocol} on ${allRates[0].chain}` : null;
9727
- printOutput({ asset, chains_scanned: registry.chains.size, rates: allRates, best_supply: best }, getOpts());
9728
- } catch (err) {
9729
- printOutput({ error: String(err) }, getOpts());
9730
- }
9731
- });
9732
9759
  yieldCmd.command("compare").description("Compare lending rates across protocols for an asset").option("--asset <token>", "Token symbol or address", "USDC").action(async (opts) => {
9733
9760
  try {
9734
9761
  const registry = Registry.loadEmbedded();
@@ -10323,7 +10350,7 @@ function decodeU2562(data, wordOffset = 0) {
10323
10350
  }
10324
10351
  function registerPortfolio(parent, getOpts) {
10325
10352
  const portfolio = parent.command("portfolio").description("Aggregate positions across all protocols");
10326
- portfolio.command("show").description("Show current portfolio positions").requiredOption("--address <address>", "Wallet address to query").action(async (opts) => {
10353
+ portfolio.command("show").description("Show current portfolio positions").option("--address <address>", "Wallet address (defaults to DEFI_WALLET_ADDRESS)").action(async (opts) => {
10327
10354
  const mode = getOpts();
10328
10355
  const registry = Registry.loadEmbedded();
10329
10356
  const chainName = requireChain(parent, getOpts);
@@ -10335,9 +10362,14 @@ function registerPortfolio(parent, getOpts) {
10335
10362
  printOutput({ error: `Chain not found: ${chainName}` }, mode);
10336
10363
  return;
10337
10364
  }
10338
- const user = opts.address;
10365
+ const addr = opts.address ?? process.env["DEFI_WALLET_ADDRESS"];
10366
+ if (!addr) {
10367
+ printOutput({ error: "--address required (or set DEFI_WALLET_ADDRESS)" }, mode);
10368
+ return;
10369
+ }
10370
+ const user = addr;
10339
10371
  if (!/^0x[0-9a-fA-F]{40}$/.test(user)) {
10340
- printOutput({ error: `Invalid address: ${opts.address}` }, mode);
10372
+ printOutput({ error: `Invalid address: ${addr}` }, mode);
10341
10373
  return;
10342
10374
  }
10343
10375
  const rpc = chain.effectiveRpcUrl();
@@ -10463,17 +10495,22 @@ function registerPortfolio(parent, getOpts) {
10463
10495
  mode
10464
10496
  );
10465
10497
  });
10466
- portfolio.command("snapshot").description("Take a new portfolio snapshot and save it locally").requiredOption("--address <address>", "Wallet address to snapshot").action(async (opts) => {
10498
+ portfolio.command("snapshot").description("Take a new portfolio snapshot and save it locally").option("--address <address>", "Wallet address (defaults to DEFI_WALLET_ADDRESS)").action(async (opts) => {
10467
10499
  const mode = getOpts();
10468
10500
  const chainName = requireChain(parent, getOpts);
10469
10501
  if (!chainName) return;
10470
10502
  const registry = Registry.loadEmbedded();
10471
- if (!/^0x[0-9a-fA-F]{40}$/.test(opts.address)) {
10472
- printOutput({ error: `Invalid address: ${opts.address}` }, mode);
10503
+ const addr = opts.address ?? process.env["DEFI_WALLET_ADDRESS"];
10504
+ if (!addr) {
10505
+ printOutput({ error: "--address required (or set DEFI_WALLET_ADDRESS)" }, mode);
10506
+ return;
10507
+ }
10508
+ if (!/^0x[0-9a-fA-F]{40}$/.test(addr)) {
10509
+ printOutput({ error: `Invalid address: ${addr}` }, mode);
10473
10510
  return;
10474
10511
  }
10475
10512
  try {
10476
- const snapshot = await takeSnapshot(chainName, opts.address, registry);
10513
+ const snapshot = await takeSnapshot(chainName, addr, registry);
10477
10514
  const filepath = saveSnapshot(snapshot);
10478
10515
  printOutput(
10479
10516
  {
@@ -10491,16 +10528,21 @@ function registerPortfolio(parent, getOpts) {
10491
10528
  printOutput({ error: errMsg(e) }, mode);
10492
10529
  }
10493
10530
  });
10494
- portfolio.command("pnl").description("Show PnL since the last snapshot").requiredOption("--address <address>", "Wallet address").option("--since <hours>", "Compare against snapshot from N hours ago (default: last snapshot)").action(async (opts) => {
10531
+ portfolio.command("pnl").description("Show PnL since the last snapshot").option("--address <address>", "Wallet address (defaults to DEFI_WALLET_ADDRESS)").option("--since <hours>", "Compare against snapshot from N hours ago (default: last snapshot)").action(async (opts) => {
10495
10532
  const mode = getOpts();
10496
10533
  const chainName = requireChain(parent, getOpts);
10497
10534
  if (!chainName) return;
10498
10535
  const registry = Registry.loadEmbedded();
10499
- if (!/^0x[0-9a-fA-F]{40}$/.test(opts.address)) {
10500
- printOutput({ error: `Invalid address: ${opts.address}` }, mode);
10536
+ const addr = opts.address ?? process.env["DEFI_WALLET_ADDRESS"];
10537
+ if (!addr) {
10538
+ printOutput({ error: "--address required (or set DEFI_WALLET_ADDRESS)" }, mode);
10539
+ return;
10540
+ }
10541
+ if (!/^0x[0-9a-fA-F]{40}$/.test(addr)) {
10542
+ printOutput({ error: `Invalid address: ${addr}` }, mode);
10501
10543
  return;
10502
10544
  }
10503
- const snapshots = loadSnapshots(chainName, opts.address, 50);
10545
+ const snapshots = loadSnapshots(chainName, addr, 50);
10504
10546
  if (snapshots.length === 0) {
10505
10547
  printOutput({ error: "No snapshots found. Run `portfolio snapshot` first." }, mode);
10506
10548
  return;
@@ -10517,12 +10559,12 @@ function registerPortfolio(parent, getOpts) {
10517
10559
  previous = match;
10518
10560
  }
10519
10561
  try {
10520
- const current = await takeSnapshot(chainName, opts.address, registry);
10562
+ const current = await takeSnapshot(chainName, addr, registry);
10521
10563
  const pnl = calculatePnL(current, previous);
10522
10564
  printOutput(
10523
10565
  {
10524
10566
  chain: chainName,
10525
- wallet: opts.address,
10567
+ wallet: addr,
10526
10568
  previous_snapshot: new Date(previous.timestamp).toISOString(),
10527
10569
  current_time: new Date(current.timestamp).toISOString(),
10528
10570
  ...pnl,
@@ -10537,16 +10579,21 @@ function registerPortfolio(parent, getOpts) {
10537
10579
  printOutput({ error: errMsg(e) }, mode);
10538
10580
  }
10539
10581
  });
10540
- portfolio.command("history").description("List saved portfolio snapshots with values").requiredOption("--address <address>", "Wallet address").option("--limit <n>", "Number of snapshots to show", "10").action(async (opts) => {
10582
+ portfolio.command("history").description("List saved portfolio snapshots with values").option("--address <address>", "Wallet address (defaults to DEFI_WALLET_ADDRESS)").option("--limit <n>", "Number of snapshots to show", "10").action(async (opts) => {
10541
10583
  const mode = getOpts();
10542
10584
  const chainName = requireChain(parent, getOpts);
10543
10585
  if (!chainName) return;
10544
- if (!/^0x[0-9a-fA-F]{40}$/.test(opts.address)) {
10545
- printOutput({ error: `Invalid address: ${opts.address}` }, mode);
10586
+ const addr = opts.address ?? process.env["DEFI_WALLET_ADDRESS"];
10587
+ if (!addr) {
10588
+ printOutput({ error: "--address required (or set DEFI_WALLET_ADDRESS)" }, mode);
10589
+ return;
10590
+ }
10591
+ if (!/^0x[0-9a-fA-F]{40}$/.test(addr)) {
10592
+ printOutput({ error: `Invalid address: ${addr}` }, mode);
10546
10593
  return;
10547
10594
  }
10548
10595
  const limit = parseInt(opts.limit, 10);
10549
- const snapshots = loadSnapshots(chainName, opts.address, limit);
10596
+ const snapshots = loadSnapshots(chainName, addr, limit);
10550
10597
  if (snapshots.length === 0) {
10551
10598
  printOutput({ message: "No snapshots found for this address on this chain." }, mode);
10552
10599
  return;
@@ -10709,16 +10756,21 @@ init_dist();
10709
10756
  import { createPublicClient as createPublicClient24, http as http24, formatEther } from "viem";
10710
10757
  function registerWallet(parent, getOpts) {
10711
10758
  const wallet = parent.command("wallet").description("Wallet management");
10712
- wallet.command("balance").description("Show native token balance").requiredOption("--address <address>", "Wallet address to query").action(async (opts) => {
10759
+ wallet.command("balance").description("Show native token balance").option("--address <address>", "Wallet address (defaults to DEFI_WALLET_ADDRESS)").action(async (opts) => {
10713
10760
  const chainName = requireChain(parent, getOpts);
10714
10761
  if (!chainName) return;
10762
+ const addr = opts.address ?? process.env["DEFI_WALLET_ADDRESS"];
10763
+ if (!addr) {
10764
+ printOutput({ error: "--address required (or set DEFI_WALLET_ADDRESS)" }, getOpts());
10765
+ return;
10766
+ }
10715
10767
  const registry = Registry.loadEmbedded();
10716
10768
  const chain = registry.getChain(chainName);
10717
10769
  const client = createPublicClient24({ transport: http24(chain.effectiveRpcUrl()) });
10718
- const balance = await client.getBalance({ address: opts.address });
10770
+ const balance = await client.getBalance({ address: addr });
10719
10771
  printOutput({
10720
10772
  chain: chain.name,
10721
- address: opts.address,
10773
+ address: addr,
10722
10774
  native_token: chain.native_token,
10723
10775
  balance_wei: balance,
10724
10776
  balance_formatted: formatEther(balance)
@@ -10735,22 +10787,27 @@ init_dist();
10735
10787
  import { createPublicClient as createPublicClient25, http as http25, maxUint256 } from "viem";
10736
10788
  function registerToken(parent, getOpts, makeExecutor2) {
10737
10789
  const token = parent.command("token").description("Token operations: approve, allowance, transfer, balance");
10738
- token.command("balance").description("Query token balance for an address").requiredOption("--token <token>", "Token symbol or address").requiredOption("--owner <address>", "Wallet address to query").action(async (opts) => {
10790
+ token.command("balance").description("Query token balance for an address").requiredOption("--token <token>", "Token symbol or address").option("--owner <address>", "Wallet address (defaults to DEFI_WALLET_ADDRESS)").action(async (opts) => {
10739
10791
  const chainName = requireChain(parent, getOpts);
10740
10792
  if (!chainName) return;
10793
+ const owner = opts.owner ?? process.env["DEFI_WALLET_ADDRESS"];
10794
+ if (!owner) {
10795
+ printOutput({ error: "--owner required (or set DEFI_WALLET_ADDRESS)" }, getOpts());
10796
+ return;
10797
+ }
10741
10798
  const registry = Registry.loadEmbedded();
10742
10799
  const chain = registry.getChain(chainName);
10743
10800
  const client = createPublicClient25({ transport: http25(chain.effectiveRpcUrl()) });
10744
10801
  const tokenAddr = resolveTokenAddress(registry, chainName, opts.token);
10745
10802
  const [balance, symbol, decimals] = await Promise.all([
10746
- client.readContract({ address: tokenAddr, abi: erc20Abi, functionName: "balanceOf", args: [opts.owner] }),
10803
+ client.readContract({ address: tokenAddr, abi: erc20Abi, functionName: "balanceOf", args: [owner] }),
10747
10804
  client.readContract({ address: tokenAddr, abi: erc20Abi, functionName: "symbol" }),
10748
10805
  client.readContract({ address: tokenAddr, abi: erc20Abi, functionName: "decimals" })
10749
10806
  ]);
10750
10807
  printOutput({
10751
10808
  token: tokenAddr,
10752
10809
  symbol,
10753
- owner: opts.owner,
10810
+ owner,
10754
10811
  balance,
10755
10812
  decimals
10756
10813
  }, getOpts());
@@ -10766,9 +10823,14 @@ function registerToken(parent, getOpts, makeExecutor2) {
10766
10823
  const result = await executor.execute(tx);
10767
10824
  printOutput(result, getOpts());
10768
10825
  });
10769
- token.command("allowance").description("Check token allowance").requiredOption("--token <token>", "Token symbol or address").requiredOption("--owner <address>", "Owner address").requiredOption("--spender <address>", "Spender address").action(async (opts) => {
10826
+ token.command("allowance").description("Check token allowance").requiredOption("--token <token>", "Token symbol or address").option("--owner <address>", "Owner address (defaults to DEFI_WALLET_ADDRESS)").requiredOption("--spender <address>", "Spender address").action(async (opts) => {
10770
10827
  const chainName = requireChain(parent, getOpts);
10771
10828
  if (!chainName) return;
10829
+ const owner = opts.owner ?? process.env["DEFI_WALLET_ADDRESS"];
10830
+ if (!owner) {
10831
+ printOutput({ error: "--owner required (or set DEFI_WALLET_ADDRESS)" }, getOpts());
10832
+ return;
10833
+ }
10772
10834
  const registry = Registry.loadEmbedded();
10773
10835
  const chain = registry.getChain(chainName);
10774
10836
  const client = createPublicClient25({ transport: http25(chain.effectiveRpcUrl()) });
@@ -10777,9 +10839,9 @@ function registerToken(parent, getOpts, makeExecutor2) {
10777
10839
  address: tokenAddr,
10778
10840
  abi: erc20Abi,
10779
10841
  functionName: "allowance",
10780
- args: [opts.owner, opts.spender]
10842
+ args: [owner, opts.spender]
10781
10843
  });
10782
- printOutput({ token: tokenAddr, owner: opts.owner, spender: opts.spender, allowance }, getOpts());
10844
+ printOutput({ token: tokenAddr, owner, spender: opts.spender, allowance }, getOpts());
10783
10845
  });
10784
10846
  token.command("transfer").description("Transfer tokens to an address").requiredOption("--token <token>", "Token symbol or address").requiredOption("--to <address>", "Recipient address").requiredOption("--amount <amount>", "Amount to transfer (in wei)").action(async (opts) => {
10785
10847
  const executor = makeExecutor2();
@@ -10798,6 +10860,29 @@ init_dist();
10798
10860
  var LIFI_API = "https://li.quest/v1";
10799
10861
  var DLN_API = "https://dln.debridge.finance/v1.0/dln/order";
10800
10862
  var CCTP_FEE_API = "https://iris-api.circle.com/v2/burn/USDC/fees";
10863
+ var DEST_CHAIN_META = {
10864
+ ethereum: { chain_id: 1, name: "Ethereum" },
10865
+ optimism: { chain_id: 10, name: "Optimism" },
10866
+ polygon: { chain_id: 137, name: "Polygon" },
10867
+ arbitrum: { chain_id: 42161, name: "Arbitrum" },
10868
+ avalanche: { chain_id: 43114, name: "Avalanche" },
10869
+ linea: { chain_id: 59144, name: "Linea" },
10870
+ zksync: { chain_id: 324, name: "zkSync" }
10871
+ };
10872
+ function resolveDestChain(registry, slug) {
10873
+ try {
10874
+ const c = registry.getChain(slug);
10875
+ return { chain_id: c.chain_id, name: c.name };
10876
+ } catch {
10877
+ const meta = DEST_CHAIN_META[slug];
10878
+ if (!meta) {
10879
+ throw new Error(
10880
+ `Unknown destination chain '${slug}'. Source chains: hyperevm, mantle, base, bnb, monad. Bridge destinations also include: ${Object.keys(DEST_CHAIN_META).join(", ")}.`
10881
+ );
10882
+ }
10883
+ return meta;
10884
+ }
10885
+ }
10801
10886
  var DLN_CHAIN_IDS = {
10802
10887
  ethereum: 1,
10803
10888
  optimism: 10,
@@ -10896,7 +10981,13 @@ function registerBridge(parent, getOpts) {
10896
10981
  if (!chainName) return;
10897
10982
  const registry = Registry.loadEmbedded();
10898
10983
  const fromChain = registry.getChain(chainName);
10899
- const toChain = registry.getChain(opts.toChain);
10984
+ let toChain;
10985
+ try {
10986
+ toChain = resolveDestChain(registry, opts.toChain);
10987
+ } catch (e) {
10988
+ printOutput({ error: errMsg(e) }, getOpts());
10989
+ return;
10990
+ }
10900
10991
  const tokenAddr = opts.token.startsWith("0x") ? opts.token : registry.resolveToken(chainName, opts.token).address;
10901
10992
  const recipient = resolveWallet(opts.recipient);
10902
10993
  const provider = opts.provider.toLowerCase();
@@ -11653,7 +11744,17 @@ function handleOwsError(e, getOpts) {
11653
11744
  // src/cli.ts
11654
11745
  var _require2 = createRequire2(import.meta.url);
11655
11746
  var _pkg = _require2("../package.json");
11656
- var BANNER = `
11747
+ function buildBanner() {
11748
+ let chainCount = 0;
11749
+ let protocolCount = 0;
11750
+ try {
11751
+ const reg = Registry.loadEmbedded();
11752
+ chainCount = reg.chains.size;
11753
+ protocolCount = reg.protocols.length;
11754
+ } catch {
11755
+ }
11756
+ const stats = chainCount && protocolCount ? `${chainCount} chains \xB7 ${protocolCount} protocols \xB7 by HypurrQuant` : `by HypurrQuant`;
11757
+ return `
11657
11758
  \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
11658
11759
  \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
11659
11760
  \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551
@@ -11661,11 +11762,13 @@ var BANNER = `
11661
11762
  \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551
11662
11763
  \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D
11663
11764
 
11664
- 2 chains \xB7 21 protocols \xB7 by HypurrQuant
11765
+ ${stats}
11665
11766
 
11666
11767
  Lending, LP farming, DEX swap, yield comparison
11667
11768
  \u2014 all from your terminal.
11668
11769
  `;
11770
+ }
11771
+ var BANNER = buildBanner();
11669
11772
  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").option("--dry-run", "Dry-run mode (default, no broadcast)", true).option("--broadcast", "Actually broadcast the transaction");
11670
11773
  function getOutputMode() {
11671
11774
  const opts = program.opts();
@@ -11699,8 +11802,13 @@ registerOws(program, getOutputMode);
11699
11802
  init_dist();
11700
11803
  import pc4 from "picocolors";
11701
11804
  import { encodeFunctionData as encodeFunctionData30, parseAbi as parseAbi34, formatUnits } from "viem";
11702
- var HYPEREVM_DISPLAY = ["HYPE", "WHYPE", "USDC", "USDT0", "USDe", "kHYPE", "wstHYPE"];
11703
- var MANTLE_DISPLAY = ["MNT", "WMNT", "USDC", "USDT", "WETH", "mETH"];
11805
+ var DASHBOARD_CHAINS = [
11806
+ { slug: "hyperevm", tokens: ["HYPE", "WHYPE", "USDC", "USDT0", "USDe", "kHYPE", "wstHYPE"] },
11807
+ { slug: "mantle", tokens: ["MNT", "WMNT", "USDC", "USDT", "WETH", "mETH"] },
11808
+ { slug: "base", tokens: ["ETH", "WETH", "USDC", "AERO"] },
11809
+ { slug: "bnb", tokens: ["BNB", "WBNB", "USDT", "USDC", "BUSD", "CAKE"] },
11810
+ { slug: "monad", tokens: ["MON", "WMON", "USDC", "USDT0", "WETH", "WBTC"] }
11811
+ ];
11704
11812
  var balanceOfAbi = parseAbi34([
11705
11813
  "function balanceOf(address account) view returns (uint256)"
11706
11814
  ]);
@@ -11713,20 +11821,12 @@ async function fetchBalances(rpcUrl, wallet, tokens) {
11713
11821
  if (isNative) {
11714
11822
  return [
11715
11823
  MULTICALL3_ADDRESS,
11716
- encodeFunctionData30({
11717
- abi: getEthBalanceAbi,
11718
- functionName: "getEthBalance",
11719
- args: [wallet]
11720
- })
11824
+ encodeFunctionData30({ abi: getEthBalanceAbi, functionName: "getEthBalance", args: [wallet] })
11721
11825
  ];
11722
11826
  }
11723
11827
  return [
11724
11828
  t.address,
11725
- encodeFunctionData30({
11726
- abi: balanceOfAbi,
11727
- functionName: "balanceOf",
11728
- args: [wallet]
11729
- })
11829
+ encodeFunctionData30({ abi: balanceOfAbi, functionName: "balanceOf", args: [wallet] })
11730
11830
  ];
11731
11831
  });
11732
11832
  let results;
@@ -11758,6 +11858,27 @@ function formatBalanceLine(sym, bal) {
11758
11858
  const balPad = padLeft(bal, 12);
11759
11859
  return ` ${symPad}${balPad}`;
11760
11860
  }
11861
+ async function resolveChainBalances(registry, wallet) {
11862
+ const chains = DASHBOARD_CHAINS.map(({ slug, tokens: order }) => {
11863
+ const chain = registry.getChain(slug);
11864
+ const allTokens = registry.tokens.get(slug) ?? [];
11865
+ const sorted = order.map((s) => allTokens.find((t) => t.symbol === s)).filter(Boolean);
11866
+ return { slug, chain, tokens: sorted };
11867
+ });
11868
+ const balanceLists = await Promise.all(
11869
+ chains.map(
11870
+ ({ chain, tokens }) => fetchBalances(chain.effectiveRpcUrl(), wallet, tokens).catch(
11871
+ () => tokens.map((t) => ({ symbol: t.symbol, balance: "?", decimals: t.decimals }))
11872
+ )
11873
+ )
11874
+ );
11875
+ return chains.map((c, i) => ({
11876
+ slug: c.slug,
11877
+ name: c.chain.name,
11878
+ tokens: c.tokens,
11879
+ balances: balanceLists[i] ?? []
11880
+ }));
11881
+ }
11761
11882
  async function showLandingPage(isJson) {
11762
11883
  const registry = Registry.loadEmbedded();
11763
11884
  const wallet = process.env.DEFI_WALLET_ADDRESS;
@@ -11766,23 +11887,10 @@ async function showLandingPage(isJson) {
11766
11887
  console.log(JSON.stringify({ error: "DEFI_WALLET_ADDRESS not set" }, null, 2));
11767
11888
  return;
11768
11889
  }
11769
- const heChain2 = registry.getChain("hyperevm");
11770
- const mantleChain2 = registry.getChain("mantle");
11771
- const heTokens2 = (registry.tokens.get("hyperevm") ?? []).filter((t) => HYPEREVM_DISPLAY.includes(t.symbol));
11772
- const mantleTokens2 = (registry.tokens.get("mantle") ?? []).filter((t) => MANTLE_DISPLAY.includes(t.symbol));
11773
- const heSorted2 = HYPEREVM_DISPLAY.map((s) => heTokens2.find((t) => t.symbol === s)).filter(Boolean);
11774
- const mantleSorted2 = MANTLE_DISPLAY.map((s) => mantleTokens2.find((t) => t.symbol === s)).filter(Boolean);
11775
- const [heBalances2, mantleBalances2] = await Promise.all([
11776
- fetchBalances(heChain2.effectiveRpcUrl(), wallet, heSorted2),
11777
- fetchBalances(mantleChain2.effectiveRpcUrl(), wallet, mantleSorted2)
11778
- ]);
11779
- console.log(JSON.stringify({
11780
- wallet,
11781
- chains: {
11782
- hyperevm: { name: heChain2.name, balances: heBalances2 },
11783
- mantle: { name: mantleChain2.name, balances: mantleBalances2 }
11784
- }
11785
- }, null, 2));
11890
+ const resolved2 = await resolveChainBalances(registry, wallet);
11891
+ const chains = {};
11892
+ for (const c of resolved2) chains[c.slug] = { name: c.name, balances: c.balances };
11893
+ console.log(JSON.stringify({ wallet, chains }, null, 2));
11786
11894
  return;
11787
11895
  }
11788
11896
  const { createRequire: createRequire3 } = await import("module");
@@ -11808,51 +11916,38 @@ async function showLandingPage(isJson) {
11808
11916
  console.log("");
11809
11917
  return;
11810
11918
  }
11811
- const heChain = registry.getChain("hyperevm");
11812
- const mantleChain = registry.getChain("mantle");
11813
- const heTokens = (registry.tokens.get("hyperevm") ?? []).filter((t) => HYPEREVM_DISPLAY.includes(t.symbol));
11814
- const mantleTokens = (registry.tokens.get("mantle") ?? []).filter((t) => MANTLE_DISPLAY.includes(t.symbol));
11815
- const heSorted = HYPEREVM_DISPLAY.map((s) => heTokens.find((t) => t.symbol === s)).filter(Boolean);
11816
- const mantleSorted = MANTLE_DISPLAY.map((s) => mantleTokens.find((t) => t.symbol === s)).filter(Boolean);
11817
- const [heBalances, mantleBalances] = await Promise.all([
11818
- fetchBalances(heChain.effectiveRpcUrl(), wallet, heSorted).catch(
11819
- () => heSorted.map((t) => ({ symbol: t.symbol, balance: "?", decimals: t.decimals }))
11820
- ),
11821
- fetchBalances(mantleChain.effectiveRpcUrl(), wallet, mantleSorted).catch(
11822
- () => mantleSorted.map((t) => ({ symbol: t.symbol, balance: "?", decimals: t.decimals }))
11823
- )
11824
- ]);
11919
+ const resolved = await resolveChainBalances(registry, wallet);
11825
11920
  const colWidth = 38;
11826
11921
  const divider = "\u2500".repeat(colWidth - 2);
11922
+ const chainNames = resolved.map((c) => c.name).join(pc4.dim(" \xB7 "));
11827
11923
  console.log("");
11828
- console.log(
11829
- pc4.bold(pc4.cyan(" DeFi CLI v" + version)) + pc4.dim(" \u2014 ") + pc4.bold(heChain.name) + pc4.dim(" \xB7 ") + pc4.bold(mantleChain.name)
11830
- );
11924
+ console.log(pc4.bold(pc4.cyan(" DeFi CLI v" + version)) + pc4.dim(" \u2014 ") + pc4.bold(chainNames));
11831
11925
  console.log("");
11832
11926
  console.log(" Wallet: " + pc4.yellow(shortenAddress(wallet)));
11833
11927
  console.log("");
11834
- const heHeader = padRight(
11835
- " " + pc4.bold(heChain.name),
11836
- colWidth + 10
11837
- /* account for ANSI */
11838
- );
11839
- const mantleHeader = pc4.bold(mantleChain.name);
11840
- console.log(heHeader + " " + mantleHeader);
11841
- const heDivider = padRight(" " + pc4.dim(divider), colWidth + 10);
11842
- const mantleDivider = pc4.dim(divider);
11843
- console.log(heDivider + " " + mantleDivider);
11844
- const maxRows = Math.max(heBalances.length, mantleBalances.length);
11845
- for (let i = 0; i < maxRows; i++) {
11846
- const heEntry = heBalances[i];
11847
- const mantleEntry = mantleBalances[i];
11848
- const heText = heEntry ? formatBalanceLine(heEntry.symbol, heEntry.balance) : "";
11849
- const mantleText = mantleEntry ? formatBalanceLine(mantleEntry.symbol, mantleEntry.balance) : "";
11850
- const heColored = heEntry ? heEntry.balance === "0.00" || heEntry.balance === "?" ? pc4.dim(heText) : heText : "";
11851
- const mantleColored = mantleEntry ? mantleEntry.balance === "0.00" || mantleEntry.balance === "?" ? pc4.dim(mantleText) : mantleText : "";
11852
- const visibleLen = heText.length;
11853
- const padNeeded = colWidth - visibleLen;
11854
- const paddedHe = heColored + (padNeeded > 0 ? " ".repeat(padNeeded) : "");
11855
- console.log(paddedHe + " " + mantleColored);
11928
+ for (let i = 0; i < resolved.length; i += 2) {
11929
+ const left = resolved[i];
11930
+ const right = resolved[i + 1];
11931
+ const leftHeader = padRight(" " + pc4.bold(left.name), colWidth + 10);
11932
+ const rightHeader = right ? pc4.bold(right.name) : "";
11933
+ console.log(leftHeader + (right ? " " + rightHeader : ""));
11934
+ const leftDivider = padRight(" " + pc4.dim(divider), colWidth + 10);
11935
+ const rightDivider = right ? pc4.dim(divider) : "";
11936
+ console.log(leftDivider + (right ? " " + rightDivider : ""));
11937
+ const maxRows = Math.max(left.balances.length, right?.balances.length ?? 0);
11938
+ for (let r = 0; r < maxRows; r++) {
11939
+ const lEntry = left.balances[r];
11940
+ const rEntry = right?.balances[r];
11941
+ const lText = lEntry ? formatBalanceLine(lEntry.symbol, lEntry.balance) : "";
11942
+ const rText = rEntry ? formatBalanceLine(rEntry.symbol, rEntry.balance) : "";
11943
+ const lColored = lEntry ? lEntry.balance === "0.00" || lEntry.balance === "?" ? pc4.dim(lText) : lText : "";
11944
+ const rColored = rEntry ? rEntry.balance === "0.00" || rEntry.balance === "?" ? pc4.dim(rText) : rText : "";
11945
+ const lVisible = lText.length;
11946
+ const lPad = colWidth - lVisible;
11947
+ const lPadded = lColored + (lPad > 0 ? " ".repeat(lPad) : "");
11948
+ console.log(lPadded + (right ? " " + rColored : ""));
11949
+ }
11950
+ if (i + 2 < resolved.length) console.log("");
11856
11951
  }
11857
11952
  console.log("");
11858
11953
  console.log(" " + pc4.bold("Commands:"));