@parallel-protocol/cli 0.1.1 → 0.2.0

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 (3) hide show
  1. package/README.md +7 -0
  2. package/dist/index.js +321 -10
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -111,6 +111,7 @@ All optional — they override the config file when set.
111
111
  | `PARALLEL_PRIVATE_KEY` | Raw private key (hex, `0x` prefix) — alternative to keystore, useful for scripts and CI |
112
112
  | `PARALLEL_DEFAULT_CHAIN` | Default chain — overrides `network.default_chain` in config |
113
113
  | `PARALLEL_JSON` | Set to `true` to force JSON output on all commands |
114
+ | `PARALLEL_API_URL` | Parallel API base URL for `--history` commands (default: `https://api.parallel.best`) |
114
115
 
115
116
 
116
117
  ---
@@ -157,6 +158,12 @@ parallel protocol exchange-rate [--chain <chain>] # sUSDp /
157
158
  parallel protocol solvency [--chain <chain>] # Backing ratio per chain
158
159
  parallel protocol surplus [--chain <chain>] # Pending surplus (USDp)
159
160
  parallel protocol flashloan [--chain <chain>] # Flash loan capacity + fee rate
161
+
162
+ # Historical data (daily series) — flags: [--from <YYYY-MM-DD>] [--to <YYYY-MM-DD>] [--chain <chain>]
163
+ parallel protocol backing --history # reserves over time, per chain (source: Parallel API)
164
+ parallel protocol tvl --history # TVL over time (source: DeFiLlama)
165
+ parallel protocol savings-rate --history # sUSDp APY over time (source: DeFiLlama)
166
+ parallel protocol supply --history usdp # USDp circulating supply over time (source: DeFiLlama)
160
167
  ```
161
168
 
162
169
  ---
package/dist/index.js CHANGED
@@ -4751,6 +4751,7 @@ async function buildBridgeTx(fromChain, toChain, amount, sender, recipient) {
4751
4751
  var CONFIG_DIR = join(homedir(), ".parallel");
4752
4752
  var CONFIG_PATH = join(CONFIG_DIR, "config.toml");
4753
4753
  var PROD_FACILITATOR_URL = "https://agents.parallel.best";
4754
+ var PROD_API_URL = "https://api.parallel.best";
4754
4755
  var _cache = null;
4755
4756
  function loadConfig() {
4756
4757
  if (_cache) return _cache;
@@ -4778,6 +4779,9 @@ function getDefaultChain() {
4778
4779
  function getFacilitatorUrl() {
4779
4780
  return process.env.FACILITATOR_URL ?? loadConfig().facilitator?.url ?? PROD_FACILITATOR_URL;
4780
4781
  }
4782
+ function getApiUrl() {
4783
+ return process.env.PARALLEL_API_URL ?? process.env.SUPPLY_API_URL ?? loadConfig().api?.url ?? PROD_API_URL;
4784
+ }
4781
4785
  function getKeystorePath() {
4782
4786
  return loadConfig().wallet?.keystore_path;
4783
4787
  }
@@ -10119,7 +10123,183 @@ async function getSupply(token, chain) {
10119
10123
  });
10120
10124
  }
10121
10125
 
10126
+ // src/providers/api.ts
10127
+ async function get2(path) {
10128
+ const base3 = getApiUrl();
10129
+ let res;
10130
+ try {
10131
+ res = await fetch(`${base3}${path}`);
10132
+ } catch (_err) {
10133
+ throw new CliError(
10134
+ ErrorCode.RPC_ERROR,
10135
+ `Cannot reach Parallel API at ${base3}`,
10136
+ "Check PARALLEL_API_URL or your network connection"
10137
+ );
10138
+ }
10139
+ if (!res.ok) {
10140
+ const text = await res.text().catch(() => res.statusText);
10141
+ throw new CliError(
10142
+ ErrorCode.UNKNOWN,
10143
+ `Parallel API error (${res.status}): ${text}`
10144
+ );
10145
+ }
10146
+ const body = await res.json();
10147
+ return body?.json ?? body;
10148
+ }
10149
+ function getHistoricalProtocols(from, to) {
10150
+ const q = new URLSearchParams();
10151
+ if (from) q.set("startDate", from);
10152
+ if (to) q.set("endDate", to);
10153
+ const query = q.toString();
10154
+ return get2(
10155
+ `/public/transparency/historical-protocols${query ? `?${query}` : ""}`
10156
+ );
10157
+ }
10158
+ function reservesByDate(points, chainId) {
10159
+ return points.map((point) => {
10160
+ const perChain = {};
10161
+ for (const protocol of Object.values(point.protocols)) {
10162
+ for (const [id, chainData] of Object.entries(protocol.chains)) {
10163
+ const numericId = Number(id);
10164
+ if (chainId !== void 0 && numericId !== chainId) continue;
10165
+ perChain[numericId] = (perChain[numericId] ?? 0) + Number(chainData.value);
10166
+ }
10167
+ }
10168
+ const total = Object.values(perChain).reduce((sum, v) => sum + v, 0);
10169
+ return { date: point.date, perChain, total };
10170
+ });
10171
+ }
10172
+
10173
+ // src/providers/defillama.ts
10174
+ var TVL_URL = "https://api.llama.fi";
10175
+ var YIELDS_URL = "https://yields.llama.fi";
10176
+ var STABLECOINS_URL = "https://stablecoins.llama.fi";
10177
+ var PARALLEL_SLUG = "parallel-protocol-v3";
10178
+ var USDP_STABLECOIN_ID = "291";
10179
+ var DEFILLAMA_CHAIN = {
10180
+ ethereum: "Ethereum",
10181
+ base: "Base",
10182
+ avalanche: "Avalanche",
10183
+ hyperevm: "Hyperliquid L1",
10184
+ sonic: "Sonic"
10185
+ };
10186
+ var STABLECOIN_CHAIN = {
10187
+ ethereum: "Ethereum",
10188
+ base: "Base",
10189
+ arbitrum: "Arbitrum",
10190
+ optimism: "OP Mainnet",
10191
+ polygon: "Polygon",
10192
+ avalanche: "Avalanche",
10193
+ bsc: "BSC",
10194
+ scroll: "Scroll",
10195
+ gnosis: "Gnosis",
10196
+ sei: "Sei",
10197
+ berachain: "Berachain",
10198
+ hyperevm: "Hyperliquid L1",
10199
+ unichain: "Unichain",
10200
+ ink: "Ink",
10201
+ tac: "TAC",
10202
+ linea: "Linea",
10203
+ xlayer: "X Layer",
10204
+ fraxtal: "Fraxtal",
10205
+ worldchain: "World Chain",
10206
+ hemi: "Hemi",
10207
+ plume: "Plume Mainnet",
10208
+ plasma: "Plasma",
10209
+ katana: "Katana"
10210
+ };
10211
+ var SAVINGS_POOL_ID = {
10212
+ ethereum: "f65159b4-7bec-40c8-8f31-1fa2f5408738",
10213
+ base: "f65159b4-7bec-40c8-8f31-1fa2f5408738",
10214
+ avalanche: "f65159b4-7bec-40c8-8f31-1fa2f5408738",
10215
+ hyperevm: "086da6ff-2302-4a9e-8cd8-1599e67c655d"
10216
+ };
10217
+ var DEFAULT_SAVINGS_POOL_ID = SAVINGS_POOL_ID.ethereum;
10218
+ async function get3(url) {
10219
+ let res;
10220
+ try {
10221
+ res = await fetch(url);
10222
+ } catch (_err) {
10223
+ throw new CliError(
10224
+ ErrorCode.RPC_ERROR,
10225
+ "Cannot reach DeFiLlama",
10226
+ "Check your network connection"
10227
+ );
10228
+ }
10229
+ if (!res.ok) {
10230
+ throw new CliError(ErrorCode.UNKNOWN, `DeFiLlama error (${res.status})`);
10231
+ }
10232
+ return res.json();
10233
+ }
10234
+ function collapseByDay(points) {
10235
+ const byDay = /* @__PURE__ */ new Map();
10236
+ for (const p of points) byDay.set(p.date, p.value);
10237
+ return [...byDay].map(([date, value]) => ({ date, value }));
10238
+ }
10239
+ function unknownChain(chain, supported) {
10240
+ return new CliError(
10241
+ ErrorCode.UNKNOWN_CHAIN,
10242
+ `No DeFiLlama history for chain "${chain}"`,
10243
+ `Supported chains: ${supported.join(", ")}`
10244
+ );
10245
+ }
10246
+ async function getTvlHistory(chain) {
10247
+ let chainName;
10248
+ if (chain) {
10249
+ chainName = DEFILLAMA_CHAIN[chain];
10250
+ if (!chainName) throw unknownChain(chain, Object.keys(DEFILLAMA_CHAIN));
10251
+ }
10252
+ const data = await get3(
10253
+ `${TVL_URL}/protocol/${PARALLEL_SLUG}`
10254
+ );
10255
+ const raw = (chainName ? data.chainTvls?.[chainName]?.tvl : data.tvl) ?? [];
10256
+ return collapseByDay(
10257
+ raw.map((p) => ({
10258
+ date: new Date(p.date * 1e3).toISOString().slice(0, 10),
10259
+ value: p.totalLiquidityUSD
10260
+ }))
10261
+ ).map(({ date, value }) => ({ date, tvl: value }));
10262
+ }
10263
+ async function getSavingsApyHistory(chain) {
10264
+ let poolId = DEFAULT_SAVINGS_POOL_ID;
10265
+ if (chain) {
10266
+ const id = SAVINGS_POOL_ID[chain];
10267
+ if (!id) throw unknownChain(chain, Object.keys(SAVINGS_POOL_ID));
10268
+ poolId = id;
10269
+ }
10270
+ const data = await get3(`${YIELDS_URL}/chart/${poolId}`);
10271
+ return collapseByDay(
10272
+ (data.data ?? []).map((p) => ({
10273
+ date: p.timestamp.slice(0, 10),
10274
+ value: p.apy
10275
+ }))
10276
+ ).map(({ date, value }) => ({ date, apy: value }));
10277
+ }
10278
+ async function getUsdpSupplyHistory(chain) {
10279
+ let chainName;
10280
+ if (chain) {
10281
+ chainName = STABLECOIN_CHAIN[chain];
10282
+ if (!chainName) throw unknownChain(chain, Object.keys(STABLECOIN_CHAIN));
10283
+ }
10284
+ const data = await get3(
10285
+ `${STABLECOINS_URL}/stablecoin/${USDP_STABLECOIN_ID}`
10286
+ );
10287
+ const raw = (chainName ? data.chainBalances?.[chainName]?.tokens : data.tokens) ?? [];
10288
+ return collapseByDay(
10289
+ raw.map((p) => ({
10290
+ date: new Date(p.date * 1e3).toISOString().slice(0, 10),
10291
+ value: p.circulating?.peggedUSD ?? 0
10292
+ }))
10293
+ ).map(({ date, value }) => ({ date, supply: value }));
10294
+ }
10295
+
10122
10296
  // src/utils/chains.ts
10297
+ var CHAIN_NAME_BY_ID = new Map(
10298
+ Object.values(CHAINS).map((cfg) => [cfg.id, cfg.name])
10299
+ );
10300
+ function chainNameById(id) {
10301
+ return CHAIN_NAME_BY_ID.get(id) ?? `chain:${id}`;
10302
+ }
10123
10303
  function assertParallelizerChain(chain) {
10124
10304
  const cfg = CHAINS[chain];
10125
10305
  if (cfg?.status === "sunset")
@@ -10260,8 +10440,13 @@ function overviewCmd() {
10260
10440
  });
10261
10441
  }
10262
10442
  function supplyCmd() {
10263
- return new Command("supply").description("Token supply: usdp | susdp | prl | sprl1 | sprl2").argument("<token>", "token symbol").option("-c, --chain <chain>", "filter to a single chain").action(async function(token) {
10264
- const { chain } = resolveOpts(this);
10443
+ return new Command("supply").description("Token supply: usdp | susdp | prl | sprl1 | sprl2").argument("<token>", "token symbol").option("-c, --chain <chain>", "filter to a single chain").option("--history", "supply over time \u2014 usdp only (source: DeFiLlama)").option("--from <date>", "history start date (YYYY-MM-DD)").option("--to <date>", "history end date (YYYY-MM-DD)").action(async function(token) {
10444
+ const opts = resolveOpts(this);
10445
+ if (opts.history) {
10446
+ await renderSupplyHistory(token, opts);
10447
+ return;
10448
+ }
10449
+ const { chain } = opts;
10265
10450
  try {
10266
10451
  const result = await getSupply(
10267
10452
  token,
@@ -10284,11 +10469,85 @@ function supplyCmd() {
10284
10469
  }
10285
10470
  });
10286
10471
  }
10472
+ function fmtDelta(delta, fmt2) {
10473
+ if (delta === void 0) return chalk5.dim("\u2014");
10474
+ return delta >= 0 ? chalk5.green(`+${fmt2(delta)}`) : chalk5.red(`\u2212${fmt2(-delta)}`);
10475
+ }
10476
+ function fmtApy(value) {
10477
+ return `${value.toFixed(2)}%`;
10478
+ }
10479
+ function renderHistory(series, opts, cfg) {
10480
+ const { from, to } = opts;
10481
+ let rows = series;
10482
+ if (from) rows = rows.filter((p) => p.date >= from);
10483
+ if (to) rows = rows.filter((p) => p.date <= to);
10484
+ rows = rows.slice().sort((a, b) => b.date.localeCompare(a.date));
10485
+ output({ interval: "day", source: cfg.source, series: rows }, () => {
10486
+ const table = rows.map((row, i) => {
10487
+ const prev = rows[i + 1];
10488
+ const delta = prev ? cfg.valueOf(row) - cfg.valueOf(prev) : void 0;
10489
+ return [row.date, cfg.fmt(cfg.valueOf(row)), fmtDelta(delta, cfg.fmt)];
10490
+ });
10491
+ printTable(cfg.title, ["Date", cfg.valueHeader, "\u0394 vs prev"], table);
10492
+ });
10493
+ }
10494
+ async function renderTvlHistory(opts) {
10495
+ try {
10496
+ const series = await getTvlHistory(opts.chain);
10497
+ renderHistory(series, opts, {
10498
+ valueOf: (r) => r.tvl,
10499
+ fmt: fmtUsd,
10500
+ title: "TVL Over Time \u2014 Daily (source: DeFiLlama)",
10501
+ valueHeader: "TVL",
10502
+ source: "defillama"
10503
+ });
10504
+ } catch (err) {
10505
+ formatCliError(err);
10506
+ }
10507
+ }
10508
+ async function renderSavingsRateHistory(opts) {
10509
+ try {
10510
+ const series = await getSavingsApyHistory(opts.chain);
10511
+ renderHistory(series, opts, {
10512
+ valueOf: (r) => r.apy,
10513
+ fmt: fmtApy,
10514
+ title: "sUSDp APY Over Time \u2014 Daily (source: DeFiLlama)",
10515
+ valueHeader: "APY",
10516
+ source: "defillama"
10517
+ });
10518
+ } catch (err) {
10519
+ formatCliError(err);
10520
+ }
10521
+ }
10522
+ async function renderSupplyHistory(token, opts) {
10523
+ try {
10524
+ if (token.toLowerCase() !== "usdp")
10525
+ throw new CliError(
10526
+ ErrorCode.UNKNOWN,
10527
+ `supply --history is only available for usdp (got "${token}")`,
10528
+ "Only USDp has a public historical supply source"
10529
+ );
10530
+ const series = await getUsdpSupplyHistory(opts.chain);
10531
+ renderHistory(series, opts, {
10532
+ valueOf: (r) => r.supply,
10533
+ fmt: fmtNum,
10534
+ title: "USDp Supply Over Time \u2014 Daily (source: DeFiLlama)",
10535
+ valueHeader: "Supply (USDp)",
10536
+ source: "defillama"
10537
+ });
10538
+ } catch (err) {
10539
+ formatCliError(err);
10540
+ }
10541
+ }
10287
10542
  function tvlCmd() {
10288
- return new Command("tvl").description("Total Value Locked across all Parallelizer chains").option("-c, --chain <chain>", "filter to a single chain").action(async function() {
10289
- const { chain } = resolveOpts(this);
10543
+ return new Command("tvl").description("Total Value Locked across all Parallelizer chains").option("-c, --chain <chain>", "filter to a single chain").option("--history", "TVL over time (source: DeFiLlama)").option("--from <date>", "history start date (YYYY-MM-DD)").option("--to <date>", "history end date (YYYY-MM-DD)").action(async function() {
10544
+ const opts = resolveOpts(this);
10545
+ if (opts.history) {
10546
+ await renderTvlHistory(opts);
10547
+ return;
10548
+ }
10290
10549
  try {
10291
- const result = await getTvl(chain);
10550
+ const result = await getTvl(opts.chain);
10292
10551
  output(result, () => {
10293
10552
  printKeyValue("TVL", [["Total TVL", fmtUsd(result.totalTVL)]]);
10294
10553
  const rows = result.breakdown.map((b) => [b.chain, fmtUsd(b.tvl)]);
@@ -10299,9 +10558,56 @@ function tvlCmd() {
10299
10558
  }
10300
10559
  });
10301
10560
  }
10561
+ function orderedChainIds(series, latest) {
10562
+ const ids = /* @__PURE__ */ new Set();
10563
+ for (const row of series) {
10564
+ for (const id of Object.keys(row.perChain)) ids.add(Number(id));
10565
+ }
10566
+ return [...ids].sort((a, b) => (latest[b] ?? 0) - (latest[a] ?? 0));
10567
+ }
10568
+ async function renderBackingHistory(opts) {
10569
+ try {
10570
+ let chainId;
10571
+ if (opts.chain) {
10572
+ const cfg = CHAINS[opts.chain];
10573
+ if (!cfg)
10574
+ throw new CliError(
10575
+ ErrorCode.UNKNOWN_CHAIN,
10576
+ `Unknown chain "${opts.chain}"`,
10577
+ "Run: parallel protocol chains to see supported chains"
10578
+ );
10579
+ chainId = cfg.id;
10580
+ }
10581
+ const points = await getHistoricalProtocols(opts.from, opts.to);
10582
+ const series = reservesByDate(points, chainId).sort((a, b) => b.date.localeCompare(a.date));
10583
+ const chainIds = orderedChainIds(series, series[0]?.perChain ?? {});
10584
+ output({ interval: "day", series }, () => {
10585
+ const headers = [
10586
+ "Date",
10587
+ ...chainIds.map((id) => chainNameById(id)),
10588
+ "Total"
10589
+ ];
10590
+ const rows = series.map((row) => [
10591
+ row.date.slice(0, 10),
10592
+ ...chainIds.map(
10593
+ (id) => row.perChain[id] ? fmtUsd(row.perChain[id]) : chalk5.dim("\u2212")
10594
+ ),
10595
+ fmtUsd(row.total)
10596
+ ]);
10597
+ printTable("Backing History (reserves over time) \u2014 Daily", headers, rows);
10598
+ });
10599
+ } catch (err) {
10600
+ formatCliError(err);
10601
+ }
10602
+ }
10302
10603
  function backingCmd() {
10303
- return new Command("backing").description("Collateral backing composition per chain").option("-c, --chain <chain>", "filter to a single chain").action(async function() {
10304
- const { chain } = resolveOpts(this);
10604
+ return new Command("backing").description("Collateral backing composition per chain").option("-c, --chain <chain>", "filter to a single chain").option("--history", "reserves (backing) over time, per chain").option("--from <date>", "history start date (YYYY-MM-DD)").option("--to <date>", "history end date (YYYY-MM-DD)").action(async function() {
10605
+ const opts = resolveOpts(this);
10606
+ if (opts.history) {
10607
+ await renderBackingHistory(opts);
10608
+ return;
10609
+ }
10610
+ const { chain } = opts;
10305
10611
  try {
10306
10612
  const [result, usdpSupply, solvency] = await Promise.all([
10307
10613
  getTvl(chain),
@@ -10614,8 +10920,13 @@ function feesCmd2() {
10614
10920
  });
10615
10921
  }
10616
10922
  function savingsRateCmd() {
10617
- return new Command("savings-rate").description("sUSDp savings rate (APY) across all chains").option("-c, --chain <chain>", "filter to a single chain").action(async function() {
10618
- const { chain } = resolveOpts(this);
10923
+ return new Command("savings-rate").description("sUSDp savings rate (APY) across all chains").option("-c, --chain <chain>", "filter to a single chain").option("--history", "APY over time (source: DeFiLlama)").option("--from <date>", "history start date (YYYY-MM-DD)").option("--to <date>", "history end date (YYYY-MM-DD)").action(async function() {
10924
+ const opts = resolveOpts(this);
10925
+ if (opts.history) {
10926
+ await renderSavingsRateHistory(opts);
10927
+ return;
10928
+ }
10929
+ const { chain } = opts;
10619
10930
  try {
10620
10931
  if (chain) assertParallelizerChain(chain);
10621
10932
  const result = await getSavingsRate();
@@ -14230,7 +14541,7 @@ var package_default = {
14230
14541
 
14231
14542
  // package.json
14232
14543
  var package_default2 = {
14233
- version: "0.1.1"};
14544
+ version: "0.2.0"};
14234
14545
 
14235
14546
  // src/commands/version.ts
14236
14547
  function versionCommand() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parallel-protocol/cli",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "files": [