@reserve-protocol/dtf-cli 0.1.1 → 0.1.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 (2) hide show
  1. package/dist/index.js +1804 -314
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -2435,19 +2435,19 @@ var init_index_sdksp5px = __esm(() => {
2435
2435
  __hasOwnProp = Object.prototype.hasOwnProperty;
2436
2436
  });
2437
2437
 
2438
- // ../sdk/dist/ccip-9vnc6854.js
2439
- var exports_ccip_9vnc6854 = {};
2440
- __export(exports_ccip_9vnc6854, {
2438
+ // ../sdk/dist/ccip-tn8s4j8m.js
2439
+ var exports_ccip_tn8s4j8m = {};
2440
+ __export(exports_ccip_tn8s4j8m, {
2441
2441
  offchainLookupSignature: () => offchainLookupSignature,
2442
2442
  offchainLookupAbiItem: () => offchainLookupAbiItem,
2443
2443
  offchainLookup: () => offchainLookup,
2444
2444
  ccipRequest: () => ccipRequest
2445
2445
  });
2446
- var init_ccip_9vnc6854 = __esm(() => {
2447
- init_index_tdmavmz7();
2446
+ var init_ccip_tn8s4j8m = __esm(() => {
2447
+ init_index_azrx1hqn();
2448
2448
  });
2449
2449
 
2450
- // ../sdk/dist/index-tdmavmz7.js
2450
+ // ../sdk/dist/index-azrx1hqn.js
2451
2451
  function execTyped(regex, string) {
2452
2452
  const match = regex.exec(string);
2453
2453
  return match?.groups;
@@ -3457,6 +3457,14 @@ function hexToNumber2(hex, opts = {}) {
3457
3457
  });
3458
3458
  return number;
3459
3459
  }
3460
+ function hexToString(hex, opts = {}) {
3461
+ let bytes = hexToBytes2(hex);
3462
+ if (opts.size) {
3463
+ assertSize3(bytes, { size: opts.size });
3464
+ bytes = trim3(bytes, { dir: "right" });
3465
+ }
3466
+ return new TextDecoder().decode(bytes);
3467
+ }
3460
3468
  function toHex(value, opts = {}) {
3461
3469
  if (typeof value === "number" || typeof value === "bigint")
3462
3470
  return numberToHex(value, opts);
@@ -4795,7 +4803,7 @@ async function call(client, args) {
4795
4803
  return { data: response };
4796
4804
  } catch (err) {
4797
4805
  const data2 = getRevertErrorData(err);
4798
- const { offchainLookup: offchainLookup2, offchainLookupSignature: offchainLookupSignature2 } = await Promise.resolve().then(() => (init_ccip_9vnc6854(), exports_ccip_9vnc6854));
4806
+ const { offchainLookup: offchainLookup2, offchainLookupSignature: offchainLookupSignature2 } = await Promise.resolve().then(() => (init_ccip_tn8s4j8m(), exports_ccip_tn8s4j8m));
4799
4807
  if (client.ccipRead !== false && data2?.slice(0, 10) === offchainLookupSignature2 && to)
4800
4808
  return { data: await offchainLookup2(client, { data: data2, to }) };
4801
4809
  if (deploylessCall && data2?.slice(0, 10) === "0x101bb98d")
@@ -5099,7 +5107,7 @@ var version = "1.2.3", BaseError, bytesRegex, integerRegex, isTupleRegex, tupleR
5099
5107
  const value2 = typeof value_ === "bigint" ? value_.toString() : value_;
5100
5108
  return typeof replacer === "function" ? replacer(key, value2) : value2;
5101
5109
  }, space), toEventSelector, etherUnits, gweiUnits, AccountStateConflictError, StateAssignmentConflictError, InvalidLegacyVError, InvalidSerializableTransactionError, InvalidStorageKeySizeError, TransactionExecutionError, TransactionNotFoundError, TransactionReceiptNotFoundError, TransactionReceiptRevertedError, WaitForTransactionReceiptTimeoutError, getContractAddress = (address) => address, getUrl = (url) => url, CallExecutionError, ContractFunctionExecutionError, ContractFunctionRevertedError, ContractFunctionZeroDataError, CounterfactualDeploymentFailedError, RawContractError, docsPath = "/docs/contract/decodeFunctionResult", docsPath2 = "/docs/contract/encodeDeployData", docsPath3 = "/docs/contract/encodeFunctionData", ExecutionRevertedError, FeeCapTooHighError, FeeCapTooLowError, NonceTooHighError, NonceTooLowError, NonceMaxValueError, InsufficientFundsError, IntrinsicGasTooHighError, IntrinsicGasTooLowError, TransactionTypeNotSupportedError, TipAboveFeeCapError, UnknownNodeError, HttpRequestError, RpcRequestError, TimeoutError, unknownErrorCode = -1, RpcError, ProviderRpcError, ParseRpcError, InvalidRequestRpcError, MethodNotFoundRpcError, InvalidParamsRpcError, InternalRpcError, InvalidInputRpcError, ResourceNotFoundRpcError, ResourceUnavailableRpcError, TransactionRejectedRpcError, MethodNotSupportedRpcError, LimitExceededRpcError, JsonRpcVersionUnsupportedError, UserRejectedRequestError, UnauthorizedProviderError, UnsupportedProviderMethodError, ProviderDisconnectedError, ChainDisconnectedError, SwitchChainError, UnsupportedNonOptionalCapabilityError, UnsupportedChainIdError, DuplicateIdError, UnknownBundleIdError, BundleTooLargeError, AtomicReadyWalletRejectedUpgradeError, AtomicityNotSupportedError, WalletConnectSessionSettlementError, UnknownRpcError, rpcTransactionType, schedulerCache, maxInt8, maxInt16, maxInt24, maxInt32, maxInt40, maxInt48, maxInt56, maxInt64, maxInt72, maxInt80, maxInt88, maxInt96, maxInt104, maxInt112, maxInt120, maxInt128, maxInt136, maxInt144, maxInt152, maxInt160, maxInt168, maxInt176, maxInt184, maxInt192, maxInt200, maxInt208, maxInt216, maxInt224, maxInt232, maxInt240, maxInt248, maxInt256, minInt8, minInt16, minInt24, minInt32, minInt40, minInt48, minInt56, minInt64, minInt72, minInt80, minInt88, minInt96, minInt104, minInt112, minInt120, minInt128, minInt136, minInt144, minInt152, minInt160, minInt168, minInt176, minInt184, minInt192, minInt200, minInt208, minInt216, minInt224, minInt232, minInt240, minInt248, minInt256, maxUint8, maxUint16, maxUint24, maxUint32, maxUint40, maxUint48, maxUint56, maxUint64, maxUint72, maxUint80, maxUint88, maxUint96, maxUint104, maxUint112, maxUint120, maxUint128, maxUint136, maxUint144, maxUint152, maxUint160, maxUint168, maxUint176, maxUint184, maxUint192, maxUint200, maxUint208, maxUint216, maxUint224, maxUint232, maxUint240, maxUint248, maxUint256, OffchainLookupError, OffchainLookupResponseMalformedError, OffchainLookupSenderMismatchError, docsPath4 = "/docs/contract/encodeErrorResult", docsPath5 = "/docs/contract/encodeFunctionResult", localBatchGatewayUrl = "x-batch-gateway:true", offchainLookupSignature = "0x556f1830", offchainLookupAbiItem;
5102
- var init_index_tdmavmz7 = __esm(() => {
5110
+ var init_index_azrx1hqn = __esm(() => {
5103
5111
  init_index_wjqsw4fc();
5104
5112
  BaseError = class BaseError extends Error {
5105
5113
  constructor(shortMessage, args = {}) {
@@ -8952,6 +8960,8 @@ function resolveConfig(args) {
8952
8960
  const limitStr = getFlag(args, "--limit");
8953
8961
  const limit = limitStr !== undefined ? Number(limitStr) : undefined;
8954
8962
  const performance = getFlag(args, "--performance") ?? undefined;
8963
+ const subgraphRaw = getFlag(args, "--subgraph");
8964
+ const subgraph = subgraphRaw === "index" || subgraphRaw === "yield" ? subgraphRaw : undefined;
8955
8965
  return {
8956
8966
  chainId: Number(chainId),
8957
8967
  allChains,
@@ -8964,7 +8974,8 @@ function resolveConfig(args) {
8964
8974
  account,
8965
8975
  nonce,
8966
8976
  limit,
8967
- performance
8977
+ performance,
8978
+ subgraph
8968
8979
  };
8969
8980
  }
8970
8981
  function getCommand(args) {
@@ -8972,7 +8983,7 @@ function getCommand(args) {
8972
8983
  let i = 0;
8973
8984
  while (i < args.length) {
8974
8985
  const arg = args[i];
8975
- if (arg === "--chain" || arg === "--rpc" || arg === "--wallet-key" || arg === "--action" || arg === "--account" || arg === "--nonce" || arg === "--limit" || arg === "--performance" || arg === "--name" || arg === "--symbol" || arg === "--basket" || arg === "--amount" || arg === "--config" || arg === "--mandate") {
8986
+ if (arg === "--chain" || arg === "--rpc" || arg === "--wallet-key" || arg === "--action" || arg === "--account" || arg === "--nonce" || arg === "--limit" || arg === "--performance" || arg === "--name" || arg === "--symbol" || arg === "--basket" || arg === "--amount" || arg === "--config" || arg === "--mandate" || arg === "--subgraph") {
8976
8987
  i += 2;
8977
8988
  } else if (arg === "--json" || arg === "--help" || arg === "--all" || arg === "--dry-run" || arg === "--ungoverned" || arg === "--list-tokens") {
8978
8989
  i++;
@@ -9006,37 +9017,49 @@ function loadRcFile() {
9006
9017
 
9007
9018
  // src/lib/dtf-registry.ts
9008
9019
  var REGISTRY = [
9009
- { symbol: "CMC20", name: "CoinMarketCap 20 Index DTF", address: "0x2f8a339b5889ffac4c5a956787cda593b3c36867", chainId: 56 },
9010
- { symbol: "LCAP", name: "CF Large Cap Index", address: "0x4da9a0f397db1397902070f93a4d6ddbc0e0e6e8", chainId: 8453 },
9011
- { symbol: "VLONE", name: "Reserve Venionaire L1 Select DTF", address: "0xe00cfa595841fb331105b93c19827797c925e3e4", chainId: 8453 },
9012
- { symbol: "BGCI", name: "Bloomberg Galaxy Crypto Index", address: "0x23418de10d422ad71c9d5713a2b8991a9c586443", chainId: 8453 },
9013
- { symbol: "ABX", name: "Alpha Base Index", address: "0xebcda5b80f62dd4dd2a96357b42bb6facbf30267", chainId: 8453 },
9014
- { symbol: "OPEN", name: "Open Stablecoin Index", address: "0x323c03c48660fe31186fa82c289b0766d331ce21", chainId: 1 },
9015
- { symbol: "CLX", name: "Clanker Index", address: "0x44551ca46fa5592bb572e20043f7c3d54c85cad7", chainId: 8453 },
9016
- { symbol: "BED", name: "BTC ETH DCA Index", address: "0x4e3b170dcbe704b248df5f56d488114ace01b1c5", chainId: 1 },
9017
- { symbol: "SMEL", name: "Imagine The SMEL", address: "0xf91384484f4717314798e8975bcd904a35fc2bf1", chainId: 1 },
9018
- { symbol: "MVTT10F", name: "MarketVector Token Terminal", address: "0xe8b46b116d3bdfa787ce9cf3f5acc78dc7ca380e", chainId: 8453 },
9019
- { symbol: "ixEdel", name: "Sagix Club Edelweiss", address: "0xe4a10951f962e6cb93cb843a4ef05d2f99db1f94", chainId: 1 },
9020
- { symbol: "DGI", name: "DeFi Growth Index", address: "0x9a1741e151233a82cf69209a2f1bc7442b1fb29c", chainId: 1 },
9021
- { symbol: "DFX", name: "CoinDesk DeFi Select Index", address: "0x188d12eb13a5eadd0867074ce8354b1ad6f4790b", chainId: 1 },
9022
- { symbol: "mvRWA", name: "RWA Index", address: "0xa5cdea03b11042fc10b52af9eca48bb17a2107d2", chainId: 1 },
9023
- { symbol: "mvDEFI", name: "Large Cap DeFi Index", address: "0x20d81101d254729a6e689418526be31e2c544290", chainId: 1 },
9024
- { symbol: "AI", name: "AIndex", address: "0xfe45eda533e97198d9f3deeda9ae6c147141f6f9", chainId: 8453 },
9025
- { symbol: "VTF", name: "Virtuals Index", address: "0x47686106181b3cefe4eaf94c4c10b48ac750370b", chainId: 8453 },
9026
- { symbol: "ZINDEX", name: "Zora Index", address: "0x160c18476f6f5099f374033fbc695c9234cda495", chainId: 8453 },
9027
- { symbol: "BDTF", name: "Base MemeIndexer DTF", address: "0xb8753941196692e322846cfee9c14c97ac81928a", chainId: 8453 },
9028
- { symbol: "CLUB", name: "Club Night DTF", address: "0xf8ef6e785473e82527908b06023ac3e401ccfdcd", chainId: 8453 },
9029
- { symbol: "MVDA25", name: "MarketVector Digital Assets 25", address: "0xd600e748c17ca237fcb5967fa13d688aff17be78", chainId: 8453 },
9030
- { symbol: "SBR", name: "Strategic Base Reserve", address: "0x89ff8f639d402839205a6bf03cc01bdffa4768b7", chainId: 8453 }
9020
+ { symbol: "CMC20", name: "CoinMarketCap 20 Index DTF", address: "0x2f8a339b5889ffac4c5a956787cda593b3c36867", chainId: 56, type: "index" },
9021
+ { symbol: "LCAP", name: "CF Large Cap Index", address: "0x4da9a0f397db1397902070f93a4d6ddbc0e0e6e8", chainId: 8453, type: "index" },
9022
+ { symbol: "VLONE", name: "Reserve Venionaire L1 Select DTF", address: "0xe00cfa595841fb331105b93c19827797c925e3e4", chainId: 8453, type: "index" },
9023
+ { symbol: "BGCI", name: "Bloomberg Galaxy Crypto Index", address: "0x23418de10d422ad71c9d5713a2b8991a9c586443", chainId: 8453, type: "index" },
9024
+ { symbol: "ABX", name: "Alpha Base Index", address: "0xebcda5b80f62dd4dd2a96357b42bb6facbf30267", chainId: 8453, type: "index" },
9025
+ { symbol: "OPEN", name: "Open Stablecoin Index", address: "0x323c03c48660fe31186fa82c289b0766d331ce21", chainId: 1, type: "index" },
9026
+ { symbol: "CLX", name: "Clanker Index", address: "0x44551ca46fa5592bb572e20043f7c3d54c85cad7", chainId: 8453, type: "index" },
9027
+ { symbol: "BED", name: "BTC ETH DCA Index", address: "0x4e3b170dcbe704b248df5f56d488114ace01b1c5", chainId: 1, type: "index" },
9028
+ { symbol: "SMEL", name: "Imagine The SMEL", address: "0xf91384484f4717314798e8975bcd904a35fc2bf1", chainId: 1, type: "index" },
9029
+ { symbol: "MVTT10F", name: "MarketVector Token Terminal", address: "0xe8b46b116d3bdfa787ce9cf3f5acc78dc7ca380e", chainId: 8453, type: "index" },
9030
+ { symbol: "ixEdel", name: "Sagix Club Edelweiss", address: "0xe4a10951f962e6cb93cb843a4ef05d2f99db1f94", chainId: 1, type: "index" },
9031
+ { symbol: "DGI", name: "DeFi Growth Index", address: "0x9a1741e151233a82cf69209a2f1bc7442b1fb29c", chainId: 1, type: "index" },
9032
+ { symbol: "DFX", name: "CoinDesk DeFi Select Index", address: "0x188d12eb13a5eadd0867074ce8354b1ad6f4790b", chainId: 1, type: "index" },
9033
+ { symbol: "mvRWA", name: "RWA Index", address: "0xa5cdea03b11042fc10b52af9eca48bb17a2107d2", chainId: 1, type: "index" },
9034
+ { symbol: "mvDEFI", name: "Large Cap DeFi Index", address: "0x20d81101d254729a6e689418526be31e2c544290", chainId: 1, type: "index" },
9035
+ { symbol: "AI", name: "AIndex", address: "0xfe45eda533e97198d9f3deeda9ae6c147141f6f9", chainId: 8453, type: "index" },
9036
+ { symbol: "VTF", name: "Virtuals Index", address: "0x47686106181b3cefe4eaf94c4c10b48ac750370b", chainId: 8453, type: "index" },
9037
+ { symbol: "ZINDEX", name: "Zora Index", address: "0x160c18476f6f5099f374033fbc695c9234cda495", chainId: 8453, type: "index" },
9038
+ { symbol: "BDTF", name: "Base MemeIndexer DTF", address: "0xb8753941196692e322846cfee9c14c97ac81928a", chainId: 8453, type: "index" },
9039
+ { symbol: "CLUB", name: "Club Night DTF", address: "0xf8ef6e785473e82527908b06023ac3e401ccfdcd", chainId: 8453, type: "index" },
9040
+ { symbol: "MVDA25", name: "MarketVector Digital Assets 25", address: "0xd600e748c17ca237fcb5967fa13d688aff17be78", chainId: 8453, type: "index" },
9041
+ { symbol: "SBR", name: "Strategic Base Reserve", address: "0x89ff8f639d402839205a6bf03cc01bdffa4768b7", chainId: 8453, type: "index" },
9042
+ { symbol: "dgnETH", name: "Degen ETH", address: "0x005F893EcD7bF9667195642f7649DA8163e23658", chainId: 1, type: "yield" },
9043
+ { symbol: "eUSD", name: "Electronic Dollar", address: "0xA0d69E286B938e21CBf7E51D71F6A4c8918f482F", chainId: 1, type: "yield" },
9044
+ { symbol: "ETH+", name: "ETHPlus", address: "0xE72B141DF173b999AE7c1aDcbF60Cc9833Ce56a8", chainId: 1, type: "yield" },
9045
+ { symbol: "hyUSD", name: "High Yield USD", address: "0xaCdf0DBA4B9839b96221a8487e9ca660a48212be", chainId: 1, type: "yield" },
9046
+ { symbol: "USDC+", name: "USDC Plus", address: "0xFc0B1EEf20e4c68B3DCF36c4537Cfa7Ce46CA70b", chainId: 1, type: "yield" },
9047
+ { symbol: "USD3", name: "Web 3 Dollar", address: "0x0d86883FAf4FfD7aEb116390af37746F45b6f378", chainId: 1, type: "yield" },
9048
+ { symbol: "rgUSD", name: "Revenue Generating USD", address: "0x78da5799CF427Fee11e9996982F4150eCe7a99A7", chainId: 1, type: "yield" },
9049
+ { symbol: "hyUSD", name: "High Yield USD", address: "0xCc7FF230365bD730eE4B352cC2492CEdAC49383e", chainId: 8453, type: "yield" },
9050
+ { symbol: "BSDX", name: "Base Yield Index", address: "0x8f0987DDb485219c767770e2080E5cC01ddc772a", chainId: 8453, type: "yield" },
9051
+ { symbol: "bsdETH", name: "Based ETH", address: "0xCb327b99fF831bF8223cCEd12B1338FF3aA322Ff", chainId: 8453, type: "yield" },
9052
+ { symbol: "Vaya", name: "Vaya", address: "0xC9a3e2B3064c1c0546D3D0edc0A748E9f93Cf18d", chainId: 8453, type: "yield" },
9053
+ { symbol: "MAAT", name: "Maat", address: "0x641B0453487C9D14c5df96d45a481ef1dc84e31f", chainId: 8453, type: "yield" }
9031
9054
  ];
9032
9055
  function resolveAlias(input) {
9033
9056
  const lower = input.toLowerCase();
9034
9057
  const bySymbol = REGISTRY.find((d) => d.symbol.toLowerCase() === lower);
9035
9058
  if (bySymbol)
9036
- return { address: bySymbol.address, chainId: bySymbol.chainId };
9059
+ return { address: bySymbol.address, chainId: bySymbol.chainId, type: bySymbol.type };
9037
9060
  const nameMatches = REGISTRY.filter((d) => d.name.toLowerCase().includes(lower));
9038
9061
  if (nameMatches.length === 1) {
9039
- return { address: nameMatches[0].address, chainId: nameMatches[0].chainId };
9062
+ return { address: nameMatches[0].address, chainId: nameMatches[0].chainId, type: nameMatches[0].type };
9040
9063
  }
9041
9064
  if (nameMatches.length > 1) {
9042
9065
  return {
@@ -9049,6 +9072,10 @@ function resolveAlias(input) {
9049
9072
  }
9050
9073
  return null;
9051
9074
  }
9075
+ function getRegistryType(address) {
9076
+ const lower = address.toLowerCase();
9077
+ return REGISTRY.find((d) => d.address.toLowerCase() === lower)?.type;
9078
+ }
9052
9079
 
9053
9080
  // src/lib/cache.ts
9054
9081
  import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync, rmSync } from "fs";
@@ -9104,7 +9131,7 @@ function setCache(chainId, address, data) {
9104
9131
 
9105
9132
  // ../sdk/dist/index.js
9106
9133
  init_index_qp8fkhx7();
9107
- init_index_tdmavmz7();
9134
+ init_index_azrx1hqn();
9108
9135
  init_index_sdksp5px();
9109
9136
  var require_types = __commonJS((exports) => {
9110
9137
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -19505,6 +19532,14 @@ var SUBGRAPH_URLS = {
19505
19532
  56: "https://api.goldsky.com/api/public/project_cmgzim3e100095np2gjnbh6ry/subgraphs/dtf-index-bsc/prod/gn",
19506
19533
  8453: "https://api.goldsky.com/api/public/project_cmgzim3e100095np2gjnbh6ry/subgraphs/dtf-index-base/prod/gn"
19507
19534
  };
19535
+ var YIELD_SUBGRAPH_URLS = {
19536
+ 1: "https://api.goldsky.com/api/public/project_cmgzim3e100095np2gjnbh6ry/subgraphs/dtf-yield-mainnet/4.2.0-v2/gn",
19537
+ 8453: "https://api.goldsky.com/api/public/project_cmgzim3e100095np2gjnbh6ry/subgraphs/dtf-yield-base/4.2.0-v2/gn"
19538
+ };
19539
+ var FACADE_READ_ADDRESS = {
19540
+ 1: "0x2C7ca56342177343A2954C250702Fd464f4d0613",
19541
+ 8453: "0xeb2071e9b542555e90e6e4e1f83fa17423583991"
19542
+ };
19508
19543
  var SUPPORTED_CHAINS = [1, 56, 8453];
19509
19544
  var CHAINLINK_BTC_USD = "0xF4030086522a5bEEa4988F8cA5B36dbC97BeE88c";
19510
19545
  var CHAINLINK_RSR_USD = "0x759bBC1be8F90eE6457C44abc7d443842a976d02";
@@ -19600,11 +19635,11 @@ async function fetchWithRetry(url, init, opts) {
19600
19635
  }
19601
19636
  throw lastError;
19602
19637
  }
19603
- async function querySubgraph(chainId, query, variables = {}) {
19604
- const url = SUBGRAPH_URLS[chainId];
19605
- if (!url)
19638
+ async function querySubgraph(chainId, query, variables = {}, url) {
19639
+ const endpoint = url ?? SUBGRAPH_URLS[chainId];
19640
+ if (!endpoint)
19606
19641
  throw new Error(`No subgraph URL for chainId ${chainId}`);
19607
- const response = await fetchWithRetry(url, {
19642
+ const response = await fetchWithRetry(endpoint, {
19608
19643
  method: "POST",
19609
19644
  headers: { "Content-Type": "application/json" },
19610
19645
  body: JSON.stringify({ query, variables })
@@ -21176,240 +21211,705 @@ async function fetchVoteLockPositions(baseUrl = RESERVE_API_BASE_URL) {
21176
21211
  }
21177
21212
  return await response.json();
21178
21213
  }
21179
- async function readMintQuote(publicClient, folioAddress, shares) {
21180
- const results = await publicClient.multicall({
21181
- contracts: [
21182
- {
21183
- address: folioAddress,
21184
- abi: folio_default,
21185
- functionName: "toAssets",
21186
- args: [shares, 1]
21187
- },
21188
- {
21189
- address: folioAddress,
21190
- abi: folio_default,
21191
- functionName: "mintFee"
21192
- }
21193
- ]
21194
- });
21195
- if (results[0].status !== "success")
21196
- throw new Error(`toAssets failed for ${folioAddress} with ${shares} shares`);
21197
- if (results[1].status !== "success")
21198
- throw new Error(`mintFee failed for ${folioAddress}`);
21199
- const [assets, amounts] = results[0].result;
21200
- const mintFee = results[1].result;
21201
- const minSharesOut = shares - shares * mintFee / D18;
21202
- return { shares, assets, amounts, mintFee, minSharesOut };
21203
- }
21204
- async function readMintFee(publicClient, folioAddress) {
21205
- return await publicClient.readContract({
21206
- address: folioAddress,
21207
- abi: folio_default,
21208
- functionName: "mintFee"
21209
- });
21210
- }
21211
- function applySlippage(amount, slippageBps) {
21212
- if (slippageBps < 0n || slippageBps > 10000n) {
21213
- throw new Error(`slippageBps must be between 0 and 10000, got ${slippageBps}`);
21214
- }
21215
- return amount - amount * slippageBps / 10000n;
21216
- }
21217
- async function readRedeemQuote(publicClient, folioAddress, shares) {
21218
- const result = await publicClient.readContract({
21219
- address: folioAddress,
21220
- abi: folio_default,
21221
- functionName: "toAssets",
21222
- args: [shares, 0]
21223
- });
21224
- const [assets, amounts] = result;
21225
- return { shares, assets, amounts };
21226
- }
21227
- async function readPendingFees(publicClient, folioAddress) {
21228
- const results = await publicClient.multicall({
21229
- contracts: [
21230
- {
21231
- address: folioAddress,
21232
- abi: folio_default,
21233
- functionName: "getPendingFeeShares"
21234
- },
21235
- {
21236
- address: folioAddress,
21237
- abi: folio_default,
21238
- functionName: "feeRecipientsPendingFeeShares"
21239
- },
21240
- {
21241
- address: folioAddress,
21242
- abi: folio_default,
21243
- functionName: "daoPendingFeeShares"
21244
- }
21245
- ]
21246
- });
21247
- const total = results[0].status === "success" ? results[0].result : 0n;
21248
- const folio = results[1].status === "success" ? results[1].result : 0n;
21249
- const dao = results[2].status === "success" ? results[2].result : 0n;
21250
- return { total, folio, dao };
21251
- }
21252
- async function readFeeRecipients(publicClient, folioAddress) {
21253
- const indices = Array.from({ length: 10 }, (_, i) => i);
21254
- const results = await publicClient.multicall({
21255
- contracts: indices.map((i) => ({
21256
- address: folioAddress,
21257
- abi: folio_default,
21258
- functionName: "feeRecipients",
21259
- args: [BigInt(i)]
21260
- }))
21261
- });
21262
- const recipients = [];
21263
- for (const r of results) {
21264
- if (r.status !== "success")
21265
- break;
21266
- const [recipient, portion] = r.result;
21267
- recipients.push({ recipient, portion });
21268
- }
21269
- return recipients;
21270
- }
21271
- async function fetchRebalanceHistory(chainId, folioAddress, opts, baseUrl = RESERVE_API_BASE_URL) {
21272
- const skip = opts?.skip ?? 0;
21273
- const limit = opts?.limit ?? 50;
21274
- const url = `${baseUrl}/dtf/rebalance?address=${folioAddress.toLowerCase()}&chainId=${chainId}&skip=${skip}&limit=${limit}`;
21275
- const response = await fetchWithRetry(url);
21276
- if (!response.ok) {
21277
- throw new Error(`Reserve API /dtf/rebalance failed: ${response.status} ${response.statusText}`);
21278
- }
21279
- return await response.json();
21280
- }
21281
- async function fetchRebalanceDetail(chainId, folioAddress, nonce, baseUrl = RESERVE_API_BASE_URL) {
21282
- const url = `${baseUrl}/dtf/rebalance?address=${folioAddress.toLowerCase()}&chainId=${chainId}&nonce=${nonce}`;
21283
- const response = await fetchWithRetry(url);
21284
- if (!response.ok) {
21285
- throw new Error(`Reserve API /dtf/rebalance detail failed: ${response.status} ${response.statusText}`);
21286
- }
21287
- return await response.json();
21288
- }
21289
- async function fetchHistoricalDtf(chainId, folioAddress, opts, baseUrl = RESERVE_API_BASE_URL) {
21290
- const now = Math.floor(Date.now() / 1000);
21291
- const to = opts?.to ?? now;
21292
- const from14 = opts?.from ?? to - 2592000;
21293
- const interval = opts?.interval ?? "1h";
21294
- const url = `${baseUrl}/historical/dtf?address=${folioAddress.toLowerCase()}&chainId=${chainId}&from=${from14}&to=${to}&interval=${interval}`;
21295
- const response = await fetchWithRetry(url);
21296
- if (!response.ok) {
21297
- throw new Error(`Reserve API /historical/dtf failed: ${response.status} ${response.statusText}`);
21298
- }
21299
- const data = await response.json();
21300
- if (Array.isArray(data))
21301
- return data;
21302
- return data.timeseries ?? [];
21303
- }
21304
- var governor_default = [
21214
+ var stRSRAbi = [
21305
21215
  {
21306
- type: "constructor",
21307
21216
  inputs: [],
21308
- stateMutability: "nonpayable"
21217
+ name: "exchangeRate",
21218
+ outputs: [{ internalType: "uint192", name: "", type: "uint192" }],
21219
+ stateMutability: "view",
21220
+ type: "function"
21309
21221
  },
21310
21222
  {
21311
- type: "receive",
21312
- stateMutability: "payable"
21223
+ inputs: [],
21224
+ name: "totalSupply",
21225
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
21226
+ stateMutability: "view",
21227
+ type: "function"
21313
21228
  },
21314
21229
  {
21315
- type: "function",
21316
- name: "BALLOT_TYPEHASH",
21317
21230
  inputs: [],
21318
- outputs: [
21319
- {
21320
- name: "",
21321
- type: "bytes32",
21322
- internalType: "bytes32"
21323
- }
21231
+ name: "unstakingDelay",
21232
+ outputs: [{ internalType: "uint48", name: "", type: "uint48" }],
21233
+ stateMutability: "view",
21234
+ type: "function"
21235
+ },
21236
+ {
21237
+ inputs: [
21238
+ { internalType: "address", name: "account", type: "address" }
21324
21239
  ],
21325
- stateMutability: "view"
21240
+ name: "balanceOf",
21241
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
21242
+ stateMutability: "view",
21243
+ type: "function"
21326
21244
  },
21327
21245
  {
21328
- type: "function",
21329
- name: "CLOCK_MODE",
21330
21246
  inputs: [],
21331
- outputs: [
21332
- {
21333
- name: "",
21334
- type: "string",
21335
- internalType: "string"
21336
- }
21337
- ],
21338
- stateMutability: "view"
21247
+ name: "main",
21248
+ outputs: [{ internalType: "contract IMain", name: "", type: "address" }],
21249
+ stateMutability: "view",
21250
+ type: "function"
21339
21251
  },
21340
21252
  {
21341
- type: "function",
21342
- name: "COUNTING_MODE",
21343
21253
  inputs: [],
21344
- outputs: [
21345
- {
21346
- name: "",
21347
- type: "string",
21348
- internalType: "string"
21349
- }
21350
- ],
21351
- stateMutability: "pure"
21254
+ name: "name",
21255
+ outputs: [{ internalType: "string", name: "", type: "string" }],
21256
+ stateMutability: "view",
21257
+ type: "function"
21352
21258
  },
21353
21259
  {
21354
- type: "function",
21355
- name: "EXTENDED_BALLOT_TYPEHASH",
21356
21260
  inputs: [],
21357
- outputs: [
21358
- {
21359
- name: "",
21360
- type: "bytes32",
21361
- internalType: "bytes32"
21362
- }
21363
- ],
21364
- stateMutability: "view"
21261
+ name: "symbol",
21262
+ outputs: [{ internalType: "string", name: "", type: "string" }],
21263
+ stateMutability: "view",
21264
+ type: "function"
21365
21265
  },
21366
21266
  {
21367
- type: "function",
21368
- name: "cancel",
21369
21267
  inputs: [
21370
- {
21371
- name: "targets",
21372
- type: "address[]",
21373
- internalType: "address[]"
21374
- },
21375
- {
21376
- name: "values",
21377
- type: "uint256[]",
21378
- internalType: "uint256[]"
21379
- },
21380
- {
21381
- name: "calldatas",
21382
- type: "bytes[]",
21383
- internalType: "bytes[]"
21384
- },
21385
- {
21386
- name: "descriptionHash",
21387
- type: "bytes32",
21388
- internalType: "bytes32"
21389
- }
21390
- ],
21391
- outputs: [
21392
- {
21393
- name: "",
21394
- type: "uint256",
21395
- internalType: "uint256"
21396
- }
21268
+ { internalType: "address", name: "account", type: "address" }
21397
21269
  ],
21398
- stateMutability: "nonpayable"
21270
+ name: "delegates",
21271
+ outputs: [{ internalType: "address", name: "", type: "address" }],
21272
+ stateMutability: "view",
21273
+ type: "function"
21399
21274
  },
21400
21275
  {
21401
- type: "function",
21402
- name: "castVote",
21403
21276
  inputs: [
21404
- {
21405
- name: "proposalId",
21406
- type: "uint256",
21407
- internalType: "uint256"
21408
- },
21409
- {
21410
- name: "support",
21411
- type: "uint8",
21412
- internalType: "uint8"
21277
+ { internalType: "address", name: "account", type: "address" }
21278
+ ],
21279
+ name: "getVotes",
21280
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
21281
+ stateMutability: "view",
21282
+ type: "function"
21283
+ }
21284
+ ];
21285
+ var mainAbi = [
21286
+ {
21287
+ inputs: [],
21288
+ name: "rToken",
21289
+ outputs: [{ internalType: "contract IRToken", name: "", type: "address" }],
21290
+ stateMutability: "view",
21291
+ type: "function"
21292
+ },
21293
+ {
21294
+ inputs: [],
21295
+ name: "stRSR",
21296
+ outputs: [{ internalType: "contract IStRSR", name: "", type: "address" }],
21297
+ stateMutability: "view",
21298
+ type: "function"
21299
+ },
21300
+ {
21301
+ inputs: [],
21302
+ name: "assetRegistry",
21303
+ outputs: [{ internalType: "contract IAssetRegistry", name: "", type: "address" }],
21304
+ stateMutability: "view",
21305
+ type: "function"
21306
+ },
21307
+ {
21308
+ inputs: [],
21309
+ name: "basketHandler",
21310
+ outputs: [{ internalType: "contract IBasketHandler", name: "", type: "address" }],
21311
+ stateMutability: "view",
21312
+ type: "function"
21313
+ },
21314
+ {
21315
+ inputs: [],
21316
+ name: "backingManager",
21317
+ outputs: [{ internalType: "contract IBackingManager", name: "", type: "address" }],
21318
+ stateMutability: "view",
21319
+ type: "function"
21320
+ },
21321
+ {
21322
+ inputs: [],
21323
+ name: "distributor",
21324
+ outputs: [{ internalType: "contract IDistributor", name: "", type: "address" }],
21325
+ stateMutability: "view",
21326
+ type: "function"
21327
+ },
21328
+ {
21329
+ inputs: [],
21330
+ name: "furnace",
21331
+ outputs: [{ internalType: "contract IFurnace", name: "", type: "address" }],
21332
+ stateMutability: "view",
21333
+ type: "function"
21334
+ },
21335
+ {
21336
+ inputs: [],
21337
+ name: "broker",
21338
+ outputs: [{ internalType: "contract IBroker", name: "", type: "address" }],
21339
+ stateMutability: "view",
21340
+ type: "function"
21341
+ },
21342
+ {
21343
+ inputs: [],
21344
+ name: "rsrTrader",
21345
+ outputs: [{ internalType: "contract IRevenueTrader", name: "", type: "address" }],
21346
+ stateMutability: "view",
21347
+ type: "function"
21348
+ },
21349
+ {
21350
+ inputs: [],
21351
+ name: "rTokenTrader",
21352
+ outputs: [{ internalType: "contract IRevenueTrader", name: "", type: "address" }],
21353
+ stateMutability: "view",
21354
+ type: "function"
21355
+ }
21356
+ ];
21357
+ async function detectDtfType(publicClient, address) {
21358
+ try {
21359
+ const mainAddress = await publicClient.readContract({
21360
+ address,
21361
+ abi: stRSRAbi,
21362
+ functionName: "main"
21363
+ });
21364
+ const rTokenAddress = await publicClient.readContract({
21365
+ address: mainAddress,
21366
+ abi: mainAbi,
21367
+ functionName: "rToken"
21368
+ });
21369
+ if (rTokenAddress.toLowerCase() === address.toLowerCase()) {
21370
+ return "yield";
21371
+ }
21372
+ return "index";
21373
+ } catch {
21374
+ return "index";
21375
+ }
21376
+ }
21377
+ async function readYieldDtfComponents(publicClient, rTokenAddress) {
21378
+ const mainAddress = await publicClient.readContract({
21379
+ address: rTokenAddress,
21380
+ abi: stRSRAbi,
21381
+ functionName: "main"
21382
+ });
21383
+ const results = await publicClient.multicall({
21384
+ contracts: [
21385
+ { address: mainAddress, abi: mainAbi, functionName: "rToken" },
21386
+ { address: mainAddress, abi: mainAbi, functionName: "stRSR" },
21387
+ { address: mainAddress, abi: mainAbi, functionName: "assetRegistry" },
21388
+ { address: mainAddress, abi: mainAbi, functionName: "basketHandler" },
21389
+ { address: mainAddress, abi: mainAbi, functionName: "backingManager" },
21390
+ { address: mainAddress, abi: mainAbi, functionName: "distributor" },
21391
+ { address: mainAddress, abi: mainAbi, functionName: "furnace" },
21392
+ { address: mainAddress, abi: mainAbi, functionName: "broker" },
21393
+ { address: mainAddress, abi: mainAbi, functionName: "rsrTrader" },
21394
+ { address: mainAddress, abi: mainAbi, functionName: "rTokenTrader" }
21395
+ ],
21396
+ allowFailure: false
21397
+ });
21398
+ return {
21399
+ main: mainAddress,
21400
+ rToken: results[0],
21401
+ stRSR: results[1],
21402
+ assetRegistry: results[2],
21403
+ basketHandler: results[3],
21404
+ backingManager: results[4],
21405
+ distributor: results[5],
21406
+ furnace: results[6],
21407
+ broker: results[7],
21408
+ rsrTrader: results[8],
21409
+ rTokenTrader: results[9]
21410
+ };
21411
+ }
21412
+ var facadeReadAbi = [
21413
+ {
21414
+ inputs: [
21415
+ { internalType: "contract IRToken", name: "rToken", type: "address" }
21416
+ ],
21417
+ name: "backingOverview",
21418
+ outputs: [
21419
+ { internalType: "uint192", name: "backing", type: "uint192" },
21420
+ { internalType: "uint192", name: "overCollateralization", type: "uint192" }
21421
+ ],
21422
+ stateMutability: "view",
21423
+ type: "function"
21424
+ },
21425
+ {
21426
+ inputs: [
21427
+ { internalType: "contract IRToken", name: "rToken", type: "address" }
21428
+ ],
21429
+ name: "basketBreakdown",
21430
+ outputs: [
21431
+ { internalType: "address[]", name: "erc20s", type: "address[]" },
21432
+ { internalType: "uint192[]", name: "uoaShares", type: "uint192[]" },
21433
+ { internalType: "bytes32[]", name: "targets", type: "bytes32[]" }
21434
+ ],
21435
+ stateMutability: "nonpayable",
21436
+ type: "function"
21437
+ },
21438
+ {
21439
+ inputs: [
21440
+ { internalType: "contract IRToken", name: "rToken", type: "address" }
21441
+ ],
21442
+ name: "basketTokens",
21443
+ outputs: [
21444
+ { internalType: "address[]", name: "tokens", type: "address[]" }
21445
+ ],
21446
+ stateMutability: "view",
21447
+ type: "function"
21448
+ },
21449
+ {
21450
+ inputs: [
21451
+ { internalType: "contract IRToken", name: "rToken", type: "address" },
21452
+ { internalType: "uint256", name: "amount", type: "uint256" }
21453
+ ],
21454
+ name: "issue",
21455
+ outputs: [
21456
+ { internalType: "address[]", name: "tokens", type: "address[]" },
21457
+ { internalType: "uint256[]", name: "deposits", type: "uint256[]" },
21458
+ { internalType: "uint192[]", name: "depositsUoA", type: "uint192[]" }
21459
+ ],
21460
+ stateMutability: "nonpayable",
21461
+ type: "function"
21462
+ },
21463
+ {
21464
+ inputs: [
21465
+ { internalType: "contract IRToken", name: "rToken", type: "address" }
21466
+ ],
21467
+ name: "price",
21468
+ outputs: [
21469
+ { internalType: "uint192", name: "low", type: "uint192" },
21470
+ { internalType: "uint192", name: "high", type: "uint192" }
21471
+ ],
21472
+ stateMutability: "view",
21473
+ type: "function"
21474
+ },
21475
+ {
21476
+ inputs: [
21477
+ { internalType: "contract IRToken", name: "rToken", type: "address" },
21478
+ { internalType: "uint256", name: "amount", type: "uint256" }
21479
+ ],
21480
+ name: "redeem",
21481
+ outputs: [
21482
+ { internalType: "address[]", name: "tokens", type: "address[]" },
21483
+ { internalType: "uint256[]", name: "withdrawals", type: "uint256[]" },
21484
+ { internalType: "uint256[]", name: "available", type: "uint256[]" }
21485
+ ],
21486
+ stateMutability: "nonpayable",
21487
+ type: "function"
21488
+ },
21489
+ {
21490
+ inputs: [
21491
+ { internalType: "contract IRToken", name: "rToken", type: "address" }
21492
+ ],
21493
+ name: "stToken",
21494
+ outputs: [
21495
+ { internalType: "contract IStRSR", name: "stTokenAddress", type: "address" }
21496
+ ],
21497
+ stateMutability: "view",
21498
+ type: "function"
21499
+ }
21500
+ ];
21501
+ async function readYieldDtfBasket(publicClient, facadeAddress, rTokenAddress) {
21502
+ const result = await publicClient.readContract({
21503
+ address: facadeAddress,
21504
+ abi: facadeReadAbi,
21505
+ functionName: "basketBreakdown",
21506
+ args: [rTokenAddress]
21507
+ });
21508
+ const [erc20s, uoaShares, targets] = result;
21509
+ const tokenMeta = await fetchTokenMetadata(publicClient, erc20s);
21510
+ return tokenMeta.map((token, i) => ({
21511
+ address: token.address,
21512
+ symbol: token.symbol,
21513
+ decimals: token.decimals,
21514
+ targetUnit: decodeBytes32(targets[i]),
21515
+ uoaShare: uoaShares[i]
21516
+ }));
21517
+ }
21518
+ function decodeBytes32(hex) {
21519
+ if (hex === "0x0000000000000000000000000000000000000000000000000000000000000000")
21520
+ return "";
21521
+ try {
21522
+ return hexToString(hex, { size: 32 });
21523
+ } catch {
21524
+ return hex;
21525
+ }
21526
+ }
21527
+ async function readBackingOverview(publicClient, facadeAddress, rTokenAddress) {
21528
+ const [backing, overCollateralization] = await publicClient.readContract({
21529
+ address: facadeAddress,
21530
+ abi: facadeReadAbi,
21531
+ functionName: "backingOverview",
21532
+ args: [rTokenAddress]
21533
+ });
21534
+ return {
21535
+ backing,
21536
+ overCollateralization
21537
+ };
21538
+ }
21539
+ async function readYieldDtfPrice(publicClient, facadeAddress, rTokenAddress) {
21540
+ const [low, high] = await publicClient.readContract({
21541
+ address: facadeAddress,
21542
+ abi: facadeReadAbi,
21543
+ functionName: "price",
21544
+ args: [rTokenAddress]
21545
+ });
21546
+ return { low, high };
21547
+ }
21548
+ async function readStRSRInfo(publicClient, stRSRAddress, account) {
21549
+ const baseContracts = [
21550
+ { address: stRSRAddress, abi: stRSRAbi, functionName: "exchangeRate" },
21551
+ { address: stRSRAddress, abi: stRSRAbi, functionName: "totalSupply" },
21552
+ { address: stRSRAddress, abi: stRSRAbi, functionName: "unstakingDelay" }
21553
+ ];
21554
+ const accountContracts = account ? [
21555
+ { address: stRSRAddress, abi: stRSRAbi, functionName: "balanceOf", args: [account] },
21556
+ { address: stRSRAddress, abi: stRSRAbi, functionName: "getVotes", args: [account] },
21557
+ { address: stRSRAddress, abi: stRSRAbi, functionName: "delegates", args: [account] }
21558
+ ] : [];
21559
+ const results = await publicClient.multicall({
21560
+ contracts: [...baseContracts, ...accountContracts],
21561
+ allowFailure: false
21562
+ });
21563
+ const info = {
21564
+ exchangeRate: results[0],
21565
+ totalSupply: results[1],
21566
+ unstakingDelay: results[2]
21567
+ };
21568
+ if (account) {
21569
+ info.balance = results[3];
21570
+ info.votingPower = results[4];
21571
+ info.delegatee = results[5];
21572
+ }
21573
+ return info;
21574
+ }
21575
+ var distributorAbi = [
21576
+ {
21577
+ inputs: [],
21578
+ name: "totals",
21579
+ outputs: [
21580
+ {
21581
+ components: [
21582
+ { internalType: "uint24", name: "rTokenTotal", type: "uint24" },
21583
+ { internalType: "uint24", name: "rsrTotal", type: "uint24" }
21584
+ ],
21585
+ internalType: "struct RevenueTotals",
21586
+ name: "revTotals",
21587
+ type: "tuple"
21588
+ }
21589
+ ],
21590
+ stateMutability: "view",
21591
+ type: "function"
21592
+ }
21593
+ ];
21594
+ async function readDistribution(publicClient, distributorAddress) {
21595
+ const result = await publicClient.readContract({
21596
+ address: distributorAddress,
21597
+ abi: distributorAbi,
21598
+ functionName: "totals"
21599
+ });
21600
+ const totals = result;
21601
+ return {
21602
+ rTokenTotal: BigInt(totals.rTokenTotal),
21603
+ rsrTotal: BigInt(totals.rsrTotal)
21604
+ };
21605
+ }
21606
+ async function fetchYieldDtfConfig(publicClient, chainId, rTokenAddress) {
21607
+ const facadeAddress = FACADE_READ_ADDRESS[chainId];
21608
+ if (!facadeAddress) {
21609
+ throw new Error(`No FacadeRead address for chainId ${chainId}`);
21610
+ }
21611
+ const components = await readYieldDtfComponents(publicClient, rTokenAddress);
21612
+ const [basket, backing, price, nameResult, symbolResult, stRSRInfo, distribution] = await Promise.all([
21613
+ readYieldDtfBasket(publicClient, facadeAddress, rTokenAddress),
21614
+ readBackingOverview(publicClient, facadeAddress, rTokenAddress),
21615
+ readYieldDtfPrice(publicClient, facadeAddress, rTokenAddress),
21616
+ publicClient.readContract({
21617
+ address: rTokenAddress,
21618
+ abi: ERC20_META_ABI,
21619
+ functionName: "name"
21620
+ }),
21621
+ publicClient.readContract({
21622
+ address: rTokenAddress,
21623
+ abi: ERC20_META_ABI,
21624
+ functionName: "symbol"
21625
+ }),
21626
+ readStRSRInfo(publicClient, components.stRSR),
21627
+ readDistribution(publicClient, components.distributor)
21628
+ ]);
21629
+ return {
21630
+ type: "yield",
21631
+ components,
21632
+ name: nameResult,
21633
+ symbol: symbolResult,
21634
+ basket,
21635
+ backing,
21636
+ priceLow: price.low,
21637
+ priceHigh: price.high,
21638
+ exchangeRate: stRSRInfo.exchangeRate,
21639
+ unstakingDelay: stRSRInfo.unstakingDelay,
21640
+ rTokenTotal: distribution.rTokenTotal,
21641
+ rsrTotal: distribution.rsrTotal
21642
+ };
21643
+ }
21644
+ async function readIssueQuote(publicClient, facadeAddress, rTokenAddress, amount) {
21645
+ const result = await publicClient.readContract({
21646
+ address: facadeAddress,
21647
+ abi: facadeReadAbi,
21648
+ functionName: "issue",
21649
+ args: [rTokenAddress, amount]
21650
+ });
21651
+ const [tokens, deposits, depositsUoA] = result;
21652
+ return { tokens, deposits, depositsUoA };
21653
+ }
21654
+ async function readRedeemQuote(publicClient, facadeAddress, rTokenAddress, amount) {
21655
+ const result = await publicClient.readContract({
21656
+ address: facadeAddress,
21657
+ abi: facadeReadAbi,
21658
+ functionName: "redeem",
21659
+ args: [rTokenAddress, amount]
21660
+ });
21661
+ const [tokens, withdrawals, available] = result;
21662
+ return { tokens, withdrawals, available };
21663
+ }
21664
+ async function readMintQuote(publicClient, folioAddress, shares) {
21665
+ const results = await publicClient.multicall({
21666
+ contracts: [
21667
+ {
21668
+ address: folioAddress,
21669
+ abi: folio_default,
21670
+ functionName: "toAssets",
21671
+ args: [shares, 1]
21672
+ },
21673
+ {
21674
+ address: folioAddress,
21675
+ abi: folio_default,
21676
+ functionName: "mintFee"
21677
+ }
21678
+ ]
21679
+ });
21680
+ if (results[0].status !== "success")
21681
+ throw new Error(`toAssets failed for ${folioAddress} with ${shares} shares`);
21682
+ if (results[1].status !== "success")
21683
+ throw new Error(`mintFee failed for ${folioAddress}`);
21684
+ const [assets, amounts] = results[0].result;
21685
+ const mintFee = results[1].result;
21686
+ const minSharesOut = shares - shares * mintFee / D18;
21687
+ return { shares, assets, amounts, mintFee, minSharesOut };
21688
+ }
21689
+ async function readMintFee(publicClient, folioAddress) {
21690
+ return await publicClient.readContract({
21691
+ address: folioAddress,
21692
+ abi: folio_default,
21693
+ functionName: "mintFee"
21694
+ });
21695
+ }
21696
+ function applySlippage(amount, slippageBps) {
21697
+ if (slippageBps < 0n || slippageBps > 10000n) {
21698
+ throw new Error(`slippageBps must be between 0 and 10000, got ${slippageBps}`);
21699
+ }
21700
+ return amount - amount * slippageBps / 10000n;
21701
+ }
21702
+ async function readRedeemQuote2(publicClient, folioAddress, shares) {
21703
+ const result = await publicClient.readContract({
21704
+ address: folioAddress,
21705
+ abi: folio_default,
21706
+ functionName: "toAssets",
21707
+ args: [shares, 0]
21708
+ });
21709
+ const [assets, amounts] = result;
21710
+ return { shares, assets, amounts };
21711
+ }
21712
+ async function readPendingFees(publicClient, folioAddress) {
21713
+ const results = await publicClient.multicall({
21714
+ contracts: [
21715
+ {
21716
+ address: folioAddress,
21717
+ abi: folio_default,
21718
+ functionName: "getPendingFeeShares"
21719
+ },
21720
+ {
21721
+ address: folioAddress,
21722
+ abi: folio_default,
21723
+ functionName: "feeRecipientsPendingFeeShares"
21724
+ },
21725
+ {
21726
+ address: folioAddress,
21727
+ abi: folio_default,
21728
+ functionName: "daoPendingFeeShares"
21729
+ }
21730
+ ]
21731
+ });
21732
+ const total = results[0].status === "success" ? results[0].result : 0n;
21733
+ const folio = results[1].status === "success" ? results[1].result : 0n;
21734
+ const dao = results[2].status === "success" ? results[2].result : 0n;
21735
+ return { total, folio, dao };
21736
+ }
21737
+ async function readFeeRecipients(publicClient, folioAddress) {
21738
+ const indices = Array.from({ length: 10 }, (_, i) => i);
21739
+ const results = await publicClient.multicall({
21740
+ contracts: indices.map((i) => ({
21741
+ address: folioAddress,
21742
+ abi: folio_default,
21743
+ functionName: "feeRecipients",
21744
+ args: [BigInt(i)]
21745
+ }))
21746
+ });
21747
+ const recipients = [];
21748
+ for (const r of results) {
21749
+ if (r.status !== "success")
21750
+ break;
21751
+ const [recipient, portion] = r.result;
21752
+ recipients.push({ recipient, portion });
21753
+ }
21754
+ return recipients;
21755
+ }
21756
+ async function fetchRebalanceHistory(chainId, folioAddress, opts, baseUrl = RESERVE_API_BASE_URL) {
21757
+ const skip = opts?.skip ?? 0;
21758
+ const limit = opts?.limit ?? 50;
21759
+ const url = `${baseUrl}/dtf/rebalance?address=${folioAddress.toLowerCase()}&chainId=${chainId}&skip=${skip}&limit=${limit}`;
21760
+ const response = await fetchWithRetry(url);
21761
+ if (!response.ok) {
21762
+ throw new Error(`Reserve API /dtf/rebalance failed: ${response.status} ${response.statusText}`);
21763
+ }
21764
+ return await response.json();
21765
+ }
21766
+ async function fetchRebalanceDetail(chainId, folioAddress, nonce, baseUrl = RESERVE_API_BASE_URL) {
21767
+ const url = `${baseUrl}/dtf/rebalance?address=${folioAddress.toLowerCase()}&chainId=${chainId}&nonce=${nonce}`;
21768
+ const response = await fetchWithRetry(url);
21769
+ if (!response.ok) {
21770
+ throw new Error(`Reserve API /dtf/rebalance detail failed: ${response.status} ${response.statusText}`);
21771
+ }
21772
+ return await response.json();
21773
+ }
21774
+ async function fetchHistoricalDtf(chainId, folioAddress, opts, baseUrl = RESERVE_API_BASE_URL) {
21775
+ const now = Math.floor(Date.now() / 1000);
21776
+ const to = opts?.to ?? now;
21777
+ const from14 = opts?.from ?? to - 2592000;
21778
+ const interval = opts?.interval ?? "1h";
21779
+ const url = `${baseUrl}/historical/dtf?address=${folioAddress.toLowerCase()}&chainId=${chainId}&from=${from14}&to=${to}&interval=${interval}`;
21780
+ const response = await fetchWithRetry(url);
21781
+ if (!response.ok) {
21782
+ throw new Error(`Reserve API /historical/dtf failed: ${response.status} ${response.statusText}`);
21783
+ }
21784
+ const data = await response.json();
21785
+ if (Array.isArray(data))
21786
+ return data;
21787
+ return data.timeseries ?? [];
21788
+ }
21789
+ async function fetchHistoricalPrices(chainId, tokenAddress, opts, baseUrl = RESERVE_API_BASE_URL) {
21790
+ const now = Math.floor(Date.now() / 1000);
21791
+ const to = opts?.to ?? now;
21792
+ const from14 = opts?.from ?? to - 2592000;
21793
+ const interval = opts?.interval ?? "1h";
21794
+ const url = `${baseUrl}/historical/prices?address=${tokenAddress.toLowerCase()}&chainId=${chainId}&from=${from14}&to=${to}&interval=${interval}`;
21795
+ const response = await fetchWithRetry(url);
21796
+ if (!response.ok) {
21797
+ throw new Error(`Reserve API /historical/prices failed: ${response.status} ${response.statusText}`);
21798
+ }
21799
+ const data = await response.json();
21800
+ if (Array.isArray(data))
21801
+ return data;
21802
+ return data.timeseries ?? [];
21803
+ }
21804
+ var governor_default = [
21805
+ {
21806
+ type: "constructor",
21807
+ inputs: [],
21808
+ stateMutability: "nonpayable"
21809
+ },
21810
+ {
21811
+ type: "receive",
21812
+ stateMutability: "payable"
21813
+ },
21814
+ {
21815
+ type: "function",
21816
+ name: "BALLOT_TYPEHASH",
21817
+ inputs: [],
21818
+ outputs: [
21819
+ {
21820
+ name: "",
21821
+ type: "bytes32",
21822
+ internalType: "bytes32"
21823
+ }
21824
+ ],
21825
+ stateMutability: "view"
21826
+ },
21827
+ {
21828
+ type: "function",
21829
+ name: "CLOCK_MODE",
21830
+ inputs: [],
21831
+ outputs: [
21832
+ {
21833
+ name: "",
21834
+ type: "string",
21835
+ internalType: "string"
21836
+ }
21837
+ ],
21838
+ stateMutability: "view"
21839
+ },
21840
+ {
21841
+ type: "function",
21842
+ name: "COUNTING_MODE",
21843
+ inputs: [],
21844
+ outputs: [
21845
+ {
21846
+ name: "",
21847
+ type: "string",
21848
+ internalType: "string"
21849
+ }
21850
+ ],
21851
+ stateMutability: "pure"
21852
+ },
21853
+ {
21854
+ type: "function",
21855
+ name: "EXTENDED_BALLOT_TYPEHASH",
21856
+ inputs: [],
21857
+ outputs: [
21858
+ {
21859
+ name: "",
21860
+ type: "bytes32",
21861
+ internalType: "bytes32"
21862
+ }
21863
+ ],
21864
+ stateMutability: "view"
21865
+ },
21866
+ {
21867
+ type: "function",
21868
+ name: "cancel",
21869
+ inputs: [
21870
+ {
21871
+ name: "targets",
21872
+ type: "address[]",
21873
+ internalType: "address[]"
21874
+ },
21875
+ {
21876
+ name: "values",
21877
+ type: "uint256[]",
21878
+ internalType: "uint256[]"
21879
+ },
21880
+ {
21881
+ name: "calldatas",
21882
+ type: "bytes[]",
21883
+ internalType: "bytes[]"
21884
+ },
21885
+ {
21886
+ name: "descriptionHash",
21887
+ type: "bytes32",
21888
+ internalType: "bytes32"
21889
+ }
21890
+ ],
21891
+ outputs: [
21892
+ {
21893
+ name: "",
21894
+ type: "uint256",
21895
+ internalType: "uint256"
21896
+ }
21897
+ ],
21898
+ stateMutability: "nonpayable"
21899
+ },
21900
+ {
21901
+ type: "function",
21902
+ name: "castVote",
21903
+ inputs: [
21904
+ {
21905
+ name: "proposalId",
21906
+ type: "uint256",
21907
+ internalType: "uint256"
21908
+ },
21909
+ {
21910
+ name: "support",
21911
+ type: "uint8",
21912
+ internalType: "uint8"
21413
21913
  }
21414
21914
  ],
21415
21915
  outputs: [
@@ -25137,6 +25637,7 @@ function computeHistoricalBurnTotals(burns, prices) {
25137
25637
 
25138
25638
  // src/lib/cached-config.ts
25139
25639
  var CACHE_NAMESPACE = "config";
25640
+ var YIELD_CACHE_NAMESPACE = "yield-config";
25140
25641
  async function fetchDtfConfigCached(fetchFn, publicClient, chainId, address) {
25141
25642
  const cacheKey3 = `${CACHE_NAMESPACE}:${address.toLowerCase()}`;
25142
25643
  const cached = getCached(chainId, cacheKey3);
@@ -25146,6 +25647,22 @@ async function fetchDtfConfigCached(fetchFn, publicClient, chainId, address) {
25146
25647
  setCache(chainId, cacheKey3, config);
25147
25648
  return config;
25148
25649
  }
25650
+ async function fetchYieldDtfConfigCached(fetchFn, publicClient, chainId, address) {
25651
+ const cacheKey3 = `${YIELD_CACHE_NAMESPACE}:${address.toLowerCase()}`;
25652
+ const cached = getCached(chainId, cacheKey3);
25653
+ if (cached)
25654
+ return cached;
25655
+ const config = await fetchFn(publicClient, chainId, address);
25656
+ setCache(chainId, cacheKey3, config);
25657
+ return config;
25658
+ }
25659
+
25660
+ // src/lib/detect-type.ts
25661
+ async function resolveDtfType(publicClient, address, registryType) {
25662
+ if (registryType)
25663
+ return registryType;
25664
+ return detectDtfType(publicClient, address);
25665
+ }
25149
25666
 
25150
25667
  // src/lib/output.ts
25151
25668
  function printTable(columns, rows) {
@@ -25231,10 +25748,16 @@ async function infoCommand(address, config) {
25231
25748
  chainId: config.chainId,
25232
25749
  rpc: config.rpc
25233
25750
  });
25751
+ const dtfType = await resolveDtfType(publicClient, address, config.dtfType);
25752
+ if (dtfType === "yield") {
25753
+ await yieldInfo(publicClient, address, config);
25754
+ return;
25755
+ }
25234
25756
  const dtfConfig = await fetchDtfConfigCached(fetchDtfConfig, publicClient, config.chainId, address);
25235
25757
  if (config.json) {
25236
25758
  printJson({
25237
25759
  dtf: address,
25760
+ type: "index",
25238
25761
  chain: config.chainId,
25239
25762
  chainLabel: chainLabel(config.chainId),
25240
25763
  ownerGovernance: govToJson(dtfConfig.ownerGovernance),
@@ -25261,6 +25784,7 @@ async function infoCommand(address, config) {
25261
25784
  console.log();
25262
25785
  const rows = [
25263
25786
  { prop: "DTF Address", value: address },
25787
+ { prop: "Type", value: "Index DTF" },
25264
25788
  { prop: "Auction Length", value: formatDuration(dtfConfig.auctionLength) }
25265
25789
  ];
25266
25790
  if (dtfConfig.mandate) {
@@ -25322,11 +25846,99 @@ async function infoCommand(address, config) {
25322
25846
  ], roleRows);
25323
25847
  }
25324
25848
  }
25849
+ async function yieldInfo(publicClient, address, config) {
25850
+ const yieldConfig = await fetchYieldDtfConfigCached(fetchYieldDtfConfig, publicClient, config.chainId, address);
25851
+ const backingPct = Number(yieldConfig.backing.backing) / Number(D18);
25852
+ const overCollPct = Number(yieldConfig.backing.overCollateralization) / Number(D18);
25853
+ const priceLow = Number(yieldConfig.priceLow) / Number(D18);
25854
+ const priceHigh = Number(yieldConfig.priceHigh) / Number(D18);
25855
+ const exchangeRate = Number(yieldConfig.exchangeRate) / Number(D18);
25856
+ const totalDist = Number(yieldConfig.rTokenTotal) + Number(yieldConfig.rsrTotal);
25857
+ const rTokenPct = totalDist > 0 ? Number(yieldConfig.rTokenTotal) / totalDist : 0;
25858
+ const rsrPct = totalDist > 0 ? 1 - rTokenPct : 0;
25859
+ if (config.json) {
25860
+ printJson({
25861
+ dtf: address,
25862
+ type: "yield",
25863
+ chain: config.chainId,
25864
+ chainLabel: chainLabel(config.chainId),
25865
+ name: yieldConfig.name,
25866
+ symbol: yieldConfig.symbol,
25867
+ components: yieldConfig.components,
25868
+ backing: yieldConfig.backing.backing.toString(),
25869
+ backingPercent: backingPct * 100,
25870
+ overCollateralization: yieldConfig.backing.overCollateralization.toString(),
25871
+ overCollateralizationPercent: overCollPct * 100,
25872
+ priceLow: yieldConfig.priceLow.toString(),
25873
+ priceHigh: yieldConfig.priceHigh.toString(),
25874
+ priceLowHuman: priceLow,
25875
+ priceHighHuman: priceHigh,
25876
+ exchangeRate: yieldConfig.exchangeRate.toString(),
25877
+ exchangeRateHuman: exchangeRate,
25878
+ unstakingDelay: yieldConfig.unstakingDelay.toString(),
25879
+ unstakingDelayHuman: formatDuration(Number(yieldConfig.unstakingDelay)),
25880
+ rTokenTotal: yieldConfig.rTokenTotal.toString(),
25881
+ rsrTotal: yieldConfig.rsrTotal.toString(),
25882
+ distributionPercent: { holders: rTokenPct * 100, stakers: rsrPct * 100 },
25883
+ basket: yieldConfig.basket.map((t) => ({
25884
+ address: t.address,
25885
+ symbol: t.symbol,
25886
+ decimals: t.decimals,
25887
+ targetUnit: t.targetUnit,
25888
+ uoaShare: t.uoaShare.toString(),
25889
+ uoaSharePercent: Number(t.uoaShare) / Number(D18) * 100
25890
+ }))
25891
+ });
25892
+ return;
25893
+ }
25894
+ printHeader(`Yield DTF Info — ${yieldConfig.symbol} (${chainLabel(config.chainId)})`);
25895
+ console.log();
25896
+ printTable([
25897
+ { header: "Property", key: "prop" },
25898
+ { header: "Value", key: "value" }
25899
+ ], [
25900
+ { prop: "Name", value: yieldConfig.name },
25901
+ { prop: "Symbol", value: yieldConfig.symbol },
25902
+ { prop: "Type", value: "Yield DTF (RToken)" },
25903
+ { prop: "Address", value: address },
25904
+ { prop: "Price", value: `$${priceLow.toFixed(4)} - $${priceHigh.toFixed(4)}` },
25905
+ { prop: "Backing", value: formatPct(backingPct) },
25906
+ { prop: "Over-collateralization", value: formatPct(overCollPct) },
25907
+ { prop: "Revenue Split", value: `${formatPct(rTokenPct)} holders / ${formatPct(rsrPct)} stakers` },
25908
+ { prop: "StRSR Exchange Rate", value: `${exchangeRate.toFixed(6)} RSR/stRSR` },
25909
+ { prop: "Unstaking Delay", value: formatDuration(Number(yieldConfig.unstakingDelay)) },
25910
+ { prop: "Main", value: yieldConfig.components.main },
25911
+ { prop: "StRSR", value: yieldConfig.components.stRSR }
25912
+ ]);
25913
+ if (yieldConfig.basket.length > 0) {
25914
+ console.log();
25915
+ printTable([
25916
+ { header: "Token", key: "symbol" },
25917
+ { header: "Target", key: "target" },
25918
+ { header: "UoA Share", key: "share", align: "right" },
25919
+ { header: "Address", key: "address" }
25920
+ ], yieldConfig.basket.map((t) => ({
25921
+ symbol: t.symbol,
25922
+ target: t.targetUnit,
25923
+ share: formatPct(Number(t.uoaShare) / Number(D18)),
25924
+ address: formatAddress(t.address)
25925
+ })));
25926
+ }
25927
+ }
25325
25928
 
25326
25929
  // ../../node_modules/.bun/viem@2.46.2+1fb4c65d43e298b9/node_modules/viem/_esm/index.js
25327
25930
  init_isAddress();
25328
25931
  // src/commands/basket.ts
25329
25932
  async function basketCommand(address, config) {
25933
+ const { publicClient } = createDtfClients({
25934
+ chainId: config.chainId,
25935
+ rpc: config.rpc
25936
+ });
25937
+ const dtfType = await resolveDtfType(publicClient, address, config.dtfType);
25938
+ if (dtfType === "yield") {
25939
+ await yieldBasket(publicClient, address, config);
25940
+ return;
25941
+ }
25330
25942
  try {
25331
25943
  await basketFromApi(address, config);
25332
25944
  } catch {
@@ -25334,6 +25946,43 @@ async function basketCommand(address, config) {
25334
25946
  await basketFromRpc(address, config);
25335
25947
  }
25336
25948
  }
25949
+ async function yieldBasket(publicClient, address, config) {
25950
+ const facadeAddress = FACADE_READ_ADDRESS[config.chainId];
25951
+ if (!facadeAddress) {
25952
+ throw new Error(`No FacadeRead address for chain ${config.chainId}`);
25953
+ }
25954
+ const basket = await readYieldDtfBasket(publicClient, facadeAddress, address);
25955
+ if (config.json) {
25956
+ printJson({
25957
+ dtf: address,
25958
+ type: "yield",
25959
+ chain: config.chainId,
25960
+ chainLabel: chainLabel(config.chainId),
25961
+ tokens: basket.map((t) => ({
25962
+ address: t.address,
25963
+ symbol: t.symbol,
25964
+ decimals: t.decimals,
25965
+ targetUnit: t.targetUnit,
25966
+ uoaShare: t.uoaShare.toString(),
25967
+ uoaSharePercent: Number(t.uoaShare) / Number(D18) * 100
25968
+ }))
25969
+ });
25970
+ return;
25971
+ }
25972
+ printHeader(`Yield DTF Basket — ${formatAddress(address)} (${chainLabel(config.chainId)})`);
25973
+ console.log();
25974
+ printTable([
25975
+ { header: "Token", key: "symbol" },
25976
+ { header: "Target", key: "target" },
25977
+ { header: "UoA Share", key: "share", align: "right" },
25978
+ { header: "Address", key: "address" }
25979
+ ], basket.map((t) => ({
25980
+ symbol: t.symbol,
25981
+ target: t.targetUnit,
25982
+ share: formatPct(Number(t.uoaShare) / Number(D18)),
25983
+ address: formatAddress(t.address)
25984
+ })));
25985
+ }
25337
25986
  async function basketFromApi(address, config) {
25338
25987
  const { publicClient } = createDtfClients({
25339
25988
  chainId: config.chainId,
@@ -25350,6 +25999,7 @@ async function basketFromApi(address, config) {
25350
25999
  if (config.json) {
25351
26000
  printJson({
25352
26001
  dtf: address,
26002
+ type: "index",
25353
26003
  chain: config.chainId,
25354
26004
  chainLabel: chainLabel(config.chainId),
25355
26005
  tokens: data.basket.map((t) => ({
@@ -25405,6 +26055,7 @@ async function basketFromRpc(address, config) {
25405
26055
  if (config.json) {
25406
26056
  printJson({
25407
26057
  dtf: address,
26058
+ type: "index",
25408
26059
  chain: config.chainId,
25409
26060
  chainLabel: chainLabel(config.chainId),
25410
26061
  tokens: tokenValues.map((t) => ({
@@ -25728,6 +26379,16 @@ async function rebalanceCommand(address, config) {
25728
26379
  chainId: config.chainId,
25729
26380
  rpc: config.rpc
25730
26381
  });
26382
+ const dtfType = await resolveDtfType(publicClient, address, config.dtfType);
26383
+ if (dtfType === "yield") {
26384
+ const msg = "Yield DTFs rebalance automatically via BackingManager. No manual rebalance state.";
26385
+ if (config.json) {
26386
+ printJson({ dtf: address, type: "yield", chain: config.chainId, chainLabel: chainLabel(config.chainId), message: msg });
26387
+ } else {
26388
+ console.log(msg);
26389
+ }
26390
+ return;
26391
+ }
25731
26392
  const [rebalance, auction] = await Promise.all([
25732
26393
  readRebalanceState(publicClient, address),
25733
26394
  readActiveAuction(publicClient, address)
@@ -25861,6 +26522,21 @@ No active auction.`);
25861
26522
  }
25862
26523
  }
25863
26524
 
26525
+ // src/lib/periods.ts
26526
+ var PERIOD_SECONDS = {
26527
+ "30d": 30 * 24 * 60 * 60,
26528
+ "3m": 90 * 24 * 60 * 60,
26529
+ "6m": 180 * 24 * 60 * 60,
26530
+ "1y": 365 * 24 * 60 * 60
26531
+ };
26532
+ function parsePeriodSeconds(period) {
26533
+ const s = PERIOD_SECONDS[period];
26534
+ if (!s) {
26535
+ throw new Error(`Unknown period "${period}". Use: 30d, 3m, 6m, 1y`);
26536
+ }
26537
+ return s;
26538
+ }
26539
+
25864
26540
  // src/commands/prices.ts
25865
26541
  async function pricesCommand(address, config) {
25866
26542
  const { publicClient } = createDtfClients({
@@ -25875,23 +26551,53 @@ async function pricesCommand(address, config) {
25875
26551
  fetchTokenVolatility(config.chainId),
25876
26552
  readChainlinkPrice(priceClient)
25877
26553
  ]);
26554
+ const perfPeriod = config.performance;
26555
+ let tokenReturns = null;
26556
+ if (perfPeriod) {
26557
+ const now = Math.floor(Date.now() / 1000);
26558
+ const seconds = parsePeriodSeconds(perfPeriod);
26559
+ const histResults = await Promise.allSettled(tokenAddresses.map((addr) => fetchHistoricalPrices(config.chainId, addr, {
26560
+ from: now - seconds,
26561
+ to: now,
26562
+ interval: "1d"
26563
+ })));
26564
+ tokenReturns = new Map;
26565
+ for (let i = 0;i < tokenAddresses.length; i++) {
26566
+ const hist = histResults[i];
26567
+ let ret = null;
26568
+ if (hist?.status === "fulfilled" && hist.value.length >= 2) {
26569
+ const first = hist.value[0];
26570
+ const last = hist.value[hist.value.length - 1];
26571
+ if (first.price > 0) {
26572
+ ret = Math.round((last.price - first.price) / first.price * 1e4) / 100;
26573
+ }
26574
+ }
26575
+ tokenReturns.set(tokenAddresses[i].toLowerCase(), ret);
26576
+ }
26577
+ }
26578
+ let tokens = basket.tokens.map((t) => {
26579
+ const addr = t.address.toLowerCase();
26580
+ const vol = volatility[addr] ?? "high";
26581
+ const ret = tokenReturns?.get(addr) ?? null;
26582
+ return { ...t, addr, vol, price: prices[addr] ?? 0, ret };
26583
+ });
26584
+ if (tokenReturns) {
26585
+ tokens = [...tokens].sort((a, b) => (b.ret ?? -Infinity) - (a.ret ?? -Infinity));
26586
+ }
25878
26587
  if (config.json) {
25879
26588
  printJson({
25880
26589
  dtf: address,
25881
26590
  chain: config.chainId,
25882
26591
  chainLabel: chainLabel(config.chainId),
25883
- tokens: basket.tokens.map((t) => {
25884
- const addr = t.address.toLowerCase();
25885
- const vol = volatility[addr] ?? "high";
25886
- return {
25887
- symbol: t.symbol,
25888
- address: t.address,
25889
- price: prices[addr] ?? 0,
25890
- volatility: vol,
25891
- auctionPriceError: volatilityToPriceError(vol),
25892
- proposalPriceError: proposalVolatilityToPriceError(vol)
25893
- };
25894
- }),
26592
+ tokens: tokens.map((t) => ({
26593
+ symbol: t.symbol,
26594
+ address: t.address,
26595
+ price: t.price,
26596
+ volatility: t.vol,
26597
+ auctionPriceError: volatilityToPriceError(t.vol),
26598
+ proposalPriceError: proposalVolatilityToPriceError(t.vol),
26599
+ ...tokenReturns ? { [`return_${perfPeriod}`]: t.ret } : {}
26600
+ })),
25895
26601
  btcUsd: btcPrice ? {
25896
26602
  price: btcPrice.record.priceUsd,
25897
26603
  timestamp: btcPrice.record.timestamp,
@@ -25902,25 +26608,30 @@ async function pricesCommand(address, config) {
25902
26608
  }
25903
26609
  printHeader(`DTF Prices — ${formatAddress(address)} (Chain ${config.chainId})`);
25904
26610
  console.log();
25905
- printTable([
26611
+ const columns = [
25906
26612
  { header: "Token", key: "symbol" },
25907
26613
  { header: "Address", key: "address" },
25908
26614
  { header: "Price (USD)", key: "price", align: "right" },
25909
26615
  { header: "Volatility", key: "volatility" },
25910
26616
  { header: "Auction Error", key: "auctionErr", align: "right" },
25911
26617
  { header: "Proposal Error", key: "proposalErr", align: "right" }
25912
- ], basket.tokens.map((t) => {
25913
- const addr = t.address.toLowerCase();
25914
- const vol = volatility[addr] ?? "high";
25915
- const price = prices[addr] ?? 0;
25916
- return {
26618
+ ];
26619
+ if (tokenReturns) {
26620
+ columns.push({ header: perfPeriod, key: "retPeriod", align: "right" });
26621
+ }
26622
+ printTable(columns, tokens.map((t) => {
26623
+ const row = {
25917
26624
  symbol: t.symbol,
25918
26625
  address: formatAddress(t.address),
25919
- price: formatUsd(price),
25920
- volatility: vol,
25921
- auctionErr: `${(volatilityToPriceError(vol) * 100).toFixed(0)}%`,
25922
- proposalErr: `${(proposalVolatilityToPriceError(vol) * 100).toFixed(0)}%`
26626
+ price: formatUsd(t.price),
26627
+ volatility: t.vol,
26628
+ auctionErr: `${(volatilityToPriceError(t.vol) * 100).toFixed(0)}%`,
26629
+ proposalErr: `${(proposalVolatilityToPriceError(t.vol) * 100).toFixed(0)}%`
25923
26630
  };
26631
+ if (tokenReturns) {
26632
+ row.retPeriod = t.ret !== null ? `${t.ret > 0 ? "+" : ""}${t.ret}%` : "—";
26633
+ }
26634
+ return row;
25924
26635
  }));
25925
26636
  if (btcPrice) {
25926
26637
  console.log();
@@ -25942,6 +26653,11 @@ async function quoteCommand(address, config, rest) {
25942
26653
  chainId: config.chainId,
25943
26654
  rpc: config.rpc
25944
26655
  });
26656
+ const dtfType = await resolveDtfType(publicClient, address, config.dtfType);
26657
+ if (dtfType === "yield") {
26658
+ await yieldQuote(publicClient, address, config, shares, action);
26659
+ return;
26660
+ }
25945
26661
  const dtfConfig = config.json ? await fetchDtfConfigCached(fetchDtfConfig, publicClient, config.chainId, address) : null;
25946
26662
  const tokenByAddress = new Map((dtfConfig?.tokens ?? []).map((t) => [t.address.toLowerCase(), t]));
25947
26663
  if (action === "mint") {
@@ -25971,55 +26687,124 @@ async function quoteCommand(address, config, rest) {
25971
26687
  });
25972
26688
  return;
25973
26689
  }
25974
- printHeader(`Mint Quote — ${formatAddress(address)}`);
26690
+ printHeader(`Mint Quote — ${formatAddress(address)}`);
26691
+ console.log(`
26692
+ Minting ${amount} shares (${formatUnits2(shares, 18)})`);
26693
+ console.log();
26694
+ printTable([
26695
+ { header: "Token", key: "token" },
26696
+ { header: "Amount Required", key: "amount", align: "right" },
26697
+ { header: "With 1% Slippage", key: "slippage", align: "right" }
26698
+ ], quote.assets.map((asset, i) => ({
26699
+ token: formatAddress(asset),
26700
+ amount: quote.amounts[i].toString(),
26701
+ slippage: applySlippage(quote.amounts[i], slippageBps).toString()
26702
+ })));
26703
+ console.log(`
26704
+ Mint Fee: ${formatUnits2(quote.mintFee, 18)}`);
26705
+ console.log(`Min Shares Out: ${formatUnits2(quote.minSharesOut, 18)}`);
26706
+ } else {
26707
+ const quote = await readRedeemQuote2(publicClient, address, shares);
26708
+ if (config.json) {
26709
+ printJson({
26710
+ dtf: address,
26711
+ chain: config.chainId,
26712
+ chainLabel: chainLabel(config.chainId),
26713
+ action: "redeem",
26714
+ shares: shares.toString(),
26715
+ sharesHuman: Number(shares) / 1000000000000000000,
26716
+ amounts: quote.assets.map((asset, i) => {
26717
+ const token = tokenByAddress.get(asset.toLowerCase());
26718
+ const decimals = token?.decimals ?? 18;
26719
+ return {
26720
+ address: asset,
26721
+ symbol: token?.symbol ?? null,
26722
+ amount: quote.amounts[i].toString(),
26723
+ amountHuman: Number(quote.amounts[i]) / 10 ** decimals
26724
+ };
26725
+ })
26726
+ });
26727
+ return;
26728
+ }
26729
+ printHeader(`Redeem Quote — ${formatAddress(address)}`);
26730
+ console.log(`
26731
+ Redeeming ${amount} shares`);
26732
+ console.log();
26733
+ printTable([
26734
+ { header: "Token", key: "token" },
26735
+ { header: "Amount Out", key: "amount", align: "right" }
26736
+ ], quote.assets.map((asset, i) => ({
26737
+ token: formatAddress(asset),
26738
+ amount: quote.amounts[i].toString()
26739
+ })));
26740
+ }
26741
+ }
26742
+ async function yieldQuote(publicClient, address, config, shares, action) {
26743
+ const facadeAddress = FACADE_READ_ADDRESS[config.chainId];
26744
+ if (!facadeAddress) {
26745
+ throw new Error(`No FacadeRead address for chain ${config.chainId}`);
26746
+ }
26747
+ if (action === "mint") {
26748
+ const quote = await readIssueQuote(publicClient, facadeAddress, address, shares);
26749
+ if (config.json) {
26750
+ printJson({
26751
+ dtf: address,
26752
+ type: "yield",
26753
+ chain: config.chainId,
26754
+ chainLabel: chainLabel(config.chainId),
26755
+ action: "mint",
26756
+ shares: shares.toString(),
26757
+ sharesHuman: Number(shares) / 1000000000000000000,
26758
+ tokens: quote.tokens.map((token, i) => ({
26759
+ address: token,
26760
+ deposit: quote.deposits[i].toString(),
26761
+ depositUoA: quote.depositsUoA[i].toString()
26762
+ }))
26763
+ });
26764
+ return;
26765
+ }
26766
+ printHeader(`Yield DTF Issue Quote — ${formatAddress(address)}`);
25975
26767
  console.log(`
25976
- Minting ${amount} shares (${formatUnits2(shares, 18)})`);
26768
+ Issuing ${Number(shares) / 1000000000000000000} RTokens`);
25977
26769
  console.log();
25978
26770
  printTable([
25979
26771
  { header: "Token", key: "token" },
25980
- { header: "Amount Required", key: "amount", align: "right" },
25981
- { header: "With 1% Slippage", key: "slippage", align: "right" }
25982
- ], quote.assets.map((asset, i) => ({
25983
- token: formatAddress(asset),
25984
- amount: quote.amounts[i].toString(),
25985
- slippage: applySlippage(quote.amounts[i], slippageBps).toString()
26772
+ { header: "Deposit Required", key: "deposit", align: "right" }
26773
+ ], quote.tokens.map((token, i) => ({
26774
+ token: formatAddress(token),
26775
+ deposit: quote.deposits[i].toString()
25986
26776
  })));
25987
- console.log(`
25988
- Mint Fee: ${formatUnits2(quote.mintFee, 18)}`);
25989
- console.log(`Min Shares Out: ${formatUnits2(quote.minSharesOut, 18)}`);
25990
26777
  } else {
25991
- const quote = await readRedeemQuote(publicClient, address, shares);
26778
+ const quote = await readRedeemQuote(publicClient, facadeAddress, address, shares);
25992
26779
  if (config.json) {
25993
26780
  printJson({
25994
26781
  dtf: address,
26782
+ type: "yield",
25995
26783
  chain: config.chainId,
25996
26784
  chainLabel: chainLabel(config.chainId),
25997
26785
  action: "redeem",
25998
26786
  shares: shares.toString(),
25999
26787
  sharesHuman: Number(shares) / 1000000000000000000,
26000
- amounts: quote.assets.map((asset, i) => {
26001
- const token = tokenByAddress.get(asset.toLowerCase());
26002
- const decimals = token?.decimals ?? 18;
26003
- return {
26004
- address: asset,
26005
- symbol: token?.symbol ?? null,
26006
- amount: quote.amounts[i].toString(),
26007
- amountHuman: Number(quote.amounts[i]) / 10 ** decimals
26008
- };
26009
- })
26788
+ tokens: quote.tokens.map((token, i) => ({
26789
+ address: token,
26790
+ withdrawal: quote.withdrawals[i].toString(),
26791
+ available: quote.available[i].toString()
26792
+ }))
26010
26793
  });
26011
26794
  return;
26012
26795
  }
26013
- printHeader(`Redeem Quote — ${formatAddress(address)}`);
26796
+ printHeader(`Yield DTF Redeem Quote — ${formatAddress(address)}`);
26014
26797
  console.log(`
26015
- Redeeming ${amount} shares`);
26798
+ Redeeming ${Number(shares) / 1000000000000000000} RTokens`);
26016
26799
  console.log();
26017
26800
  printTable([
26018
26801
  { header: "Token", key: "token" },
26019
- { header: "Amount Out", key: "amount", align: "right" }
26020
- ], quote.assets.map((asset, i) => ({
26021
- token: formatAddress(asset),
26022
- amount: quote.amounts[i].toString()
26802
+ { header: "Withdrawal", key: "withdrawal", align: "right" },
26803
+ { header: "Available", key: "available", align: "right" }
26804
+ ], quote.tokens.map((token, i) => ({
26805
+ token: formatAddress(token),
26806
+ withdrawal: quote.withdrawals[i].toString(),
26807
+ available: quote.available[i].toString()
26023
26808
  })));
26024
26809
  }
26025
26810
  }
@@ -26030,6 +26815,11 @@ async function feesCommand(address, config) {
26030
26815
  chainId: config.chainId,
26031
26816
  rpc: config.rpc
26032
26817
  });
26818
+ const dtfType = await resolveDtfType(publicClient, address, config.dtfType);
26819
+ if (dtfType === "yield") {
26820
+ await yieldFees(publicClient, address, config);
26821
+ return;
26822
+ }
26033
26823
  const [pending, recipients, mintFee, dtfConfig] = await Promise.all([
26034
26824
  readPendingFees(publicClient, address),
26035
26825
  readFeeRecipients(publicClient, address),
@@ -26102,6 +26892,39 @@ Fee Recipients:`);
26102
26892
  })));
26103
26893
  }
26104
26894
  }
26895
+ async function yieldFees(publicClient, address, config) {
26896
+ const yieldConfig = await fetchYieldDtfConfigCached(fetchYieldDtfConfig, publicClient, config.chainId, address);
26897
+ const total = Number(yieldConfig.rTokenTotal) + Number(yieldConfig.rsrTotal);
26898
+ const holdersPct = total > 0 ? Number(yieldConfig.rTokenTotal) / total : 0;
26899
+ const stakersPct = total > 0 ? Number(yieldConfig.rsrTotal) / total : 0;
26900
+ if (config.json) {
26901
+ printJson({
26902
+ dtf: address,
26903
+ type: "yield",
26904
+ chain: config.chainId,
26905
+ chainLabel: chainLabel(config.chainId),
26906
+ distribution: {
26907
+ rTokenTotal: yieldConfig.rTokenTotal.toString(),
26908
+ rsrTotal: yieldConfig.rsrTotal.toString(),
26909
+ holdersPercent: holdersPct * 100,
26910
+ stakersPercent: stakersPct * 100
26911
+ }
26912
+ });
26913
+ return;
26914
+ }
26915
+ printHeader(`Yield DTF Revenue Distribution — ${yieldConfig.symbol} (${chainLabel(config.chainId)})`);
26916
+ console.log();
26917
+ printTable([
26918
+ { header: "Destination", key: "dest" },
26919
+ { header: "Share", key: "share", align: "right" },
26920
+ { header: "Basis Points", key: "bps", align: "right" }
26921
+ ], [
26922
+ { dest: "RToken Holders (Furnace)", share: formatPct(holdersPct), bps: yieldConfig.rTokenTotal.toString() },
26923
+ { dest: "RSR Stakers (StRSR)", share: formatPct(stakersPct), bps: yieldConfig.rsrTotal.toString() }
26924
+ ]);
26925
+ console.log();
26926
+ console.log("Revenue comes from collateral yield, distributed via Furnace (melting) and StRSR rewards.");
26927
+ }
26105
26928
 
26106
26929
  // src/commands/governance.ts
26107
26930
  async function governanceCommand(address, config) {
@@ -26109,6 +26932,11 @@ async function governanceCommand(address, config) {
26109
26932
  chainId: config.chainId,
26110
26933
  rpc: config.rpc
26111
26934
  });
26935
+ const dtfType = await resolveDtfType(publicClient, address, config.dtfType);
26936
+ if (dtfType === "yield") {
26937
+ await yieldGovernance(address, config);
26938
+ return;
26939
+ }
26112
26940
  const dtfConfig = await fetchDtfConfigCached(fetchDtfConfig, publicClient, config.chainId, address);
26113
26941
  const govs = [];
26114
26942
  if (dtfConfig.ownerGovernance) {
@@ -26197,6 +27025,62 @@ ${g.label} Governance:`);
26197
27025
  ]);
26198
27026
  }
26199
27027
  }
27028
+ async function yieldGovernance(address, config) {
27029
+ const subgraphUrl = YIELD_SUBGRAPH_URLS[config.chainId];
27030
+ if (!subgraphUrl) {
27031
+ const msg = `Yield DTF governance not available for chain ${config.chainId} (no subgraph).`;
27032
+ if (config.json) {
27033
+ printJson({ dtf: address, type: "yield", chain: config.chainId, governances: [], message: msg });
27034
+ } else {
27035
+ console.log(msg);
27036
+ }
27037
+ return;
27038
+ }
27039
+ const query = `{
27040
+ rtoken(id: "${address.toLowerCase()}") {
27041
+ id
27042
+ governance {
27043
+ id
27044
+ governor
27045
+ timelock
27046
+ }
27047
+ }
27048
+ }`;
27049
+ try {
27050
+ const data = await querySubgraph(config.chainId, query, {}, subgraphUrl);
27051
+ const gov = data.rtoken?.governance;
27052
+ if (config.json) {
27053
+ printJson({
27054
+ dtf: address,
27055
+ type: "yield",
27056
+ chain: config.chainId,
27057
+ chainLabel: chainLabel(config.chainId),
27058
+ governance: gov ? { governor: gov.governor, timelock: gov.timelock } : null
27059
+ });
27060
+ return;
27061
+ }
27062
+ printHeader(`Yield DTF Governance — ${formatAddress(address)} (${chainLabel(config.chainId)})`);
27063
+ console.log();
27064
+ if (gov) {
27065
+ printTable([
27066
+ { header: "Property", key: "prop" },
27067
+ { header: "Value", key: "value" }
27068
+ ], [
27069
+ { prop: "Governor", value: gov.governor },
27070
+ { prop: "Timelock", value: gov.timelock }
27071
+ ]);
27072
+ } else {
27073
+ console.log("No governance found for this yield DTF.");
27074
+ }
27075
+ } catch {
27076
+ const msg = "Failed to fetch yield DTF governance from subgraph.";
27077
+ if (config.json) {
27078
+ printJson({ dtf: address, type: "yield", chain: config.chainId, governance: null, error: msg });
27079
+ } else {
27080
+ console.log(msg);
27081
+ }
27082
+ }
27083
+ }
26200
27084
 
26201
27085
  // src/commands/staking.ts
26202
27086
  async function stakingCommand(address, config, rest) {
@@ -26204,6 +27088,11 @@ async function stakingCommand(address, config, rest) {
26204
27088
  chainId: config.chainId,
26205
27089
  rpc: config.rpc
26206
27090
  });
27091
+ const dtfType = await resolveDtfType(publicClient, address, config.dtfType);
27092
+ if (dtfType === "yield") {
27093
+ await yieldStaking(publicClient, address, config);
27094
+ return;
27095
+ }
26207
27096
  const dtfConfig = await fetchDtfConfigCached(fetchDtfConfig, publicClient, config.chainId, address);
26208
27097
  const stToken = dtfConfig.stTokenAddress;
26209
27098
  if (!stToken) {
@@ -26290,6 +27179,59 @@ Account: ${account}`);
26290
27179
  ]);
26291
27180
  }
26292
27181
  }
27182
+ async function yieldStaking(publicClient, address, config) {
27183
+ const yieldConfig = await fetchYieldDtfConfigCached(fetchYieldDtfConfig, publicClient, config.chainId, address);
27184
+ const account = config.account;
27185
+ const stRSRInfo = await readStRSRInfo(publicClient, yieldConfig.components.stRSR, account);
27186
+ const exchangeRate = Number(stRSRInfo.exchangeRate) / Number(D18);
27187
+ if (config.json) {
27188
+ printJson({
27189
+ dtf: address,
27190
+ type: "yield",
27191
+ chain: config.chainId,
27192
+ chainLabel: chainLabel(config.chainId),
27193
+ stRSR: yieldConfig.components.stRSR,
27194
+ exchangeRate: stRSRInfo.exchangeRate.toString(),
27195
+ exchangeRateHuman: exchangeRate,
27196
+ totalSupply: stRSRInfo.totalSupply.toString(),
27197
+ totalSupplyHuman: Number(stRSRInfo.totalSupply) / 1000000000000000000,
27198
+ unstakingDelay: stRSRInfo.unstakingDelay.toString(),
27199
+ unstakingDelayHuman: formatDuration(Number(stRSRInfo.unstakingDelay)),
27200
+ account: account && stRSRInfo.balance !== undefined ? {
27201
+ address: account,
27202
+ balance: stRSRInfo.balance.toString(),
27203
+ balanceHuman: Number(stRSRInfo.balance) / 1000000000000000000,
27204
+ votingPower: stRSRInfo.votingPower.toString(),
27205
+ votingPowerHuman: Number(stRSRInfo.votingPower) / 1000000000000000000,
27206
+ delegatee: stRSRInfo.delegatee
27207
+ } : null
27208
+ });
27209
+ return;
27210
+ }
27211
+ printHeader(`Yield DTF Staking — ${yieldConfig.symbol} (${chainLabel(config.chainId)})`);
27212
+ console.log();
27213
+ printTable([
27214
+ { header: "Property", key: "prop" },
27215
+ { header: "Value", key: "value" }
27216
+ ], [
27217
+ { prop: "StRSR Address", value: yieldConfig.components.stRSR },
27218
+ { prop: "Exchange Rate", value: `${exchangeRate.toFixed(6)} RSR/stRSR` },
27219
+ { prop: "Total Supply", value: `${(Number(stRSRInfo.totalSupply) / 1000000000000000000).toLocaleString("en-US", { maximumFractionDigits: 2 })} stRSR` },
27220
+ { prop: "Unstaking Delay", value: formatDuration(Number(stRSRInfo.unstakingDelay)) }
27221
+ ]);
27222
+ if (account && stRSRInfo.balance !== undefined) {
27223
+ console.log(`
27224
+ Account: ${account}`);
27225
+ printTable([
27226
+ { header: "Property", key: "prop" },
27227
+ { header: "Value", key: "value", align: "right" }
27228
+ ], [
27229
+ { prop: "Balance", value: formatUnits2(stRSRInfo.balance, 18) },
27230
+ { prop: "Voting Power", value: formatUnits2(stRSRInfo.votingPower, 18) },
27231
+ { prop: "Delegatee", value: stRSRInfo.delegatee }
27232
+ ]);
27233
+ }
27234
+ }
26293
27235
 
26294
27236
  // src/commands/roles.ts
26295
27237
  async function rolesCommand(address, config) {
@@ -26297,6 +27239,11 @@ async function rolesCommand(address, config) {
26297
27239
  chainId: config.chainId,
26298
27240
  rpc: config.rpc
26299
27241
  });
27242
+ const dtfType = await resolveDtfType(publicClient, address, config.dtfType);
27243
+ if (dtfType === "yield") {
27244
+ await yieldRoles(address, config);
27245
+ return;
27246
+ }
26300
27247
  const dtfConfig = await fetchDtfConfigCached(fetchDtfConfig, publicClient, config.chainId, address);
26301
27248
  if (config.json) {
26302
27249
  printJson({
@@ -26341,6 +27288,63 @@ async function rolesCommand(address, config) {
26341
27288
  { header: "Holder", key: "holder" }
26342
27289
  ], rows);
26343
27290
  }
27291
+ async function yieldRoles(address, config) {
27292
+ const subgraphUrl = YIELD_SUBGRAPH_URLS[config.chainId];
27293
+ if (!subgraphUrl) {
27294
+ const msg = `Yield DTF roles not available for chain ${config.chainId} (no subgraph).`;
27295
+ if (config.json) {
27296
+ printJson({ dtf: address, type: "yield", chain: config.chainId, roles: null, message: msg });
27297
+ } else {
27298
+ console.log(msg);
27299
+ }
27300
+ return;
27301
+ }
27302
+ const query = `{
27303
+ rtoken(id: "${address.toLowerCase()}") {
27304
+ id
27305
+ main {
27306
+ owner
27307
+ pauser
27308
+ freezer
27309
+ }
27310
+ }
27311
+ }`;
27312
+ try {
27313
+ const data = await querySubgraph(config.chainId, query, {}, subgraphUrl);
27314
+ const main = data.rtoken?.main;
27315
+ if (config.json) {
27316
+ printJson({
27317
+ dtf: address,
27318
+ type: "yield",
27319
+ chain: config.chainId,
27320
+ chainLabel: chainLabel(config.chainId),
27321
+ roles: main ? { owner: main.owner, pauser: main.pauser, freezer: main.freezer } : null
27322
+ });
27323
+ return;
27324
+ }
27325
+ printHeader(`Yield DTF Roles — ${formatAddress(address)} (${chainLabel(config.chainId)})`);
27326
+ console.log();
27327
+ if (main) {
27328
+ printTable([
27329
+ { header: "Role", key: "role" },
27330
+ { header: "Holder", key: "holder" }
27331
+ ], [
27332
+ { role: "Owner", holder: main.owner },
27333
+ { role: "Pauser", holder: main.pauser },
27334
+ { role: "Freezer", holder: main.freezer }
27335
+ ]);
27336
+ } else {
27337
+ console.log("No role data found for this yield DTF.");
27338
+ }
27339
+ } catch {
27340
+ const msg = "Failed to fetch yield DTF roles from subgraph.";
27341
+ if (config.json) {
27342
+ printJson({ dtf: address, type: "yield", chain: config.chainId, roles: null, error: msg });
27343
+ } else {
27344
+ console.log(msg);
27345
+ }
27346
+ }
27347
+ }
26344
27348
 
26345
27349
  // src/commands/rebalance-history.ts
26346
27350
  function summarizeRebalance(detail) {
@@ -26571,19 +27575,6 @@ function computeReturn(performance) {
26571
27575
  return null;
26572
27576
  return Math.round((last.value - first.value) / first.value * 1e4) / 100;
26573
27577
  }
26574
- var PERIOD_SECONDS = {
26575
- "30d": 30 * 24 * 60 * 60,
26576
- "3m": 90 * 24 * 60 * 60,
26577
- "6m": 180 * 24 * 60 * 60,
26578
- "1y": 365 * 24 * 60 * 60
26579
- };
26580
- function parsePeriodSeconds(period) {
26581
- const s = PERIOD_SECONDS[period];
26582
- if (!s) {
26583
- throw new Error(`Unknown period "${period}". Use: 30d, 3m, 6m, 1y`);
26584
- }
26585
- return s;
26586
- }
26587
27578
  async function discoverCommand(config) {
26588
27579
  const isAll = config.allChains || !config.chainExplicit;
26589
27580
  const chains = isAll ? [...SUPPORTED_CHAINS] : [config.chainId];
@@ -27695,9 +28686,492 @@ function getFlag2(args, flag) {
27695
28686
  return args[idx + 1];
27696
28687
  }
27697
28688
 
28689
+ // src/commands/query.ts
28690
+ var INDEX_ONLY = new Set([
28691
+ "dtf",
28692
+ "dtfs",
28693
+ "rebalance",
28694
+ "rebalances",
28695
+ "auction",
28696
+ "auctions",
28697
+ "auctionBid",
28698
+ "auctionBids",
28699
+ "rebalanceAuctionBid",
28700
+ "rebalanceAuctionBids",
28701
+ "stakingToken",
28702
+ "stakingTokens",
28703
+ "stakingTokenRewards",
28704
+ "lock",
28705
+ "locks",
28706
+ "rsrBurn",
28707
+ "rsrBurns",
28708
+ "rsrBurnGlobal",
28709
+ "rsrBurnDailySnapshot",
28710
+ "rsrBurnDailySnapshots",
28711
+ "rsrBurnMonthlySnapshot",
28712
+ "rsrBurnMonthlySnapshots",
28713
+ "minting",
28714
+ "mintings",
28715
+ "transferEvent",
28716
+ "transferEvents",
28717
+ "unstakingManager",
28718
+ "version",
28719
+ "versions",
28720
+ "timelockOperation",
28721
+ "timelockOperations",
28722
+ "timelockOperationByTx"
28723
+ ]);
28724
+ var YIELD_ONLY = new Set([
28725
+ "rtoken",
28726
+ "rtokens",
28727
+ "protocol",
28728
+ "protocols",
28729
+ "accountRToken",
28730
+ "accountRTokens",
28731
+ "accountRTokenDailySnapshot",
28732
+ "accountRTokenDailySnapshots",
28733
+ "accountStakeRecord",
28734
+ "accountStakeRecords",
28735
+ "governanceFramework",
28736
+ "governanceFrameworks",
28737
+ "revenueDistribution",
28738
+ "revenueDistributions",
28739
+ "financialsDailySnapshot",
28740
+ "financialsDailySnapshots",
28741
+ "rtokenDailySnapshot",
28742
+ "rtokenDailySnapshots",
28743
+ "rtokenHourlySnapshot",
28744
+ "rtokenHourlySnapshots",
28745
+ "rtokenHistoricalBaskets",
28746
+ "deployer",
28747
+ "deployers",
28748
+ "collateral",
28749
+ "collaterals",
28750
+ "stTokenDailySnapshot",
28751
+ "stTokenDailySnapshots",
28752
+ "rewardToken",
28753
+ "rewardTokens",
28754
+ "tokenHolder",
28755
+ "tokenHolders",
28756
+ "usageMetricsDailySnapshot",
28757
+ "usageMetricsDailySnapshots",
28758
+ "usageMetricsHourlySnapshot",
28759
+ "usageMetricsHourlySnapshots",
28760
+ "rtokenContract",
28761
+ "rtokenContracts",
28762
+ "entry",
28763
+ "entries",
28764
+ "activeAccount",
28765
+ "activeAccounts"
28766
+ ]);
28767
+ var SHARED = new Set([
28768
+ "account",
28769
+ "accounts",
28770
+ "accountBalance",
28771
+ "accountBalances",
28772
+ "accountBalanceDailySnapshot",
28773
+ "accountBalanceDailySnapshots",
28774
+ "token",
28775
+ "tokens",
28776
+ "tokenDailySnapshot",
28777
+ "tokenDailySnapshots",
28778
+ "tokenHourlySnapshot",
28779
+ "tokenHourlySnapshots",
28780
+ "tokenMonthlySnapshot",
28781
+ "tokenMonthlySnapshots",
28782
+ "proposal",
28783
+ "proposals",
28784
+ "vote",
28785
+ "votes",
28786
+ "delegate",
28787
+ "delegates",
28788
+ "delegateChange",
28789
+ "delegateChanges",
28790
+ "delegateVotingPowerChange",
28791
+ "delegateVotingPowerChanges",
28792
+ "governance",
28793
+ "governances",
28794
+ "governanceTimelock",
28795
+ "governanceTimelocks",
28796
+ "trade",
28797
+ "trades",
28798
+ "voteDailySnapshot",
28799
+ "voteDailySnapshots"
28800
+ ]);
28801
+ function extractRootEntities(query) {
28802
+ const firstBrace = query.indexOf("{");
28803
+ if (firstBrace === -1)
28804
+ return [];
28805
+ const bodyStart = firstBrace + 1;
28806
+ const body = query.slice(bodyStart);
28807
+ const entities = [];
28808
+ let braceDepth = 0;
28809
+ let parenDepth = 0;
28810
+ const tokenRegex = /(\w+)|([{}()])/g;
28811
+ let match;
28812
+ while ((match = tokenRegex.exec(body)) !== null) {
28813
+ if (match[2] === "{")
28814
+ braceDepth++;
28815
+ else if (match[2] === "}")
28816
+ braceDepth--;
28817
+ else if (match[2] === "(")
28818
+ parenDepth++;
28819
+ else if (match[2] === ")")
28820
+ parenDepth--;
28821
+ else if (match[1] && braceDepth === 0 && parenDepth === 0) {
28822
+ entities.push(match[1]);
28823
+ }
28824
+ }
28825
+ return entities;
28826
+ }
28827
+ function detectSubgraph(query) {
28828
+ const entities = extractRootEntities(query);
28829
+ let hasIndex = false;
28830
+ let hasYield = false;
28831
+ let hasShared = false;
28832
+ let hasUnknown = false;
28833
+ for (const entity of entities) {
28834
+ if (INDEX_ONLY.has(entity))
28835
+ hasIndex = true;
28836
+ else if (YIELD_ONLY.has(entity))
28837
+ hasYield = true;
28838
+ else if (SHARED.has(entity))
28839
+ hasShared = true;
28840
+ else
28841
+ hasUnknown = true;
28842
+ }
28843
+ if (hasIndex && hasYield) {
28844
+ throw new Error("Query contains both index-only and yield-only entities. Use --subgraph to specify which subgraph to query.");
28845
+ }
28846
+ if (hasYield)
28847
+ return { subgraph: "yield" };
28848
+ if (hasIndex)
28849
+ return { subgraph: "index" };
28850
+ return {
28851
+ subgraph: "index",
28852
+ note: "Auto-detected as index subgraph. Use --subgraph yield to query the yield subgraph instead."
28853
+ };
28854
+ }
28855
+ var YIELD_CHAINS = Object.keys(YIELD_SUBGRAPH_URLS).map(Number);
28856
+ async function queryCommand(config, rest) {
28857
+ const query = rest[0];
28858
+ if (!query) {
28859
+ throw new Error("Usage: dtf query '<graphql>' [--subgraph index|yield] [--chain <id|all>]");
28860
+ }
28861
+ let subgraph;
28862
+ let note;
28863
+ if (config.subgraph) {
28864
+ subgraph = config.subgraph;
28865
+ } else {
28866
+ const detected = detectSubgraph(query);
28867
+ subgraph = detected.subgraph;
28868
+ note = detected.note;
28869
+ }
28870
+ const getUrl2 = (chainId) => {
28871
+ if (subgraph === "yield") {
28872
+ const url = YIELD_SUBGRAPH_URLS[chainId];
28873
+ if (!url)
28874
+ throw new Error(`No yield subgraph for chain ${chainId}. Yield DTFs are only on Ethereum (1) and Base (8453).`);
28875
+ return url;
28876
+ }
28877
+ return SUBGRAPH_URLS[chainId];
28878
+ };
28879
+ if (config.allChains) {
28880
+ const chains = subgraph === "yield" ? YIELD_CHAINS : SUPPORTED_CHAINS;
28881
+ const results = await Promise.allSettled(chains.map(async (chainId) => {
28882
+ const url = getUrl2(chainId);
28883
+ const data = await querySubgraph(chainId, query, {}, url);
28884
+ return { chain: chainId, chainLabel: chainLabel(chainId), data };
28885
+ }));
28886
+ const output = results.map((r, i) => {
28887
+ if (r.status === "fulfilled")
28888
+ return r.value;
28889
+ return { chain: chains[i], chainLabel: chainLabel(chains[i]), error: String(r.reason) };
28890
+ });
28891
+ printJson({ subgraph, ...note ? { note } : {}, results: output });
28892
+ } else {
28893
+ const url = getUrl2(config.chainId);
28894
+ const data = await querySubgraph(config.chainId, query, {}, url);
28895
+ printJson({ subgraph, chain: config.chainId, chainLabel: chainLabel(config.chainId), ...note ? { note } : {}, data });
28896
+ }
28897
+ }
28898
+
28899
+ // src/commands/holders.ts
28900
+ async function holdersCommand(address, config) {
28901
+ const { publicClient } = createDtfClients({ chainId: config.chainId, rpc: config.rpc });
28902
+ const dtfType = await resolveDtfType(publicClient, address, config.dtfType);
28903
+ const limit = config.limit ?? 20;
28904
+ const addrLower = address.toLowerCase();
28905
+ if (dtfType === "yield") {
28906
+ await yieldHolders(addrLower, config, limit);
28907
+ } else {
28908
+ await indexHolders(addrLower, config, limit);
28909
+ }
28910
+ }
28911
+ async function indexHolders(address, config, limit) {
28912
+ const query = `{
28913
+ token(id: "${address}") { currentHolderCount decimals }
28914
+ accountBalances(
28915
+ where: { token: "${address}", amount_gt: "0" }
28916
+ orderBy: amount, orderDirection: desc
28917
+ first: ${limit}
28918
+ ) {
28919
+ account { id }
28920
+ amount
28921
+ delegate { address }
28922
+ }
28923
+ }`;
28924
+ const chainId = config.chainId;
28925
+ const url = SUBGRAPH_URLS[chainId];
28926
+ const [data, prices] = await Promise.all([
28927
+ querySubgraph(chainId, query, {}, url),
28928
+ fetchTokenPrices(chainId, [address]).catch(() => ({}))
28929
+ ]);
28930
+ const decimals = data.token?.decimals ?? 18;
28931
+ const totalHolders = data.token?.currentHolderCount ?? 0;
28932
+ const tokenPrice = prices[address] ?? 0;
28933
+ const holders = data.accountBalances.map((h, i) => {
28934
+ const balance = Number(h.amount) / 10 ** decimals;
28935
+ return {
28936
+ account: h.account.id,
28937
+ balance: h.amount,
28938
+ balanceHuman: balance,
28939
+ balanceUsd: balance * tokenPrice,
28940
+ delegate: h.delegate?.address,
28941
+ rank: i + 1
28942
+ };
28943
+ });
28944
+ if (config.json) {
28945
+ printJson({
28946
+ dtf: address,
28947
+ chain: config.chainId,
28948
+ chainLabel: chainLabel(config.chainId),
28949
+ type: "index",
28950
+ tokenPrice,
28951
+ holders,
28952
+ totalHolders
28953
+ });
28954
+ return;
28955
+ }
28956
+ printHeader(`Top Holders — ${formatAddress(address)} (${chainLabel(config.chainId)})`);
28957
+ console.log(`Total holders: ${totalHolders}`);
28958
+ if (tokenPrice > 0)
28959
+ console.log(`Token price: ${formatUsd(tokenPrice)}`);
28960
+ console.log();
28961
+ printTable([
28962
+ { header: "#", key: "rank", align: "right" },
28963
+ { header: "Account", key: "account" },
28964
+ { header: "Balance", key: "balance", align: "right" },
28965
+ ...tokenPrice > 0 ? [{ header: "USD", key: "usd", align: "right" }] : []
28966
+ ], holders.map((h) => ({
28967
+ rank: String(h.rank),
28968
+ account: formatAddress(h.account),
28969
+ balance: h.balanceHuman.toLocaleString("en-US", { maximumFractionDigits: 2 }),
28970
+ usd: formatUsd(h.balanceUsd)
28971
+ })));
28972
+ }
28973
+ async function yieldHolders(address, config, limit) {
28974
+ const query = `{
28975
+ token(id: "${address}") { holderCount decimals lastPriceUSD }
28976
+ accountBalances(
28977
+ where: { token: "${address}", amount_gt: "0" }
28978
+ orderBy: amount, orderDirection: desc
28979
+ first: ${limit}
28980
+ ) {
28981
+ account { id }
28982
+ amount
28983
+ }
28984
+ }`;
28985
+ const chainId = config.chainId;
28986
+ const url = YIELD_SUBGRAPH_URLS[chainId];
28987
+ if (!url)
28988
+ throw new Error(`No yield subgraph for chain ${chainId}`);
28989
+ const data = await querySubgraph(chainId, query, {}, url);
28990
+ const decimals = data.token?.decimals ?? 18;
28991
+ const totalHolders = data.token?.holderCount ?? 0;
28992
+ const tokenPrice = Number(data.token?.lastPriceUSD ?? "0");
28993
+ const holders = data.accountBalances.map((h, i) => {
28994
+ const balance = Number(h.amount) / 10 ** decimals;
28995
+ return {
28996
+ account: h.account.id,
28997
+ balance: h.amount,
28998
+ balanceHuman: balance,
28999
+ balanceUsd: balance * tokenPrice,
29000
+ rank: i + 1
29001
+ };
29002
+ });
29003
+ if (config.json) {
29004
+ printJson({
29005
+ dtf: address,
29006
+ chain: config.chainId,
29007
+ chainLabel: chainLabel(config.chainId),
29008
+ type: "yield",
29009
+ tokenPrice,
29010
+ holders,
29011
+ totalHolders
29012
+ });
29013
+ return;
29014
+ }
29015
+ printHeader(`Top Holders — ${formatAddress(address)} (${chainLabel(config.chainId)})`);
29016
+ console.log(`Total holders: ${totalHolders}`);
29017
+ if (tokenPrice > 0)
29018
+ console.log(`Token price: ${formatUsd(tokenPrice)}`);
29019
+ console.log();
29020
+ printTable([
29021
+ { header: "#", key: "rank", align: "right" },
29022
+ { header: "Account", key: "account" },
29023
+ { header: "Balance", key: "balance", align: "right" },
29024
+ ...tokenPrice > 0 ? [{ header: "USD", key: "usd", align: "right" }] : []
29025
+ ], holders.map((h) => ({
29026
+ rank: String(h.rank),
29027
+ account: formatAddress(h.account),
29028
+ balance: h.balanceHuman.toLocaleString("en-US", { maximumFractionDigits: 2 }),
29029
+ usd: formatUsd(h.balanceUsd)
29030
+ })));
29031
+ }
29032
+
29033
+ // src/commands/delegates.ts
29034
+ async function delegatesCommand(address, config) {
29035
+ const { publicClient } = createDtfClients({ chainId: config.chainId, rpc: config.rpc });
29036
+ const dtfType = await resolveDtfType(publicClient, address, config.dtfType);
29037
+ const limit = config.limit ?? 20;
29038
+ const addrLower = address.toLowerCase();
29039
+ if (dtfType === "yield") {
29040
+ await yieldDelegates(addrLower, config, limit);
29041
+ } else {
29042
+ await indexDelegates(publicClient, addrLower, config, limit);
29043
+ }
29044
+ }
29045
+ async function indexDelegates(publicClient, address, config, limit) {
29046
+ const dtfConfig = await fetchDtfConfigCached(fetchDtfConfig, publicClient, config.chainId, address);
29047
+ const stTokenAddress = dtfConfig.stTokenAddress?.toLowerCase();
29048
+ if (!stTokenAddress) {
29049
+ throw new Error(`No stToken found for DTF ${address}. This DTF may not have governance.`);
29050
+ }
29051
+ const query = `{
29052
+ stakingToken(id: "${stTokenAddress}") {
29053
+ currentDelegates totalDelegates delegatedVotesRaw
29054
+ delegates(
29055
+ first: ${limit}
29056
+ orderBy: delegatedVotesRaw
29057
+ orderDirection: desc
29058
+ ) {
29059
+ address
29060
+ delegatedVotesRaw
29061
+ tokenHoldersRepresentedAmount
29062
+ numberVotes
29063
+ }
29064
+ }
29065
+ }`;
29066
+ const chainId = config.chainId;
29067
+ const url = SUBGRAPH_URLS[chainId];
29068
+ const data = await querySubgraph(chainId, query, {}, url);
29069
+ if (!data.stakingToken) {
29070
+ throw new Error(`StakingToken ${stTokenAddress} not found in subgraph`);
29071
+ }
29072
+ const delegates = data.stakingToken.delegates.map((d, i) => ({
29073
+ address: d.address,
29074
+ delegatedVotes: d.delegatedVotesRaw,
29075
+ delegatedVotesHuman: Number(d.delegatedVotesRaw) / 1000000000000000000,
29076
+ representedHolders: d.tokenHoldersRepresentedAmount,
29077
+ votesCast: d.numberVotes,
29078
+ rank: i + 1
29079
+ }));
29080
+ if (config.json) {
29081
+ printJson({
29082
+ dtf: address,
29083
+ chain: config.chainId,
29084
+ chainLabel: chainLabel(config.chainId),
29085
+ type: "index",
29086
+ governanceToken: stTokenAddress,
29087
+ delegates,
29088
+ totalDelegates: data.stakingToken.currentDelegates
29089
+ });
29090
+ return;
29091
+ }
29092
+ printHeader(`Delegates — ${formatAddress(address)} (${chainLabel(config.chainId)})`);
29093
+ console.log(`Active delegates: ${data.stakingToken.currentDelegates} (${data.stakingToken.totalDelegates} total)`);
29094
+ console.log();
29095
+ printTable([
29096
+ { header: "#", key: "rank", align: "right" },
29097
+ { header: "Delegate", key: "delegate" },
29098
+ { header: "Votes", key: "votes", align: "right" },
29099
+ { header: "Holders", key: "holders", align: "right" },
29100
+ { header: "Cast", key: "cast", align: "right" }
29101
+ ], delegates.map((d) => ({
29102
+ rank: String(d.rank),
29103
+ delegate: formatAddress(d.address),
29104
+ votes: d.delegatedVotesHuman.toLocaleString("en-US", { maximumFractionDigits: 0 }),
29105
+ holders: String(d.representedHolders),
29106
+ cast: String(d.votesCast)
29107
+ })));
29108
+ }
29109
+ async function yieldDelegates(address, config, limit) {
29110
+ const addrLower = address.toLowerCase();
29111
+ const query = `{
29112
+ governance(id: "${addrLower}") {
29113
+ currentDelegates totalDelegates delegatedVotesRaw
29114
+ }
29115
+ delegates(
29116
+ where: { governance: "${addrLower}" }
29117
+ first: ${limit}
29118
+ orderBy: delegatedVotesRaw
29119
+ orderDirection: desc
29120
+ ) {
29121
+ address
29122
+ delegatedVotesRaw
29123
+ tokenHoldersRepresentedAmount
29124
+ numberVotes
29125
+ }
29126
+ }`;
29127
+ const chainId = config.chainId;
29128
+ const url = YIELD_SUBGRAPH_URLS[chainId];
29129
+ if (!url)
29130
+ throw new Error(`No yield subgraph for chain ${chainId}`);
29131
+ const data = await querySubgraph(chainId, query, {}, url);
29132
+ const delegates = data.delegates.map((d, i) => ({
29133
+ address: d.address,
29134
+ delegatedVotes: d.delegatedVotesRaw,
29135
+ delegatedVotesHuman: Number(d.delegatedVotesRaw) / 1000000000000000000,
29136
+ representedHolders: d.tokenHoldersRepresentedAmount,
29137
+ votesCast: d.numberVotes,
29138
+ rank: i + 1
29139
+ }));
29140
+ if (config.json) {
29141
+ printJson({
29142
+ dtf: address,
29143
+ chain: config.chainId,
29144
+ chainLabel: chainLabel(config.chainId),
29145
+ type: "yield",
29146
+ governanceToken: addrLower,
29147
+ delegates,
29148
+ totalDelegates: data.governance?.currentDelegates ?? 0
29149
+ });
29150
+ return;
29151
+ }
29152
+ printHeader(`Delegates — ${formatAddress(address)} (${chainLabel(config.chainId)})`);
29153
+ if (data.governance) {
29154
+ console.log(`Active delegates: ${data.governance.currentDelegates} (${data.governance.totalDelegates} total)`);
29155
+ }
29156
+ console.log();
29157
+ printTable([
29158
+ { header: "#", key: "rank", align: "right" },
29159
+ { header: "Delegate", key: "delegate" },
29160
+ { header: "Votes", key: "votes", align: "right" },
29161
+ { header: "Holders", key: "holders", align: "right" },
29162
+ { header: "Cast", key: "cast", align: "right" }
29163
+ ], delegates.map((d) => ({
29164
+ rank: String(d.rank),
29165
+ delegate: formatAddress(d.address),
29166
+ votes: d.delegatedVotesHuman.toLocaleString("en-US", { maximumFractionDigits: 0 }),
29167
+ holders: String(d.representedHolders),
29168
+ cast: String(d.votesCast)
29169
+ })));
29170
+ }
29171
+
27698
29172
  // src/index.ts
27699
29173
  var HELP = `
27700
- dtf — Reserve Protocol Index DTF CLI
29174
+ dtf — Reserve Protocol DTF CLI (Index + Yield)
27701
29175
 
27702
29176
  Usage:
27703
29177
  dtf <command> <address|symbol> [options]
@@ -27719,6 +29193,9 @@ Commands:
27719
29193
  earn Vote-lock yield opportunities
27720
29194
  revenue <address> | --all Revenue breakdown (single DTF or ecosystem)
27721
29195
  rsr-burns RSR burn analytics + projections
29196
+ holders <address> Top token holders with balances
29197
+ delegates <address> Governance delegation graph
29198
+ query '<graphql>' Raw subgraph query (auto-detects index vs yield)
27722
29199
  deploy Deploy a new DTF (--help for options)
27723
29200
  forum Reserve governance forum (monthly top, search, topic detail)
27724
29201
  cache-clear Clear local disk cache (~/.dtf/cache)
@@ -27734,6 +29211,7 @@ Command Options:
27734
29211
  --account <address> Account address for staking command
27735
29212
  --nonce <n> Rebalance nonce for history detail
27736
29213
  --performance <period> Return over period (30d, 3m, 6m, 1y) for discover
29214
+ --subgraph <index|yield> Subgraph to query (auto-detected from entity names)
27737
29215
 
27738
29216
  Address:
27739
29217
  Use a 0x address or a known DTF symbol (case-insensitive).
@@ -27747,7 +29225,7 @@ Examples:
27747
29225
  dtf discover All chains by default
27748
29226
  dtf discover --chain 8453 Single chain
27749
29227
  `;
27750
- var NO_ADDRESS_COMMANDS = new Set(["discover", "earn", "cache-clear", "proposals", "rsr-burns", "revenue", "forum", "deploy"]);
29228
+ var NO_ADDRESS_COMMANDS = new Set(["discover", "earn", "cache-clear", "proposals", "rsr-burns", "revenue", "forum", "deploy", "query"]);
27751
29229
  async function main() {
27752
29230
  const args = process.argv.slice(2);
27753
29231
  if (args.length === 0 || args[0] === "help" || args.includes("--help") && !args[0]?.match(/^[a-z]/)) {
@@ -27783,6 +29261,9 @@ async function main() {
27783
29261
  if (!config.chainExplicit) {
27784
29262
  config.chainId = resolved.chainId;
27785
29263
  }
29264
+ config.dtfType = resolved.type;
29265
+ } else {
29266
+ config.dtfType = getRegistryType(address);
27786
29267
  }
27787
29268
  }
27788
29269
  try {
@@ -27821,6 +29302,12 @@ async function main() {
27821
29302
  case "rebalance-history":
27822
29303
  await rebalanceHistoryCommand(resolvedAddress, config, rest);
27823
29304
  break;
29305
+ case "holders":
29306
+ await holdersCommand(resolvedAddress, config);
29307
+ break;
29308
+ case "delegates":
29309
+ await delegatesCommand(resolvedAddress, config);
29310
+ break;
27824
29311
  case "discover":
27825
29312
  await discoverCommand(config);
27826
29313
  break;
@@ -27840,6 +29327,9 @@ async function main() {
27840
29327
  case "deploy":
27841
29328
  await deployCommand(config, args.slice(1));
27842
29329
  break;
29330
+ case "query":
29331
+ await queryCommand(config, address ? [address, ...rest] : rest);
29332
+ break;
27843
29333
  case "forum":
27844
29334
  await forumCommand(config, address ? [address, ...rest] : rest);
27845
29335
  break;