@parabolicfamily/mcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +195 -0
- package/abi/ParabolicBondingCurve.json +1223 -0
- package/abi/ParabolicLaunchFactory.json +2212 -0
- package/abi/ParabolicLauncherToken.json +577 -0
- package/abi/ParabolicMemoRouter.json +950 -0
- package/dist/abi.d.ts +17 -0
- package/dist/abi.js +37 -0
- package/dist/abi.js.map +1 -0
- package/dist/chain.d.ts +32 -0
- package/dist/chain.js +48 -0
- package/dist/chain.js.map +1 -0
- package/dist/coins.d.ts +52 -0
- package/dist/coins.js +75 -0
- package/dist/coins.js.map +1 -0
- package/dist/config.d.ts +55 -0
- package/dist/config.js +86 -0
- package/dist/config.js.map +1 -0
- package/dist/curve.d.ts +92 -0
- package/dist/curve.js +127 -0
- package/dist/curve.js.map +1 -0
- package/dist/docs.d.ts +3 -0
- package/dist/docs.js +63 -0
- package/dist/docs.js.map +1 -0
- package/dist/format.d.ts +19 -0
- package/dist/format.js +30 -0
- package/dist/format.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/parabolic.d.ts +780 -0
- package/dist/parabolic.js +710 -0
- package/dist/parabolic.js.map +1 -0
- package/dist/server.d.ts +32 -0
- package/dist/server.js +149 -0
- package/dist/server.js.map +1 -0
- package/dist/subgraph.d.ts +85 -0
- package/dist/subgraph.js +43 -0
- package/dist/subgraph.js.map +1 -0
- package/dist/tx.d.ts +115 -0
- package/dist/tx.js +105 -0
- package/dist/tx.js.map +1 -0
- package/package.json +64 -0
- package/src/abi.ts +41 -0
- package/src/chain.ts +60 -0
- package/src/coins.ts +118 -0
- package/src/config.ts +123 -0
- package/src/curve.ts +171 -0
- package/src/docs.ts +63 -0
- package/src/format.ts +34 -0
- package/src/index.ts +14 -0
- package/src/parabolic.ts +753 -0
- package/src/server.ts +229 -0
- package/src/subgraph.ts +61 -0
- package/src/tx.ts +146 -0
package/dist/abi.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Abi } from "viem";
|
|
2
|
+
/**
|
|
3
|
+
* ABI loader. `scripts/sync-abis.mjs` copies the `abi` field of the Foundry artifacts in
|
|
4
|
+
* ../contracts/out into mcp/abi/*.json; this module reads them at import time. The directory is
|
|
5
|
+
* one level above both src/ (tests) and dist/ (build), so the same relative path works for both.
|
|
6
|
+
* Override with PARABOLIC_ABI_DIR when the package is bundled elsewhere.
|
|
7
|
+
*/
|
|
8
|
+
export declare const ABI_DIR: string;
|
|
9
|
+
export declare const ABI_NAMES: readonly ["ParabolicLaunchFactory", "ParabolicBondingCurve", "ParabolicLauncherToken", "ParabolicMemoRouter"];
|
|
10
|
+
export type AbiName = (typeof ABI_NAMES)[number];
|
|
11
|
+
export declare function loadAbi(name: AbiName): Abi;
|
|
12
|
+
export declare const factoryAbi: Abi;
|
|
13
|
+
export declare const curveAbi: Abi;
|
|
14
|
+
export declare const tokenAbi: Abi;
|
|
15
|
+
export declare const memoRouterAbi: Abi;
|
|
16
|
+
/** Names of all functions in an ABI (used to document what the factory can and cannot enumerate). */
|
|
17
|
+
export declare const functionNames: (abi: Abi) => string[];
|
package/dist/abi.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join, resolve } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
/**
|
|
5
|
+
* ABI loader. `scripts/sync-abis.mjs` copies the `abi` field of the Foundry artifacts in
|
|
6
|
+
* ../contracts/out into mcp/abi/*.json; this module reads them at import time. The directory is
|
|
7
|
+
* one level above both src/ (tests) and dist/ (build), so the same relative path works for both.
|
|
8
|
+
* Override with PARABOLIC_ABI_DIR when the package is bundled elsewhere.
|
|
9
|
+
*/
|
|
10
|
+
export const ABI_DIR = process.env.PARABOLIC_ABI_DIR ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", "abi");
|
|
11
|
+
export const ABI_NAMES = ["ParabolicLaunchFactory", "ParabolicBondingCurve", "ParabolicLauncherToken", "ParabolicMemoRouter"];
|
|
12
|
+
const cache = new Map();
|
|
13
|
+
export function loadAbi(name) {
|
|
14
|
+
const hit = cache.get(name);
|
|
15
|
+
if (hit)
|
|
16
|
+
return hit;
|
|
17
|
+
const file = join(ABI_DIR, `${name}.json`);
|
|
18
|
+
let parsed;
|
|
19
|
+
try {
|
|
20
|
+
parsed = JSON.parse(readFileSync(file, "utf8"));
|
|
21
|
+
}
|
|
22
|
+
catch (e) {
|
|
23
|
+
throw new Error(`Cannot read the bundled ABI ${file}. The package ships abi/*.json; a missing file means a damaged install, so reinstall @parabolicfamily/mcp (${e.message})`);
|
|
24
|
+
}
|
|
25
|
+
if (!Array.isArray(parsed))
|
|
26
|
+
throw new Error(`ABI ${file} is not a JSON array`);
|
|
27
|
+
const abi = parsed;
|
|
28
|
+
cache.set(name, abi);
|
|
29
|
+
return abi;
|
|
30
|
+
}
|
|
31
|
+
export const factoryAbi = loadAbi("ParabolicLaunchFactory");
|
|
32
|
+
export const curveAbi = loadAbi("ParabolicBondingCurve");
|
|
33
|
+
export const tokenAbi = loadAbi("ParabolicLauncherToken");
|
|
34
|
+
export const memoRouterAbi = loadAbi("ParabolicMemoRouter");
|
|
35
|
+
/** Names of all functions in an ABI (used to document what the factory can and cannot enumerate). */
|
|
36
|
+
export const functionNames = (abi) => abi.flatMap((e) => (e.type === "function" ? [e.name] : []));
|
|
37
|
+
//# sourceMappingURL=abi.js.map
|
package/dist/abi.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"abi.js","sourceRoot":"","sources":["../src/abi.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAGzC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;AAEtH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,wBAAwB,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,qBAAqB,CAAU,CAAC;AAGvI,MAAM,KAAK,GAAG,IAAI,GAAG,EAAgB,CAAC;AAEtC,MAAM,UAAU,OAAO,CAAC,IAAa;IACnC,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,GAAG;QAAE,OAAO,GAAG,CAAC;IACpB,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,GAAG,IAAI,OAAO,CAAC,CAAC;IAC3C,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;IAClD,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,+BAA+B,IAAI,8GAA+G,CAAW,CAAC,OAAO,GAAG,CAAC,CAAC;IAC5L,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,OAAO,IAAI,sBAAsB,CAAC,CAAC;IAC/E,MAAM,GAAG,GAAG,MAAa,CAAC;IAC1B,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACrB,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,CAAC,MAAM,UAAU,GAAQ,OAAO,CAAC,wBAAwB,CAAC,CAAC;AACjE,MAAM,CAAC,MAAM,QAAQ,GAAQ,OAAO,CAAC,uBAAuB,CAAC,CAAC;AAC9D,MAAM,CAAC,MAAM,QAAQ,GAAQ,OAAO,CAAC,wBAAwB,CAAC,CAAC;AAC/D,MAAM,CAAC,MAAM,aAAa,GAAQ,OAAO,CAAC,qBAAqB,CAAC,CAAC;AAEjE,qGAAqG;AACrG,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,GAAQ,EAAY,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC"}
|
package/dist/chain.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type Abi, type Address, type Chain, type Hex } from "viem";
|
|
2
|
+
/** The narrow RPC surface the server needs; tests substitute an in-memory implementation. */
|
|
3
|
+
export type ReadArgs = {
|
|
4
|
+
address: Address;
|
|
5
|
+
abi: Abi;
|
|
6
|
+
functionName: string;
|
|
7
|
+
args?: readonly unknown[];
|
|
8
|
+
};
|
|
9
|
+
export type LogsArgs = {
|
|
10
|
+
address: Address;
|
|
11
|
+
abi: Abi;
|
|
12
|
+
eventName: string;
|
|
13
|
+
fromBlock: bigint;
|
|
14
|
+
toBlock?: bigint;
|
|
15
|
+
};
|
|
16
|
+
export type DecodedLog = {
|
|
17
|
+
args: Record<string, unknown>;
|
|
18
|
+
blockNumber: bigint;
|
|
19
|
+
transactionHash: Hex;
|
|
20
|
+
logIndex: number;
|
|
21
|
+
};
|
|
22
|
+
export interface Rpc {
|
|
23
|
+
read(a: ReadArgs): Promise<unknown>;
|
|
24
|
+
logs(a: LogsArgs): Promise<DecodedLog[]>;
|
|
25
|
+
blockNumber(): Promise<bigint>;
|
|
26
|
+
}
|
|
27
|
+
/** Arc chain definition for viem: native USDC with 18-decimal native accounting. */
|
|
28
|
+
export declare function arcChain(chainId: number, rpcUrl: string): Chain;
|
|
29
|
+
/** viem-backed Rpc. HTTP batching folds the many small reads of get_coin / list_coins into a few JSON-RPC requests. */
|
|
30
|
+
export declare function viemRpc(rpcUrl: string, chainId: number): Rpc;
|
|
31
|
+
/** Runs `fn` over `items` with at most `limit` in flight, preserving order. */
|
|
32
|
+
export declare function mapLimit<T, R>(items: readonly T[], limit: number, fn: (item: T, index: number) => Promise<R>): Promise<R[]>;
|
package/dist/chain.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { createPublicClient, defineChain, getAbiItem, http } from "viem";
|
|
2
|
+
import { chainName, explorerBase } from "./config.js";
|
|
3
|
+
/** Arc chain definition for viem: native USDC with 18-decimal native accounting. */
|
|
4
|
+
export function arcChain(chainId, rpcUrl) {
|
|
5
|
+
const explorer = explorerBase(chainId);
|
|
6
|
+
return defineChain({
|
|
7
|
+
id: chainId,
|
|
8
|
+
name: chainName(chainId),
|
|
9
|
+
nativeCurrency: { name: "USDC", symbol: "USDC", decimals: 18 },
|
|
10
|
+
rpcUrls: { default: { http: [rpcUrl] } },
|
|
11
|
+
...(explorer ? { blockExplorers: { default: { name: "Arcscan", url: explorer } } } : {}),
|
|
12
|
+
testnet: chainId !== 5042,
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
/** viem-backed Rpc. HTTP batching folds the many small reads of get_coin / list_coins into a few JSON-RPC requests. */
|
|
16
|
+
export function viemRpc(rpcUrl, chainId) {
|
|
17
|
+
const client = createPublicClient({ chain: arcChain(chainId, rpcUrl), transport: http(rpcUrl, { batch: { batchSize: 50, wait: 5 } }) });
|
|
18
|
+
return {
|
|
19
|
+
read: (a) => client.readContract({ address: a.address, abi: a.abi, functionName: a.functionName, args: a.args ?? [] }),
|
|
20
|
+
async logs(a) {
|
|
21
|
+
const event = getAbiItem({ abi: a.abi, name: a.eventName });
|
|
22
|
+
if (!event || event.type !== "event")
|
|
23
|
+
throw new Error(`No event ${a.eventName} in ABI`);
|
|
24
|
+
const logs = await client.getLogs({ address: a.address, event, fromBlock: a.fromBlock, toBlock: a.toBlock ?? "latest", strict: false });
|
|
25
|
+
return logs.map((l) => ({
|
|
26
|
+
args: (l.args ?? {}),
|
|
27
|
+
blockNumber: l.blockNumber ?? 0n,
|
|
28
|
+
transactionHash: (l.transactionHash ?? "0x"),
|
|
29
|
+
logIndex: l.logIndex ?? 0,
|
|
30
|
+
}));
|
|
31
|
+
},
|
|
32
|
+
blockNumber: () => client.getBlockNumber(),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/** Runs `fn` over `items` with at most `limit` in flight, preserving order. */
|
|
36
|
+
export async function mapLimit(items, limit, fn) {
|
|
37
|
+
const out = new Array(items.length);
|
|
38
|
+
let next = 0;
|
|
39
|
+
const workers = Array.from({ length: Math.min(limit, items.length) }, async () => {
|
|
40
|
+
while (next < items.length) {
|
|
41
|
+
const i = next++;
|
|
42
|
+
out[i] = await fn(items[i], i);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
await Promise.all(workers);
|
|
46
|
+
return out;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=chain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chain.js","sourceRoot":"","sources":["../src/chain.ts"],"names":[],"mappings":"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"}
|
package/dist/coins.d.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type Address } from "viem";
|
|
2
|
+
import type { GqlCoin } from "./subgraph.js";
|
|
3
|
+
export type StatusKey = "climbing" | "graduated";
|
|
4
|
+
export declare const SORT_KEYS: readonly ["trending", "new", "near_graduation", "market_cap"];
|
|
5
|
+
export type SortKey = (typeof SORT_KEYS)[number];
|
|
6
|
+
export type CoinSummary = {
|
|
7
|
+
address: Address;
|
|
8
|
+
curve: Address;
|
|
9
|
+
name: string;
|
|
10
|
+
ticker: string;
|
|
11
|
+
creator: Address;
|
|
12
|
+
pair: "USDC" | "EURC";
|
|
13
|
+
pairToken: Address;
|
|
14
|
+
quoteDecimals: number;
|
|
15
|
+
status: StatusKey;
|
|
16
|
+
/** Quote raised on the curve (net of fees), whole units; for a graduated coin the threshold it crossed. */
|
|
17
|
+
raised: string;
|
|
18
|
+
raisedRaw: bigint;
|
|
19
|
+
threshold: string;
|
|
20
|
+
thresholdRaw: bigint;
|
|
21
|
+
progressPct: number;
|
|
22
|
+
/** Whole quote units per whole token. */
|
|
23
|
+
price: number;
|
|
24
|
+
marketCap: number;
|
|
25
|
+
holders?: number;
|
|
26
|
+
volume24h?: number;
|
|
27
|
+
change24h?: number;
|
|
28
|
+
tradeCount?: number;
|
|
29
|
+
createdAt?: number;
|
|
30
|
+
lastTradeAt?: number;
|
|
31
|
+
graduatedAt?: number;
|
|
32
|
+
poolId?: string;
|
|
33
|
+
logo?: string;
|
|
34
|
+
description?: string;
|
|
35
|
+
launchConfigId?: number;
|
|
36
|
+
source: "subgraph" | "chain";
|
|
37
|
+
};
|
|
38
|
+
export declare const isNative: (pairToken: string) => boolean;
|
|
39
|
+
export declare const quoteDecimalsOf: (pairToken: string) => number;
|
|
40
|
+
/** Curve progress in percent with three decimals, capped at 100. */
|
|
41
|
+
export declare const progressPct: (raised: bigint, threshold: bigint) => number;
|
|
42
|
+
/**
|
|
43
|
+
* 24h figures from the coin's two most recent UTC day buckets, the same derivation as web/lib/data.ts:
|
|
44
|
+
* volume = today + yesterday; change = current price against yesterday's close (or today's open).
|
|
45
|
+
*/
|
|
46
|
+
export declare function dayStats(g: GqlCoin, now: number, quoteDecimals: number): {
|
|
47
|
+
volume24h: number;
|
|
48
|
+
change24h: number;
|
|
49
|
+
};
|
|
50
|
+
export declare function summaryFromGql(g: GqlCoin, now?: number): CoinSummary;
|
|
51
|
+
/** Pure sort used by list_coins; `near_graduation` ranks climbing coins by progress and puts graduated ones last. */
|
|
52
|
+
export declare function sortCoins(list: CoinSummary[], sort: SortKey): CoinSummary[];
|
package/dist/coins.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { formatUnits, getAddress } from "viem";
|
|
2
|
+
import { NATIVE_QUOTE } from "./config.js";
|
|
3
|
+
import { toNumber } from "./curve.js";
|
|
4
|
+
export const SORT_KEYS = ["trending", "new", "near_graduation", "market_cap"];
|
|
5
|
+
export const isNative = (pairToken) => pairToken.toLowerCase() === NATIVE_QUOTE;
|
|
6
|
+
export const quoteDecimalsOf = (pairToken) => (isNative(pairToken) ? 18 : 6);
|
|
7
|
+
/** Curve progress in percent with three decimals, capped at 100. */
|
|
8
|
+
export const progressPct = (raised, threshold) => (threshold <= 0n ? 0 : Math.min(100, Number((raised * 100000n) / threshold) / 1000));
|
|
9
|
+
const dayOf = (sec) => Math.floor(sec / 86_400);
|
|
10
|
+
/**
|
|
11
|
+
* 24h figures from the coin's two most recent UTC day buckets, the same derivation as web/lib/data.ts:
|
|
12
|
+
* volume = today + yesterday; change = current price against yesterday's close (or today's open).
|
|
13
|
+
*/
|
|
14
|
+
export function dayStats(g, now, quoteDecimals) {
|
|
15
|
+
const today = dayOf(now);
|
|
16
|
+
const buckets = g.dayData ?? [];
|
|
17
|
+
const volume24h = buckets.filter((d) => d.day >= today - 1).reduce((sum, d) => sum + toNumber(BigInt(d.volumeQuote), quoteDecimals), 0);
|
|
18
|
+
const todayBucket = buckets.find((d) => d.day === today);
|
|
19
|
+
const yesterday = buckets.find((d) => d.day === today - 1);
|
|
20
|
+
const ref = yesterday ? Number(yesterday.closePrice) : todayBucket ? Number(todayBucket.openPrice) : 0;
|
|
21
|
+
const current = Number(g.price);
|
|
22
|
+
const change24h = ref > 0 && current > 0 ? ((current - ref) / ref) * 100 : 0;
|
|
23
|
+
return { volume24h, change24h };
|
|
24
|
+
}
|
|
25
|
+
export function summaryFromGql(g, now = Math.floor(Date.now() / 1000)) {
|
|
26
|
+
const native = isNative(g.pairToken);
|
|
27
|
+
const quoteDecimals = native ? 18 : 6;
|
|
28
|
+
const graduated = g.status !== "CLIMBING";
|
|
29
|
+
const raisedRaw = BigInt(g.raised);
|
|
30
|
+
const thresholdRaw = BigInt(g.graduationThreshold);
|
|
31
|
+
const { volume24h, change24h } = dayStats(g, now, quoteDecimals);
|
|
32
|
+
return {
|
|
33
|
+
address: getAddress(g.id),
|
|
34
|
+
curve: getAddress(g.curve),
|
|
35
|
+
name: g.name,
|
|
36
|
+
ticker: g.symbol,
|
|
37
|
+
creator: getAddress(g.deployer),
|
|
38
|
+
pair: native ? "USDC" : "EURC",
|
|
39
|
+
pairToken: getAddress(g.pairToken),
|
|
40
|
+
quoteDecimals,
|
|
41
|
+
status: graduated ? "graduated" : "climbing",
|
|
42
|
+
raised: formatUnits(graduated ? thresholdRaw : raisedRaw, quoteDecimals),
|
|
43
|
+
raisedRaw: graduated ? thresholdRaw : raisedRaw,
|
|
44
|
+
threshold: formatUnits(thresholdRaw, quoteDecimals),
|
|
45
|
+
thresholdRaw,
|
|
46
|
+
progressPct: graduated ? 100 : progressPct(raisedRaw, thresholdRaw),
|
|
47
|
+
price: Number(g.price),
|
|
48
|
+
marketCap: Number(g.marketCap),
|
|
49
|
+
holders: Number(g.holderCount),
|
|
50
|
+
volume24h,
|
|
51
|
+
change24h,
|
|
52
|
+
tradeCount: Number(g.tradeCount),
|
|
53
|
+
createdAt: Number(g.createdAt),
|
|
54
|
+
lastTradeAt: Number(g.lastTradeAt),
|
|
55
|
+
graduatedAt: g.graduatedAt ? Number(g.graduatedAt) : undefined,
|
|
56
|
+
poolId: g.poolId ?? undefined,
|
|
57
|
+
logo: g.logo || undefined,
|
|
58
|
+
description: g.description || undefined,
|
|
59
|
+
launchConfigId: Number(g.launchConfigId),
|
|
60
|
+
source: "subgraph",
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const cmpBig = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
|
|
64
|
+
const statusRank = (c) => (c.status === "climbing" ? 0 : 1);
|
|
65
|
+
/** Pure sort used by list_coins; `near_graduation` ranks climbing coins by progress and puts graduated ones last. */
|
|
66
|
+
export function sortCoins(list, sort) {
|
|
67
|
+
const by = {
|
|
68
|
+
trending: (a, b) => (b.volume24h ?? 0) - (a.volume24h ?? 0) || (b.lastTradeAt ?? 0) - (a.lastTradeAt ?? 0) || cmpBig(b.raisedRaw, a.raisedRaw),
|
|
69
|
+
new: (a, b) => (b.createdAt ?? 0) - (a.createdAt ?? 0),
|
|
70
|
+
near_graduation: (a, b) => statusRank(a) - statusRank(b) || b.progressPct - a.progressPct || cmpBig(b.raisedRaw, a.raisedRaw),
|
|
71
|
+
market_cap: (a, b) => b.marketCap - a.marketCap,
|
|
72
|
+
};
|
|
73
|
+
return [...list].sort(by[sort]);
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=coins.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coins.js","sourceRoot":"","sources":["../src/coins.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { type Address } from "viem";
|
|
2
|
+
/** Arc's system `Memo` contract (CREATE2, zero salt), verified on Arc Testnet; expected to match on mainnet. */
|
|
3
|
+
export declare const ARC_MEMO_DEFAULT: Address;
|
|
4
|
+
/** ERC-20 view of native USDC on Arc (6 decimals, same balance as the 18-decimal native asset). */
|
|
5
|
+
export declare const USDC_VIEW: Address;
|
|
6
|
+
/** `address(0)` as the pair token means the curve is quoted in native USDC. */
|
|
7
|
+
export declare const NATIVE_QUOTE: Address;
|
|
8
|
+
export declare const DEFAULT_CHAIN_ID = 5042002;
|
|
9
|
+
/**
|
|
10
|
+
* Deployed Parabolic addresses per chain, so the server is useful with no configuration at all.
|
|
11
|
+
* The source repository is private; these are the same addresses the site publishes at
|
|
12
|
+
* https://www.parabolic.family/docs#contracts and that are verified on Arcscan.
|
|
13
|
+
*
|
|
14
|
+
* Arc mainnet (5042) opens on 16 September 2026. Its addresses ship in the release that follows the
|
|
15
|
+
* deployment; until then a mainnet chain id resolves the RPC and nothing else, and any tool that
|
|
16
|
+
* needs the factory says so.
|
|
17
|
+
*/
|
|
18
|
+
export declare const DEPLOYMENTS: Record<number, {
|
|
19
|
+
rpcUrl: string;
|
|
20
|
+
factory?: Address;
|
|
21
|
+
memoRouter?: Address;
|
|
22
|
+
eurc?: Address;
|
|
23
|
+
/** Deployment block. The no-subgraph listing scans TokenLaunched from here; a public RPC refuses a scan from 0. */
|
|
24
|
+
startBlock?: number;
|
|
25
|
+
}>;
|
|
26
|
+
export declare const DEFAULT_RPC_URL: string;
|
|
27
|
+
export type Config = {
|
|
28
|
+
/** JSON-RPC endpoint for Arc. */
|
|
29
|
+
rpcUrl: string;
|
|
30
|
+
/** 5042002 = Arc Testnet, 5042 = Arc mainnet (id unofficial until Circle publishes it). */
|
|
31
|
+
chainId: number;
|
|
32
|
+
/** ParabolicLaunchFactory. Required for on-chain coin lookups and for building launch transactions. */
|
|
33
|
+
factory?: Address;
|
|
34
|
+
/** ParabolicMemoRouter. Optional; enables the memo-routed (referral-carrying) buy variant. */
|
|
35
|
+
memoRouter?: Address;
|
|
36
|
+
/** Parabolic subgraph GraphQL endpoint. Recommended: listing, holders, volume and stats come from here. */
|
|
37
|
+
subgraphUrl?: string;
|
|
38
|
+
/** ERC-20 EURC on Arc, required only to build EURC-quoted launches. */
|
|
39
|
+
eurc?: Address;
|
|
40
|
+
/** ParabolicHook (Uniswap v4). Optional; read from `factory.memeHook()` when unset. Used to derive pool ids. */
|
|
41
|
+
hook?: Address;
|
|
42
|
+
/** Arc's Memo system contract. */
|
|
43
|
+
arcMemo: Address;
|
|
44
|
+
/** Launch config index on the factory; preset 0 is the adopted $5K / $12,500 curve. */
|
|
45
|
+
launchConfigId: bigint;
|
|
46
|
+
/** First block to scan for `TokenLaunched` logs when listing without a subgraph. */
|
|
47
|
+
factoryStartBlock: bigint;
|
|
48
|
+
};
|
|
49
|
+
/** Environment variables the server reads, with their meaning (also rendered by `parabolic.docs`). */
|
|
50
|
+
export declare const ENV_VARS: Array<[name: string, meaning: string]>;
|
|
51
|
+
export declare function configFromEnv(env?: Record<string, string | undefined>): Config;
|
|
52
|
+
export declare const chainName: (chainId: number) => string;
|
|
53
|
+
/** Arcscan base URL for the chain, undefined where there is no explorer (Anvil). */
|
|
54
|
+
export declare const explorerBase: (chainId: number) => string | undefined;
|
|
55
|
+
export declare const explorerUrl: (chainId: number, kind: "address" | "tx", value: string) => string | undefined;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { getAddress, isAddress } from "viem";
|
|
2
|
+
/** Arc's system `Memo` contract (CREATE2, zero salt), verified on Arc Testnet; expected to match on mainnet. */
|
|
3
|
+
export const ARC_MEMO_DEFAULT = "0x5294E9927c3306DcBaDb03fe70b92e01cCede505";
|
|
4
|
+
/** ERC-20 view of native USDC on Arc (6 decimals, same balance as the 18-decimal native asset). */
|
|
5
|
+
export const USDC_VIEW = "0x3600000000000000000000000000000000000000";
|
|
6
|
+
/** `address(0)` as the pair token means the curve is quoted in native USDC. */
|
|
7
|
+
export const NATIVE_QUOTE = "0x0000000000000000000000000000000000000000";
|
|
8
|
+
export const DEFAULT_CHAIN_ID = 5042002;
|
|
9
|
+
/**
|
|
10
|
+
* Deployed Parabolic addresses per chain, so the server is useful with no configuration at all.
|
|
11
|
+
* The source repository is private; these are the same addresses the site publishes at
|
|
12
|
+
* https://www.parabolic.family/docs#contracts and that are verified on Arcscan.
|
|
13
|
+
*
|
|
14
|
+
* Arc mainnet (5042) opens on 16 September 2026. Its addresses ship in the release that follows the
|
|
15
|
+
* deployment; until then a mainnet chain id resolves the RPC and nothing else, and any tool that
|
|
16
|
+
* needs the factory says so.
|
|
17
|
+
*/
|
|
18
|
+
export const DEPLOYMENTS = {
|
|
19
|
+
5042002: {
|
|
20
|
+
rpcUrl: "https://rpc.testnet.arc.io",
|
|
21
|
+
factory: "0xDA14d24385483c5876e0E170c6135F14Fc3BCEc3",
|
|
22
|
+
memoRouter: "0x32E6e071eA1Fe58DF86C4D3f419a8BAF2118f505",
|
|
23
|
+
eurc: "0x89B50855Aa3bE2F677cD6303Cec089B5F319D72a",
|
|
24
|
+
startBlock: 61040913,
|
|
25
|
+
},
|
|
26
|
+
5042: {
|
|
27
|
+
rpcUrl: "https://rpc.arc.io",
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
export const DEFAULT_RPC_URL = DEPLOYMENTS[DEFAULT_CHAIN_ID].rpcUrl;
|
|
31
|
+
/** Environment variables the server reads, with their meaning (also rendered by `parabolic.docs`). */
|
|
32
|
+
export const ENV_VARS = [
|
|
33
|
+
["PARABOLIC_RPC_URL", `Arc JSON-RPC endpoint (default ${DEFAULT_RPC_URL})`],
|
|
34
|
+
["PARABOLIC_CHAIN_ID", `chain id (default ${DEFAULT_CHAIN_ID}, Arc Testnet)`],
|
|
35
|
+
["PARABOLIC_FACTORY", "ParabolicLaunchFactory address (on-chain lookups, launch transactions, log-scan listing)"],
|
|
36
|
+
["PARABOLIC_MEMO_ROUTER", "ParabolicMemoRouter address (memo-routed buys carrying a referral code; EOA-only)"],
|
|
37
|
+
["PARABOLIC_SUBGRAPH_URL", "Parabolic subgraph endpoint (listing, holders, volume, protocol stats)"],
|
|
38
|
+
["PARABOLIC_EURC", "ERC-20 EURC address (EURC-quoted launches only)"],
|
|
39
|
+
["PARABOLIC_HOOK", "ParabolicHook address (optional; read from the factory when unset)"],
|
|
40
|
+
["PARABOLIC_ARC_MEMO", `Arc Memo system contract (default ${ARC_MEMO_DEFAULT})`],
|
|
41
|
+
["PARABOLIC_LAUNCH_CONFIG_ID", "factory launch config index (default 0)"],
|
|
42
|
+
["PARABOLIC_FACTORY_START_BLOCK", "first block for the TokenLaunched log scan (default 0)"],
|
|
43
|
+
];
|
|
44
|
+
function addr(name, v) {
|
|
45
|
+
if (v === undefined || v.trim() === "")
|
|
46
|
+
return undefined;
|
|
47
|
+
const s = v.trim();
|
|
48
|
+
if (!isAddress(s, { strict: false }))
|
|
49
|
+
throw new Error(`${name} is not an address: ${s}`);
|
|
50
|
+
return getAddress(s);
|
|
51
|
+
}
|
|
52
|
+
function int(name, v, fallback) {
|
|
53
|
+
if (v === undefined || v.trim() === "")
|
|
54
|
+
return fallback;
|
|
55
|
+
const n = Number(v.trim());
|
|
56
|
+
if (!Number.isInteger(n) || n < 0)
|
|
57
|
+
throw new Error(`${name} must be a non-negative integer, got ${v}`);
|
|
58
|
+
return n;
|
|
59
|
+
}
|
|
60
|
+
export function configFromEnv(env = process.env) {
|
|
61
|
+
// Chain id first: every other default is read off the deployment for that chain, so setting
|
|
62
|
+
// PARABOLIC_CHAIN_ID=5042 alone moves the RPC and the addresses together instead of leaving a
|
|
63
|
+
// mainnet chain id pointed at the testnet endpoint.
|
|
64
|
+
const chainId = int("PARABOLIC_CHAIN_ID", env.PARABOLIC_CHAIN_ID, DEFAULT_CHAIN_ID);
|
|
65
|
+
const known = DEPLOYMENTS[chainId];
|
|
66
|
+
return {
|
|
67
|
+
rpcUrl: env.PARABOLIC_RPC_URL?.trim() || known?.rpcUrl || DEFAULT_RPC_URL,
|
|
68
|
+
chainId,
|
|
69
|
+
factory: addr("PARABOLIC_FACTORY", env.PARABOLIC_FACTORY) ?? known?.factory,
|
|
70
|
+
memoRouter: addr("PARABOLIC_MEMO_ROUTER", env.PARABOLIC_MEMO_ROUTER) ?? known?.memoRouter,
|
|
71
|
+
subgraphUrl: env.PARABOLIC_SUBGRAPH_URL?.trim() || undefined,
|
|
72
|
+
eurc: addr("PARABOLIC_EURC", env.PARABOLIC_EURC) ?? known?.eurc,
|
|
73
|
+
hook: addr("PARABOLIC_HOOK", env.PARABOLIC_HOOK),
|
|
74
|
+
arcMemo: addr("PARABOLIC_ARC_MEMO", env.PARABOLIC_ARC_MEMO) ?? ARC_MEMO_DEFAULT,
|
|
75
|
+
launchConfigId: BigInt(int("PARABOLIC_LAUNCH_CONFIG_ID", env.PARABOLIC_LAUNCH_CONFIG_ID, 0)),
|
|
76
|
+
factoryStartBlock: BigInt(int("PARABOLIC_FACTORY_START_BLOCK", env.PARABOLIC_FACTORY_START_BLOCK, known?.startBlock ?? 0)),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
export const chainName = (chainId) => (chainId === 5042002 ? "Arc Testnet" : chainId === 5042 ? "Arc" : `chain ${chainId}`);
|
|
80
|
+
/** Arcscan base URL for the chain, undefined where there is no explorer (Anvil). */
|
|
81
|
+
export const explorerBase = (chainId) => chainId === 5042002 ? "https://testnet.arcscan.app" : chainId === 5042 ? "https://arcscan.app" : undefined;
|
|
82
|
+
export const explorerUrl = (chainId, kind, value) => {
|
|
83
|
+
const base = explorerBase(chainId);
|
|
84
|
+
return base ? `${base}/${kind}/${value}` : undefined;
|
|
85
|
+
};
|
|
86
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/curve.d.ts
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { type Address, type Hex } from "viem";
|
|
2
|
+
/**
|
|
3
|
+
* Bonding-curve math, ported from contracts/src/ParabolicBondingCurve.sol + libraries/ParabolicBondingCurveMath.sol
|
|
4
|
+
* and web/lib/curve.ts. Everything is bigint in raw units (18-dec tokens, 18-dec native USDC or 6-dec EURC),
|
|
5
|
+
* matching the contract's integer arithmetic step for step, including the clamped "crossing" buy that fills the
|
|
6
|
+
* curve's last sellable tokens and refunds the rest.
|
|
7
|
+
*/
|
|
8
|
+
export declare const BPS = 10000n;
|
|
9
|
+
export declare const TOKEN_DECIMALS = 18;
|
|
10
|
+
/** OpenZeppelin Math.mulDiv(a, b, d, Rounding.Ceil) for non-negative inputs. */
|
|
11
|
+
export declare const mulDivCeil: (a: bigint, b: bigint, d: bigint) => bigint;
|
|
12
|
+
/**
|
|
13
|
+
* Constant-product output for an exact input, net of `feeBps` on the input. Mirrors
|
|
14
|
+
* ParabolicBondingCurveMath._amountOut; returns 0 where the library would revert (web/lib/curve.ts does the same).
|
|
15
|
+
* With feeBps = 0 this is exactly `amountIn * reserveOut / (reserveIn + amountIn)`.
|
|
16
|
+
*/
|
|
17
|
+
export declare function getAmountOut(amountIn: bigint, reserveIn: bigint, reserveOut: bigint, feeBps?: bigint): bigint;
|
|
18
|
+
/** Input required for an exact output; mirrors ParabolicBondingCurveMath.getAmountIn (rounds up by one). */
|
|
19
|
+
export declare function getAmountIn(amountOut: bigint, reserveIn: bigint, reserveOut: bigint, feeBps?: bigint): bigint;
|
|
20
|
+
export type CurveTerms = {
|
|
21
|
+
/** phantomQuote + trackedQuote - fees pending sweep (curve.getReserves()[0]). */
|
|
22
|
+
quoteReserve: bigint;
|
|
23
|
+
/** trackedTokens (curve.getReserves()[1]). */
|
|
24
|
+
tokenReserve: bigint;
|
|
25
|
+
feeBps: bigint;
|
|
26
|
+
creatorTaxBps: bigint;
|
|
27
|
+
/** Snipe tax the buyer pays now (curve.currentSnipeTaxBps(), 0 for exempt buyers); ignored on sells. */
|
|
28
|
+
snipeBps: bigint;
|
|
29
|
+
/** curve.sellableTokens(): tokenReserve minus the reserved pool seed. Defaults to the whole reserve when unknown. */
|
|
30
|
+
sellableTokens?: bigint;
|
|
31
|
+
};
|
|
32
|
+
export type BuyQuote = {
|
|
33
|
+
tokensOut: bigint;
|
|
34
|
+
/** Quote actually taken by the curve; equals quoteIn unless the buy is clamped to the last sellable tokens. */
|
|
35
|
+
spent: bigint;
|
|
36
|
+
refund: bigint;
|
|
37
|
+
fee: bigint;
|
|
38
|
+
creatorTax: bigint;
|
|
39
|
+
snipeTax: bigint;
|
|
40
|
+
/** spent - fee - creatorTax - snipeTax: what moves the reserves. */
|
|
41
|
+
netToCurve: bigint;
|
|
42
|
+
/** True when the buy takes the curve's last sellable token, which graduates the coin in the same transaction. */
|
|
43
|
+
crossing: boolean;
|
|
44
|
+
};
|
|
45
|
+
export declare class CurveClosedError extends Error {
|
|
46
|
+
constructor();
|
|
47
|
+
}
|
|
48
|
+
/** Port of ParabolicBondingCurve._buy pricing: fee legs off the input, constant product, clamp to sellable, refund. */
|
|
49
|
+
export declare function quoteBuy(quoteIn: bigint, t: CurveTerms): BuyQuote;
|
|
50
|
+
export type SellQuote = {
|
|
51
|
+
quoteOut: bigint;
|
|
52
|
+
gross: bigint;
|
|
53
|
+
fee: bigint;
|
|
54
|
+
creatorTax: bigint;
|
|
55
|
+
};
|
|
56
|
+
/** Port of ParabolicBondingCurve.sell pricing: constant product, then fee and creator tax off the gross quote output. */
|
|
57
|
+
export declare function quoteSell(tokensIn: bigint, t: Pick<CurveTerms, "quoteReserve" | "tokenReserve" | "feeBps" | "creatorTaxBps">): SellQuote;
|
|
58
|
+
/**
|
|
59
|
+
* The curve enforces `spent * minTokensOut <= received * tokensOut` (a price bound, so a clamped crossing buy still
|
|
60
|
+
* honours the caller's terms). True when a buy quoted as `q` for `quoteIn` passes with `minTokensOut`.
|
|
61
|
+
*/
|
|
62
|
+
export declare const passesPriceBound: (quoteIn: bigint, q: BuyQuote, minTokensOut: bigint) => boolean;
|
|
63
|
+
export declare const applySlippage: (v: bigint, bps: bigint) => bigint;
|
|
64
|
+
/** Tokens the curve never sells (the v4 pool seed): supply * phantomQuote / (phantomQuote + graduationThreshold), floored. */
|
|
65
|
+
export declare const reservedTokensFor: (supply: bigint, phantomQuote: bigint, graduationThreshold: bigint) => bigint;
|
|
66
|
+
/**
|
|
67
|
+
* Where the reserved tokens go at graduation. The v4 full-range position is seeded at the curve's terminal price
|
|
68
|
+
* ((phantom + threshold) / reserved) with the real quote raised, which takes reserved × threshold / (phantom + threshold)
|
|
69
|
+
* tokens; the rest, the phantom reserve's share, is permanently locked (GraduationTokensPermanentlyLocked).
|
|
70
|
+
* Adopted parameters: 28.57% reserved → ≈ 20.4% of supply seeds the pool, ≈ 8.2% is locked.
|
|
71
|
+
*/
|
|
72
|
+
export declare const graduationSplit: (reservedTokens: bigint, phantomQuote: bigint, graduationThreshold: bigint) => {
|
|
73
|
+
poolSeed: bigint;
|
|
74
|
+
locked: bigint;
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Port of ParabolicBondingCurve.currentSnipeTaxBps: starts at `startBps` when the launch opens and decays to zero at
|
|
78
|
+
* `windowSeconds` along a quadratic ease-out (remaining² / window²), capped so at least one basis point of the spend
|
|
79
|
+
* reaches the curve. Integer seconds, like block.timestamp.
|
|
80
|
+
*/
|
|
81
|
+
export declare function snipeTaxBpsAt(elapsedSeconds: bigint, windowSeconds: bigint, startBps: bigint, feeBps: bigint, creatorTaxBps: bigint): bigint;
|
|
82
|
+
export declare const toNumber: (v: bigint, decimals?: number) => number;
|
|
83
|
+
/** Spot market cap in whole quote units for `supply` at the current curve price (quoteDecimals 18 for native USDC, 6 for EURC). */
|
|
84
|
+
export declare const marketCap: (quoteReserve: bigint, tokenReserve: bigint, supply: bigint, quoteDecimals?: number) => number;
|
|
85
|
+
/** Market cap at the price the v4 pool is seeded with: (phantomQuote + graduationThreshold) / reservedTokens. */
|
|
86
|
+
export declare const graduationMarketCap: (phantomQuote: bigint, graduationThreshold: bigint, reservedTokens: bigint, supply: bigint, quoteDecimals?: number) => number;
|
|
87
|
+
/** Spot price in whole quote units per whole token. */
|
|
88
|
+
export declare const spotPrice: (quoteReserve: bigint, tokenReserve: bigint, quoteDecimals?: number) => number;
|
|
89
|
+
/** Price impact in percent of a fill (gross amount in, amount out) against the spot price; same definition as the web trade panel. */
|
|
90
|
+
export declare const priceImpact: (amountIn: bigint, amountOut: bigint, reserveIn: bigint, reserveOut: bigint) => number;
|
|
91
|
+
/** Uniswap v4 PoolId = keccak256(abi.encode(PoolKey)) with currency0 < currency1 (native USDC, address(0), is always currency0). */
|
|
92
|
+
export declare const poolIdOf: (token: Address, pairToken: Address, fee: number, tickSpacing: number, hooks: Address) => Hex;
|