@parabolicfamily/mcp 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -33,10 +33,20 @@ Set `PARABOLIC_CHAIN_ID` and the RPC and contract addresses for that chain follo
33
33
  | `PARABOLIC_HOOK` | `ParabolicHook` address | read from `factory.memeHook()` |
34
34
  | `PARABOLIC_ARC_MEMO` | Arc `Memo` system contract | `0x5294E9927c3306DcBaDb03fe70b92e01cCede505` |
35
35
  | `PARABOLIC_LAUNCH_CONFIG_ID` | Factory launch-config index (0 = the $5K open / $12,500 graduation curve) | `0` |
36
- | `PARABOLIC_FACTORY_START_BLOCK` | First block of the `TokenLaunched` scan used when there is no subgraph | `0` |
36
+ | `PARABOLIC_FACTORY_START_BLOCK` | First block of the `TokenLaunched` scan, used only when neither a subgraph nor the API is available | the factory's deployment block |
37
37
 
38
- Every deployed address is published at <https://www.parabolic.family/docs#contracts> and verified on
39
- Arcscan, so you can check the built-in defaults against the site before you sign anything.
38
+ Call `parabolic.docs` to print the exact addresses this server is using, including the memo router
39
+ every launch with an opening buy is sent to. Check each one on Arcscan, where all of them are verified
40
+ with source, before you sign anything:
41
+
42
+ | Contract | Arc Testnet (5042002) |
43
+ |---|---|
44
+ | `ParabolicLaunchFactory` | [`0xDA14d24385483c5876e0E170c6135F14Fc3BCEc3`](https://testnet.arcscan.app/address/0xDA14d24385483c5876e0E170c6135F14Fc3BCEc3) |
45
+ | `ParabolicMemoRouter` | [`0x32E6e071eA1Fe58DF86C4D3f419a8BAF2118f505`](https://testnet.arcscan.app/address/0x32E6e071eA1Fe58DF86C4D3f419a8BAF2118f505) |
46
+ | EURC (test token) | [`0x89B50855Aa3bE2F677cD6303Cec089B5F319D72a`](https://testnet.arcscan.app/address/0x89B50855Aa3bE2F677cD6303Cec089B5F319D72a) |
47
+ | Arc `Memo` (system) | [`0x5294E9927c3306DcBaDb03fe70b92e01cCede505`](https://testnet.arcscan.app/address/0x5294E9927c3306DcBaDb03fe70b92e01cCede505) |
48
+
49
+ Arc mainnet (5042) addresses ship in the release that follows the 16 September 2026 deployment.
40
50
 
41
51
  ### Claude Code
42
52
 
@@ -44,8 +54,10 @@ Arcscan, so you can check the built-in defaults against the site before you sign
44
54
  claude mcp add parabolic -- npx -y @parabolicfamily/mcp
45
55
  ```
46
56
 
47
- That is enough to browse coins and quote trades on Arc Testnet. For mainnet, or to use a subgraph for
48
- richer listings, add the environment:
57
+ That is enough to launch, quote and trade on Arc Testnet: `get_coin`, `quote_buy`, `quote_sell` and
58
+ every `build_*` tool read the chain directly. `list_coins` is the exception — with no subgraph it can
59
+ only scan the most recent blocks, because public Arc RPCs cap log queries, so it marks its result
60
+ `partial` and names the range it covered. Point it at a subgraph for the full board:
49
61
 
50
62
  ```sh
51
63
  claude mcp add parabolic \
@@ -152,7 +164,7 @@ With `PARABOLIC_MEMO_ROUTER` set the result is `kind: "launch_and_buy"`: one `tx
152
164
  - At graduation the curve still holds the reserved 28.57% of supply: ≈ 20.4% seeds the Uniswap v4 full-range position with the $12,500 raised at the same terminal price, and ≈ 8.2% is permanently locked. Fully diluted graduation market cap: $61,250.
153
165
  - Fair launch: `ParabolicMemoRouter.launchAndBuy` / `launchAndBuyWithToken` put the creator's opening buy inside the launch transaction (snipe-tax exempt, before anyone else can trade). Called directly they work from any wallet; the router forwards no exemption list and rejects a `creatorFeeRecipient` other than zero or the signer.
154
166
  - A memo-routed buy (`memo: { referral }`) wraps `ParabolicMemoRouter.buy` inside Arc's `Memo.memo(...)` so the referral code is attributed on-chain. It is **EOA-only** (the Memo contract's caller must be `tx.origin`) and forwards no value, so the router pulls whole 6-decimal USDC units through the ERC-20 view (`0x3600…0000`) and needs a one-time approval; smart-account wallets should use the direct buy. Memo-wrapped launches (referral on a launch) are not built here.
155
- - The factory has no enumeration function (only `getLaunchedToken(address)`), so listing needs the subgraph; without it the server scans `TokenLaunched` logs from `PARABOLIC_FACTORY_START_BLOCK` and reads the newest 50 launches.
167
+ - The factory has no enumeration function (only `getLaunchedToken(address)`), so listing comes from an indexer: `PARABOLIC_SUBGRAPH_URL` when set, otherwise Parabolic's public read API. Only if both are unavailable does it scan `TokenLaunched` logs, which public Arc RPCs rate-limit hard.
156
168
 
157
169
  ## Development
158
170
 
@@ -1905,6 +1905,11 @@
1905
1905
  "name": "CurveNotQuotable",
1906
1906
  "inputs": []
1907
1907
  },
1908
+ {
1909
+ "type": "error",
1910
+ "name": "FeeRecipientMustBeCreator",
1911
+ "inputs": []
1912
+ },
1908
1913
  {
1909
1914
  "type": "error",
1910
1915
  "name": "FeeTransferFailed",
package/dist/config.d.ts CHANGED
@@ -6,6 +6,7 @@ export declare const USDC_VIEW: Address;
6
6
  /** `address(0)` as the pair token means the curve is quoted in native USDC. */
7
7
  export declare const NATIVE_QUOTE: Address;
8
8
  export declare const DEFAULT_CHAIN_ID = 5042002;
9
+ export declare const DEFAULT_API_URL = "https://www.parabolic.family/api/v1";
9
10
  /**
10
11
  * Deployed Parabolic addresses per chain, so the server is useful with no configuration at all.
11
12
  * The source repository is private; these are the same addresses the site publishes at
@@ -31,6 +32,11 @@ export type Config = {
31
32
  chainId: number;
32
33
  /** ParabolicLaunchFactory. Required for on-chain coin lookups and for building launch transactions. */
33
34
  factory?: Address;
35
+ /**
36
+ * Parabolic's public read API, used for listings when no subgraph is configured. Set to "" to opt
37
+ * out and fall back to a TokenLaunched log scan, which public Arc RPCs rate-limit hard.
38
+ */
39
+ apiUrl?: string;
34
40
  /** ParabolicMemoRouter. Optional; enables the memo-routed (referral-carrying) buy variant. */
35
41
  memoRouter?: Address;
36
42
  /** Parabolic subgraph GraphQL endpoint. Recommended: listing, holders, volume and stats come from here. */
package/dist/config.js CHANGED
@@ -6,6 +6,7 @@ export const USDC_VIEW = "0x3600000000000000000000000000000000000000";
6
6
  /** `address(0)` as the pair token means the curve is quoted in native USDC. */
7
7
  export const NATIVE_QUOTE = "0x0000000000000000000000000000000000000000";
8
8
  export const DEFAULT_CHAIN_ID = 5042002;
9
+ export const DEFAULT_API_URL = "https://www.parabolic.family/api/v1";
9
10
  /**
10
11
  * Deployed Parabolic addresses per chain, so the server is useful with no configuration at all.
11
12
  * The source repository is private; these are the same addresses the site publishes at
@@ -37,9 +38,10 @@ export const ENV_VARS = [
37
38
  ["PARABOLIC_SUBGRAPH_URL", "Parabolic subgraph endpoint (listing, holders, volume, protocol stats)"],
38
39
  ["PARABOLIC_EURC", "ERC-20 EURC address (EURC-quoted launches only)"],
39
40
  ["PARABOLIC_HOOK", "ParabolicHook address (optional; read from the factory when unset)"],
41
+ ["PARABOLIC_API_URL", `Parabolic public read API for listings (default ${DEFAULT_API_URL}; "" to use a log scan instead)`],
40
42
  ["PARABOLIC_ARC_MEMO", `Arc Memo system contract (default ${ARC_MEMO_DEFAULT})`],
41
43
  ["PARABOLIC_LAUNCH_CONFIG_ID", "factory launch config index (default 0)"],
42
- ["PARABOLIC_FACTORY_START_BLOCK", "first block for the TokenLaunched log scan (default 0)"],
44
+ ["PARABOLIC_FACTORY_START_BLOCK", "first block for the TokenLaunched log scan (default: the factory's deployment block for this chain)"],
43
45
  ];
44
46
  function addr(name, v) {
45
47
  if (v === undefined || v.trim() === "")
@@ -68,6 +70,7 @@ export function configFromEnv(env = process.env) {
68
70
  chainId,
69
71
  factory: addr("PARABOLIC_FACTORY", env.PARABOLIC_FACTORY) ?? known?.factory,
70
72
  memoRouter: addr("PARABOLIC_MEMO_ROUTER", env.PARABOLIC_MEMO_ROUTER) ?? known?.memoRouter,
73
+ apiUrl: env.PARABOLIC_API_URL === undefined ? DEFAULT_API_URL : env.PARABOLIC_API_URL.trim() || undefined,
71
74
  subgraphUrl: env.PARABOLIC_SUBGRAPH_URL?.trim() || undefined,
72
75
  eurc: addr("PARABOLIC_EURC", env.PARABOLIC_EURC) ?? known?.eurc,
73
76
  hook: addr("PARABOLIC_HOOK", env.PARABOLIC_HOOK),
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
package/dist/docs.js CHANGED
@@ -1,18 +1,7 @@
1
1
  import { ARC_MEMO_DEFAULT, ENV_VARS, USDC_VIEW, chainName } from "./config.js";
2
+ import { endpointHost } from "./format.js";
2
3
  /** Adopted product parameters (adopted 2026-09-06; published at https://www.parabolic.family/docs#parameters) and verified Arc facts, as text. */
3
4
  export function docsText(config) {
4
- /**
5
- * Host only. An operator may point PARABOLIC_RPC_URL or PARABOLIC_SUBGRAPH_URL at an endpoint whose
6
- * path carries an API key; this text goes straight into an agent's context, so the path never does.
7
- */
8
- const endpointHost = (url) => {
9
- try {
10
- return new URL(url).host;
11
- }
12
- catch {
13
- return "(configured)";
14
- }
15
- };
16
5
  const set = (v) => v ?? "(not configured)";
17
6
  return [
18
7
  "PARABOLIC — dollar-native token launchpad on Arc",
package/dist/docs.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"docs.js","sourceRoot":"","sources":["../src/docs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAe,MAAM,aAAa,CAAC;AAE5F,kJAAkJ;AAClJ,MAAM,UAAU,QAAQ,CAAC,MAAc;IACvC;;;OAGG;IACH,MAAM,YAAY,GAAG,CAAC,GAAW,EAAU,EAAE;QAC3C,IAAI,CAAC;YACH,OAAO,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;QAC3B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,cAAc,CAAC;QACxB,CAAC;IACH,CAAC,CAAC;IAEA,MAAM,GAAG,GAAG,CAAC,CAAqB,EAAE,EAAE,CAAC,CAAC,IAAI,kBAAkB,CAAC;IAC/D,OAAO;QACL,kDAAkD;QAClD,4HAA4H;QAC5H,kPAAkP;QAClP,EAAE;QACF,iCAAiC;QACjC,wEAAwE;QACxE,yIAAyI;QACzI,uHAAuH;QACvH,sRAAsR;QACtR,geAAge;QAChe,oEAAoE;QACpE,0TAA0T;QAC1T,kIAAkI;QAClI,wYAAwY;QACxY,uJAAuJ;QACvJ,qjBAAqjB;QACrjB,EAAE;QACF,0CAA0C;QAC1C,8MAA8M;QAC9M,2QAA2Q;QAC3Q,sKAAsK;QACtK,0JAA0J;QAC1J,6KAA6K;QAC7K,EAAE;QACF,kDAAkD;QAClD,8LAA8L;QAC9L,+FAA+F,SAAS,gFAAgF;QACxL,mMAAmM;QACnM,wCAAwC,gBAAgB,8IAA8I;QACtM,iLAAiL;QACjL,4IAA4I;QAC5I,8CAA8C;QAC9C,EAAE;QACF,aAAa;QACb,YAAY,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG;QAChG,cAAc,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,mBAAmB,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,gBAAgB,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,kBAAkB,YAAY,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,MAAM,CAAC,IAAI,IAAI,qBAAqB,wBAAwB,MAAM,CAAC,cAAc,GAAG;QAChS,0RAA0R;QAC1R,yNAAyN;QACzN,8PAA8P,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,+BAA+B,gHAAgH;QAC1b,sKAAsK;QACtK,EAAE;QACF,aAAa;QACb,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;KAC5C,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
1
+ {"version":3,"file":"docs.js","sourceRoot":"","sources":["../src/docs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAe,MAAM,aAAa,CAAC;AAC5F,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,kJAAkJ;AAClJ,MAAM,UAAU,QAAQ,CAAC,MAAc;IACrC,MAAM,GAAG,GAAG,CAAC,CAAqB,EAAE,EAAE,CAAC,CAAC,IAAI,kBAAkB,CAAC;IAC/D,OAAO;QACL,kDAAkD;QAClD,4HAA4H;QAC5H,kPAAkP;QAClP,EAAE;QACF,iCAAiC;QACjC,wEAAwE;QACxE,yIAAyI;QACzI,uHAAuH;QACvH,sRAAsR;QACtR,geAAge;QAChe,oEAAoE;QACpE,0TAA0T;QAC1T,kIAAkI;QAClI,wYAAwY;QACxY,uJAAuJ;QACvJ,qjBAAqjB;QACrjB,EAAE;QACF,0CAA0C;QAC1C,8MAA8M;QAC9M,2QAA2Q;QAC3Q,sKAAsK;QACtK,0JAA0J;QAC1J,6KAA6K;QAC7K,EAAE;QACF,kDAAkD;QAClD,8LAA8L;QAC9L,+FAA+F,SAAS,gFAAgF;QACxL,mMAAmM;QACnM,wCAAwC,gBAAgB,8IAA8I;QACtM,iLAAiL;QACjL,4IAA4I;QAC5I,8CAA8C;QAC9C,EAAE;QACF,aAAa;QACb,YAAY,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG;QAChG,cAAc,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,mBAAmB,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,gBAAgB,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,kBAAkB,YAAY,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,MAAM,CAAC,IAAI,IAAI,qBAAqB,wBAAwB,MAAM,CAAC,cAAc,GAAG;QAChS,0RAA0R;QAC1R,yNAAyN;QACzN,8PAA8P,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,+BAA+B,gHAAgH;QAC1b,sKAAsK;QACtK,EAAE;QACF,aAAa;QACb,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;KAC5C,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
package/dist/format.d.ts CHANGED
@@ -17,3 +17,16 @@ export declare const num: (v: unknown) => number;
17
17
  export declare const bool: (v: unknown) => boolean;
18
18
  export declare const str: (v: unknown) => string;
19
19
  export declare const addrOf: (v: unknown) => Address;
20
+ /**
21
+ * Host only. PARABOLIC_RPC_URL and PARABOLIC_SUBGRAPH_URL may carry an API key in the path (Alchemy,
22
+ * Infura and QuickNode all do), and anything returned from a tool lands in an agent's transcript.
23
+ */
24
+ export declare const endpointHost: (url: string) => string;
25
+ /**
26
+ * An error message safe to hand back to an agent. viem's HttpRequestError embeds the full request URL
27
+ * and body, so a user who set a keyed endpoint would otherwise have that key copied into any failed
28
+ * call. Our own thrown messages carry no URL and pass through untouched; anything else keeps its first
29
+ * line, which is where the actual cause is ("HTTP request failed. Status: 401"), with URLs masked.
30
+ * The unredacted error still goes to stderr, which is where a server log would come from.
31
+ */
32
+ export declare function safeMessage(e: unknown): string;
package/dist/format.js CHANGED
@@ -27,4 +27,38 @@ export const num = (v) => (typeof v === "number" ? v : typeof v === "bigint" ? N
27
27
  export const bool = (v) => v === true;
28
28
  export const str = (v) => (typeof v === "string" ? v : "");
29
29
  export const addrOf = (v) => (typeof v === "string" && isAddress(v, { strict: false }) ? getAddress(v) : "0x0000000000000000000000000000000000000000");
30
+ /**
31
+ * Host only. PARABOLIC_RPC_URL and PARABOLIC_SUBGRAPH_URL may carry an API key in the path (Alchemy,
32
+ * Infura and QuickNode all do), and anything returned from a tool lands in an agent's transcript.
33
+ */
34
+ export const endpointHost = (url) => {
35
+ try {
36
+ return new URL(url).host;
37
+ }
38
+ catch {
39
+ return "(configured)";
40
+ }
41
+ };
42
+ /**
43
+ * An error message safe to hand back to an agent. viem's HttpRequestError embeds the full request URL
44
+ * and body, so a user who set a keyed endpoint would otherwise have that key copied into any failed
45
+ * call. Our own thrown messages carry no URL and pass through untouched; anything else keeps its first
46
+ * line, which is where the actual cause is ("HTTP request failed. Status: 401"), with URLs masked.
47
+ * The unredacted error still goes to stderr, which is where a server log would come from.
48
+ */
49
+ export function safeMessage(e) {
50
+ const raw = e instanceof Error ? e.message : String(e);
51
+ if (!/https?:\/\//.test(raw))
52
+ return raw;
53
+ console.error("[parabolic-mcp]", raw);
54
+ const lines = raw.split("\n").map((l) => l.trim()).filter(Boolean);
55
+ // viem puts the headline first and the actual cause on a `Details:` line further down, past the
56
+ // URL and the request body. Taking the first two lines kept the headline and the URL and dropped
57
+ // the cause, so a rate limit surfaced as "Request exceeds defined limit" with no way to tell why.
58
+ const headline = lines[0] ?? "Request failed";
59
+ // Keep the diagnostic lines and drop the ones that carry the endpoint or the request body.
60
+ const diagnostics = lines.slice(1).filter((l) => /^(Status|Details|Code):/.test(l));
61
+ const kept = [headline, ...diagnostics].join(" ");
62
+ return `${kept.replace(/https?:\/\/[^\s"']+/g, "<endpoint>")} (endpoint masked)`;
63
+ }
30
64
  //# sourceMappingURL=format.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"format.js","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAgB,MAAM,MAAM,CAAC;AAEpF,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAE,CAAU,EAAW,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAEnG,qDAAqD;AACrD,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,CAAU,EAAU,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;AAE/E,yEAAyE;AACzE,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,CAAU,EAA2B,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAA4B,CAAC;AAEnH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAExG,MAAM,UAAU,YAAY,CAAC,CAAU,EAAE,IAAY;IACnD,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,+CAA+C,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACjK,OAAO,UAAU,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;AAC9B,CAAC;AAED,oHAAoH;AACpH,MAAM,UAAU,WAAW,CAAC,CAAU,EAAE,QAAgB,EAAE,IAAY;IACpE,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,qBAAqB,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACnM,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,mFAAmF,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC7J,MAAM,GAAG,GAAG,UAAU,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;IACpC,IAAI,GAAG,IAAI,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,4BAA4B,CAAC,CAAC;IACpE,OAAO,GAAG,CAAC;AACb,CAAC;AAED,wFAAwF;AACxF,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,GAAW,EAAE,QAAgB,EAAmC,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,GAAG,EAAE,QAAQ,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;AAExJ,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,CAAU,EAAU,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AACzI,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,CAAU,EAAU,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACnJ,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,CAAU,EAAW,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC;AACxD,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,CAAU,EAAU,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AAC5E,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,CAAU,EAAW,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,SAAS,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,4CAA4C,CAAC,CAAC;AAEzK;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,GAAW,EAAU,EAAE;IAClD,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,cAAc,CAAC;IACxB,CAAC;AACH,CAAC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,CAAU;IACpC,MAAM,GAAG,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACvD,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC;IACzC,OAAO,CAAC,KAAK,CAAC,iBAAiB,EAAE,GAAG,CAAC,CAAC;IACtC,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACnE,gGAAgG;IAChG,iGAAiG;IACjG,kGAAkG;IAClG,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,gBAAgB,CAAC;IAC9C,2FAA2F;IAC3F,MAAM,WAAW,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,yBAAyB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACpF,MAAM,IAAI,GAAG,CAAC,QAAQ,EAAE,GAAG,WAAW,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAClD,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,sBAAsB,EAAE,YAAY,CAAC,oBAAoB,CAAC;AACnF,CAAC"}
@@ -2,6 +2,7 @@ import { type Address } from "viem";
2
2
  import { type Rpc } from "./chain.js";
3
3
  import { type CoinSummary, type SortKey, type StatusKey } from "./coins.js";
4
4
  import { type Config } from "./config.js";
5
+ import { RestApi } from "./restapi.js";
5
6
  import { Subgraph, type Fetch } from "./subgraph.js";
6
7
  import { type Socials, type TokenParams } from "./tx.js";
7
8
  export declare const PHASES: readonly ["NotGraduated", "Swept", "PoolCreated", "Rescued"];
@@ -17,6 +18,9 @@ export declare const METADATA_LIMITS: {
17
18
  };
18
19
  /** Newest launches the TokenLaunched log scan considers when no subgraph is configured. */
19
20
  export declare const CHAIN_LIST_MAX = 50;
21
+ /** Extra gas a crossing buy needs for the graduation it triggers, and a limit safe to send outright. */
22
+ export declare const GRADUATION_GAS_ALLOWANCE = 300000n;
23
+ export declare const GRADUATION_GAS_LIMIT = 800000n;
20
24
  export type Launch = {
21
25
  token: Address;
22
26
  curve: Address;
@@ -79,6 +83,7 @@ export declare class Parabolic {
79
83
  readonly config: Config;
80
84
  readonly rpc: Rpc;
81
85
  readonly subgraph: Subgraph | undefined;
86
+ readonly api: RestApi | undefined;
82
87
  private hookPromise;
83
88
  constructor(config: Config, deps?: {
84
89
  rpc?: Rpc;
@@ -99,6 +104,7 @@ export declare class Parabolic {
99
104
  sort: "trending" | "new" | "near_graduation" | "market_cap";
100
105
  status: string;
101
106
  count: number;
107
+ partial: boolean;
102
108
  coins: CoinSummary[];
103
109
  }>;
104
110
  /**
@@ -407,7 +413,13 @@ export declare class Parabolic {
407
413
  };
408
414
  };
409
415
  gas: {
416
+ limit: string;
410
417
  note: string;
418
+ formula: string;
419
+ } | {
420
+ limit: null;
421
+ note: string;
422
+ formula: string;
411
423
  };
412
424
  warnings: string[];
413
425
  notes: string[];
@@ -446,7 +458,13 @@ export declare class Parabolic {
446
458
  };
447
459
  };
448
460
  gas: {
461
+ limit: string;
462
+ note: string;
463
+ formula: string;
464
+ } | {
465
+ limit: null;
449
466
  note: string;
467
+ formula: string;
450
468
  };
451
469
  warnings: string[];
452
470
  notes: string[];
@@ -660,7 +678,7 @@ export declare class Parabolic {
660
678
  chain: {
661
679
  id: number;
662
680
  name: string;
663
- rpc: string;
681
+ rpcHost: string;
664
682
  };
665
683
  indexed: {
666
684
  coinsLaunched: number;
package/dist/parabolic.js CHANGED
@@ -3,10 +3,11 @@ import { encodeFunctionData, toHex } from "viem";
3
3
  import { curveAbi, factoryAbi, tokenAbi } from "./abi.js";
4
4
  import { mapLimit, viemRpc } from "./chain.js";
5
5
  import { isNative, progressPct, sortCoins, summaryFromGql } from "./coins.js";
6
- import { NATIVE_QUOTE, chainName, explorerUrl } from "./config.js";
6
+ import { DEPLOYMENTS, NATIVE_QUOTE, chainName, explorerUrl } from "./config.js";
7
7
  import { applySlippage, graduationMarketCap, graduationSplit, marketCap, passesPriceBound, poolIdOf, priceImpact, quoteBuy, quoteSell, reservedTokensFor, spotPrice, toNumber } from "./curve.js";
8
8
  import { docsText } from "./docs.js";
9
- import { addrOf, amount, big, bool, num, parseAddress, parseAmount, str } from "./format.js";
9
+ import { addrOf, amount, big, bool, endpointHost, num, parseAddress, parseAmount, safeMessage, str } from "./format.js";
10
+ import { RestApi } from "./restapi.js";
10
11
  import { Subgraph } from "./subgraph.js";
11
12
  import { encodeApprove, encodeBuy, encodeLaunch, encodeLaunchAndBuy, encodeMemoBuy, encodeSell } from "./tx.js";
12
13
  export const PHASES = ["NotGraduated", "Swept", "PoolCreated", "Rescued"];
@@ -16,6 +17,9 @@ export const DEV_BUY_SLIPPAGE_BPS = 200n;
16
17
  export const METADATA_LIMITS = { name: 64, symbol: 16, logo: 512, description: 2048, social: 256 };
17
18
  /** Newest launches the TokenLaunched log scan considers when no subgraph is configured. */
18
19
  export const CHAIN_LIST_MAX = 50;
20
+ /** Extra gas a crossing buy needs for the graduation it triggers, and a limit safe to send outright. */
21
+ export const GRADUATION_GAS_ALLOWANCE = 300000n;
22
+ export const GRADUATION_GAS_LIMIT = 800000n;
19
23
  const LAUNCHED_FIELDS = ["token", "curve", "deployer", "creatorFeeRecipient", "pairToken", "graduationThreshold", "poolFee", "tickSpacing", "creatorTaxBps", "buybackEnabled", "phase", "sweptQuote", "sweptTokens", "sweptAt", "exists"];
20
24
  const CONFIG_FIELDS = ["supply", "curveFeeBps", "phantomQuote", "graduationThreshold", "poolFee", "tickSpacing", "enabled"];
21
25
  /** viem returns a tuple whose components are all named as an object; accept a positional array as well. */
@@ -41,11 +45,13 @@ export class Parabolic {
41
45
  config;
42
46
  rpc;
43
47
  subgraph;
48
+ api;
44
49
  hookPromise;
45
50
  constructor(config, deps = {}) {
46
51
  this.config = config;
47
52
  this.rpc = deps.rpc ?? viemRpc(config.rpcUrl, config.chainId);
48
53
  this.subgraph = config.subgraphUrl ? new Subgraph(config.subgraphUrl, deps.fetch) : undefined;
54
+ this.api = config.apiUrl ? new RestApi(config.apiUrl, config.chainId, deps.fetch) : undefined;
49
55
  }
50
56
  // ---- reads --------------------------------------------------------------------------------------------------
51
57
  read(address, abi, functionName, args) {
@@ -56,8 +62,14 @@ export class Parabolic {
56
62
  return Object.fromEntries(names.map((n, i) => [n, values[i]]));
57
63
  }
58
64
  requireFactory() {
59
- if (!this.config.factory)
60
- throw new Error("PARABOLIC_FACTORY is not set: needed for this operation");
65
+ if (!this.config.factory) {
66
+ // The addresses for every chain we know about ship in DEPLOYMENTS, so a missing factory almost
67
+ // always means the chain has no deployment yet rather than a configuration mistake.
68
+ const known = DEPLOYMENTS[this.config.chainId]?.factory !== undefined;
69
+ throw new Error(known
70
+ ? "PARABOLIC_FACTORY is not set: needed for this operation"
71
+ : `Parabolic is not deployed on ${chainName(this.config.chainId)} (chain ${this.config.chainId}) yet. Arc mainnet opens on 16 September 2026; until then use chain 5042002 (Arc Testnet), which needs no configuration. Set PARABOLIC_FACTORY to point at your own deployment.`);
72
+ }
61
73
  return this.config.factory;
62
74
  }
63
75
  hookAddress() {
@@ -129,22 +141,41 @@ export class Parabolic {
129
141
  const limit = Math.min(100, Math.max(1, Math.floor(input.limit ?? 20)));
130
142
  let coins;
131
143
  let source;
144
+ let partial = false;
145
+ // Preferred over the log scan whenever there is no subgraph: one cached request instead of the
146
+ // forty rate-limited eth_getLogs calls the scan needs, and it carries holders and volume.
147
+ const fromApi = this.subgraph === undefined && this.api ? await this.api.coins(input.status, 100) : undefined;
132
148
  if (this.subgraph) {
133
149
  const where = input.status === "climbing" ? { status: "CLIMBING" } : input.status === "graduated" ? { status_not: "CLIMBING" } : undefined;
134
150
  coins = (await this.subgraph.coins(where, 200)).map((g) => summaryFromGql(g));
135
151
  source = "subgraph";
136
152
  }
153
+ else if (fromApi !== undefined) {
154
+ coins = fromApi;
155
+ source = `Parabolic public API (${this.config.apiUrl}): full history, with holders and volume. Set PARABOLIC_SUBGRAPH_URL to read an indexer directly.`;
156
+ }
137
157
  else if (this.config.factory) {
138
- coins = await this.chainCoins(this.config.factory);
139
- source = `factory TokenLaunched log scan of recent blocks (newest ${CHAIN_LIST_MAX} launches at most; public Arc RPCs cap the range, so older coins are not in this list. Set PARABOLIC_SUBGRAPH_URL for the full list with holders and volume, or read https://www.parabolic.family/api/v1/coins)`;
158
+ const scanned = await this.chainCoins(this.config.factory);
159
+ coins = scanned.coins;
160
+ const { scan } = scanned;
161
+ // An empty window is not an empty protocol. Public Arc RPCs cap eth_getLogs near 10,000 blocks
162
+ // and Arc produces about two blocks a second, so this scan reaches roughly a day back however
163
+ // many windows it walks. Saying so is the difference between "no coins yet" and "none here".
164
+ const range = scan.scannedTo > 0n ? ` blocks ${scan.scannedFrom}-${scan.scannedTo}` : "";
165
+ partial = !scan.complete;
166
+ source = scan.complete
167
+ ? `factory TokenLaunched log scan${range} (complete)`
168
+ : `factory TokenLaunched log scan${range}${scan.failed ? ", ended early: the RPC refused or rate-limited a window" : ""}. PARTIAL: this is the newest slice of the chain, not every coin. Public Arc RPCs cap the log range, so anything older is missing. Set PARABOLIC_SUBGRAPH_URL for the full list with holders and volume, or read https://www.parabolic.family/api/v1/coins`;
140
169
  }
141
170
  else {
142
- throw new Error("Listing coins needs PARABOLIC_SUBGRAPH_URL (recommended) or PARABOLIC_FACTORY (TokenLaunched log-scan fallback): the factory ABI exposes getLaunchedToken(address) but no enumeration function");
171
+ throw new Error(DEPLOYMENTS[this.config.chainId]?.factory === undefined
172
+ ? `Parabolic is not deployed on ${chainName(this.config.chainId)} (chain ${this.config.chainId}) yet, so there is nothing to list. Arc mainnet opens on 16 September 2026; chain 5042002 (Arc Testnet) works with no configuration.`
173
+ : "Listing coins needs PARABOLIC_SUBGRAPH_URL, PARABOLIC_API_URL or PARABOLIC_FACTORY: the factory ABI exposes getLaunchedToken(address) but no enumeration function, so a list has to come from an indexer");
143
174
  }
144
175
  if (input.status)
145
176
  coins = coins.filter((c) => c.status === input.status);
146
177
  const sorted = sortCoins(coins, sort).slice(0, limit);
147
- return { source, sort, status: input.status ?? "all", count: sorted.length, coins: sorted };
178
+ return { source, sort, status: input.status ?? "all", count: sorted.length, partial, coins: sorted };
148
179
  }
149
180
  /**
150
181
  * Newest TokenLaunched logs, walked backwards from head in bounded windows.
@@ -160,7 +191,7 @@ export class Parabolic {
160
191
  */
161
192
  async recentLaunchLogs(factory) {
162
193
  const WINDOW = 9000n;
163
- const MAX_WINDOWS = 20;
194
+ const MAX_WINDOWS = 40;
164
195
  const floor = this.config.factoryStartBlock;
165
196
  const collected = [];
166
197
  let to;
@@ -168,28 +199,36 @@ export class Parabolic {
168
199
  to = await this.rpc.blockNumber();
169
200
  }
170
201
  catch {
171
- // No block number, no windowing: fall back to one scan from the configured floor.
172
- return this.rpc.logs({ address: factory, abi: factoryAbi, eventName: "TokenLaunched", fromBlock: floor });
202
+ // No block number, no windowing: one scan from the configured floor, and it is complete or it throws.
203
+ const logs = await this.rpc.logs({ address: factory, abi: factoryAbi, eventName: "TokenLaunched", fromBlock: floor });
204
+ return { logs, scannedFrom: floor, scannedTo: 0n, complete: true, failed: false };
173
205
  }
206
+ const head = to;
207
+ let from = to;
208
+ let failed = false;
174
209
  for (let i = 0; i < MAX_WINDOWS && to >= floor; i++) {
175
- const from = to - WINDOW + 1n > floor ? to - WINDOW + 1n : floor;
210
+ from = to - WINDOW + 1n > floor ? to - WINDOW + 1n : floor;
176
211
  try {
177
212
  const batch = await this.rpc.logs({ address: factory, abi: factoryAbi, eventName: "TokenLaunched", fromBlock: from, toBlock: to });
178
213
  collected.unshift(...batch);
179
214
  }
180
- catch {
215
+ catch (e) {
216
+ // A refused or rate-limited window is not an empty one, and the caller has to be able to say so.
217
+ failed = true;
218
+ safeMessage(e);
219
+ from = to + 1n;
181
220
  break;
182
221
  }
183
222
  if (collected.length >= CHAIN_LIST_MAX || from === floor)
184
223
  break;
185
224
  to = from - 1n;
186
225
  }
187
- return collected;
226
+ return { logs: collected, scannedFrom: from, scannedTo: head, complete: from <= floor && !failed, failed };
188
227
  }
189
228
  async chainCoins(factory) {
190
- const logs = await this.recentLaunchLogs(factory);
191
- const newest = logs.slice(-CHAIN_LIST_MAX).reverse();
192
- return mapLimit(newest, 8, async (log) => {
229
+ const scan = await this.recentLaunchLogs(factory);
230
+ const newest = scan.logs.slice(-CHAIN_LIST_MAX).reverse();
231
+ const coins = await mapLimit(newest, 8, async (log) => {
193
232
  const a = log.args;
194
233
  const token = addrOf(a.token);
195
234
  const curve = addrOf(a.curve);
@@ -216,6 +255,7 @@ export class Parabolic {
216
255
  createdAt: num(c.launchedAt), launchConfigId: num(a.launchConfigId), source: "chain",
217
256
  };
218
257
  });
258
+ return { coins, scan };
219
259
  }
220
260
  // ---- coin detail --------------------------------------------------------------------------------------------
221
261
  async getCoin(address) {
@@ -352,7 +392,7 @@ export class Parabolic {
352
392
  graduatesCoin: q.crossing,
353
393
  reserves: { quote: amount(t.quoteReserve, qd), tokens: amount(t.tokenReserve, 18), sellableTokens: amount(t.sellableTokens, 18) },
354
394
  notes: [
355
- ...(q.crossing ? ["This buy takes the curve's last sellable tokens: it fills to the threshold, refunds the rest, and graduates the coin in the same transaction. Send extra gas (the web adds 300,000 plus 25% headroom)."] : []),
395
+ ...(q.crossing ? [`This buy takes the curve's last sellable tokens: it fills to the threshold, refunds the rest, and graduates the coin in the same transaction. Send gas.limit (${GRADUATION_GAS_LIMIT}); an estimate plus the usual 25% is not enough and reverts out of gas.`] : []),
356
396
  ...(t.snipeBps > 0n && !exempt ? ["The launch snipe-tax window is open; the tax decays every second, so a quote taken now overstates the tax a later block charges."] : []),
357
397
  "minTokensOut is enforced as a price bound: spent × minTokensOut ≤ received × tokensOut.",
358
398
  ],
@@ -411,7 +451,21 @@ export class Parabolic {
411
451
  quoteIn: amount(quoteIn, qd), expectedTokensOut: amount(q.tokensOut, 18), minTokensOut: amount(minTokensOut, 18), slippageBps: input.minTokensOut !== undefined ? null : Number(slippage),
412
452
  fees: { feeBps: Number(t.feeBps), creatorTaxBps: Number(t.creatorTaxBps), snipeTaxBps: Number(snipeBps), buyerExempt: exempt }, priceImpactPct: priceImpact(q.spent, q.tokensOut, t.quoteReserve, t.tokenReserve), graduatesCoin: q.crossing, refund: amount(q.refund, qd),
413
453
  };
414
- const gas = { note: q.crossing ? "estimate, add 300,000 for the graduation the buy triggers, then multiply by 1.25 (web/lib/gas.ts)" : "estimate then multiply by 1.25: the snipe tax is priced per block, so a buy quoted in one block runs a slightly different path in the next (web/lib/gas.ts)" };
454
+ // A crossing buy graduates the coin in the same transaction, which is a different and much more
455
+ // expensive code path than the estimate covers: taking eth_estimateGas and adding the usual 25%
456
+ // is not enough and the transaction reverts out of gas. The server knows when a buy crosses, so
457
+ // it returns the limit to send rather than a sentence about how to compute one.
458
+ const gas = q.crossing
459
+ ? {
460
+ limit: GRADUATION_GAS_LIMIT.toString(),
461
+ note: `This buy graduates the coin, which eth_estimateGas does not price. Send gas = ${GRADUATION_GAS_LIMIT} (an estimate plus ${GRADUATION_GAS_ALLOWANCE} for the graduation, plus 25%). Adding only 25% to the estimate reverts out of gas.`,
462
+ formula: `(estimate + ${GRADUATION_GAS_ALLOWANCE}) * 1.25`,
463
+ }
464
+ : {
465
+ limit: null,
466
+ note: "Take eth_estimateGas and multiply by 1.25: the snipe tax is priced per block, so a buy quoted in one block runs a slightly different path in the next.",
467
+ formula: "estimate * 1.25",
468
+ };
415
469
  if (input.memo) {
416
470
  if (!this.config.memoRouter)
417
471
  throw new Error("Memo-routed buys need PARABOLIC_MEMO_ROUTER");
@@ -633,11 +687,13 @@ export class Parabolic {
633
687
  // ---- protocol -----------------------------------------------------------------------------------------------
634
688
  async protocolStats() {
635
689
  const [indexed, factory] = await Promise.all([
636
- this.subgraph ? this.subgraph.protocol().then(mapProtocol).catch((e) => ({ error: e instanceof Error ? e.message : String(e) })) : Promise.resolve(null),
637
- this.config.factory ? this.factoryStats(this.config.factory).catch((e) => ({ error: e instanceof Error ? e.message : String(e) })) : Promise.resolve(null),
690
+ this.subgraph ? this.subgraph.protocol().then(mapProtocol).catch((e) => ({ error: safeMessage(e) })) : Promise.resolve(null),
691
+ this.config.factory ? this.factoryStats(this.config.factory).catch((e) => ({ error: safeMessage(e) })) : Promise.resolve(null),
638
692
  ]);
639
693
  return {
640
- chain: { id: this.config.chainId, name: chainName(this.config.chainId), rpc: this.config.rpcUrl },
694
+ // Host, not the URL, and named so nothing mistakes it for a dialable endpoint: an operator's
695
+ // PARABOLIC_RPC_URL may carry an API key in its path, and this payload goes to the agent.
696
+ chain: { id: this.config.chainId, name: chainName(this.config.chainId), rpcHost: endpointHost(this.config.rpcUrl) },
641
697
  indexed,
642
698
  factory,
643
699
  notes: [