@hypurrquant/defi-cli 1.0.11 → 1.0.12

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/index.js CHANGED
@@ -1968,12 +1968,16 @@ var init_dist2 = __esm({
1968
1968
  functionName: "add_liquidity",
1969
1969
  args: [[params.amount_a, params.amount_b], 0n]
1970
1970
  });
1971
+ const approvals = [];
1972
+ if (params.amount_a > 0n) approvals.push({ token: params.token_a, spender: this.router, amount: params.amount_a });
1973
+ if (params.amount_b > 0n) approvals.push({ token: params.token_b, spender: this.router, amount: params.amount_b });
1971
1974
  return {
1972
1975
  description: `[${this.protocolName}] Curve add liquidity`,
1973
1976
  to: this.router,
1974
1977
  data,
1975
1978
  value: 0n,
1976
- gas_estimate: 4e5
1979
+ gas_estimate: 4e5,
1980
+ approvals
1977
1981
  };
1978
1982
  }
1979
1983
  async buildRemoveLiquidity(params) {
@@ -7333,6 +7337,13 @@ var Executor = class _Executor {
7333
7337
  static applyGasBuffer(gas) {
7334
7338
  return gas * GAS_BUFFER_BPS / 10000n;
7335
7339
  }
7340
+ /**
7341
+ * EIP-1559 max-fee formula: `baseFee * 1.25 + priorityFee`.
7342
+ * Extracted as a static so the math is unit-testable without mocking viem.
7343
+ */
7344
+ static computeMaxFee(baseFeePerGas, priorityFee) {
7345
+ return baseFeePerGas * 125n / 100n + priorityFee;
7346
+ }
7336
7347
  /**
7337
7348
  * Check allowance for a single token/spender pair and send an approve tx if needed.
7338
7349
  * Only called in broadcast mode (not dry-run).
@@ -7453,8 +7464,7 @@ var Executor = class _Executor {
7453
7464
  try {
7454
7465
  const block = await client.getBlock({ blockTag: "latest" });
7455
7466
  if (block.baseFeePerGas !== null && block.baseFeePerGas !== void 0) {
7456
- const maxFee = block.baseFeePerGas * 125n / 100n + priorityFee;
7457
- return [maxFee, priorityFee];
7467
+ return [_Executor.computeMaxFee(block.baseFeePerGas, priorityFee), priorityFee];
7458
7468
  }
7459
7469
  } catch {
7460
7470
  }
@@ -8276,7 +8286,8 @@ function handleSchema(params) {
8276
8286
  function registerSchema(parent, getOpts) {
8277
8287
  parent.command("schema [command]").description("Output JSON schema for a command (agent-friendly)").option("--all", "Show all schemas").action(async (command, opts) => {
8278
8288
  const mode = getOpts();
8279
- const action = opts.all ? "all" : command ?? "all";
8289
+ const raw = opts.all ? "all" : command ?? "all";
8290
+ const action = raw.replace(/-/g, ".");
8280
8291
  const params = { action };
8281
8292
  const schema = handleSchema(params);
8282
8293
  printOutput(schema, mode);
@@ -8467,6 +8478,11 @@ function buildPipelineSteps(p, input = {}) {
8467
8478
  ["--protocol", slug, "slug"],
8468
8479
  ["--gauge", input.gauge, "gauge-from-voter.gaugeForPool"]
8469
8480
  ]);
8481
+ const claimAutoStakeNftGauge = () => `defi ${chainFlag}lp claim ` + buildCmd([
8482
+ ["--protocol", slug, "slug"],
8483
+ ["--gauge", input.gauge, "gauge-from-voter.gaugeForPool"],
8484
+ ["--token-id", input.tokenId, "token-id-from-mint-result"]
8485
+ ]);
8470
8486
  switch (p.reward_strategy) {
8471
8487
  case "lp_fee_only":
8472
8488
  return [
@@ -8491,11 +8507,18 @@ function buildPipelineSteps(p, input = {}) {
8491
8507
  { step: "stake", function: "gauge.deposit(amount)", cli_command: baseFarm },
8492
8508
  { step: "claim", function: "gauge.earned(token, account) \u2192 gauge.getReward(account, tokens[])", cli_command: claimWithGauge() }
8493
8509
  ];
8494
- case "auto_stake":
8510
+ case "auto_stake": {
8511
+ const isNftAutoStake = p.interface === "uniswap_v3";
8495
8512
  return [
8496
8513
  { step: "mint", function: "Router.addLiquidity / NPM.mint", note: "LP automatically receives x(3,3) emissions \u2014 no separate stake step", cli_command: baseAdd },
8497
- { step: "claim", function: "gauge.getReward(account, tokens[])", note: "Multi-token reward (xRAM + WHYPE on Ramses HL)", cli_command: claimWithGauge() }
8514
+ {
8515
+ step: "claim",
8516
+ function: isNftAutoStake ? "NPM.getPeriodReward(currentEpoch, tokenId, tokens[], receiver)" : "gauge.getReward(account, tokens[])",
8517
+ note: isNftAutoStake ? "Ramses CL: claim via NPM with --token-id; gauge.getReward* reverts NOT_AUTHORIZED_CLAIMER for EOAs" : "Multi-token reward (xRAM + WHYPE on Ramses HL)",
8518
+ cli_command: isNftAutoStake ? claimAutoStakeNftGauge() : claimWithGauge()
8519
+ }
8498
8520
  ];
8521
+ }
8499
8522
  case "on_chain_masterchef":
8500
8523
  return [
8501
8524
  { step: "mint", function: "NPM.mint or pool.mint", cli_command: baseAdd },
@@ -9206,7 +9229,7 @@ function registerLP(parent, getOpts, makeExecutor2) {
9206
9229
  note: "Plan output. Run each cli_command sequentially. After the mint step, broadcast mode prints `details.minted_token_id` \u2014 feed that into the next step's --token-id."
9207
9230
  }, getOpts());
9208
9231
  });
9209
- lp.command("remove").description("Auto-unstake (if staked) and remove liquidity from a pool").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--token-a <token>", "First token symbol or address").requiredOption("--token-b <token>", "Second token symbol or address").requiredOption("--liquidity <amount>", "Liquidity amount to remove in wei").option("--pool <address>", "Pool address (needed to resolve gauge)").option("--gauge <address>", "Gauge contract address (for solidly/hybra unstake)").option("--token-id <id>", "NFT tokenId (for CL gauge or farming positions)").option("--recipient <address>", "Recipient address").option("--redeem-type <n>", "Hybra: 0=instant exit (with penalty), 1=lock into 2-year veHYBR (default \u2014 WARNING: long lock)").option("--bins <binIds>", "Merchant Moe LB: comma-separated bin IDs to withdraw").option("--amounts <wei>", "Merchant Moe LB: comma-separated bin amounts (parallel to --bins, default: full balance)").action(async (opts) => {
9232
+ lp.command("remove").description("Auto-unstake (if staked) and remove liquidity from a pool").requiredOption("--protocol <protocol>", "Protocol slug").option("--token-a <token>", "First token symbol or address (required for V2/Curve/LB)").option("--token-b <token>", "Second token symbol or address (required for V2/Curve/LB)").option("--liquidity <amount>", "Liquidity amount to remove in wei (required for V2/Curve/LB)").option("--pool <address>", "Pool address (needed to resolve gauge)").option("--gauge <address>", "Gauge contract address (for solidly/hybra unstake)").option("--token-id <id>", "NFT tokenId (for CL gauge or farming positions)").option("--recipient <address>", "Recipient address").option("--redeem-type <n>", "Hybra: 0=instant exit (with penalty), 1=lock into 2-year veHYBR (default \u2014 WARNING: long lock)").option("--bins <binIds>", "Merchant Moe LB: comma-separated bin IDs to withdraw").option("--amounts <wei>", "Merchant Moe LB: comma-separated bin amounts (parallel to --bins, default: full balance)").action(async (opts) => {
9210
9233
  const executor = makeExecutor2();
9211
9234
  const chainName = parent.opts().chain;
9212
9235
  if (!chainName) {
@@ -9222,6 +9245,9 @@ function registerLP(parent, getOpts, makeExecutor2) {
9222
9245
  if (iface === "uniswap_v2" && protocol.contracts?.["lb_factory"]) {
9223
9246
  if (!opts.pool) throw new Error(`--pool is required for ${protocol.name} (Liquidity Book \u2014 pass --pool <addr>)`);
9224
9247
  if (!opts.bins) throw new Error("--bins <id1,id2,...> is required for Merchant Moe LB remove");
9248
+ if (!opts.tokenA || !opts.tokenB) {
9249
+ throw new Error(`--token-a and --token-b are required for ${protocol.name} (Liquidity Book) remove`);
9250
+ }
9225
9251
  const lbAdapter = createMerchantMoeLB(protocol, rpcUrl);
9226
9252
  const tokenA2 = opts.tokenA.startsWith("0x") ? opts.tokenA : registry.resolveToken(chainName, opts.tokenA).address;
9227
9253
  const tokenB2 = opts.tokenB.startsWith("0x") ? opts.tokenB : registry.resolveToken(chainName, opts.tokenB).address;
@@ -9264,8 +9290,22 @@ function registerLP(parent, getOpts, makeExecutor2) {
9264
9290
  printOutput({ step: "lb_remove", ...result }, getOpts());
9265
9291
  return;
9266
9292
  }
9267
- const tokenA = opts.tokenA.startsWith("0x") ? opts.tokenA : registry.resolveToken(chainName, opts.tokenA).address;
9268
- const tokenB = opts.tokenB.startsWith("0x") ? opts.tokenB : registry.resolveToken(chainName, opts.tokenB).address;
9293
+ const NFT_REMOVE_IFACES = /* @__PURE__ */ new Set(["uniswap_v3", "algebra_v3", "thena_cl", "hybra"]);
9294
+ const isNftRemove = !!opts.tokenId && NFT_REMOVE_IFACES.has(iface);
9295
+ if (!isNftRemove) {
9296
+ const missing = [];
9297
+ if (!opts.tokenA) missing.push("--token-a");
9298
+ if (!opts.tokenB) missing.push("--token-b");
9299
+ if (!opts.liquidity) missing.push("--liquidity");
9300
+ if (missing.length > 0) {
9301
+ printOutput({
9302
+ error: `${missing.join(", ")} required for ${protocol.name} remove (or pass --token-id for V3/CL NFT-based remove).`
9303
+ }, getOpts());
9304
+ return;
9305
+ }
9306
+ }
9307
+ const tokenA = opts.tokenA ? opts.tokenA.startsWith("0x") ? opts.tokenA : registry.resolveToken(chainName, opts.tokenA).address : void 0;
9308
+ const tokenB = opts.tokenB ? opts.tokenB.startsWith("0x") ? opts.tokenB : registry.resolveToken(chainName, opts.tokenB).address : void 0;
9269
9309
  const poolAddr = opts.pool ? opts.pool : void 0;
9270
9310
  let didUnstake = false;
9271
9311
  if (iface === "algebra_v3" && protocol.contracts?.["farming_center"] && opts.tokenId && poolAddr) {
@@ -9315,7 +9355,7 @@ function registerLP(parent, getOpts, makeExecutor2) {
9315
9355
  if (iface === "hybra" && (!wOpts || wOpts.redeemType === 1)) {
9316
9356
  process.stderr.write("WARNING: Hybra default redeemType=1 locks rewards into 2-year veHYBR NFT. Pass --redeem-type 0 for instant exit (with penalty).\n");
9317
9357
  }
9318
- const withdrawTx = await gaugeAdapter.buildWithdraw(gaugeAddr, BigInt(opts.liquidity), tokenId, wOpts);
9358
+ const withdrawTx = await gaugeAdapter.buildWithdraw(gaugeAddr, opts.liquidity ? BigInt(opts.liquidity) : 0n, tokenId, wOpts);
9319
9359
  const withdrawResult = await executor.execute(withdrawTx);
9320
9360
  printOutput({ step: "unstake_gauge", ...withdrawResult }, getOpts());
9321
9361
  if (withdrawResult.status !== "confirmed" && withdrawResult.status !== "simulated") {
@@ -9330,11 +9370,31 @@ function registerLP(parent, getOpts, makeExecutor2) {
9330
9370
  }
9331
9371
  process.stderr.write("Step 2/2: Removing liquidity...\n");
9332
9372
  const dexAdapter = createDex(protocol, rpcUrl);
9373
+ let removeLiquidity = opts.liquidity ? BigInt(opts.liquidity) : 0n;
9374
+ if (isNftRemove && removeLiquidity === 0n) {
9375
+ const npm = protocol.contracts?.["position_manager"];
9376
+ if (npm) {
9377
+ const c = createPublicClient23({ transport: http23(rpcUrl) });
9378
+ const pos = await detectV3Liquidity(c, npm, BigInt(opts.tokenId));
9379
+ if (pos) {
9380
+ removeLiquidity = pos.liquidity;
9381
+ process.stderr.write(` Read live liquidity ${removeLiquidity} from NPM.positions(${opts.tokenId}).
9382
+ `);
9383
+ }
9384
+ }
9385
+ }
9386
+ if (isNftRemove && removeLiquidity === 0n) {
9387
+ printOutput({
9388
+ error: `tokenId ${opts.tokenId} has zero liquidity (already removed?). Pass --liquidity explicitly to override, or pick a different tokenId.`
9389
+ }, getOpts());
9390
+ return;
9391
+ }
9392
+ const ZERO = "0x0000000000000000000000000000000000000000";
9333
9393
  const removeTx = await dexAdapter.buildRemoveLiquidity({
9334
9394
  protocol: protocol.name,
9335
- token_a: tokenA,
9336
- token_b: tokenB,
9337
- liquidity: BigInt(opts.liquidity),
9395
+ token_a: tokenA ?? ZERO,
9396
+ token_b: tokenB ?? ZERO,
9397
+ liquidity: removeLiquidity,
9338
9398
  recipient,
9339
9399
  token_id: opts.tokenId ? BigInt(opts.tokenId) : void 0
9340
9400
  });
@@ -9713,12 +9773,20 @@ function resolveTokenAddress(registry, chainName, tokenOrAddress) {
9713
9773
  return registry.resolveToken(chainName, tokenOrAddress).address;
9714
9774
  }
9715
9775
  var FALLBACK_ADDRESS = "0x0000000000000000000000000000000000000001";
9776
+ var warnedFallback = false;
9716
9777
  function resolveWallet(override) {
9717
9778
  if (override) return override;
9718
9779
  try {
9719
9780
  const { address } = resolveWalletWithSigner();
9720
9781
  return address;
9721
9782
  } catch {
9783
+ if (!warnedFallback) {
9784
+ process.stderr.write(
9785
+ `WARNING: no wallet configured (set DEFI_WALLET_ADDRESS or DEFI_PRIVATE_KEY, or use --wallet <name>). Using placeholder ${FALLBACK_ADDRESS} for dry-run preview ONLY \u2014 do NOT pass --broadcast with this address.
9786
+ `
9787
+ );
9788
+ warnedFallback = true;
9789
+ }
9722
9790
  return FALLBACK_ADDRESS;
9723
9791
  }
9724
9792
  }
@@ -9821,7 +9889,8 @@ function resolveAsset(registry, chain, asset) {
9821
9889
  }
9822
9890
  async function collectLendingRates(registry, chainName, rpc, assetAddr) {
9823
9891
  const protos = registry.getProtocolsForChain(chainName).filter((p) => p.category === ProtocolCategory.Lending);
9824
- const results = [];
9892
+ const rates = [];
9893
+ const errors = [];
9825
9894
  let first = true;
9826
9895
  for (const proto of protos) {
9827
9896
  if (!first) {
@@ -9830,19 +9899,22 @@ async function collectLendingRates(registry, chainName, rpc, assetAddr) {
9830
9899
  first = false;
9831
9900
  try {
9832
9901
  const lending = createLending(proto, rpc);
9833
- const rates = await lending.getRates(assetAddr);
9834
- results.push(rates);
9902
+ const r = await lending.getRates(assetAddr);
9903
+ rates.push(r);
9835
9904
  } catch (err) {
9836
9905
  process.stderr.write(`Warning: ${proto.name} rates unavailable: ${err}
9837
9906
  `);
9907
+ errors.push({ protocol: proto.name, type: "lending_supply", reason: errMsg(err) });
9838
9908
  }
9839
9909
  }
9840
- return results;
9910
+ return { rates, errors };
9841
9911
  }
9842
9912
  async function collectAllYields(registry, chainName, rpc, asset, assetAddr) {
9843
9913
  const opportunities = [];
9844
- const lendingRates = await collectLendingRates(registry, chainName, rpc, assetAddr);
9845
- for (const r of lendingRates) {
9914
+ const errors = [];
9915
+ const lendingResult = await collectLendingRates(registry, chainName, rpc, assetAddr);
9916
+ errors.push(...lendingResult.errors);
9917
+ for (const r of lendingResult.rates) {
9846
9918
  if (r.supply_apy > 0) {
9847
9919
  opportunities.push({
9848
9920
  protocol: r.protocol,
@@ -9868,7 +9940,8 @@ async function collectAllYields(registry, chainName, rpc, asset, assetAddr) {
9868
9940
  utilization: rates.utilization
9869
9941
  });
9870
9942
  }
9871
- } catch {
9943
+ } catch (e) {
9944
+ errors.push({ protocol: proto.name, type: "morpho_vault", reason: errMsg(e) });
9872
9945
  }
9873
9946
  }
9874
9947
  }
@@ -9884,7 +9957,8 @@ async function collectAllYields(registry, chainName, rpc, asset, assetAddr) {
9884
9957
  apy: info.apy ?? 0,
9885
9958
  total_assets: info.total_assets.toString()
9886
9959
  });
9887
- } catch {
9960
+ } catch (e) {
9961
+ errors.push({ protocol: proto.name, type: "vault", reason: errMsg(e) });
9888
9962
  }
9889
9963
  }
9890
9964
  }
@@ -9893,7 +9967,7 @@ async function collectAllYields(registry, chainName, rpc, asset, assetAddr) {
9893
9967
  const ba = b["apy"] ?? 0;
9894
9968
  return ba - aa;
9895
9969
  });
9896
- return opportunities;
9970
+ return { opportunities, errors };
9897
9971
  }
9898
9972
  async function runYieldScan(registry, asset, output) {
9899
9973
  const t0 = Date.now();
@@ -10035,10 +10109,13 @@ function registerYield(parent, getOpts, makeExecutor2) {
10035
10109
  const chain = registry.getChain(chainName);
10036
10110
  const rpc = chain.effectiveRpcUrl();
10037
10111
  const assetAddr = resolveAsset(registry, chainName, opts.asset);
10038
- const results = await collectLendingRates(registry, chainName, rpc, assetAddr);
10112
+ const { rates: results, errors: ratesErrors } = await collectLendingRates(registry, chainName, rpc, assetAddr);
10039
10113
  if (results.length === 0) {
10040
10114
  printOutput(
10041
- { error: `No lending rate data available for asset '${opts.asset}'` },
10115
+ ratesErrors.length > 0 ? {
10116
+ error: `Could not collect lending rates for '${opts.asset}': ${ratesErrors.length} probe(s) failed (likely RPC throttling). Retry, or set ${chainName.toUpperCase()}_RPC_URL.`,
10117
+ failed_probes: ratesErrors
10118
+ } : { error: `No lending rate data available for asset '${opts.asset}'` },
10042
10119
  getOpts()
10043
10120
  );
10044
10121
  process.exit(1);
@@ -10280,9 +10357,16 @@ function registerYield(parent, getOpts, makeExecutor2) {
10280
10357
  const assetAddr = resolveAsset(registry, chainName, asset);
10281
10358
  const strategy = opts.strategy ?? "auto";
10282
10359
  if (strategy === "auto") {
10283
- const opportunities = await collectAllYields(registry, chainName, rpc, asset, assetAddr);
10360
+ const { opportunities, errors } = await collectAllYields(registry, chainName, rpc, asset, assetAddr);
10284
10361
  if (opportunities.length === 0) {
10285
- printOutput({ error: `No yield opportunities found for '${asset}'` }, getOpts());
10362
+ if (errors.length > 0) {
10363
+ printOutput({
10364
+ error: `Could not collect yield data for '${asset}': ${errors.length} probe(s) failed (likely RPC throttling or transport error). Retry, or set a private RPC URL via ${chainName.toUpperCase()}_RPC_URL.`,
10365
+ failed_probes: errors
10366
+ }, getOpts());
10367
+ } else {
10368
+ printOutput({ error: `No yield opportunities found for '${asset}'` }, getOpts());
10369
+ }
10286
10370
  process.exit(1);
10287
10371
  return;
10288
10372
  }
@@ -10312,9 +10396,12 @@ function registerYield(parent, getOpts, makeExecutor2) {
10312
10396
  getOpts()
10313
10397
  );
10314
10398
  } else if (strategy === "best-supply") {
10315
- const results = await collectLendingRates(registry, chainName, rpc, assetAddr);
10399
+ const { rates: results, errors: rErr } = await collectLendingRates(registry, chainName, rpc, assetAddr);
10316
10400
  if (results.length === 0) {
10317
- printOutput({ error: `No lending rate data available for asset '${asset}'` }, getOpts());
10401
+ printOutput(
10402
+ rErr.length > 0 ? { error: `Could not collect lending rates for '${asset}': ${rErr.length} probe(s) failed (RPC throttling likely).`, failed_probes: rErr } : { error: `No lending rate data available for asset '${asset}'` },
10403
+ getOpts()
10404
+ );
10318
10405
  process.exit(1);
10319
10406
  return;
10320
10407
  }
@@ -10337,9 +10424,12 @@ function registerYield(parent, getOpts, makeExecutor2) {
10337
10424
  getOpts()
10338
10425
  );
10339
10426
  } else if (strategy === "leverage-loop") {
10340
- const results = await collectLendingRates(registry, chainName, rpc, assetAddr);
10427
+ const { rates: results, errors: lErr } = await collectLendingRates(registry, chainName, rpc, assetAddr);
10341
10428
  if (results.length === 0) {
10342
- printOutput({ error: `No lending rate data available for asset '${asset}'` }, getOpts());
10429
+ printOutput(
10430
+ lErr.length > 0 ? { error: `Could not collect lending rates for '${asset}': ${lErr.length} probe(s) failed (RPC throttling likely).`, failed_probes: lErr } : { error: `No lending rate data available for asset '${asset}'` },
10431
+ getOpts()
10432
+ );
10343
10433
  process.exit(1);
10344
10434
  return;
10345
10435
  }
@@ -10403,14 +10493,14 @@ function registerYield(parent, getOpts, makeExecutor2) {
10403
10493
 
10404
10494
  // src/commands/portfolio.ts
10405
10495
  init_dist();
10406
- import { encodeFunctionData as encodeFunctionData29, parseAbi as parseAbi33 } from "viem";
10496
+ import { createPublicClient as createPublicClient25, encodeFunctionData as encodeFunctionData29, http as http25, parseAbi as parseAbi33 } from "viem";
10407
10497
 
10408
10498
  // src/portfolio-tracker.ts
10409
10499
  init_dist();
10410
10500
  import { mkdirSync, writeFileSync, readdirSync as readdirSync2, readFileSync as readFileSync3, existsSync as existsSync2 } from "fs";
10411
10501
  import { homedir } from "os";
10412
10502
  import { resolve as resolve3 } from "path";
10413
- import { encodeFunctionData as encodeFunctionData28, parseAbi as parseAbi31 } from "viem";
10503
+ import { createPublicClient as createPublicClient24, encodeFunctionData as encodeFunctionData28, http as http24, parseAbi as parseAbi31 } from "viem";
10414
10504
  var ERC20_ABI4 = parseAbi31([
10415
10505
  "function balanceOf(address owner) external view returns (uint256)"
10416
10506
  ]);
@@ -10461,7 +10551,16 @@ async function takeSnapshot(chainName, wallet, registry) {
10461
10551
  const oracleEntry = registry.getProtocolsForChain(chainName).find((p) => p.interface === "aave_v3" && p.contracts?.["oracle"]);
10462
10552
  const oracleAddr = oracleEntry?.contracts?.["oracle"];
10463
10553
  const wrappedNative = chain.wrapped_native ?? "0x5555555555555555555555555555555555555555";
10554
+ const priceTokens = [];
10464
10555
  if (oracleAddr) {
10556
+ for (const t of tokenEntries) {
10557
+ calls.push([
10558
+ oracleAddr,
10559
+ encodeFunctionData28({ abi: ORACLE_ABI4, functionName: "getAssetPrice", args: [t.address] })
10560
+ ]);
10561
+ callLabels.push(`price:${t.address}`);
10562
+ priceTokens.push(t.address);
10563
+ }
10465
10564
  calls.push([
10466
10565
  oracleAddr,
10467
10566
  encodeFunctionData28({ abi: ORACLE_ABI4, functionName: "getAssetPrice", args: [wrappedNative] })
@@ -10472,10 +10571,19 @@ async function takeSnapshot(chainName, wallet, registry) {
10472
10571
  if (calls.length > 0) {
10473
10572
  results = await multicallRead(rpc, calls);
10474
10573
  }
10574
+ const balanceCount = tokenEntries.length;
10575
+ const lendingCount = lendingProtocols.length;
10576
+ const priceStartIdx = balanceCount + lendingCount;
10475
10577
  let nativePriceUsd = 0;
10578
+ const priceByToken = /* @__PURE__ */ new Map();
10476
10579
  if (oracleAddr) {
10477
- const priceData = results[results.length - 1] ?? null;
10478
- nativePriceUsd = Number(decodeU256Word(priceData)) / 1e8;
10580
+ for (let i = 0; i < priceTokens.length; i++) {
10581
+ const priceData = results[priceStartIdx + i] ?? null;
10582
+ const px = Number(decodeU256Word(priceData)) / 1e8;
10583
+ if (px > 0) priceByToken.set(priceTokens[i].toLowerCase(), px);
10584
+ }
10585
+ const nativePriceData = results[priceStartIdx + priceTokens.length] ?? null;
10586
+ nativePriceUsd = Number(decodeU256Word(nativePriceData)) / 1e8;
10479
10587
  }
10480
10588
  let idx = 0;
10481
10589
  const tokens = [];
@@ -10485,8 +10593,20 @@ async function takeSnapshot(chainName, wallet, registry) {
10485
10593
  const balance = decodeU256Word(results[idx] ?? null);
10486
10594
  const balF64 = Number(balance) / 10 ** entry.decimals;
10487
10595
  const symbolUpper = entry.symbol.toUpperCase();
10488
- const priceUsd = symbolUpper.includes("USD") ? 1 : nativePriceUsd;
10489
- const valueUsd = balF64 * priceUsd;
10596
+ const tokenAddrLower = entry.address.toLowerCase();
10597
+ let priceUsd;
10598
+ let valueUsd;
10599
+ if (symbolUpper.includes("USD")) {
10600
+ priceUsd = 1;
10601
+ valueUsd = balF64;
10602
+ } else if (tokenAddrLower === wrappedNative.toLowerCase()) {
10603
+ priceUsd = nativePriceUsd;
10604
+ valueUsd = balF64 * nativePriceUsd;
10605
+ } else {
10606
+ const px = priceByToken.get(tokenAddrLower);
10607
+ priceUsd = px ?? 0;
10608
+ valueUsd = px && px > 0 ? balF64 * px : 0;
10609
+ }
10490
10610
  totalValueUsd += valueUsd;
10491
10611
  tokens.push({
10492
10612
  token: entry.address,
@@ -10527,6 +10647,23 @@ async function takeSnapshot(chainName, wallet, registry) {
10527
10647
  }
10528
10648
  idx++;
10529
10649
  }
10650
+ try {
10651
+ const client = createPublicClient24({ transport: http24(rpc) });
10652
+ const nativeBalance = await client.getBalance({ address: user });
10653
+ if (nativeBalance > 0n) {
10654
+ const nativeF64 = Number(nativeBalance) / 1e18;
10655
+ const nativeValueUsd = nativeF64 * nativePriceUsd;
10656
+ totalValueUsd += nativeValueUsd;
10657
+ tokens.push({
10658
+ token: wrappedNative,
10659
+ symbol: chain.native_token ?? "NATIVE",
10660
+ balance: nativeBalance,
10661
+ value_usd: nativeValueUsd,
10662
+ price_usd: nativePriceUsd
10663
+ });
10664
+ }
10665
+ } catch {
10666
+ }
10530
10667
  return {
10531
10668
  timestamp: Date.now(),
10532
10669
  chain: chainName,
@@ -10644,6 +10781,10 @@ function registerPortfolio(parent, getOpts) {
10644
10781
  const calls = [];
10645
10782
  const callLabels = [];
10646
10783
  const tokenSymbols = (registry.tokens.get(chainName) ?? []).map((t) => t.symbol);
10784
+ const tokenAddrsByCallIdx = [];
10785
+ const oracleEntry = registry.getProtocolsForChain(chainName).find((p) => p.interface === "aave_v3" && p.contracts?.["oracle"]);
10786
+ const oracleAddr = oracleEntry?.contracts?.["oracle"];
10787
+ const wrappedNative = chain.wrapped_native ?? "0x5555555555555555555555555555555555555555";
10647
10788
  for (const symbol of tokenSymbols) {
10648
10789
  let entry;
10649
10790
  try {
@@ -10657,6 +10798,7 @@ function registerPortfolio(parent, getOpts) {
10657
10798
  encodeFunctionData29({ abi: ERC20_ABI5, functionName: "balanceOf", args: [user] })
10658
10799
  ]);
10659
10800
  callLabels.push(`balance:${symbol}`);
10801
+ tokenAddrsByCallIdx.push(entry.address);
10660
10802
  }
10661
10803
  const lendingProtocols = registry.getProtocolsForChain(chainName).filter((p) => p.category === ProtocolCategory.Lending && p.interface === "aave_v3").filter((p) => p.contracts?.["pool"]);
10662
10804
  for (const p of lendingProtocols) {
@@ -10666,10 +10808,16 @@ function registerPortfolio(parent, getOpts) {
10666
10808
  ]);
10667
10809
  callLabels.push(`lending:${p.name}`);
10668
10810
  }
10669
- const oracleEntry = registry.getProtocolsForChain(chainName).find((p) => p.interface === "aave_v3" && p.contracts?.["oracle"]);
10670
- const oracleAddr = oracleEntry?.contracts?.["oracle"];
10671
- const wrappedNative = chain.wrapped_native ?? "0x5555555555555555555555555555555555555555";
10811
+ const priceTokens = [];
10672
10812
  if (oracleAddr) {
10813
+ for (const tokenAddr of tokenAddrsByCallIdx) {
10814
+ calls.push([
10815
+ oracleAddr,
10816
+ encodeFunctionData29({ abi: ORACLE_ABI5, functionName: "getAssetPrice", args: [tokenAddr] })
10817
+ ]);
10818
+ callLabels.push(`price:${tokenAddr}`);
10819
+ priceTokens.push(tokenAddr);
10820
+ }
10673
10821
  calls.push([
10674
10822
  oracleAddr,
10675
10823
  encodeFunctionData29({ abi: ORACLE_ABI5, functionName: "getAssetPrice", args: [wrappedNative] })
@@ -10694,10 +10842,19 @@ function registerPortfolio(parent, getOpts) {
10694
10842
  printOutput({ error: `Multicall failed: ${errMsg(e)}` }, mode);
10695
10843
  return;
10696
10844
  }
10845
+ const balanceCallCount = tokenAddrsByCallIdx.length;
10846
+ const lendingCallCount = lendingProtocols.length;
10847
+ const priceStartIdx = balanceCallCount + lendingCallCount;
10697
10848
  let nativePriceUsd = 0;
10849
+ const priceByToken = /* @__PURE__ */ new Map();
10698
10850
  if (oracleAddr) {
10699
- const priceData = results[results.length - 1] ?? null;
10700
- nativePriceUsd = Number(decodeU2562(priceData)) / 1e8;
10851
+ for (let i = 0; i < priceTokens.length; i++) {
10852
+ const priceData = results[priceStartIdx + i] ?? null;
10853
+ const px = Number(decodeU2562(priceData)) / 1e8;
10854
+ if (px > 0) priceByToken.set(priceTokens[i].toLowerCase(), px);
10855
+ }
10856
+ const nativePriceData = results[priceStartIdx + priceTokens.length] ?? null;
10857
+ nativePriceUsd = Number(decodeU2562(nativePriceData)) / 1e8;
10701
10858
  }
10702
10859
  let totalValueUsd = 0;
10703
10860
  let idx = 0;
@@ -10716,12 +10873,21 @@ function registerPortfolio(parent, getOpts) {
10716
10873
  const decimals = entry.decimals;
10717
10874
  const balF64 = Number(balance) / 10 ** decimals;
10718
10875
  const symbolUpper = symbol.toUpperCase();
10719
- const valueUsd = symbolUpper.includes("USD") || symbolUpper.includes("usd") ? balF64 : balF64 * nativePriceUsd;
10720
- totalValueUsd += valueUsd;
10876
+ const tokenAddrLower = entry.address.toLowerCase();
10877
+ let valueUsd;
10878
+ if (symbolUpper.includes("USD")) {
10879
+ valueUsd = balF64;
10880
+ } else if (tokenAddrLower === wrappedNative.toLowerCase()) {
10881
+ valueUsd = balF64 * nativePriceUsd;
10882
+ } else {
10883
+ const px = priceByToken.get(tokenAddrLower);
10884
+ valueUsd = px && px > 0 ? balF64 * px : null;
10885
+ }
10886
+ if (valueUsd !== null) totalValueUsd += valueUsd;
10721
10887
  tokenBalances.push({
10722
10888
  symbol,
10723
10889
  balance: balF64.toFixed(4),
10724
- value_usd: valueUsd.toFixed(2)
10890
+ value_usd: valueUsd !== null ? valueUsd.toFixed(2) : null
10725
10891
  });
10726
10892
  }
10727
10893
  idx++;
@@ -10751,11 +10917,23 @@ function registerPortfolio(parent, getOpts) {
10751
10917
  }
10752
10918
  idx++;
10753
10919
  }
10920
+ let nativeBalance = 0n;
10921
+ let nativeValueUsd = 0;
10922
+ try {
10923
+ const client = createPublicClient25({ transport: http25(rpc) });
10924
+ nativeBalance = await client.getBalance({ address: user });
10925
+ const nativeF64 = Number(nativeBalance) / 1e18;
10926
+ nativeValueUsd = nativeF64 * nativePriceUsd;
10927
+ if (nativeBalance > 0n) totalValueUsd += nativeValueUsd;
10928
+ } catch {
10929
+ }
10754
10930
  printOutput(
10755
10931
  {
10756
10932
  address: user,
10757
10933
  chain: chain.name,
10758
10934
  native_price_usd: nativePriceUsd.toFixed(2),
10935
+ native_balance: (Number(nativeBalance) / 1e18).toFixed(6),
10936
+ native_value_usd: nativeValueUsd.toFixed(2),
10759
10937
  total_value_usd: totalValueUsd.toFixed(2),
10760
10938
  token_balances: tokenBalances,
10761
10939
  lending_positions: lendingPositions
@@ -11021,38 +11199,50 @@ function registerPrice(parent, getOpts) {
11021
11199
 
11022
11200
  // src/commands/wallet.ts
11023
11201
  init_dist();
11024
- import { createPublicClient as createPublicClient24, http as http24, formatEther } from "viem";
11202
+ import { createPublicClient as createPublicClient26, http as http26, formatEther } from "viem";
11203
+ function resolveCurrentAddress(override) {
11204
+ if (override) return { address: override, source: "flag" };
11205
+ try {
11206
+ const { address, signer } = resolveWalletWithSigner();
11207
+ return { address, source: signer ? "ows" : process.env["DEFI_PRIVATE_KEY"] ? "private_key" : "env" };
11208
+ } catch {
11209
+ return { address: null, source: "none" };
11210
+ }
11211
+ }
11025
11212
  function registerWallet(parent, getOpts) {
11026
11213
  const wallet = parent.command("wallet").description("Wallet management");
11027
- wallet.command("balance").description("Show native token balance").option("--address <address>", "Wallet address (defaults to DEFI_WALLET_ADDRESS)").action(async (opts) => {
11214
+ wallet.command("balance").description("Show native token balance").option("--address <address>", "Wallet address (defaults to OWS vault, DEFI_PRIVATE_KEY, or DEFI_WALLET_ADDRESS)").action(async (opts) => {
11028
11215
  const chainName = requireChain(parent, getOpts);
11029
11216
  if (!chainName) return;
11030
- const addr = opts.address ?? process.env["DEFI_WALLET_ADDRESS"];
11217
+ const { address: addr, source } = resolveCurrentAddress(opts.address);
11031
11218
  if (!addr) {
11032
- printOutput({ error: "--address required (or set DEFI_WALLET_ADDRESS)" }, getOpts());
11219
+ printOutput({
11220
+ error: "No wallet configured. Set DEFI_WALLET_ADDRESS, set DEFI_PRIVATE_KEY, or pass --address."
11221
+ }, getOpts());
11033
11222
  return;
11034
11223
  }
11035
11224
  const registry = Registry.loadEmbedded();
11036
11225
  const chain = registry.getChain(chainName);
11037
- const client = createPublicClient24({ transport: http24(chain.effectiveRpcUrl()) });
11226
+ const client = createPublicClient26({ transport: http26(chain.effectiveRpcUrl()) });
11038
11227
  const balance = await client.getBalance({ address: addr });
11039
11228
  printOutput({
11040
11229
  chain: chain.name,
11041
11230
  address: addr,
11231
+ wallet_source: source,
11042
11232
  native_token: chain.native_token,
11043
11233
  balance_wei: balance,
11044
11234
  balance_formatted: formatEther(balance)
11045
11235
  }, getOpts());
11046
11236
  });
11047
11237
  wallet.command("address").description("Show configured wallet address").action(async () => {
11048
- const addr = process.env.DEFI_WALLET_ADDRESS ?? "(not set)";
11049
- printOutput({ address: addr }, getOpts());
11238
+ const { address, source } = resolveCurrentAddress();
11239
+ printOutput({ address, source }, getOpts());
11050
11240
  });
11051
11241
  }
11052
11242
 
11053
11243
  // src/commands/token.ts
11054
11244
  init_dist();
11055
- import { createPublicClient as createPublicClient25, http as http25, maxUint256 } from "viem";
11245
+ import { createPublicClient as createPublicClient27, http as http27, maxUint256 } from "viem";
11056
11246
  function registerToken(parent, getOpts, makeExecutor2) {
11057
11247
  const token = parent.command("token").description("Token operations: approve, allowance, transfer, balance");
11058
11248
  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) => {
@@ -11065,7 +11255,7 @@ function registerToken(parent, getOpts, makeExecutor2) {
11065
11255
  }
11066
11256
  const registry = Registry.loadEmbedded();
11067
11257
  const chain = registry.getChain(chainName);
11068
- const client = createPublicClient25({ transport: http25(chain.effectiveRpcUrl()) });
11258
+ const client = createPublicClient27({ transport: http27(chain.effectiveRpcUrl()) });
11069
11259
  const tokenAddr = resolveTokenAddress(registry, chainName, opts.token);
11070
11260
  const [balance, symbol, decimals] = await Promise.all([
11071
11261
  client.readContract({ address: tokenAddr, abi: erc20Abi, functionName: "balanceOf", args: [owner] }),
@@ -11101,7 +11291,7 @@ function registerToken(parent, getOpts, makeExecutor2) {
11101
11291
  }
11102
11292
  const registry = Registry.loadEmbedded();
11103
11293
  const chain = registry.getChain(chainName);
11104
- const client = createPublicClient25({ transport: http25(chain.effectiveRpcUrl()) });
11294
+ const client = createPublicClient27({ transport: http27(chain.effectiveRpcUrl()) });
11105
11295
  const tokenAddr = resolveTokenAddress(registry, chainName, opts.token);
11106
11296
  const allowance = await client.readContract({
11107
11297
  address: tokenAddr,
@@ -11219,6 +11409,17 @@ var CCTP_USDC_ADDRESSES = {
11219
11409
  base: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
11220
11410
  polygon: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359"
11221
11411
  };
11412
+ function cctpMinFeeGuard(amountWei, maxFeeSubunits, feeUsdc) {
11413
+ if (amountWei <= maxFeeSubunits) {
11414
+ const amountUsdc = Number(amountWei) / 1e6;
11415
+ return {
11416
+ error: `CCTP: amount ${amountWei.toString()} (${amountUsdc} USDC) is below the minimum bridge fee of ${maxFeeSubunits} (${feeUsdc} USDC). Increase --amount.`,
11417
+ minimum_amount_wei: maxFeeSubunits.toString(),
11418
+ minimum_amount_usdc: feeUsdc
11419
+ };
11420
+ }
11421
+ return null;
11422
+ }
11222
11423
  async function getCctpFeeEstimate(srcDomain, dstDomain, amountUsdc) {
11223
11424
  try {
11224
11425
  const res = await fetch(`${CCTP_FEE_API}/${srcDomain}/${dstDomain}`);
@@ -11307,12 +11508,9 @@ function registerBridge(parent, getOpts) {
11307
11508
  }
11308
11509
  const amountUsdc = Number(BigInt(opts.amount)) / 1e6;
11309
11510
  const { fee, maxFeeSubunits } = await getCctpFeeEstimate(srcDomain, dstDomain, amountUsdc);
11310
- if (BigInt(opts.amount) <= maxFeeSubunits) {
11311
- printOutput({
11312
- error: `CCTP: amount ${opts.amount} (${amountUsdc} USDC) is below the minimum bridge fee of ${maxFeeSubunits} (${fee} USDC). Increase --amount.`,
11313
- minimum_amount_wei: maxFeeSubunits.toString(),
11314
- minimum_amount_usdc: fee
11315
- }, getOpts());
11511
+ const guardErr = cctpMinFeeGuard(BigInt(opts.amount), maxFeeSubunits, fee);
11512
+ if (guardErr) {
11513
+ printOutput(guardErr, getOpts());
11316
11514
  return;
11317
11515
  }
11318
11516
  const recipientPadded = `0x${"0".repeat(24)}${recipient.replace("0x", "").toLowerCase()}`;
@@ -11593,12 +11791,14 @@ function registerSwap(parent, getOpts, makeExecutor2) {
11593
11791
  slippagePct,
11594
11792
  wallet
11595
11793
  );
11794
+ const fromLower = fromAddr.toLowerCase();
11795
+ const isNativeInput = fromLower === "0x0000000000000000000000000000000000000000" || fromLower === "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";
11596
11796
  const tx = {
11597
11797
  description: `OpenOcean: swap ${opts.amount} of ${fromAddr} -> ${toAddr}`,
11598
11798
  to: swap.to,
11599
11799
  data: swap.data,
11600
11800
  value: parseBigIntValue(swap.value),
11601
- approvals: [{ token: fromAddr, spender: swap.to, amount: BigInt(opts.amount) }]
11801
+ ...isNativeInput ? {} : { approvals: [{ token: fromAddr, spender: swap.to, amount: BigInt(opts.amount) }] }
11602
11802
  };
11603
11803
  const result = await executor.execute(tx);
11604
11804
  printOutput({
@@ -11690,7 +11890,8 @@ import pc2 from "picocolors";
11690
11890
  import { createInterface } from "readline";
11691
11891
  import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
11692
11892
  import { resolve as resolve4 } from "path";
11693
- var DEFI_DIR = resolve4(process.env.HOME || "~", ".defi");
11893
+ import { homedir as homedir2 } from "os";
11894
+ var DEFI_DIR = resolve4(homedir2(), ".defi");
11694
11895
  var ENV_FILE = resolve4(DEFI_DIR, ".env");
11695
11896
  function ensureDefiDir() {
11696
11897
  if (!existsSync3(DEFI_DIR)) mkdirSync2(DEFI_DIR, { recursive: true, mode: 448 });
@@ -11725,6 +11926,44 @@ function writeEnvFile(env) {
11725
11926
  function ask(rl, question) {
11726
11927
  return new Promise((res) => rl.question(question, (answer) => res(answer.trim())));
11727
11928
  }
11929
+ function askSecret(rl, question) {
11930
+ const stdin = process.stdin;
11931
+ if (!stdin.isTTY) return ask(rl, question);
11932
+ process.stdout.write(question);
11933
+ const rlAny = rl;
11934
+ const original = rlAny._writeToOutput;
11935
+ rlAny._writeToOutput = (s) => {
11936
+ if (s.includes("\n") || s.includes("\r")) {
11937
+ original?.call(rl, s);
11938
+ }
11939
+ };
11940
+ return new Promise((res) => {
11941
+ rl.question("", (answer) => {
11942
+ rlAny._writeToOutput = original;
11943
+ process.stdout.write("\n");
11944
+ res(answer.trim());
11945
+ });
11946
+ });
11947
+ }
11948
+ function maskRpcUrl(s) {
11949
+ try {
11950
+ const u = new URL(s);
11951
+ if (u.pathname && u.pathname !== "/" && u.pathname.length > 1) {
11952
+ return `${u.protocol}//${u.host}/***`;
11953
+ }
11954
+ return `${u.protocol}//${u.host}`;
11955
+ } catch {
11956
+ return "***";
11957
+ }
11958
+ }
11959
+ function isValidRpcUrl(s) {
11960
+ try {
11961
+ const u = new URL(s);
11962
+ return u.protocol === "http:" || u.protocol === "https:";
11963
+ } catch {
11964
+ return false;
11965
+ }
11966
+ }
11728
11967
  function isValidAddress(s) {
11729
11968
  return /^0x[0-9a-fA-F]{40}$/.test(s);
11730
11969
  }
@@ -11749,7 +11988,14 @@ function registerSetup(program2) {
11749
11988
  if (Object.keys(existing).length > 0) {
11750
11989
  console.log(pc2.white(" Current configuration:"));
11751
11990
  for (const [key, value] of Object.entries(existing)) {
11752
- const masked = key.toLowerCase().includes("key") ? value.slice(0, 6) + "..." + value.slice(-4) : value;
11991
+ let masked;
11992
+ if (key.toLowerCase().includes("key")) {
11993
+ masked = value.slice(0, 6) + "..." + value.slice(-4);
11994
+ } else if (key.endsWith("RPC_URL")) {
11995
+ masked = maskRpcUrl(value);
11996
+ } else {
11997
+ masked = value;
11998
+ }
11753
11999
  console.log(` ${pc2.cyan(key.padEnd(24))} ${pc2.gray(masked)}`);
11754
12000
  }
11755
12001
  console.log();
@@ -11763,7 +12009,7 @@ function registerSetup(program2) {
11763
12009
  }
11764
12010
  const newEnv = {};
11765
12011
  console.log(pc2.cyan(pc2.bold(" Wallet")));
11766
- const privateKey = await ask(rl, " Private key (optional, for --broadcast, 0x...): ");
12012
+ const privateKey = await askSecret(rl, " Private key (optional, for --broadcast, 0x... \u2014 input hidden): ");
11767
12013
  if (privateKey) {
11768
12014
  const normalized = privateKey.startsWith("0x") ? privateKey : `0x${privateKey}`;
11769
12015
  if (!isValidPrivateKey(normalized)) {
@@ -11799,6 +12045,10 @@ function registerSetup(program2) {
11799
12045
  for (const { env, label } of rpcPrompts) {
11800
12046
  const value = await ask(rl, ` ${label} RPC URL: `);
11801
12047
  if (value) {
12048
+ if (!isValidRpcUrl(value)) {
12049
+ console.log(pc2.yellow(` Invalid URL (${value}). Skipped \u2014 re-run setup to retry.`));
12050
+ continue;
12051
+ }
11802
12052
  newEnv[env] = value;
11803
12053
  console.log(` ${pc2.green("OK")} ${label} RPC set`);
11804
12054
  }
@@ -11822,7 +12072,7 @@ function registerSetup(program2) {
11822
12072
  ];
11823
12073
  for (const [k, label] of rpcSummary) {
11824
12074
  const v = finalEnv[k];
11825
- if (v) console.log(` ${label}: ${pc2.gray(v)}`);
12075
+ if (v) console.log(` ${label}: ${pc2.gray(maskRpcUrl(v))}`);
11826
12076
  }
11827
12077
  console.log(pc2.bold(pc2.white("\n Next steps:")));
11828
12078
  console.log(` ${pc2.green("defi portfolio")} view balances & positions`);
@@ -11841,11 +12091,11 @@ function registerSetup(program2) {
11841
12091
  // src/commands/ows.ts
11842
12092
  import pc3 from "picocolors";
11843
12093
  init_dist();
11844
- import { createPublicClient as createPublicClient26, http as http26, formatEther as formatEther2 } from "viem";
12094
+ import { createPublicClient as createPublicClient28, http as http28, formatEther as formatEther2 } from "viem";
11845
12095
  async function getEvmBalance(address, chainName) {
11846
12096
  const registry = Registry.loadEmbedded();
11847
12097
  const chain = registry.getChain(chainName);
11848
- const client = createPublicClient26({ transport: http26(chain.effectiveRpcUrl()) });
12098
+ const client = createPublicClient28({ transport: http28(chain.effectiveRpcUrl()) });
11849
12099
  const balance = await client.getBalance({ address });
11850
12100
  return {
11851
12101
  native_token: chain.native_token,