@parallel-protocol/cli 0.1.1 → 0.2.1

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 +346 -16
  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
  }
@@ -8075,15 +8079,34 @@ function slippageDown(amount) {
8075
8079
  return amount - amount * SLIPPAGE_BPS / 10000n;
8076
8080
  }
8077
8081
  async function readTokenDomain(chain, token) {
8082
+ return readTokenDomainFull(chain, token);
8083
+ }
8084
+ async function readTokenDomainFull(chain, token) {
8078
8085
  const client = getClient(chain);
8079
- const name = await client.readContract({
8080
- address: token,
8081
- abi: ERC20_ABI,
8082
- functionName: "name"
8083
- });
8086
+ const versionAbi = [
8087
+ {
8088
+ name: "version",
8089
+ type: "function",
8090
+ stateMutability: "view",
8091
+ inputs: [],
8092
+ outputs: [{ type: "string" }]
8093
+ }
8094
+ ];
8095
+ const [name, version] = await Promise.all([
8096
+ client.readContract({
8097
+ address: token,
8098
+ abi: ERC20_ABI,
8099
+ functionName: "name"
8100
+ }),
8101
+ client.readContract({
8102
+ address: token,
8103
+ abi: versionAbi,
8104
+ functionName: "version"
8105
+ }).catch(() => "1")
8106
+ ]);
8084
8107
  return {
8085
8108
  name,
8086
- version: "1",
8109
+ version,
8087
8110
  chainId: client.chain.id,
8088
8111
  verifyingContract: token
8089
8112
  };
@@ -10119,7 +10142,183 @@ async function getSupply(token, chain) {
10119
10142
  });
10120
10143
  }
10121
10144
 
10145
+ // src/providers/api.ts
10146
+ async function get2(path) {
10147
+ const base3 = getApiUrl();
10148
+ let res;
10149
+ try {
10150
+ res = await fetch(`${base3}${path}`);
10151
+ } catch (_err) {
10152
+ throw new CliError(
10153
+ ErrorCode.RPC_ERROR,
10154
+ `Cannot reach Parallel API at ${base3}`,
10155
+ "Check PARALLEL_API_URL or your network connection"
10156
+ );
10157
+ }
10158
+ if (!res.ok) {
10159
+ const text = await res.text().catch(() => res.statusText);
10160
+ throw new CliError(
10161
+ ErrorCode.UNKNOWN,
10162
+ `Parallel API error (${res.status}): ${text}`
10163
+ );
10164
+ }
10165
+ const body = await res.json();
10166
+ return body?.json ?? body;
10167
+ }
10168
+ function getHistoricalProtocols(from, to) {
10169
+ const q = new URLSearchParams();
10170
+ if (from) q.set("startDate", from);
10171
+ if (to) q.set("endDate", to);
10172
+ const query = q.toString();
10173
+ return get2(
10174
+ `/public/transparency/historical-protocols${query ? `?${query}` : ""}`
10175
+ );
10176
+ }
10177
+ function reservesByDate(points, chainId) {
10178
+ return points.map((point) => {
10179
+ const perChain = {};
10180
+ for (const protocol of Object.values(point.protocols)) {
10181
+ for (const [id, chainData] of Object.entries(protocol.chains)) {
10182
+ const numericId = Number(id);
10183
+ if (chainId !== void 0 && numericId !== chainId) continue;
10184
+ perChain[numericId] = (perChain[numericId] ?? 0) + Number(chainData.value);
10185
+ }
10186
+ }
10187
+ const total = Object.values(perChain).reduce((sum, v) => sum + v, 0);
10188
+ return { date: point.date, perChain, total };
10189
+ });
10190
+ }
10191
+
10192
+ // src/providers/defillama.ts
10193
+ var TVL_URL = "https://api.llama.fi";
10194
+ var YIELDS_URL = "https://yields.llama.fi";
10195
+ var STABLECOINS_URL = "https://stablecoins.llama.fi";
10196
+ var PARALLEL_SLUG = "parallel-protocol-v3";
10197
+ var USDP_STABLECOIN_ID = "291";
10198
+ var DEFILLAMA_CHAIN = {
10199
+ ethereum: "Ethereum",
10200
+ base: "Base",
10201
+ avalanche: "Avalanche",
10202
+ hyperevm: "Hyperliquid L1",
10203
+ sonic: "Sonic"
10204
+ };
10205
+ var STABLECOIN_CHAIN = {
10206
+ ethereum: "Ethereum",
10207
+ base: "Base",
10208
+ arbitrum: "Arbitrum",
10209
+ optimism: "OP Mainnet",
10210
+ polygon: "Polygon",
10211
+ avalanche: "Avalanche",
10212
+ bsc: "BSC",
10213
+ scroll: "Scroll",
10214
+ gnosis: "Gnosis",
10215
+ sei: "Sei",
10216
+ berachain: "Berachain",
10217
+ hyperevm: "Hyperliquid L1",
10218
+ unichain: "Unichain",
10219
+ ink: "Ink",
10220
+ tac: "TAC",
10221
+ linea: "Linea",
10222
+ xlayer: "X Layer",
10223
+ fraxtal: "Fraxtal",
10224
+ worldchain: "World Chain",
10225
+ hemi: "Hemi",
10226
+ plume: "Plume Mainnet",
10227
+ plasma: "Plasma",
10228
+ katana: "Katana"
10229
+ };
10230
+ var SAVINGS_POOL_ID = {
10231
+ ethereum: "f65159b4-7bec-40c8-8f31-1fa2f5408738",
10232
+ base: "f65159b4-7bec-40c8-8f31-1fa2f5408738",
10233
+ avalanche: "f65159b4-7bec-40c8-8f31-1fa2f5408738",
10234
+ hyperevm: "086da6ff-2302-4a9e-8cd8-1599e67c655d"
10235
+ };
10236
+ var DEFAULT_SAVINGS_POOL_ID = SAVINGS_POOL_ID.ethereum;
10237
+ async function get3(url) {
10238
+ let res;
10239
+ try {
10240
+ res = await fetch(url);
10241
+ } catch (_err) {
10242
+ throw new CliError(
10243
+ ErrorCode.RPC_ERROR,
10244
+ "Cannot reach DeFiLlama",
10245
+ "Check your network connection"
10246
+ );
10247
+ }
10248
+ if (!res.ok) {
10249
+ throw new CliError(ErrorCode.UNKNOWN, `DeFiLlama error (${res.status})`);
10250
+ }
10251
+ return res.json();
10252
+ }
10253
+ function collapseByDay(points) {
10254
+ const byDay = /* @__PURE__ */ new Map();
10255
+ for (const p of points) byDay.set(p.date, p.value);
10256
+ return [...byDay].map(([date, value]) => ({ date, value }));
10257
+ }
10258
+ function unknownChain(chain, supported) {
10259
+ return new CliError(
10260
+ ErrorCode.UNKNOWN_CHAIN,
10261
+ `No DeFiLlama history for chain "${chain}"`,
10262
+ `Supported chains: ${supported.join(", ")}`
10263
+ );
10264
+ }
10265
+ async function getTvlHistory(chain) {
10266
+ let chainName;
10267
+ if (chain) {
10268
+ chainName = DEFILLAMA_CHAIN[chain];
10269
+ if (!chainName) throw unknownChain(chain, Object.keys(DEFILLAMA_CHAIN));
10270
+ }
10271
+ const data = await get3(
10272
+ `${TVL_URL}/protocol/${PARALLEL_SLUG}`
10273
+ );
10274
+ const raw = (chainName ? data.chainTvls?.[chainName]?.tvl : data.tvl) ?? [];
10275
+ return collapseByDay(
10276
+ raw.map((p) => ({
10277
+ date: new Date(p.date * 1e3).toISOString().slice(0, 10),
10278
+ value: p.totalLiquidityUSD
10279
+ }))
10280
+ ).map(({ date, value }) => ({ date, tvl: value }));
10281
+ }
10282
+ async function getSavingsApyHistory(chain) {
10283
+ let poolId = DEFAULT_SAVINGS_POOL_ID;
10284
+ if (chain) {
10285
+ const id = SAVINGS_POOL_ID[chain];
10286
+ if (!id) throw unknownChain(chain, Object.keys(SAVINGS_POOL_ID));
10287
+ poolId = id;
10288
+ }
10289
+ const data = await get3(`${YIELDS_URL}/chart/${poolId}`);
10290
+ return collapseByDay(
10291
+ (data.data ?? []).map((p) => ({
10292
+ date: p.timestamp.slice(0, 10),
10293
+ value: p.apy
10294
+ }))
10295
+ ).map(({ date, value }) => ({ date, apy: value }));
10296
+ }
10297
+ async function getUsdpSupplyHistory(chain) {
10298
+ let chainName;
10299
+ if (chain) {
10300
+ chainName = STABLECOIN_CHAIN[chain];
10301
+ if (!chainName) throw unknownChain(chain, Object.keys(STABLECOIN_CHAIN));
10302
+ }
10303
+ const data = await get3(
10304
+ `${STABLECOINS_URL}/stablecoin/${USDP_STABLECOIN_ID}`
10305
+ );
10306
+ const raw = (chainName ? data.chainBalances?.[chainName]?.tokens : data.tokens) ?? [];
10307
+ return collapseByDay(
10308
+ raw.map((p) => ({
10309
+ date: new Date(p.date * 1e3).toISOString().slice(0, 10),
10310
+ value: p.circulating?.peggedUSD ?? 0
10311
+ }))
10312
+ ).map(({ date, value }) => ({ date, supply: value }));
10313
+ }
10314
+
10122
10315
  // src/utils/chains.ts
10316
+ var CHAIN_NAME_BY_ID = new Map(
10317
+ Object.values(CHAINS).map((cfg) => [cfg.id, cfg.name])
10318
+ );
10319
+ function chainNameById(id) {
10320
+ return CHAIN_NAME_BY_ID.get(id) ?? `chain:${id}`;
10321
+ }
10123
10322
  function assertParallelizerChain(chain) {
10124
10323
  const cfg = CHAINS[chain];
10125
10324
  if (cfg?.status === "sunset")
@@ -10260,8 +10459,13 @@ function overviewCmd() {
10260
10459
  });
10261
10460
  }
10262
10461
  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);
10462
+ 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) {
10463
+ const opts = resolveOpts(this);
10464
+ if (opts.history) {
10465
+ await renderSupplyHistory(token, opts);
10466
+ return;
10467
+ }
10468
+ const { chain } = opts;
10265
10469
  try {
10266
10470
  const result = await getSupply(
10267
10471
  token,
@@ -10284,11 +10488,85 @@ function supplyCmd() {
10284
10488
  }
10285
10489
  });
10286
10490
  }
10491
+ function fmtDelta(delta, fmt2) {
10492
+ if (delta === void 0) return chalk5.dim("\u2014");
10493
+ return delta >= 0 ? chalk5.green(`+${fmt2(delta)}`) : chalk5.red(`\u2212${fmt2(-delta)}`);
10494
+ }
10495
+ function fmtApy(value) {
10496
+ return `${value.toFixed(2)}%`;
10497
+ }
10498
+ function renderHistory(series, opts, cfg) {
10499
+ const { from, to } = opts;
10500
+ let rows = series;
10501
+ if (from) rows = rows.filter((p) => p.date >= from);
10502
+ if (to) rows = rows.filter((p) => p.date <= to);
10503
+ rows = rows.slice().sort((a, b) => b.date.localeCompare(a.date));
10504
+ output({ interval: "day", source: cfg.source, series: rows }, () => {
10505
+ const table = rows.map((row, i) => {
10506
+ const prev = rows[i + 1];
10507
+ const delta = prev ? cfg.valueOf(row) - cfg.valueOf(prev) : void 0;
10508
+ return [row.date, cfg.fmt(cfg.valueOf(row)), fmtDelta(delta, cfg.fmt)];
10509
+ });
10510
+ printTable(cfg.title, ["Date", cfg.valueHeader, "\u0394 vs prev"], table);
10511
+ });
10512
+ }
10513
+ async function renderTvlHistory(opts) {
10514
+ try {
10515
+ const series = await getTvlHistory(opts.chain);
10516
+ renderHistory(series, opts, {
10517
+ valueOf: (r) => r.tvl,
10518
+ fmt: fmtUsd,
10519
+ title: "TVL Over Time \u2014 Daily (source: DeFiLlama)",
10520
+ valueHeader: "TVL",
10521
+ source: "defillama"
10522
+ });
10523
+ } catch (err) {
10524
+ formatCliError(err);
10525
+ }
10526
+ }
10527
+ async function renderSavingsRateHistory(opts) {
10528
+ try {
10529
+ const series = await getSavingsApyHistory(opts.chain);
10530
+ renderHistory(series, opts, {
10531
+ valueOf: (r) => r.apy,
10532
+ fmt: fmtApy,
10533
+ title: "sUSDp APY Over Time \u2014 Daily (source: DeFiLlama)",
10534
+ valueHeader: "APY",
10535
+ source: "defillama"
10536
+ });
10537
+ } catch (err) {
10538
+ formatCliError(err);
10539
+ }
10540
+ }
10541
+ async function renderSupplyHistory(token, opts) {
10542
+ try {
10543
+ if (token.toLowerCase() !== "usdp")
10544
+ throw new CliError(
10545
+ ErrorCode.UNKNOWN,
10546
+ `supply --history is only available for usdp (got "${token}")`,
10547
+ "Only USDp has a public historical supply source"
10548
+ );
10549
+ const series = await getUsdpSupplyHistory(opts.chain);
10550
+ renderHistory(series, opts, {
10551
+ valueOf: (r) => r.supply,
10552
+ fmt: fmtNum,
10553
+ title: "USDp Supply Over Time \u2014 Daily (source: DeFiLlama)",
10554
+ valueHeader: "Supply (USDp)",
10555
+ source: "defillama"
10556
+ });
10557
+ } catch (err) {
10558
+ formatCliError(err);
10559
+ }
10560
+ }
10287
10561
  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);
10562
+ 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() {
10563
+ const opts = resolveOpts(this);
10564
+ if (opts.history) {
10565
+ await renderTvlHistory(opts);
10566
+ return;
10567
+ }
10290
10568
  try {
10291
- const result = await getTvl(chain);
10569
+ const result = await getTvl(opts.chain);
10292
10570
  output(result, () => {
10293
10571
  printKeyValue("TVL", [["Total TVL", fmtUsd(result.totalTVL)]]);
10294
10572
  const rows = result.breakdown.map((b) => [b.chain, fmtUsd(b.tvl)]);
@@ -10299,9 +10577,56 @@ function tvlCmd() {
10299
10577
  }
10300
10578
  });
10301
10579
  }
10580
+ function orderedChainIds(series, latest) {
10581
+ const ids = /* @__PURE__ */ new Set();
10582
+ for (const row of series) {
10583
+ for (const id of Object.keys(row.perChain)) ids.add(Number(id));
10584
+ }
10585
+ return [...ids].sort((a, b) => (latest[b] ?? 0) - (latest[a] ?? 0));
10586
+ }
10587
+ async function renderBackingHistory(opts) {
10588
+ try {
10589
+ let chainId;
10590
+ if (opts.chain) {
10591
+ const cfg = CHAINS[opts.chain];
10592
+ if (!cfg)
10593
+ throw new CliError(
10594
+ ErrorCode.UNKNOWN_CHAIN,
10595
+ `Unknown chain "${opts.chain}"`,
10596
+ "Run: parallel protocol chains to see supported chains"
10597
+ );
10598
+ chainId = cfg.id;
10599
+ }
10600
+ const points = await getHistoricalProtocols(opts.from, opts.to);
10601
+ const series = reservesByDate(points, chainId).sort((a, b) => b.date.localeCompare(a.date));
10602
+ const chainIds = orderedChainIds(series, series[0]?.perChain ?? {});
10603
+ output({ interval: "day", series }, () => {
10604
+ const headers = [
10605
+ "Date",
10606
+ ...chainIds.map((id) => chainNameById(id)),
10607
+ "Total"
10608
+ ];
10609
+ const rows = series.map((row) => [
10610
+ row.date.slice(0, 10),
10611
+ ...chainIds.map(
10612
+ (id) => row.perChain[id] ? fmtUsd(row.perChain[id]) : chalk5.dim("\u2212")
10613
+ ),
10614
+ fmtUsd(row.total)
10615
+ ]);
10616
+ printTable("Backing History (reserves over time) \u2014 Daily", headers, rows);
10617
+ });
10618
+ } catch (err) {
10619
+ formatCliError(err);
10620
+ }
10621
+ }
10302
10622
  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);
10623
+ 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() {
10624
+ const opts = resolveOpts(this);
10625
+ if (opts.history) {
10626
+ await renderBackingHistory(opts);
10627
+ return;
10628
+ }
10629
+ const { chain } = opts;
10305
10630
  try {
10306
10631
  const [result, usdpSupply, solvency] = await Promise.all([
10307
10632
  getTvl(chain),
@@ -10614,8 +10939,13 @@ function feesCmd2() {
10614
10939
  });
10615
10940
  }
10616
10941
  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);
10942
+ 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() {
10943
+ const opts = resolveOpts(this);
10944
+ if (opts.history) {
10945
+ await renderSavingsRateHistory(opts);
10946
+ return;
10947
+ }
10948
+ const { chain } = opts;
10619
10949
  try {
10620
10950
  if (chain) assertParallelizerChain(chain);
10621
10951
  const result = await getSavingsRate();
@@ -14230,7 +14560,7 @@ var package_default = {
14230
14560
 
14231
14561
  // package.json
14232
14562
  var package_default2 = {
14233
- version: "0.1.1"};
14563
+ version: "0.2.1"};
14234
14564
 
14235
14565
  // src/commands/version.ts
14236
14566
  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.1",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "files": [