@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/main.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) {
@@ -7337,6 +7341,13 @@ var Executor = class _Executor {
7337
7341
  static applyGasBuffer(gas) {
7338
7342
  return gas * GAS_BUFFER_BPS / 10000n;
7339
7343
  }
7344
+ /**
7345
+ * EIP-1559 max-fee formula: `baseFee * 1.25 + priorityFee`.
7346
+ * Extracted as a static so the math is unit-testable without mocking viem.
7347
+ */
7348
+ static computeMaxFee(baseFeePerGas, priorityFee) {
7349
+ return baseFeePerGas * 125n / 100n + priorityFee;
7350
+ }
7340
7351
  /**
7341
7352
  * Check allowance for a single token/spender pair and send an approve tx if needed.
7342
7353
  * Only called in broadcast mode (not dry-run).
@@ -7457,8 +7468,7 @@ var Executor = class _Executor {
7457
7468
  try {
7458
7469
  const block = await client.getBlock({ blockTag: "latest" });
7459
7470
  if (block.baseFeePerGas !== null && block.baseFeePerGas !== void 0) {
7460
- const maxFee = block.baseFeePerGas * 125n / 100n + priorityFee;
7461
- return [maxFee, priorityFee];
7471
+ return [_Executor.computeMaxFee(block.baseFeePerGas, priorityFee), priorityFee];
7462
7472
  }
7463
7473
  } catch {
7464
7474
  }
@@ -8280,7 +8290,8 @@ function handleSchema(params) {
8280
8290
  function registerSchema(parent, getOpts) {
8281
8291
  parent.command("schema [command]").description("Output JSON schema for a command (agent-friendly)").option("--all", "Show all schemas").action(async (command, opts) => {
8282
8292
  const mode = getOpts();
8283
- const action = opts.all ? "all" : command ?? "all";
8293
+ const raw = opts.all ? "all" : command ?? "all";
8294
+ const action = raw.replace(/-/g, ".");
8284
8295
  const params = { action };
8285
8296
  const schema = handleSchema(params);
8286
8297
  printOutput(schema, mode);
@@ -8471,6 +8482,11 @@ function buildPipelineSteps(p, input = {}) {
8471
8482
  ["--protocol", slug, "slug"],
8472
8483
  ["--gauge", input.gauge, "gauge-from-voter.gaugeForPool"]
8473
8484
  ]);
8485
+ const claimAutoStakeNftGauge = () => `defi ${chainFlag}lp claim ` + buildCmd([
8486
+ ["--protocol", slug, "slug"],
8487
+ ["--gauge", input.gauge, "gauge-from-voter.gaugeForPool"],
8488
+ ["--token-id", input.tokenId, "token-id-from-mint-result"]
8489
+ ]);
8474
8490
  switch (p.reward_strategy) {
8475
8491
  case "lp_fee_only":
8476
8492
  return [
@@ -8495,11 +8511,18 @@ function buildPipelineSteps(p, input = {}) {
8495
8511
  { step: "stake", function: "gauge.deposit(amount)", cli_command: baseFarm },
8496
8512
  { step: "claim", function: "gauge.earned(token, account) \u2192 gauge.getReward(account, tokens[])", cli_command: claimWithGauge() }
8497
8513
  ];
8498
- case "auto_stake":
8514
+ case "auto_stake": {
8515
+ const isNftAutoStake = p.interface === "uniswap_v3";
8499
8516
  return [
8500
8517
  { step: "mint", function: "Router.addLiquidity / NPM.mint", note: "LP automatically receives x(3,3) emissions \u2014 no separate stake step", cli_command: baseAdd },
8501
- { step: "claim", function: "gauge.getReward(account, tokens[])", note: "Multi-token reward (xRAM + WHYPE on Ramses HL)", cli_command: claimWithGauge() }
8518
+ {
8519
+ step: "claim",
8520
+ function: isNftAutoStake ? "NPM.getPeriodReward(currentEpoch, tokenId, tokens[], receiver)" : "gauge.getReward(account, tokens[])",
8521
+ 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)",
8522
+ cli_command: isNftAutoStake ? claimAutoStakeNftGauge() : claimWithGauge()
8523
+ }
8502
8524
  ];
8525
+ }
8503
8526
  case "on_chain_masterchef":
8504
8527
  return [
8505
8528
  { step: "mint", function: "NPM.mint or pool.mint", cli_command: baseAdd },
@@ -9210,7 +9233,7 @@ function registerLP(parent, getOpts, makeExecutor2) {
9210
9233
  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."
9211
9234
  }, getOpts());
9212
9235
  });
9213
- 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) => {
9236
+ 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) => {
9214
9237
  const executor = makeExecutor2();
9215
9238
  const chainName = parent.opts().chain;
9216
9239
  if (!chainName) {
@@ -9226,6 +9249,9 @@ function registerLP(parent, getOpts, makeExecutor2) {
9226
9249
  if (iface === "uniswap_v2" && protocol.contracts?.["lb_factory"]) {
9227
9250
  if (!opts.pool) throw new Error(`--pool is required for ${protocol.name} (Liquidity Book \u2014 pass --pool <addr>)`);
9228
9251
  if (!opts.bins) throw new Error("--bins <id1,id2,...> is required for Merchant Moe LB remove");
9252
+ if (!opts.tokenA || !opts.tokenB) {
9253
+ throw new Error(`--token-a and --token-b are required for ${protocol.name} (Liquidity Book) remove`);
9254
+ }
9229
9255
  const lbAdapter = createMerchantMoeLB(protocol, rpcUrl);
9230
9256
  const tokenA2 = opts.tokenA.startsWith("0x") ? opts.tokenA : registry.resolveToken(chainName, opts.tokenA).address;
9231
9257
  const tokenB2 = opts.tokenB.startsWith("0x") ? opts.tokenB : registry.resolveToken(chainName, opts.tokenB).address;
@@ -9268,8 +9294,22 @@ function registerLP(parent, getOpts, makeExecutor2) {
9268
9294
  printOutput({ step: "lb_remove", ...result }, getOpts());
9269
9295
  return;
9270
9296
  }
9271
- const tokenA = opts.tokenA.startsWith("0x") ? opts.tokenA : registry.resolveToken(chainName, opts.tokenA).address;
9272
- const tokenB = opts.tokenB.startsWith("0x") ? opts.tokenB : registry.resolveToken(chainName, opts.tokenB).address;
9297
+ const NFT_REMOVE_IFACES = /* @__PURE__ */ new Set(["uniswap_v3", "algebra_v3", "thena_cl", "hybra"]);
9298
+ const isNftRemove = !!opts.tokenId && NFT_REMOVE_IFACES.has(iface);
9299
+ if (!isNftRemove) {
9300
+ const missing = [];
9301
+ if (!opts.tokenA) missing.push("--token-a");
9302
+ if (!opts.tokenB) missing.push("--token-b");
9303
+ if (!opts.liquidity) missing.push("--liquidity");
9304
+ if (missing.length > 0) {
9305
+ printOutput({
9306
+ error: `${missing.join(", ")} required for ${protocol.name} remove (or pass --token-id for V3/CL NFT-based remove).`
9307
+ }, getOpts());
9308
+ return;
9309
+ }
9310
+ }
9311
+ const tokenA = opts.tokenA ? opts.tokenA.startsWith("0x") ? opts.tokenA : registry.resolveToken(chainName, opts.tokenA).address : void 0;
9312
+ const tokenB = opts.tokenB ? opts.tokenB.startsWith("0x") ? opts.tokenB : registry.resolveToken(chainName, opts.tokenB).address : void 0;
9273
9313
  const poolAddr = opts.pool ? opts.pool : void 0;
9274
9314
  let didUnstake = false;
9275
9315
  if (iface === "algebra_v3" && protocol.contracts?.["farming_center"] && opts.tokenId && poolAddr) {
@@ -9319,7 +9359,7 @@ function registerLP(parent, getOpts, makeExecutor2) {
9319
9359
  if (iface === "hybra" && (!wOpts || wOpts.redeemType === 1)) {
9320
9360
  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");
9321
9361
  }
9322
- const withdrawTx = await gaugeAdapter.buildWithdraw(gaugeAddr, BigInt(opts.liquidity), tokenId, wOpts);
9362
+ const withdrawTx = await gaugeAdapter.buildWithdraw(gaugeAddr, opts.liquidity ? BigInt(opts.liquidity) : 0n, tokenId, wOpts);
9323
9363
  const withdrawResult = await executor.execute(withdrawTx);
9324
9364
  printOutput({ step: "unstake_gauge", ...withdrawResult }, getOpts());
9325
9365
  if (withdrawResult.status !== "confirmed" && withdrawResult.status !== "simulated") {
@@ -9334,11 +9374,31 @@ function registerLP(parent, getOpts, makeExecutor2) {
9334
9374
  }
9335
9375
  process.stderr.write("Step 2/2: Removing liquidity...\n");
9336
9376
  const dexAdapter = createDex(protocol, rpcUrl);
9377
+ let removeLiquidity = opts.liquidity ? BigInt(opts.liquidity) : 0n;
9378
+ if (isNftRemove && removeLiquidity === 0n) {
9379
+ const npm = protocol.contracts?.["position_manager"];
9380
+ if (npm) {
9381
+ const c = createPublicClient23({ transport: http23(rpcUrl) });
9382
+ const pos = await detectV3Liquidity(c, npm, BigInt(opts.tokenId));
9383
+ if (pos) {
9384
+ removeLiquidity = pos.liquidity;
9385
+ process.stderr.write(` Read live liquidity ${removeLiquidity} from NPM.positions(${opts.tokenId}).
9386
+ `);
9387
+ }
9388
+ }
9389
+ }
9390
+ if (isNftRemove && removeLiquidity === 0n) {
9391
+ printOutput({
9392
+ error: `tokenId ${opts.tokenId} has zero liquidity (already removed?). Pass --liquidity explicitly to override, or pick a different tokenId.`
9393
+ }, getOpts());
9394
+ return;
9395
+ }
9396
+ const ZERO = "0x0000000000000000000000000000000000000000";
9337
9397
  const removeTx = await dexAdapter.buildRemoveLiquidity({
9338
9398
  protocol: protocol.name,
9339
- token_a: tokenA,
9340
- token_b: tokenB,
9341
- liquidity: BigInt(opts.liquidity),
9399
+ token_a: tokenA ?? ZERO,
9400
+ token_b: tokenB ?? ZERO,
9401
+ liquidity: removeLiquidity,
9342
9402
  recipient,
9343
9403
  token_id: opts.tokenId ? BigInt(opts.tokenId) : void 0
9344
9404
  });
@@ -9717,12 +9777,20 @@ function resolveTokenAddress(registry, chainName, tokenOrAddress) {
9717
9777
  return registry.resolveToken(chainName, tokenOrAddress).address;
9718
9778
  }
9719
9779
  var FALLBACK_ADDRESS = "0x0000000000000000000000000000000000000001";
9780
+ var warnedFallback = false;
9720
9781
  function resolveWallet(override) {
9721
9782
  if (override) return override;
9722
9783
  try {
9723
9784
  const { address } = resolveWalletWithSigner();
9724
9785
  return address;
9725
9786
  } catch {
9787
+ if (!warnedFallback) {
9788
+ process.stderr.write(
9789
+ `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.
9790
+ `
9791
+ );
9792
+ warnedFallback = true;
9793
+ }
9726
9794
  return FALLBACK_ADDRESS;
9727
9795
  }
9728
9796
  }
@@ -9825,7 +9893,8 @@ function resolveAsset(registry, chain, asset) {
9825
9893
  }
9826
9894
  async function collectLendingRates(registry, chainName, rpc, assetAddr) {
9827
9895
  const protos = registry.getProtocolsForChain(chainName).filter((p) => p.category === ProtocolCategory.Lending);
9828
- const results = [];
9896
+ const rates = [];
9897
+ const errors = [];
9829
9898
  let first = true;
9830
9899
  for (const proto of protos) {
9831
9900
  if (!first) {
@@ -9834,19 +9903,22 @@ async function collectLendingRates(registry, chainName, rpc, assetAddr) {
9834
9903
  first = false;
9835
9904
  try {
9836
9905
  const lending = createLending(proto, rpc);
9837
- const rates = await lending.getRates(assetAddr);
9838
- results.push(rates);
9906
+ const r = await lending.getRates(assetAddr);
9907
+ rates.push(r);
9839
9908
  } catch (err) {
9840
9909
  process.stderr.write(`Warning: ${proto.name} rates unavailable: ${err}
9841
9910
  `);
9911
+ errors.push({ protocol: proto.name, type: "lending_supply", reason: errMsg(err) });
9842
9912
  }
9843
9913
  }
9844
- return results;
9914
+ return { rates, errors };
9845
9915
  }
9846
9916
  async function collectAllYields(registry, chainName, rpc, asset, assetAddr) {
9847
9917
  const opportunities = [];
9848
- const lendingRates = await collectLendingRates(registry, chainName, rpc, assetAddr);
9849
- for (const r of lendingRates) {
9918
+ const errors = [];
9919
+ const lendingResult = await collectLendingRates(registry, chainName, rpc, assetAddr);
9920
+ errors.push(...lendingResult.errors);
9921
+ for (const r of lendingResult.rates) {
9850
9922
  if (r.supply_apy > 0) {
9851
9923
  opportunities.push({
9852
9924
  protocol: r.protocol,
@@ -9872,7 +9944,8 @@ async function collectAllYields(registry, chainName, rpc, asset, assetAddr) {
9872
9944
  utilization: rates.utilization
9873
9945
  });
9874
9946
  }
9875
- } catch {
9947
+ } catch (e) {
9948
+ errors.push({ protocol: proto.name, type: "morpho_vault", reason: errMsg(e) });
9876
9949
  }
9877
9950
  }
9878
9951
  }
@@ -9888,7 +9961,8 @@ async function collectAllYields(registry, chainName, rpc, asset, assetAddr) {
9888
9961
  apy: info.apy ?? 0,
9889
9962
  total_assets: info.total_assets.toString()
9890
9963
  });
9891
- } catch {
9964
+ } catch (e) {
9965
+ errors.push({ protocol: proto.name, type: "vault", reason: errMsg(e) });
9892
9966
  }
9893
9967
  }
9894
9968
  }
@@ -9897,7 +9971,7 @@ async function collectAllYields(registry, chainName, rpc, asset, assetAddr) {
9897
9971
  const ba = b["apy"] ?? 0;
9898
9972
  return ba - aa;
9899
9973
  });
9900
- return opportunities;
9974
+ return { opportunities, errors };
9901
9975
  }
9902
9976
  async function runYieldScan(registry, asset, output) {
9903
9977
  const t0 = Date.now();
@@ -10039,10 +10113,13 @@ function registerYield(parent, getOpts, makeExecutor2) {
10039
10113
  const chain = registry.getChain(chainName);
10040
10114
  const rpc = chain.effectiveRpcUrl();
10041
10115
  const assetAddr = resolveAsset(registry, chainName, opts.asset);
10042
- const results = await collectLendingRates(registry, chainName, rpc, assetAddr);
10116
+ const { rates: results, errors: ratesErrors } = await collectLendingRates(registry, chainName, rpc, assetAddr);
10043
10117
  if (results.length === 0) {
10044
10118
  printOutput(
10045
- { error: `No lending rate data available for asset '${opts.asset}'` },
10119
+ ratesErrors.length > 0 ? {
10120
+ error: `Could not collect lending rates for '${opts.asset}': ${ratesErrors.length} probe(s) failed (likely RPC throttling). Retry, or set ${chainName.toUpperCase()}_RPC_URL.`,
10121
+ failed_probes: ratesErrors
10122
+ } : { error: `No lending rate data available for asset '${opts.asset}'` },
10046
10123
  getOpts()
10047
10124
  );
10048
10125
  process.exit(1);
@@ -10284,9 +10361,16 @@ function registerYield(parent, getOpts, makeExecutor2) {
10284
10361
  const assetAddr = resolveAsset(registry, chainName, asset);
10285
10362
  const strategy = opts.strategy ?? "auto";
10286
10363
  if (strategy === "auto") {
10287
- const opportunities = await collectAllYields(registry, chainName, rpc, asset, assetAddr);
10364
+ const { opportunities, errors } = await collectAllYields(registry, chainName, rpc, asset, assetAddr);
10288
10365
  if (opportunities.length === 0) {
10289
- printOutput({ error: `No yield opportunities found for '${asset}'` }, getOpts());
10366
+ if (errors.length > 0) {
10367
+ printOutput({
10368
+ 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.`,
10369
+ failed_probes: errors
10370
+ }, getOpts());
10371
+ } else {
10372
+ printOutput({ error: `No yield opportunities found for '${asset}'` }, getOpts());
10373
+ }
10290
10374
  process.exit(1);
10291
10375
  return;
10292
10376
  }
@@ -10316,9 +10400,12 @@ function registerYield(parent, getOpts, makeExecutor2) {
10316
10400
  getOpts()
10317
10401
  );
10318
10402
  } else if (strategy === "best-supply") {
10319
- const results = await collectLendingRates(registry, chainName, rpc, assetAddr);
10403
+ const { rates: results, errors: rErr } = await collectLendingRates(registry, chainName, rpc, assetAddr);
10320
10404
  if (results.length === 0) {
10321
- printOutput({ error: `No lending rate data available for asset '${asset}'` }, getOpts());
10405
+ printOutput(
10406
+ 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}'` },
10407
+ getOpts()
10408
+ );
10322
10409
  process.exit(1);
10323
10410
  return;
10324
10411
  }
@@ -10341,9 +10428,12 @@ function registerYield(parent, getOpts, makeExecutor2) {
10341
10428
  getOpts()
10342
10429
  );
10343
10430
  } else if (strategy === "leverage-loop") {
10344
- const results = await collectLendingRates(registry, chainName, rpc, assetAddr);
10431
+ const { rates: results, errors: lErr } = await collectLendingRates(registry, chainName, rpc, assetAddr);
10345
10432
  if (results.length === 0) {
10346
- printOutput({ error: `No lending rate data available for asset '${asset}'` }, getOpts());
10433
+ printOutput(
10434
+ 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}'` },
10435
+ getOpts()
10436
+ );
10347
10437
  process.exit(1);
10348
10438
  return;
10349
10439
  }
@@ -10407,14 +10497,14 @@ function registerYield(parent, getOpts, makeExecutor2) {
10407
10497
 
10408
10498
  // src/commands/portfolio.ts
10409
10499
  init_dist();
10410
- import { encodeFunctionData as encodeFunctionData29, parseAbi as parseAbi33 } from "viem";
10500
+ import { createPublicClient as createPublicClient25, encodeFunctionData as encodeFunctionData29, http as http25, parseAbi as parseAbi33 } from "viem";
10411
10501
 
10412
10502
  // src/portfolio-tracker.ts
10413
10503
  init_dist();
10414
10504
  import { mkdirSync, writeFileSync, readdirSync as readdirSync2, readFileSync as readFileSync3, existsSync as existsSync2 } from "fs";
10415
10505
  import { homedir } from "os";
10416
10506
  import { resolve as resolve3 } from "path";
10417
- import { encodeFunctionData as encodeFunctionData28, parseAbi as parseAbi31 } from "viem";
10507
+ import { createPublicClient as createPublicClient24, encodeFunctionData as encodeFunctionData28, http as http24, parseAbi as parseAbi31 } from "viem";
10418
10508
  var ERC20_ABI4 = parseAbi31([
10419
10509
  "function balanceOf(address owner) external view returns (uint256)"
10420
10510
  ]);
@@ -10465,7 +10555,16 @@ async function takeSnapshot(chainName, wallet, registry) {
10465
10555
  const oracleEntry = registry.getProtocolsForChain(chainName).find((p) => p.interface === "aave_v3" && p.contracts?.["oracle"]);
10466
10556
  const oracleAddr = oracleEntry?.contracts?.["oracle"];
10467
10557
  const wrappedNative = chain.wrapped_native ?? "0x5555555555555555555555555555555555555555";
10558
+ const priceTokens = [];
10468
10559
  if (oracleAddr) {
10560
+ for (const t of tokenEntries) {
10561
+ calls.push([
10562
+ oracleAddr,
10563
+ encodeFunctionData28({ abi: ORACLE_ABI4, functionName: "getAssetPrice", args: [t.address] })
10564
+ ]);
10565
+ callLabels.push(`price:${t.address}`);
10566
+ priceTokens.push(t.address);
10567
+ }
10469
10568
  calls.push([
10470
10569
  oracleAddr,
10471
10570
  encodeFunctionData28({ abi: ORACLE_ABI4, functionName: "getAssetPrice", args: [wrappedNative] })
@@ -10476,10 +10575,19 @@ async function takeSnapshot(chainName, wallet, registry) {
10476
10575
  if (calls.length > 0) {
10477
10576
  results = await multicallRead(rpc, calls);
10478
10577
  }
10578
+ const balanceCount = tokenEntries.length;
10579
+ const lendingCount = lendingProtocols.length;
10580
+ const priceStartIdx = balanceCount + lendingCount;
10479
10581
  let nativePriceUsd = 0;
10582
+ const priceByToken = /* @__PURE__ */ new Map();
10480
10583
  if (oracleAddr) {
10481
- const priceData = results[results.length - 1] ?? null;
10482
- nativePriceUsd = Number(decodeU256Word(priceData)) / 1e8;
10584
+ for (let i = 0; i < priceTokens.length; i++) {
10585
+ const priceData = results[priceStartIdx + i] ?? null;
10586
+ const px = Number(decodeU256Word(priceData)) / 1e8;
10587
+ if (px > 0) priceByToken.set(priceTokens[i].toLowerCase(), px);
10588
+ }
10589
+ const nativePriceData = results[priceStartIdx + priceTokens.length] ?? null;
10590
+ nativePriceUsd = Number(decodeU256Word(nativePriceData)) / 1e8;
10483
10591
  }
10484
10592
  let idx = 0;
10485
10593
  const tokens = [];
@@ -10489,8 +10597,20 @@ async function takeSnapshot(chainName, wallet, registry) {
10489
10597
  const balance = decodeU256Word(results[idx] ?? null);
10490
10598
  const balF64 = Number(balance) / 10 ** entry.decimals;
10491
10599
  const symbolUpper = entry.symbol.toUpperCase();
10492
- const priceUsd = symbolUpper.includes("USD") ? 1 : nativePriceUsd;
10493
- const valueUsd = balF64 * priceUsd;
10600
+ const tokenAddrLower = entry.address.toLowerCase();
10601
+ let priceUsd;
10602
+ let valueUsd;
10603
+ if (symbolUpper.includes("USD")) {
10604
+ priceUsd = 1;
10605
+ valueUsd = balF64;
10606
+ } else if (tokenAddrLower === wrappedNative.toLowerCase()) {
10607
+ priceUsd = nativePriceUsd;
10608
+ valueUsd = balF64 * nativePriceUsd;
10609
+ } else {
10610
+ const px = priceByToken.get(tokenAddrLower);
10611
+ priceUsd = px ?? 0;
10612
+ valueUsd = px && px > 0 ? balF64 * px : 0;
10613
+ }
10494
10614
  totalValueUsd += valueUsd;
10495
10615
  tokens.push({
10496
10616
  token: entry.address,
@@ -10531,6 +10651,23 @@ async function takeSnapshot(chainName, wallet, registry) {
10531
10651
  }
10532
10652
  idx++;
10533
10653
  }
10654
+ try {
10655
+ const client = createPublicClient24({ transport: http24(rpc) });
10656
+ const nativeBalance = await client.getBalance({ address: user });
10657
+ if (nativeBalance > 0n) {
10658
+ const nativeF64 = Number(nativeBalance) / 1e18;
10659
+ const nativeValueUsd = nativeF64 * nativePriceUsd;
10660
+ totalValueUsd += nativeValueUsd;
10661
+ tokens.push({
10662
+ token: wrappedNative,
10663
+ symbol: chain.native_token ?? "NATIVE",
10664
+ balance: nativeBalance,
10665
+ value_usd: nativeValueUsd,
10666
+ price_usd: nativePriceUsd
10667
+ });
10668
+ }
10669
+ } catch {
10670
+ }
10534
10671
  return {
10535
10672
  timestamp: Date.now(),
10536
10673
  chain: chainName,
@@ -10648,6 +10785,10 @@ function registerPortfolio(parent, getOpts) {
10648
10785
  const calls = [];
10649
10786
  const callLabels = [];
10650
10787
  const tokenSymbols = (registry.tokens.get(chainName) ?? []).map((t) => t.symbol);
10788
+ const tokenAddrsByCallIdx = [];
10789
+ const oracleEntry = registry.getProtocolsForChain(chainName).find((p) => p.interface === "aave_v3" && p.contracts?.["oracle"]);
10790
+ const oracleAddr = oracleEntry?.contracts?.["oracle"];
10791
+ const wrappedNative = chain.wrapped_native ?? "0x5555555555555555555555555555555555555555";
10651
10792
  for (const symbol of tokenSymbols) {
10652
10793
  let entry;
10653
10794
  try {
@@ -10661,6 +10802,7 @@ function registerPortfolio(parent, getOpts) {
10661
10802
  encodeFunctionData29({ abi: ERC20_ABI5, functionName: "balanceOf", args: [user] })
10662
10803
  ]);
10663
10804
  callLabels.push(`balance:${symbol}`);
10805
+ tokenAddrsByCallIdx.push(entry.address);
10664
10806
  }
10665
10807
  const lendingProtocols = registry.getProtocolsForChain(chainName).filter((p) => p.category === ProtocolCategory.Lending && p.interface === "aave_v3").filter((p) => p.contracts?.["pool"]);
10666
10808
  for (const p of lendingProtocols) {
@@ -10670,10 +10812,16 @@ function registerPortfolio(parent, getOpts) {
10670
10812
  ]);
10671
10813
  callLabels.push(`lending:${p.name}`);
10672
10814
  }
10673
- const oracleEntry = registry.getProtocolsForChain(chainName).find((p) => p.interface === "aave_v3" && p.contracts?.["oracle"]);
10674
- const oracleAddr = oracleEntry?.contracts?.["oracle"];
10675
- const wrappedNative = chain.wrapped_native ?? "0x5555555555555555555555555555555555555555";
10815
+ const priceTokens = [];
10676
10816
  if (oracleAddr) {
10817
+ for (const tokenAddr of tokenAddrsByCallIdx) {
10818
+ calls.push([
10819
+ oracleAddr,
10820
+ encodeFunctionData29({ abi: ORACLE_ABI5, functionName: "getAssetPrice", args: [tokenAddr] })
10821
+ ]);
10822
+ callLabels.push(`price:${tokenAddr}`);
10823
+ priceTokens.push(tokenAddr);
10824
+ }
10677
10825
  calls.push([
10678
10826
  oracleAddr,
10679
10827
  encodeFunctionData29({ abi: ORACLE_ABI5, functionName: "getAssetPrice", args: [wrappedNative] })
@@ -10698,10 +10846,19 @@ function registerPortfolio(parent, getOpts) {
10698
10846
  printOutput({ error: `Multicall failed: ${errMsg(e)}` }, mode);
10699
10847
  return;
10700
10848
  }
10849
+ const balanceCallCount = tokenAddrsByCallIdx.length;
10850
+ const lendingCallCount = lendingProtocols.length;
10851
+ const priceStartIdx = balanceCallCount + lendingCallCount;
10701
10852
  let nativePriceUsd = 0;
10853
+ const priceByToken = /* @__PURE__ */ new Map();
10702
10854
  if (oracleAddr) {
10703
- const priceData = results[results.length - 1] ?? null;
10704
- nativePriceUsd = Number(decodeU2562(priceData)) / 1e8;
10855
+ for (let i = 0; i < priceTokens.length; i++) {
10856
+ const priceData = results[priceStartIdx + i] ?? null;
10857
+ const px = Number(decodeU2562(priceData)) / 1e8;
10858
+ if (px > 0) priceByToken.set(priceTokens[i].toLowerCase(), px);
10859
+ }
10860
+ const nativePriceData = results[priceStartIdx + priceTokens.length] ?? null;
10861
+ nativePriceUsd = Number(decodeU2562(nativePriceData)) / 1e8;
10705
10862
  }
10706
10863
  let totalValueUsd = 0;
10707
10864
  let idx = 0;
@@ -10720,12 +10877,21 @@ function registerPortfolio(parent, getOpts) {
10720
10877
  const decimals = entry.decimals;
10721
10878
  const balF64 = Number(balance) / 10 ** decimals;
10722
10879
  const symbolUpper = symbol.toUpperCase();
10723
- const valueUsd = symbolUpper.includes("USD") || symbolUpper.includes("usd") ? balF64 : balF64 * nativePriceUsd;
10724
- totalValueUsd += valueUsd;
10880
+ const tokenAddrLower = entry.address.toLowerCase();
10881
+ let valueUsd;
10882
+ if (symbolUpper.includes("USD")) {
10883
+ valueUsd = balF64;
10884
+ } else if (tokenAddrLower === wrappedNative.toLowerCase()) {
10885
+ valueUsd = balF64 * nativePriceUsd;
10886
+ } else {
10887
+ const px = priceByToken.get(tokenAddrLower);
10888
+ valueUsd = px && px > 0 ? balF64 * px : null;
10889
+ }
10890
+ if (valueUsd !== null) totalValueUsd += valueUsd;
10725
10891
  tokenBalances.push({
10726
10892
  symbol,
10727
10893
  balance: balF64.toFixed(4),
10728
- value_usd: valueUsd.toFixed(2)
10894
+ value_usd: valueUsd !== null ? valueUsd.toFixed(2) : null
10729
10895
  });
10730
10896
  }
10731
10897
  idx++;
@@ -10755,11 +10921,23 @@ function registerPortfolio(parent, getOpts) {
10755
10921
  }
10756
10922
  idx++;
10757
10923
  }
10924
+ let nativeBalance = 0n;
10925
+ let nativeValueUsd = 0;
10926
+ try {
10927
+ const client = createPublicClient25({ transport: http25(rpc) });
10928
+ nativeBalance = await client.getBalance({ address: user });
10929
+ const nativeF64 = Number(nativeBalance) / 1e18;
10930
+ nativeValueUsd = nativeF64 * nativePriceUsd;
10931
+ if (nativeBalance > 0n) totalValueUsd += nativeValueUsd;
10932
+ } catch {
10933
+ }
10758
10934
  printOutput(
10759
10935
  {
10760
10936
  address: user,
10761
10937
  chain: chain.name,
10762
10938
  native_price_usd: nativePriceUsd.toFixed(2),
10939
+ native_balance: (Number(nativeBalance) / 1e18).toFixed(6),
10940
+ native_value_usd: nativeValueUsd.toFixed(2),
10763
10941
  total_value_usd: totalValueUsd.toFixed(2),
10764
10942
  token_balances: tokenBalances,
10765
10943
  lending_positions: lendingPositions
@@ -11025,38 +11203,50 @@ function registerPrice(parent, getOpts) {
11025
11203
 
11026
11204
  // src/commands/wallet.ts
11027
11205
  init_dist();
11028
- import { createPublicClient as createPublicClient24, http as http24, formatEther } from "viem";
11206
+ import { createPublicClient as createPublicClient26, http as http26, formatEther } from "viem";
11207
+ function resolveCurrentAddress(override) {
11208
+ if (override) return { address: override, source: "flag" };
11209
+ try {
11210
+ const { address, signer } = resolveWalletWithSigner();
11211
+ return { address, source: signer ? "ows" : process.env["DEFI_PRIVATE_KEY"] ? "private_key" : "env" };
11212
+ } catch {
11213
+ return { address: null, source: "none" };
11214
+ }
11215
+ }
11029
11216
  function registerWallet(parent, getOpts) {
11030
11217
  const wallet = parent.command("wallet").description("Wallet management");
11031
- wallet.command("balance").description("Show native token balance").option("--address <address>", "Wallet address (defaults to DEFI_WALLET_ADDRESS)").action(async (opts) => {
11218
+ 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) => {
11032
11219
  const chainName = requireChain(parent, getOpts);
11033
11220
  if (!chainName) return;
11034
- const addr = opts.address ?? process.env["DEFI_WALLET_ADDRESS"];
11221
+ const { address: addr, source } = resolveCurrentAddress(opts.address);
11035
11222
  if (!addr) {
11036
- printOutput({ error: "--address required (or set DEFI_WALLET_ADDRESS)" }, getOpts());
11223
+ printOutput({
11224
+ error: "No wallet configured. Set DEFI_WALLET_ADDRESS, set DEFI_PRIVATE_KEY, or pass --address."
11225
+ }, getOpts());
11037
11226
  return;
11038
11227
  }
11039
11228
  const registry = Registry.loadEmbedded();
11040
11229
  const chain = registry.getChain(chainName);
11041
- const client = createPublicClient24({ transport: http24(chain.effectiveRpcUrl()) });
11230
+ const client = createPublicClient26({ transport: http26(chain.effectiveRpcUrl()) });
11042
11231
  const balance = await client.getBalance({ address: addr });
11043
11232
  printOutput({
11044
11233
  chain: chain.name,
11045
11234
  address: addr,
11235
+ wallet_source: source,
11046
11236
  native_token: chain.native_token,
11047
11237
  balance_wei: balance,
11048
11238
  balance_formatted: formatEther(balance)
11049
11239
  }, getOpts());
11050
11240
  });
11051
11241
  wallet.command("address").description("Show configured wallet address").action(async () => {
11052
- const addr = process.env.DEFI_WALLET_ADDRESS ?? "(not set)";
11053
- printOutput({ address: addr }, getOpts());
11242
+ const { address, source } = resolveCurrentAddress();
11243
+ printOutput({ address, source }, getOpts());
11054
11244
  });
11055
11245
  }
11056
11246
 
11057
11247
  // src/commands/token.ts
11058
11248
  init_dist();
11059
- import { createPublicClient as createPublicClient25, http as http25, maxUint256 } from "viem";
11249
+ import { createPublicClient as createPublicClient27, http as http27, maxUint256 } from "viem";
11060
11250
  function registerToken(parent, getOpts, makeExecutor2) {
11061
11251
  const token = parent.command("token").description("Token operations: approve, allowance, transfer, balance");
11062
11252
  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) => {
@@ -11069,7 +11259,7 @@ function registerToken(parent, getOpts, makeExecutor2) {
11069
11259
  }
11070
11260
  const registry = Registry.loadEmbedded();
11071
11261
  const chain = registry.getChain(chainName);
11072
- const client = createPublicClient25({ transport: http25(chain.effectiveRpcUrl()) });
11262
+ const client = createPublicClient27({ transport: http27(chain.effectiveRpcUrl()) });
11073
11263
  const tokenAddr = resolveTokenAddress(registry, chainName, opts.token);
11074
11264
  const [balance, symbol, decimals] = await Promise.all([
11075
11265
  client.readContract({ address: tokenAddr, abi: erc20Abi, functionName: "balanceOf", args: [owner] }),
@@ -11105,7 +11295,7 @@ function registerToken(parent, getOpts, makeExecutor2) {
11105
11295
  }
11106
11296
  const registry = Registry.loadEmbedded();
11107
11297
  const chain = registry.getChain(chainName);
11108
- const client = createPublicClient25({ transport: http25(chain.effectiveRpcUrl()) });
11298
+ const client = createPublicClient27({ transport: http27(chain.effectiveRpcUrl()) });
11109
11299
  const tokenAddr = resolveTokenAddress(registry, chainName, opts.token);
11110
11300
  const allowance = await client.readContract({
11111
11301
  address: tokenAddr,
@@ -11223,6 +11413,17 @@ var CCTP_USDC_ADDRESSES = {
11223
11413
  base: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
11224
11414
  polygon: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359"
11225
11415
  };
11416
+ function cctpMinFeeGuard(amountWei, maxFeeSubunits, feeUsdc) {
11417
+ if (amountWei <= maxFeeSubunits) {
11418
+ const amountUsdc = Number(amountWei) / 1e6;
11419
+ return {
11420
+ error: `CCTP: amount ${amountWei.toString()} (${amountUsdc} USDC) is below the minimum bridge fee of ${maxFeeSubunits} (${feeUsdc} USDC). Increase --amount.`,
11421
+ minimum_amount_wei: maxFeeSubunits.toString(),
11422
+ minimum_amount_usdc: feeUsdc
11423
+ };
11424
+ }
11425
+ return null;
11426
+ }
11226
11427
  async function getCctpFeeEstimate(srcDomain, dstDomain, amountUsdc) {
11227
11428
  try {
11228
11429
  const res = await fetch(`${CCTP_FEE_API}/${srcDomain}/${dstDomain}`);
@@ -11311,12 +11512,9 @@ function registerBridge(parent, getOpts) {
11311
11512
  }
11312
11513
  const amountUsdc = Number(BigInt(opts.amount)) / 1e6;
11313
11514
  const { fee, maxFeeSubunits } = await getCctpFeeEstimate(srcDomain, dstDomain, amountUsdc);
11314
- if (BigInt(opts.amount) <= maxFeeSubunits) {
11315
- printOutput({
11316
- error: `CCTP: amount ${opts.amount} (${amountUsdc} USDC) is below the minimum bridge fee of ${maxFeeSubunits} (${fee} USDC). Increase --amount.`,
11317
- minimum_amount_wei: maxFeeSubunits.toString(),
11318
- minimum_amount_usdc: fee
11319
- }, getOpts());
11515
+ const guardErr = cctpMinFeeGuard(BigInt(opts.amount), maxFeeSubunits, fee);
11516
+ if (guardErr) {
11517
+ printOutput(guardErr, getOpts());
11320
11518
  return;
11321
11519
  }
11322
11520
  const recipientPadded = `0x${"0".repeat(24)}${recipient.replace("0x", "").toLowerCase()}`;
@@ -11597,12 +11795,14 @@ function registerSwap(parent, getOpts, makeExecutor2) {
11597
11795
  slippagePct,
11598
11796
  wallet
11599
11797
  );
11798
+ const fromLower = fromAddr.toLowerCase();
11799
+ const isNativeInput = fromLower === "0x0000000000000000000000000000000000000000" || fromLower === "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";
11600
11800
  const tx = {
11601
11801
  description: `OpenOcean: swap ${opts.amount} of ${fromAddr} -> ${toAddr}`,
11602
11802
  to: swap.to,
11603
11803
  data: swap.data,
11604
11804
  value: parseBigIntValue(swap.value),
11605
- approvals: [{ token: fromAddr, spender: swap.to, amount: BigInt(opts.amount) }]
11805
+ ...isNativeInput ? {} : { approvals: [{ token: fromAddr, spender: swap.to, amount: BigInt(opts.amount) }] }
11606
11806
  };
11607
11807
  const result = await executor.execute(tx);
11608
11808
  printOutput({
@@ -11694,7 +11894,8 @@ import pc2 from "picocolors";
11694
11894
  import { createInterface } from "readline";
11695
11895
  import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
11696
11896
  import { resolve as resolve4 } from "path";
11697
- var DEFI_DIR = resolve4(process.env.HOME || "~", ".defi");
11897
+ import { homedir as homedir2 } from "os";
11898
+ var DEFI_DIR = resolve4(homedir2(), ".defi");
11698
11899
  var ENV_FILE = resolve4(DEFI_DIR, ".env");
11699
11900
  function ensureDefiDir() {
11700
11901
  if (!existsSync3(DEFI_DIR)) mkdirSync2(DEFI_DIR, { recursive: true, mode: 448 });
@@ -11729,6 +11930,44 @@ function writeEnvFile(env) {
11729
11930
  function ask(rl, question) {
11730
11931
  return new Promise((res) => rl.question(question, (answer) => res(answer.trim())));
11731
11932
  }
11933
+ function askSecret(rl, question) {
11934
+ const stdin = process.stdin;
11935
+ if (!stdin.isTTY) return ask(rl, question);
11936
+ process.stdout.write(question);
11937
+ const rlAny = rl;
11938
+ const original = rlAny._writeToOutput;
11939
+ rlAny._writeToOutput = (s) => {
11940
+ if (s.includes("\n") || s.includes("\r")) {
11941
+ original?.call(rl, s);
11942
+ }
11943
+ };
11944
+ return new Promise((res) => {
11945
+ rl.question("", (answer) => {
11946
+ rlAny._writeToOutput = original;
11947
+ process.stdout.write("\n");
11948
+ res(answer.trim());
11949
+ });
11950
+ });
11951
+ }
11952
+ function maskRpcUrl(s) {
11953
+ try {
11954
+ const u = new URL(s);
11955
+ if (u.pathname && u.pathname !== "/" && u.pathname.length > 1) {
11956
+ return `${u.protocol}//${u.host}/***`;
11957
+ }
11958
+ return `${u.protocol}//${u.host}`;
11959
+ } catch {
11960
+ return "***";
11961
+ }
11962
+ }
11963
+ function isValidRpcUrl(s) {
11964
+ try {
11965
+ const u = new URL(s);
11966
+ return u.protocol === "http:" || u.protocol === "https:";
11967
+ } catch {
11968
+ return false;
11969
+ }
11970
+ }
11732
11971
  function isValidAddress(s) {
11733
11972
  return /^0x[0-9a-fA-F]{40}$/.test(s);
11734
11973
  }
@@ -11753,7 +11992,14 @@ function registerSetup(program2) {
11753
11992
  if (Object.keys(existing).length > 0) {
11754
11993
  console.log(pc2.white(" Current configuration:"));
11755
11994
  for (const [key, value] of Object.entries(existing)) {
11756
- const masked = key.toLowerCase().includes("key") ? value.slice(0, 6) + "..." + value.slice(-4) : value;
11995
+ let masked;
11996
+ if (key.toLowerCase().includes("key")) {
11997
+ masked = value.slice(0, 6) + "..." + value.slice(-4);
11998
+ } else if (key.endsWith("RPC_URL")) {
11999
+ masked = maskRpcUrl(value);
12000
+ } else {
12001
+ masked = value;
12002
+ }
11757
12003
  console.log(` ${pc2.cyan(key.padEnd(24))} ${pc2.gray(masked)}`);
11758
12004
  }
11759
12005
  console.log();
@@ -11767,7 +12013,7 @@ function registerSetup(program2) {
11767
12013
  }
11768
12014
  const newEnv = {};
11769
12015
  console.log(pc2.cyan(pc2.bold(" Wallet")));
11770
- const privateKey = await ask(rl, " Private key (optional, for --broadcast, 0x...): ");
12016
+ const privateKey = await askSecret(rl, " Private key (optional, for --broadcast, 0x... \u2014 input hidden): ");
11771
12017
  if (privateKey) {
11772
12018
  const normalized = privateKey.startsWith("0x") ? privateKey : `0x${privateKey}`;
11773
12019
  if (!isValidPrivateKey(normalized)) {
@@ -11803,6 +12049,10 @@ function registerSetup(program2) {
11803
12049
  for (const { env, label } of rpcPrompts) {
11804
12050
  const value = await ask(rl, ` ${label} RPC URL: `);
11805
12051
  if (value) {
12052
+ if (!isValidRpcUrl(value)) {
12053
+ console.log(pc2.yellow(` Invalid URL (${value}). Skipped \u2014 re-run setup to retry.`));
12054
+ continue;
12055
+ }
11806
12056
  newEnv[env] = value;
11807
12057
  console.log(` ${pc2.green("OK")} ${label} RPC set`);
11808
12058
  }
@@ -11826,7 +12076,7 @@ function registerSetup(program2) {
11826
12076
  ];
11827
12077
  for (const [k, label] of rpcSummary) {
11828
12078
  const v = finalEnv[k];
11829
- if (v) console.log(` ${label}: ${pc2.gray(v)}`);
12079
+ if (v) console.log(` ${label}: ${pc2.gray(maskRpcUrl(v))}`);
11830
12080
  }
11831
12081
  console.log(pc2.bold(pc2.white("\n Next steps:")));
11832
12082
  console.log(` ${pc2.green("defi portfolio")} view balances & positions`);
@@ -11845,11 +12095,11 @@ function registerSetup(program2) {
11845
12095
  // src/commands/ows.ts
11846
12096
  import pc3 from "picocolors";
11847
12097
  init_dist();
11848
- import { createPublicClient as createPublicClient26, http as http26, formatEther as formatEther2 } from "viem";
12098
+ import { createPublicClient as createPublicClient28, http as http28, formatEther as formatEther2 } from "viem";
11849
12099
  async function getEvmBalance(address, chainName) {
11850
12100
  const registry = Registry.loadEmbedded();
11851
12101
  const chain = registry.getChain(chainName);
11852
- const client = createPublicClient26({ transport: http26(chain.effectiveRpcUrl()) });
12102
+ const client = createPublicClient28({ transport: http28(chain.effectiveRpcUrl()) });
11853
12103
  const balance = await client.getBalance({ address });
11854
12104
  return {
11855
12105
  native_token: chain.native_token,