@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 +18 -6
- package/abi/ParabolicLaunchFactory.json +5 -0
- package/dist/config.d.ts +6 -0
- package/dist/config.js +4 -1
- package/dist/config.js.map +1 -1
- package/dist/docs.js +1 -12
- package/dist/docs.js.map +1 -1
- package/dist/format.d.ts +13 -0
- package/dist/format.js +34 -0
- package/dist/format.js.map +1 -1
- package/dist/parabolic.d.ts +19 -1
- package/dist/parabolic.js +78 -22
- package/dist/parabolic.js.map +1 -1
- package/dist/restapi.d.ts +26 -0
- package/dist/restapi.js +64 -0
- package/dist/restapi.js.map +1 -0
- package/dist/server.d.ts +1 -8
- package/dist/server.js +2 -14
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
- package/src/config.ts +9 -1
- package/src/docs.ts +1 -12
- package/src/format.ts +34 -0
- package/src/parabolic.ts +83 -23
- package/src/restapi.ts +79 -0
- package/src/server.ts +2 -13
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
|
|
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
|
-
|
|
39
|
-
|
|
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
|
|
48
|
-
|
|
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
|
|
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
|
|
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
|
|
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),
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAgB,MAAM,MAAM,CAAC;AAE3D,gHAAgH;AAChH,MAAM,CAAC,MAAM,gBAAgB,GAAY,4CAA4C,CAAC;AACtF,mGAAmG;AACnG,MAAM,CAAC,MAAM,SAAS,GAAY,4CAA4C,CAAC;AAC/E,+EAA+E;AAC/E,MAAM,CAAC,MAAM,YAAY,GAAY,4CAA4C,CAAC;AAElF,MAAM,CAAC,MAAM,gBAAgB,GAAG,OAAO,CAAC;
|
|
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;
|
|
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
|
package/dist/format.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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"}
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"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"}
|
package/dist/parabolic.d.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
139
|
-
|
|
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(
|
|
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 =
|
|
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:
|
|
172
|
-
|
|
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
|
-
|
|
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
|
|
191
|
-
const newest = logs.slice(-CHAIN_LIST_MAX).reverse();
|
|
192
|
-
|
|
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 ? [
|
|
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
|
-
|
|
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:
|
|
637
|
-
this.config.factory ? this.factoryStats(this.config.factory).catch((e) => ({ error:
|
|
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
|
-
|
|
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: [
|