@parabolicfamily/mcp 0.1.3 → 0.1.5
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 +49 -9
- package/dist/agents.d.ts +106 -0
- package/dist/agents.js +87 -0
- package/dist/agents.js.map +1 -0
- package/dist/config.d.ts +40 -8
- package/dist/config.js +56 -10
- package/dist/config.js.map +1 -1
- package/dist/curve.d.ts +1 -1
- package/dist/curve.js +1 -1
- package/dist/docs.d.ts +1 -1
- package/dist/docs.js +6 -6
- package/dist/docs.js.map +1 -1
- package/dist/parabolic.d.ts +86 -2
- package/dist/parabolic.js +135 -6
- package/dist/parabolic.js.map +1 -1
- package/dist/restapi.d.ts +10 -1
- package/dist/restapi.js +11 -1
- package/dist/restapi.js.map +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.js +19 -0
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
- package/src/agents.ts +96 -0
- package/src/config.ts +64 -12
- package/src/curve.ts +1 -1
- package/src/docs.ts +6 -6
- package/src/parabolic.ts +137 -8
- package/src/restapi.ts +12 -1
- package/src/server.ts +29 -0
package/README.md
CHANGED
|
@@ -16,23 +16,28 @@ The ABIs ship with the package, so it works offline against any Arc RPC you poin
|
|
|
16
16
|
|
|
17
17
|
## Configure
|
|
18
18
|
|
|
19
|
-
Nothing is required. Out of the box the server talks to
|
|
20
|
-
addresses already built in, so `npx -y @parabolicfamily/mcp` is a working install.
|
|
19
|
+
Nothing is required. Out of the box the server talks to the newest Arc chain it has addresses for,
|
|
20
|
+
with those addresses already built in, so `npx -y @parabolicfamily/mcp` is a working install. Since
|
|
21
|
+
0.1.5 that is **Arc mainnet** (5042), live since 16 September 2026; set `PARABOLIC_CHAIN_ID=5042002`
|
|
22
|
+
for Arc Testnet.
|
|
21
23
|
|
|
22
|
-
Set `PARABOLIC_CHAIN_ID` and the RPC and contract addresses for that chain follow it.
|
|
23
|
-
|
|
24
|
+
Set `PARABOLIC_CHAIN_ID` and the RPC and contract addresses for that chain follow it. Every unsigned
|
|
25
|
+
transaction the server builds carries its `chainId`, so a wallet cannot sign a testnet launch onto
|
|
26
|
+
mainnet by mistake, and `protocol_stats` reports the chain and RPC host the server is actually using.
|
|
24
27
|
|
|
25
28
|
| Variable | Meaning | Default |
|
|
26
29
|
|---|---|---|
|
|
27
|
-
| `PARABOLIC_CHAIN_ID` | 5042002 Arc Testnet, 5042 Arc mainnet | `
|
|
30
|
+
| `PARABOLIC_CHAIN_ID` | 5042002 Arc Testnet, 5042 Arc mainnet | `5042` |
|
|
28
31
|
| `PARABOLIC_RPC_URL` | Arc JSON-RPC endpoint | the public endpoint for the chain id |
|
|
29
32
|
| `PARABOLIC_FACTORY` | `ParabolicLaunchFactory`: coin lookups and launch transactions | the deployment for the chain id |
|
|
30
33
|
| `PARABOLIC_MEMO_ROUTER` | `ParabolicMemoRouter`: the atomic launch-and-buy and the referral-carrying buy | the deployment for the chain id |
|
|
31
34
|
| `PARABOLIC_EURC` | EURC address, only for EURC-quoted coins | the deployment for the chain id |
|
|
32
35
|
| `PARABOLIC_SUBGRAPH_URL` | Subgraph endpoint: richer listings, holders, 24h volume, protocol stats | unset (the public API is used instead) |
|
|
36
|
+
| `PARABOLIC_EXPLORER_URL` | Block explorer the address and transaction links are built from | Arcscan for the chain id; Arc mainnet's host is unconfirmed |
|
|
33
37
|
| `PARABOLIC_HOOK` | `ParabolicHook` address | read from `factory.memeHook()` |
|
|
34
38
|
| `PARABOLIC_ARC_MEMO` | Arc `Memo` system contract | `0x5294E9927c3306DcBaDb03fe70b92e01cCede505` |
|
|
35
|
-
| `
|
|
39
|
+
| `PARABOLIC_AGENT_REGISTRY` | Arc's ERC-8004 `IdentityRegistry`: `agent_status`, `build_register_agent_tx` and the `pair: "Agents"` listing filter | the registry Arc publishes for the chain id (Arc Testnet only, today) |
|
|
40
|
+
| `PARABOLIC_LAUNCH_CONFIG_ID` | Factory launch-config index (0 = the $5K open / $8,000 graduation curve) | `0` |
|
|
36
41
|
| `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
42
|
|
|
38
43
|
The API default follows the chain: Arc Testnet reads the testnet site (`https://parabolic-testnet.vercel.app/api/v1`,
|
|
@@ -51,8 +56,16 @@ with source, before you sign anything:
|
|
|
51
56
|
| `ParabolicMemoRouter` | [`0x1381567ea31a094e64f6B39Bd880d0b59c48e08F`](https://testnet.arcscan.app/address/0x1381567ea31a094e64f6B39Bd880d0b59c48e08F) |
|
|
52
57
|
| EURC (test token) | [`0x89B50855Aa3bE2F677cD6303Cec089B5F319D72a`](https://testnet.arcscan.app/address/0x89B50855Aa3bE2F677cD6303Cec089B5F319D72a) |
|
|
53
58
|
| Arc `Memo` (system) | [`0x5294E9927c3306DcBaDb03fe70b92e01cCede505`](https://testnet.arcscan.app/address/0x5294E9927c3306DcBaDb03fe70b92e01cCede505) |
|
|
59
|
+
| ERC-8004 `IdentityRegistry` (Arc's) | [`0x8004A818BFB912233c491871b3d84c89A494BD9e`](https://testnet.arcscan.app/address/0x8004A818BFB912233c491871b3d84c89A494BD9e) |
|
|
54
60
|
|
|
55
|
-
|
|
61
|
+
The identity registry is **Arc's contract, not ours**: Arc deploys it, documents it and can change it.
|
|
62
|
+
It is an ERC-1967 proxy, so the implementation behind that address is upgradeable by Arc. Parabolic only
|
|
63
|
+
reads it (and builds the registration for your wallet to sign); nothing on the launchpad depends on it.
|
|
64
|
+
|
|
65
|
+
Arc mainnet (5042), deployed 16 September 2026 at block 21,105,294: factory
|
|
66
|
+
`0x27EAeC564fd049E6920C73323ffA7eb6a32A4B1e`, memo router `0x7d642150A222D0F6c98e2fC6bBe86E8Bb6DC90d0`,
|
|
67
|
+
source verified on Sourcify. Arc has published no identity registry for mainnet, so on chain 5042
|
|
68
|
+
`agent_status` says there is no registry rather than reporting a wallet as unregistered.
|
|
56
69
|
|
|
57
70
|
### Claude Code
|
|
58
71
|
|
|
@@ -110,13 +123,15 @@ Arguments are strict: a key a tool does not know fails with `Unrecognized key` i
|
|
|
110
123
|
|
|
111
124
|
| Tool | Purpose |
|
|
112
125
|
|---|---|
|
|
113
|
-
| `parabolic.list_coins` | `{ status?: "climbing" \| "graduated", sort?: "trending" \| "new" \| "near_graduation" \| "market_cap", limit
|
|
126
|
+
| `parabolic.list_coins` | `{ status?: "climbing" \| "graduated", sort?: "trending" \| "new" \| "near_graduation" \| "market_cap", limit?, pair?: "USDC" \| "EURC" \| "Agents" }`. Subgraph when configured, otherwise Parabolic's public read API; a `TokenLaunched` log scan only when both are unavailable. `pair` is the quote asset, except `"Agents"`, which means the coin's creator holds an ERC-8004 identity: that is read from Arc's registry (one `balanceOf` per creator, over the top 100 of the sort) whatever the listing source is. Without a registry for the chain the result sets `pairFilterApplied: false` and the `source` line says the filter did not run. |
|
|
114
127
|
| `parabolic.get_coin` | `{ address }` (token or curve). Name, ticker, creator, curve, status/phase, raised/threshold/progress, price, market cap, holders, pool id, fee terms and 50/30/20 split, snipe-tax window and `currentSnipeTaxBps()`. |
|
|
115
128
|
| `parabolic.quote_buy` | `{ address, quoteIn, buyer?, slippageBps? }`. Exact curve math on live reserves: fee, creator tax and snipe tax legs, spot vs effective price, price impact, whether the buy graduates the coin, suggested `minTokensOut`. `buyer` checks the snipe-tax exemption. |
|
|
116
129
|
| `parabolic.quote_sell` | `{ address, tokensIn, slippageBps? }`. Gross, fee, creator tax, effective price, price impact, suggested `minQuoteOut`. |
|
|
117
130
|
| `parabolic.build_buy_tx` | `{ address, quoteIn, minTokensOut?, recipient, buyer?, slippageBps?, memo? }` → unsigned `curve.buy(...)` (value = quote on native-USDC curves; an approval step for EURC pairs). `memo: { referral, note? }` returns the memo-routed variant instead (see below). |
|
|
118
131
|
| `parabolic.build_sell_tx` | `{ address, tokensIn, minQuoteOut?, recipient, slippageBps? }` → unsigned ERC-20 approval + `curve.sell(...)`. |
|
|
119
132
|
| `parabolic.build_launch_tx` | `{ name, ticker, image?, description?, socials?, pair?, devBuy?, creatorTaxBps?, creator, buybackEnabled? }`. The `TokenParams` are built exactly like the web Create form (economics pinned with `previewLaunchEconomics`, random salt). With `devBuy` and `PARABOLIC_MEMO_ROUTER` set: one unsigned `ParabolicMemoRouter.launchAndBuy` (value = launch fee + dev buy, `creatorFeeRecipient` zero, `minTokensOut` from the curve math on the launch config; `launchAndBuyWithToken` plus an approval for EURC). Otherwise: unsigned `factory.launchToken(...)` with the launch fee as value and, for a `devBuy`, the follow-up buy calldata to send to the curve from the `TokenLaunched` event. |
|
|
133
|
+
| `parabolic.agent_status` | `{ address }`. Whether the wallet holds an ERC-8004 agent identity on Arc's `IdentityRegistry` (`balanceOf`), how many it holds, and the registry address and chain. On a chain where Arc has published no registry the answer is that fact, with `registered` and `identities` null. |
|
|
134
|
+
| `parabolic.build_register_agent_tx` | `{ from, metadataURI? }` → unsigned `IdentityRegistry.register(metadataURI)` (no value). The registry mints the ERC-721 identity to the signer, so `from` is the wallet that sends it, and returns the agent id; read it from the `Transfer` log in the receipt. `metadataURI` is a pointer to the agent's metadata stored as `tokenURI` and may be empty. Warns when the wallet already holds an identity. |
|
|
120
135
|
| `parabolic.protocol_stats` | Subgraph totals (launches, graduations, trades, volume, fees, daily series) plus the factory's live launch fee, curve preset, snipe-tax terms and contract addresses. |
|
|
121
136
|
| `parabolic.docs` | The adopted parameters, curve mechanics, verified Arc facts and this server's configuration, as text. |
|
|
122
137
|
|
|
@@ -162,14 +177,38 @@ Launch a coin with a $200 dev buy:
|
|
|
162
177
|
|
|
163
178
|
With `PARABOLIC_MEMO_ROUTER` set the result is `kind: "launch_and_buy"`: one `tx` to the router (`value` = $1 launch fee + $200), the encoded `params` (`creatorFeeRecipient` zero, as the router requires), the curve `terms` (opening and fully diluted graduation market caps, reserved / pool-seed / locked token shares, snipe-tax window) and `devBuy` (expected and minimum tokens, snipe-tax exempt). Without the router it is `kind: "launch"`: `tx` to the factory (value = the launch fee) and `devBuy` with the calldata for the second, snipe-tax-exempt transaction to the new curve. There is no exemption list: only the creator's own address is exempt from the snipe tax.
|
|
164
179
|
|
|
180
|
+
Check whether a wallet is a registered agent, then register it:
|
|
181
|
+
|
|
182
|
+
```json
|
|
183
|
+
{ "name": "parabolic.agent_status", "arguments": { "address": "0xYourWallet" } }
|
|
184
|
+
```
|
|
185
|
+
```json
|
|
186
|
+
{
|
|
187
|
+
"address": "0xYourWallet",
|
|
188
|
+
"chain": { "id": 5042002, "name": "Arc Testnet" },
|
|
189
|
+
"registry": { "address": "0x8004A818BFB912233c491871b3d84c89A494BD9e", "contract": "ERC-8004 IdentityRegistry", "operator": "Arc" },
|
|
190
|
+
"registered": false, "identities": 0
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
```json
|
|
194
|
+
{ "name": "parabolic.build_register_agent_tx", "arguments": { "from": "0xYourWallet", "metadataURI": "ipfs://…/agent-card.json" } }
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
The result is `kind: "register_agent"`: one unsigned `tx` to the registry carrying no value, the
|
|
198
|
+
`metadataURI` as encoded, and `currentIdentities` for the wallet. Send it from `from`, because the
|
|
199
|
+
registry mints to `msg.sender`. Once it lands, `parabolic.list_coins` with `pair: "Agents"` keeps the
|
|
200
|
+
coins launched from that wallet, and the site tags them `[ Agent ]`.
|
|
201
|
+
|
|
165
202
|
## Notes on the mechanics
|
|
166
203
|
|
|
167
204
|
- Curve math is a port of `ParabolicBondingCurve` / `ParabolicBondingCurveMath` (and of `web/lib/curve.ts`): fee legs are taken off the buy input separately, the constant product is priced on `getReserves()`, and a buy that would take more than `sellableTokens()` is clamped to the last sellable tokens, charged the grossed-up price and refunded the rest; that buy graduates the coin, so give it extra gas. Sells take the fee off the gross quote output and close once the curve is ready to graduate.
|
|
168
205
|
- `minTokensOut` on the curve is a price bound (`spent × minTokensOut ≤ received × tokensOut`), so a clamped fill still honours the caller's terms.
|
|
169
206
|
- The snipe tax decays per second along `remaining² / window²`; a quote in the launch window overstates the tax a later block charges. Pass `buyer` to have the exemption checked.
|
|
170
|
-
- At graduation the curve still holds the reserved
|
|
207
|
+
- At graduation the curve still holds the reserved 38.46% of supply: ≈ 23.7% seeds the Uniswap v4 full-range position with the $8,000 raised at the same terminal price, and ≈ 14.8% is permanently locked. Fully diluted graduation market cap: $33,800.
|
|
171
208
|
- 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.
|
|
172
209
|
- 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.
|
|
210
|
+
- Agent identity is ERC-8004 on Arc's own `IdentityRegistry` (an ERC-721 named `AgentIdentity`, symbol `AGENT`): `register(metadataURI)` mints one to the caller, `balanceOf` answers whether a wallet holds any. The registry is **not** ERC-721 Enumerable, so there is no way to list a wallet's agent ids without scanning `Transfer` logs, which public Arc RPCs cap and rate-limit; this server does not try. Arc can upgrade the registry behind its address (ERC-1967 proxy). Holding an identity is not an endorsement: registration is open to anyone.
|
|
211
|
+
- `pair: "Agents"` is a property of the creator, not of the coin, so it is read at call time: one `balanceOf` per distinct creator over the top 100 coins of the sort, deduplicated and batched. A creator the registry will not answer for is left out rather than counted either way, and the `source` line reports how many. When the registry answers for nobody the list comes back empty and says so, because an unreachable registry and a board with no agent launches are not the same answer.
|
|
173
212
|
- 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.
|
|
174
213
|
|
|
175
214
|
## Development
|
|
@@ -181,6 +220,7 @@ src/
|
|
|
181
220
|
parabolic.ts the service: reads (RPC + subgraph) and unsigned tx builders
|
|
182
221
|
curve.ts ported curve math
|
|
183
222
|
tx.ts viem encodeFunctionData builders (buy, sell, approve, launchToken, memo buy)
|
|
223
|
+
agents.ts ERC-8004 identity registry: balanceOf / ownerOf / tokenURI reads, register builder
|
|
184
224
|
chain.ts viem RPC wrapper (batched HTTP) behind a small Rpc interface
|
|
185
225
|
subgraph.ts GraphQL client and Coin/Protocol shapes
|
|
186
226
|
coins.ts coin summaries, sorting
|
package/dist/agents.d.ts
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { type Address } from "viem";
|
|
2
|
+
import { type Rpc } from "./chain.js";
|
|
3
|
+
import type { UnsignedTx } from "./tx.js";
|
|
4
|
+
/**
|
|
5
|
+
* ERC-8004 agent identity on Arc.
|
|
6
|
+
*
|
|
7
|
+
* The registries are Arc's, not Parabolic's: Arc deploys them and documents them
|
|
8
|
+
* (https://docs.arc.io/arc/tutorials/register-your-first-ai-agent.md, which registers an agent with
|
|
9
|
+
* `register(string metadataURI)`). The IdentityRegistry is an ERC-1967 proxy, so Arc can change the
|
|
10
|
+
* implementation behind the address in DEPLOYMENTS without the address moving, and the facts recorded
|
|
11
|
+
* below hold for the implementation that answered on the date given.
|
|
12
|
+
*
|
|
13
|
+
* Verified against https://rpc.testnet.arc.io on 13 September 2026: name() = "AgentIdentity",
|
|
14
|
+
* symbol() = "AGENT", supportsInterface(0x80ac58cd) and (0x5b5e139f) true (ERC-721 and Metadata),
|
|
15
|
+
* balanceOf / ownerOf / tokenURI answer, and register(string) is in the implementation bytecode with
|
|
16
|
+
* selector 0xf2c298be.
|
|
17
|
+
*
|
|
18
|
+
* There is deliberately no "list this wallet's agent ids" function. supportsInterface(0x780e9d63) is
|
|
19
|
+
* false: the registry is not ERC-721 Enumerable and has neither totalSupply nor tokenOfOwnerByIndex,
|
|
20
|
+
* so the ids could only come from a Transfer log scan. Public Arc RPCs cap eth_getLogs near 10,000
|
|
21
|
+
* blocks and rate-limit the walk, which is the same cost that moved coin listing off the chain and
|
|
22
|
+
* onto an indexer. balanceOf answers the question the tools actually ask in one eth_call.
|
|
23
|
+
*/
|
|
24
|
+
export declare const identityRegistryAbi: readonly [{
|
|
25
|
+
readonly name: "register";
|
|
26
|
+
readonly type: "function";
|
|
27
|
+
readonly stateMutability: "nonpayable";
|
|
28
|
+
readonly inputs: readonly [{
|
|
29
|
+
readonly type: "string";
|
|
30
|
+
readonly name: "metadataURI";
|
|
31
|
+
}];
|
|
32
|
+
readonly outputs: readonly [{
|
|
33
|
+
readonly type: "uint256";
|
|
34
|
+
readonly name: "agentId";
|
|
35
|
+
}];
|
|
36
|
+
}, {
|
|
37
|
+
readonly name: "balanceOf";
|
|
38
|
+
readonly type: "function";
|
|
39
|
+
readonly stateMutability: "view";
|
|
40
|
+
readonly inputs: readonly [{
|
|
41
|
+
readonly type: "address";
|
|
42
|
+
readonly name: "owner";
|
|
43
|
+
}];
|
|
44
|
+
readonly outputs: readonly [{
|
|
45
|
+
readonly type: "uint256";
|
|
46
|
+
}];
|
|
47
|
+
}, {
|
|
48
|
+
readonly name: "ownerOf";
|
|
49
|
+
readonly type: "function";
|
|
50
|
+
readonly stateMutability: "view";
|
|
51
|
+
readonly inputs: readonly [{
|
|
52
|
+
readonly type: "uint256";
|
|
53
|
+
readonly name: "tokenId";
|
|
54
|
+
}];
|
|
55
|
+
readonly outputs: readonly [{
|
|
56
|
+
readonly type: "address";
|
|
57
|
+
}];
|
|
58
|
+
}, {
|
|
59
|
+
readonly name: "tokenURI";
|
|
60
|
+
readonly type: "function";
|
|
61
|
+
readonly stateMutability: "view";
|
|
62
|
+
readonly inputs: readonly [{
|
|
63
|
+
readonly type: "uint256";
|
|
64
|
+
readonly name: "tokenId";
|
|
65
|
+
}];
|
|
66
|
+
readonly outputs: readonly [{
|
|
67
|
+
readonly type: "string";
|
|
68
|
+
}];
|
|
69
|
+
}];
|
|
70
|
+
/** Longest metadata URI this server encodes; a pointer, not a document. */
|
|
71
|
+
export declare const MAX_METADATA_URI_BYTES = 512;
|
|
72
|
+
export type AgentStatus = {
|
|
73
|
+
address: Address;
|
|
74
|
+
registered: boolean;
|
|
75
|
+
identities: number;
|
|
76
|
+
};
|
|
77
|
+
/** `balanceOf(owner)`: whether the wallet holds an agent identity, and how many it holds. */
|
|
78
|
+
export declare function readAgentStatus(rpc: Rpc, registry: Address, owner: Address): Promise<AgentStatus>;
|
|
79
|
+
/** Most wallets one listing checks against the registry: one eth_call each, batched by the transport. */
|
|
80
|
+
export declare const AGENT_LOOKUP_MAX = 100;
|
|
81
|
+
/**
|
|
82
|
+
* Which of these wallets hold an identity, as a set of lowercase addresses. This is how a coin list is
|
|
83
|
+
* filtered to agent launches: the flag is a property of the creator, not of the coin, so it is read
|
|
84
|
+
* per creator at call time.
|
|
85
|
+
*
|
|
86
|
+
* A read that fails is counted, not guessed: "the registry did not answer" is not "this wallet holds
|
|
87
|
+
* none", and the caller has to be able to say how many creators went unchecked.
|
|
88
|
+
*/
|
|
89
|
+
export declare function readAgentOwners(rpc: Rpc, registry: Address, owners: readonly Address[]): Promise<{
|
|
90
|
+
agents: Set<string>;
|
|
91
|
+
checked: number;
|
|
92
|
+
failed: number;
|
|
93
|
+
}>;
|
|
94
|
+
export type AgentIdentity = {
|
|
95
|
+
agentId: string;
|
|
96
|
+
owner: Address;
|
|
97
|
+
metadataURI: string;
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* One identity by agent id: the ERC-721 tokenId that `register` returns, also readable from the
|
|
101
|
+
* Transfer log in the registration receipt. The metadata URI comes back empty for an identity
|
|
102
|
+
* registered without one, which is common on Arc Testnet (tokenURI(1) is the empty string).
|
|
103
|
+
*/
|
|
104
|
+
export declare function readAgent(rpc: Rpc, registry: Address, agentId: bigint): Promise<AgentIdentity>;
|
|
105
|
+
/** `registry.register(metadataURI)`: mints an identity to whoever signs and returns its agent id. Carries no value. */
|
|
106
|
+
export declare function encodeRegisterAgent(registry: Address, metadataURI: string, chainId: number): UnsignedTx;
|
package/dist/agents.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { encodeFunctionData, parseAbi, toHex } from "viem";
|
|
2
|
+
import { mapLimit } from "./chain.js";
|
|
3
|
+
import { addrOf, big, str } from "./format.js";
|
|
4
|
+
/**
|
|
5
|
+
* ERC-8004 agent identity on Arc.
|
|
6
|
+
*
|
|
7
|
+
* The registries are Arc's, not Parabolic's: Arc deploys them and documents them
|
|
8
|
+
* (https://docs.arc.io/arc/tutorials/register-your-first-ai-agent.md, which registers an agent with
|
|
9
|
+
* `register(string metadataURI)`). The IdentityRegistry is an ERC-1967 proxy, so Arc can change the
|
|
10
|
+
* implementation behind the address in DEPLOYMENTS without the address moving, and the facts recorded
|
|
11
|
+
* below hold for the implementation that answered on the date given.
|
|
12
|
+
*
|
|
13
|
+
* Verified against https://rpc.testnet.arc.io on 13 September 2026: name() = "AgentIdentity",
|
|
14
|
+
* symbol() = "AGENT", supportsInterface(0x80ac58cd) and (0x5b5e139f) true (ERC-721 and Metadata),
|
|
15
|
+
* balanceOf / ownerOf / tokenURI answer, and register(string) is in the implementation bytecode with
|
|
16
|
+
* selector 0xf2c298be.
|
|
17
|
+
*
|
|
18
|
+
* There is deliberately no "list this wallet's agent ids" function. supportsInterface(0x780e9d63) is
|
|
19
|
+
* false: the registry is not ERC-721 Enumerable and has neither totalSupply nor tokenOfOwnerByIndex,
|
|
20
|
+
* so the ids could only come from a Transfer log scan. Public Arc RPCs cap eth_getLogs near 10,000
|
|
21
|
+
* blocks and rate-limit the walk, which is the same cost that moved coin listing off the chain and
|
|
22
|
+
* onto an indexer. balanceOf answers the question the tools actually ask in one eth_call.
|
|
23
|
+
*/
|
|
24
|
+
export const identityRegistryAbi = parseAbi([
|
|
25
|
+
"function register(string metadataURI) returns (uint256 agentId)",
|
|
26
|
+
"function balanceOf(address owner) view returns (uint256)",
|
|
27
|
+
"function ownerOf(uint256 tokenId) view returns (address)",
|
|
28
|
+
"function tokenURI(uint256 tokenId) view returns (string)",
|
|
29
|
+
]);
|
|
30
|
+
/** Longest metadata URI this server encodes; a pointer, not a document. */
|
|
31
|
+
export const MAX_METADATA_URI_BYTES = 512;
|
|
32
|
+
/** `balanceOf(owner)`: whether the wallet holds an agent identity, and how many it holds. */
|
|
33
|
+
export async function readAgentStatus(rpc, registry, owner) {
|
|
34
|
+
const balance = big(await rpc.read({ address: registry, abi: identityRegistryAbi, functionName: "balanceOf", args: [owner] }));
|
|
35
|
+
return { address: owner, registered: balance > 0n, identities: Number(balance) };
|
|
36
|
+
}
|
|
37
|
+
/** Most wallets one listing checks against the registry: one eth_call each, batched by the transport. */
|
|
38
|
+
export const AGENT_LOOKUP_MAX = 100;
|
|
39
|
+
/**
|
|
40
|
+
* Which of these wallets hold an identity, as a set of lowercase addresses. This is how a coin list is
|
|
41
|
+
* filtered to agent launches: the flag is a property of the creator, not of the coin, so it is read
|
|
42
|
+
* per creator at call time.
|
|
43
|
+
*
|
|
44
|
+
* A read that fails is counted, not guessed: "the registry did not answer" is not "this wallet holds
|
|
45
|
+
* none", and the caller has to be able to say how many creators went unchecked.
|
|
46
|
+
*/
|
|
47
|
+
export async function readAgentOwners(rpc, registry, owners) {
|
|
48
|
+
const unique = [...new Set(owners.map((o) => o.toLowerCase()))].slice(0, AGENT_LOOKUP_MAX);
|
|
49
|
+
const agents = new Set();
|
|
50
|
+
let failed = 0;
|
|
51
|
+
await mapLimit(unique, 8, async (owner) => {
|
|
52
|
+
try {
|
|
53
|
+
const balance = big(await rpc.read({ address: registry, abi: identityRegistryAbi, functionName: "balanceOf", args: [owner] }));
|
|
54
|
+
if (balance > 0n)
|
|
55
|
+
agents.add(owner);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
failed += 1;
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
return { agents, checked: unique.length, failed };
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* One identity by agent id: the ERC-721 tokenId that `register` returns, also readable from the
|
|
65
|
+
* Transfer log in the registration receipt. The metadata URI comes back empty for an identity
|
|
66
|
+
* registered without one, which is common on Arc Testnet (tokenURI(1) is the empty string).
|
|
67
|
+
*/
|
|
68
|
+
export async function readAgent(rpc, registry, agentId) {
|
|
69
|
+
const [owner, uri] = await Promise.all([
|
|
70
|
+
rpc.read({ address: registry, abi: identityRegistryAbi, functionName: "ownerOf", args: [agentId] }),
|
|
71
|
+
rpc.read({ address: registry, abi: identityRegistryAbi, functionName: "tokenURI", args: [agentId] }),
|
|
72
|
+
]);
|
|
73
|
+
return { agentId: agentId.toString(), owner: addrOf(owner), metadataURI: str(uri) };
|
|
74
|
+
}
|
|
75
|
+
/** `registry.register(metadataURI)`: mints an identity to whoever signs and returns its agent id. Carries no value. */
|
|
76
|
+
export function encodeRegisterAgent(registry, metadataURI, chainId) {
|
|
77
|
+
const data = encodeFunctionData({ abi: identityRegistryAbi, functionName: "register", args: [metadataURI] });
|
|
78
|
+
return {
|
|
79
|
+
to: registry,
|
|
80
|
+
data,
|
|
81
|
+
value: toHex(0n),
|
|
82
|
+
valueWei: "0",
|
|
83
|
+
chainId,
|
|
84
|
+
description: `ERC-8004 IdentityRegistry.register(metadataURI=${metadataURI === "" ? "(empty)" : metadataURI}): mints an agent identity to the wallet that signs`,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=agents.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agents.js","sourceRoot":"","sources":["../src/agents.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
CHANGED
|
@@ -5,16 +5,14 @@ export declare const ARC_MEMO_DEFAULT: Address;
|
|
|
5
5
|
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
|
-
export declare const DEFAULT_CHAIN_ID = 5042002;
|
|
9
8
|
export declare const DEFAULT_API_URL = "https://www.parabolic.family/api/v1";
|
|
10
9
|
/**
|
|
11
10
|
* Deployed Parabolic addresses per chain, so the server is useful with no configuration at all.
|
|
12
11
|
* The source repository is private; these are the same addresses the site publishes at
|
|
13
12
|
* https://www.parabolic.family/docs#contracts and that are verified on Arcscan.
|
|
14
13
|
*
|
|
15
|
-
* Arc mainnet (5042)
|
|
16
|
-
*
|
|
17
|
-
* needs the factory says so.
|
|
14
|
+
* Arc mainnet (5042) opened on 16 September 2026; Parabolic deployed at block 21,105,294 and these are
|
|
15
|
+
* the live addresses (contracts/deployments/5042.json). With them in place the default chain is Arc.
|
|
18
16
|
*/
|
|
19
17
|
export declare const DEPLOYMENTS: Record<number, {
|
|
20
18
|
rpcUrl: string;
|
|
@@ -25,7 +23,23 @@ export declare const DEPLOYMENTS: Record<number, {
|
|
|
25
23
|
startBlock?: number;
|
|
26
24
|
/** The site API that serves this deployment. Production serves mainnet; the testnet site serves the testnet factory. */
|
|
27
25
|
apiUrl?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Arc's ERC-8004 IdentityRegistry, not one of ours. It is an ERC-1967 proxy, so Arc can upgrade the
|
|
28
|
+
* implementation behind this address at any time. Arc publishes one for Arc Testnet only, so a chain
|
|
29
|
+
* without an entry here has no registry to read rather than a missing configuration.
|
|
30
|
+
*/
|
|
31
|
+
agentRegistry?: Address;
|
|
28
32
|
}>;
|
|
33
|
+
/**
|
|
34
|
+
* The chain an agent gets with no configuration at all: Arc mainnet once its addresses are in
|
|
35
|
+
* `DEPLOYMENTS`, Arc Testnet until then.
|
|
36
|
+
*
|
|
37
|
+
* This is derived rather than written down because the alternative is a second thing to remember on
|
|
38
|
+
* launch day. A hardcoded testnet default would survive the mainnet release and quietly hand every
|
|
39
|
+
* unconfigured agent a testnet factory, and a hardcoded mainnet default would break the server today,
|
|
40
|
+
* when chain 5042 has an RPC and no contracts. Filling in the 5042 entry moves the default with it.
|
|
41
|
+
*/
|
|
42
|
+
export declare const DEFAULT_CHAIN_ID: number;
|
|
29
43
|
export declare const DEFAULT_RPC_URL: string;
|
|
30
44
|
export type Config = {
|
|
31
45
|
/** JSON-RPC endpoint for Arc. */
|
|
@@ -51,7 +65,9 @@ export type Config = {
|
|
|
51
65
|
hook?: Address;
|
|
52
66
|
/** Arc's Memo system contract. */
|
|
53
67
|
arcMemo: Address;
|
|
54
|
-
/**
|
|
68
|
+
/** Arc's ERC-8004 IdentityRegistry. Optional; undefined on a chain where Arc has published none. */
|
|
69
|
+
agentRegistry?: Address;
|
|
70
|
+
/** Launch config index on the factory; preset 0 is the adopted $5K / $8,000 curve. */
|
|
55
71
|
launchConfigId: bigint;
|
|
56
72
|
/** First block to scan for `TokenLaunched` logs when listing without a subgraph. */
|
|
57
73
|
factoryStartBlock: bigint;
|
|
@@ -59,7 +75,23 @@ export type Config = {
|
|
|
59
75
|
/** Environment variables the server reads, with their meaning (also rendered by `parabolic.docs`). */
|
|
60
76
|
export declare const ENV_VARS: Array<[name: string, meaning: string]>;
|
|
61
77
|
export declare function configFromEnv(env?: Record<string, string | undefined>): Config;
|
|
62
|
-
export declare
|
|
63
|
-
/**
|
|
64
|
-
|
|
78
|
+
export declare function chainName(chainId: number): string;
|
|
79
|
+
/**
|
|
80
|
+
* The Parabolic site that serves this chain's deployment, with no trailing slash.
|
|
81
|
+
*
|
|
82
|
+
* Derived from the deployment's API URL so a coin link always points at the site where that coin
|
|
83
|
+
* actually exists: a testnet coin on the testnet site, a mainnet coin on parabolic.family. Hardcoding
|
|
84
|
+
* the mainnet host sent every testnet link to a page that has never heard of the coin.
|
|
85
|
+
*/
|
|
86
|
+
export declare function siteBase(chainId: number): string | undefined;
|
|
87
|
+
/**
|
|
88
|
+
* Arcscan base URL for the chain, undefined where there is no explorer (Anvil).
|
|
89
|
+
*
|
|
90
|
+
* Arc mainnet's host is a guess: Circle has published an explorer for Arc Testnet only, and
|
|
91
|
+
* `https://explorer.arc.io` is Circle's published explorer (16 Sep 2026), behind a login at launch. `PARABOLIC_EXPLORER_URL` overrides it so a wrong guess
|
|
92
|
+
* costs an environment variable rather than a republish of this package. Read from `process.env`
|
|
93
|
+
* directly, unlike every other setting here, because this is called from `arcChain()` which builds a
|
|
94
|
+
* viem chain from a chain id alone and never sees a Config.
|
|
95
|
+
*/
|
|
96
|
+
export declare function explorerBase(chainId: number): string | undefined;
|
|
65
97
|
export declare const explorerUrl: (chainId: number, kind: "address" | "tx", value: string) => string | undefined;
|
package/dist/config.js
CHANGED
|
@@ -5,16 +5,14 @@ export const ARC_MEMO_DEFAULT = "0x5294E9927c3306DcBaDb03fe70b92e01cCede505";
|
|
|
5
5
|
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
|
-
export const DEFAULT_CHAIN_ID = 5042002;
|
|
9
8
|
export const DEFAULT_API_URL = "https://www.parabolic.family/api/v1";
|
|
10
9
|
/**
|
|
11
10
|
* Deployed Parabolic addresses per chain, so the server is useful with no configuration at all.
|
|
12
11
|
* The source repository is private; these are the same addresses the site publishes at
|
|
13
12
|
* https://www.parabolic.family/docs#contracts and that are verified on Arcscan.
|
|
14
13
|
*
|
|
15
|
-
* Arc mainnet (5042)
|
|
16
|
-
*
|
|
17
|
-
* needs the factory says so.
|
|
14
|
+
* Arc mainnet (5042) opened on 16 September 2026; Parabolic deployed at block 21,105,294 and these are
|
|
15
|
+
* the live addresses (contracts/deployments/5042.json). With them in place the default chain is Arc.
|
|
18
16
|
*/
|
|
19
17
|
export const DEPLOYMENTS = {
|
|
20
18
|
5042002: {
|
|
@@ -24,26 +22,43 @@ export const DEPLOYMENTS = {
|
|
|
24
22
|
eurc: "0x89B50855Aa3bE2F677cD6303Cec089B5F319D72a",
|
|
25
23
|
startBlock: 61736670,
|
|
26
24
|
apiUrl: "https://parabolic-testnet.vercel.app/api/v1",
|
|
25
|
+
agentRegistry: "0x8004A818BFB912233c491871b3d84c89A494BD9e",
|
|
27
26
|
},
|
|
28
27
|
5042: {
|
|
29
|
-
rpcUrl: "https://rpc.arc.io",
|
|
28
|
+
rpcUrl: "https://rpc.mainnet.arc.io",
|
|
29
|
+
factory: "0x27EAeC564fd049E6920C73323ffA7eb6a32A4B1e",
|
|
30
|
+
memoRouter: "0x7d642150A222D0F6c98e2fC6bBe86E8Bb6DC90d0",
|
|
31
|
+
startBlock: 21105294,
|
|
30
32
|
apiUrl: DEFAULT_API_URL,
|
|
33
|
+
// No EURC pair is approved at launch and Arc has published no ERC-8004 registry for mainnet.
|
|
31
34
|
},
|
|
32
35
|
};
|
|
36
|
+
/**
|
|
37
|
+
* The chain an agent gets with no configuration at all: Arc mainnet once its addresses are in
|
|
38
|
+
* `DEPLOYMENTS`, Arc Testnet until then.
|
|
39
|
+
*
|
|
40
|
+
* This is derived rather than written down because the alternative is a second thing to remember on
|
|
41
|
+
* launch day. A hardcoded testnet default would survive the mainnet release and quietly hand every
|
|
42
|
+
* unconfigured agent a testnet factory, and a hardcoded mainnet default would break the server today,
|
|
43
|
+
* when chain 5042 has an RPC and no contracts. Filling in the 5042 entry moves the default with it.
|
|
44
|
+
*/
|
|
45
|
+
export const DEFAULT_CHAIN_ID = DEPLOYMENTS[5042].factory ? 5042 : 5042002;
|
|
33
46
|
export const DEFAULT_RPC_URL = DEPLOYMENTS[DEFAULT_CHAIN_ID].rpcUrl;
|
|
34
47
|
/** Environment variables the server reads, with their meaning (also rendered by `parabolic.docs`). */
|
|
35
48
|
export const ENV_VARS = [
|
|
36
49
|
["PARABOLIC_RPC_URL", `Arc JSON-RPC endpoint (default ${DEFAULT_RPC_URL})`],
|
|
37
|
-
["PARABOLIC_CHAIN_ID", `chain id (default ${DEFAULT_CHAIN_ID},
|
|
50
|
+
["PARABOLIC_CHAIN_ID", `chain id: 5042002 for Arc Testnet, 5042 for Arc (default ${DEFAULT_CHAIN_ID}, ${chainName(DEFAULT_CHAIN_ID)})`],
|
|
38
51
|
["PARABOLIC_FACTORY", "ParabolicLaunchFactory address (on-chain lookups, launch transactions, log-scan listing)"],
|
|
39
52
|
["PARABOLIC_MEMO_ROUTER", "ParabolicMemoRouter address (memo-routed buys carrying a referral code; EOA-only)"],
|
|
40
53
|
["PARABOLIC_SUBGRAPH_URL", "Parabolic subgraph endpoint (listing, holders, volume, protocol stats)"],
|
|
41
54
|
["PARABOLIC_EURC", "ERC-20 EURC address (EURC-quoted launches only)"],
|
|
42
55
|
["PARABOLIC_HOOK", "ParabolicHook address (optional; read from the factory when unset)"],
|
|
43
|
-
["PARABOLIC_API_URL", `Parabolic public read API for listings (default: the site that serves the chain's deployment, ${DEPLOYMENTS[DEFAULT_CHAIN_ID].apiUrl} on
|
|
56
|
+
["PARABOLIC_API_URL", `Parabolic public read API for listings (default: the site that serves the chain's deployment, ${DEPLOYMENTS[DEFAULT_CHAIN_ID].apiUrl} on ${chainName(DEFAULT_CHAIN_ID)}; "" to use a log scan instead)`],
|
|
44
57
|
["PARABOLIC_ARC_MEMO", `Arc Memo system contract (default ${ARC_MEMO_DEFAULT})`],
|
|
58
|
+
["PARABOLIC_AGENT_REGISTRY", `Arc's ERC-8004 IdentityRegistry (agent_status, build_register_agent_tx and the list_coins pair "Agents" filter). Arc's, not ours, and Arc has published one for Arc Testnet only, so on ${chainName(DEFAULT_CHAIN_ID)} the default is ${DEPLOYMENTS[DEFAULT_CHAIN_ID].agentRegistry ?? "none and those tools say so instead of answering"}`],
|
|
45
59
|
["PARABOLIC_LAUNCH_CONFIG_ID", "factory launch config index (default 0)"],
|
|
46
60
|
["PARABOLIC_FACTORY_START_BLOCK", "first block for the TokenLaunched log scan (default: the factory's deployment block for this chain)"],
|
|
61
|
+
["PARABOLIC_EXPLORER_URL", `block explorer base URL for building address and transaction links (default ${explorerBase(DEFAULT_CHAIN_ID) ?? "none for this chain"}; Arc mainnet's host is unconfirmed)`],
|
|
47
62
|
];
|
|
48
63
|
function addr(name, v) {
|
|
49
64
|
if (v === undefined || v.trim() === "")
|
|
@@ -79,13 +94,44 @@ export function configFromEnv(env = process.env) {
|
|
|
79
94
|
eurc: addr("PARABOLIC_EURC", env.PARABOLIC_EURC) ?? known?.eurc,
|
|
80
95
|
hook: addr("PARABOLIC_HOOK", env.PARABOLIC_HOOK),
|
|
81
96
|
arcMemo: addr("PARABOLIC_ARC_MEMO", env.PARABOLIC_ARC_MEMO) ?? ARC_MEMO_DEFAULT,
|
|
97
|
+
agentRegistry: addr("PARABOLIC_AGENT_REGISTRY", env.PARABOLIC_AGENT_REGISTRY) ?? known?.agentRegistry,
|
|
82
98
|
launchConfigId: BigInt(int("PARABOLIC_LAUNCH_CONFIG_ID", env.PARABOLIC_LAUNCH_CONFIG_ID, 0)),
|
|
83
99
|
factoryStartBlock: BigInt(int("PARABOLIC_FACTORY_START_BLOCK", env.PARABOLIC_FACTORY_START_BLOCK, known?.startBlock ?? 0)),
|
|
84
100
|
};
|
|
85
101
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
102
|
+
// A function declaration, not a const: ENV_VARS above is built at module load and calls this.
|
|
103
|
+
export function chainName(chainId) {
|
|
104
|
+
return chainId === 5042002 ? "Arc Testnet" : chainId === 5042 ? "Arc" : `chain ${chainId}`;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* The Parabolic site that serves this chain's deployment, with no trailing slash.
|
|
108
|
+
*
|
|
109
|
+
* Derived from the deployment's API URL so a coin link always points at the site where that coin
|
|
110
|
+
* actually exists: a testnet coin on the testnet site, a mainnet coin on parabolic.family. Hardcoding
|
|
111
|
+
* the mainnet host sent every testnet link to a page that has never heard of the coin.
|
|
112
|
+
*/
|
|
113
|
+
export function siteBase(chainId) {
|
|
114
|
+
const api = DEPLOYMENTS[chainId]?.apiUrl;
|
|
115
|
+
if (!api)
|
|
116
|
+
return undefined;
|
|
117
|
+
const at = api.indexOf("/api/");
|
|
118
|
+
return at === -1 ? api.replace(/\/+$/, "") : api.slice(0, at);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Arcscan base URL for the chain, undefined where there is no explorer (Anvil).
|
|
122
|
+
*
|
|
123
|
+
* Arc mainnet's host is a guess: Circle has published an explorer for Arc Testnet only, and
|
|
124
|
+
* `https://explorer.arc.io` is Circle's published explorer (16 Sep 2026), behind a login at launch. `PARABOLIC_EXPLORER_URL` overrides it so a wrong guess
|
|
125
|
+
* costs an environment variable rather than a republish of this package. Read from `process.env`
|
|
126
|
+
* directly, unlike every other setting here, because this is called from `arcChain()` which builds a
|
|
127
|
+
* viem chain from a chain id alone and never sees a Config.
|
|
128
|
+
*/
|
|
129
|
+
export function explorerBase(chainId) {
|
|
130
|
+
const override = process.env.PARABOLIC_EXPLORER_URL?.trim();
|
|
131
|
+
if (override && /^https?:\/\//i.test(override))
|
|
132
|
+
return override.replace(/\/+$/, "");
|
|
133
|
+
return chainId === 5042002 ? "https://testnet.arcscan.app" : chainId === 5042 ? "https://explorer.arc.io" : undefined;
|
|
134
|
+
}
|
|
89
135
|
export const explorerUrl = (chainId, kind, value) => {
|
|
90
136
|
const base = explorerBase(chainId);
|
|
91
137
|
return base ? `${base}/${kind}/${value}` : undefined;
|
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,
|
|
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,eAAe,GAAG,qCAAqC,CAAC;AAErE;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,WAAW,GAenB;IACH,OAAO,EAAE;QACP,MAAM,EAAE,4BAA4B;QACpC,OAAO,EAAE,4CAA4C;QACrD,UAAU,EAAE,4CAA4C;QACxD,IAAI,EAAE,4CAA4C;QAClD,UAAU,EAAE,QAAQ;QACpB,MAAM,EAAE,6CAA6C;QACrD,aAAa,EAAE,4CAA4C;KAC5D;IACD,IAAI,EAAE;QACJ,MAAM,EAAE,4BAA4B;QACpC,OAAO,EAAE,4CAA4C;QACrD,UAAU,EAAE,4CAA4C;QACxD,UAAU,EAAE,QAAQ;QACpB,MAAM,EAAE,eAAe;QACvB,6FAA6F;KAC9F;CACF,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC;AAC3E,MAAM,CAAC,MAAM,eAAe,GAAG,WAAW,CAAC,gBAAgB,CAAC,CAAC,MAAM,CAAC;AAkCpE,sGAAsG;AACtG,MAAM,CAAC,MAAM,QAAQ,GAA2C;IAC9D,CAAC,mBAAmB,EAAE,kCAAkC,eAAe,GAAG,CAAC;IAC3E,CAAC,oBAAoB,EAAE,4DAA4D,gBAAgB,KAAK,SAAS,CAAC,gBAAgB,CAAC,GAAG,CAAC;IACvI,CAAC,mBAAmB,EAAE,0FAA0F,CAAC;IACjH,CAAC,uBAAuB,EAAE,mFAAmF,CAAC;IAC9G,CAAC,wBAAwB,EAAE,wEAAwE,CAAC;IACpG,CAAC,gBAAgB,EAAE,iDAAiD,CAAC;IACrE,CAAC,gBAAgB,EAAE,oEAAoE,CAAC;IACxF,CAAC,mBAAmB,EAAE,iGAAiG,WAAW,CAAC,gBAAgB,CAAC,CAAC,MAAM,OAAO,SAAS,CAAC,gBAAgB,CAAC,iCAAiC,CAAC;IAC/N,CAAC,oBAAoB,EAAE,qCAAqC,gBAAgB,GAAG,CAAC;IAChF,CAAC,0BAA0B,EAAE,2LAA2L,SAAS,CAAC,gBAAgB,CAAC,mBAAmB,WAAW,CAAC,gBAAgB,CAAC,CAAC,aAAa,IAAI,kDAAkD,EAAE,CAAC;IAC1W,CAAC,4BAA4B,EAAE,yCAAyC,CAAC;IACzE,CAAC,+BAA+B,EAAE,qGAAqG,CAAC;IACxI,CAAC,wBAAwB,EAAE,+EAA+E,YAAY,CAAC,gBAAgB,CAAC,IAAI,qBAAqB,sCAAsC,CAAC;CACzM,CAAC;AAEF,SAAS,IAAI,CAAC,IAAY,EAAE,CAAqB;IAC/C,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,SAAS,CAAC;IACzD,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IACnB,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,uBAAuB,CAAC,EAAE,CAAC,CAAC;IACzF,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC;AACvB,CAAC;AAED,SAAS,GAAG,CAAC,IAAY,EAAE,CAAqB,EAAE,QAAgB;IAChE,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,QAAQ,CAAC;IACxD,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAC3B,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,wCAAwC,CAAC,EAAE,CAAC,CAAC;IACvG,OAAO,CAAC,CAAC;AACX,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,MAA0C,OAAO,CAAC,GAAG;IACjF,4FAA4F;IAC5F,8FAA8F;IAC9F,oDAAoD;IACpD,MAAM,OAAO,GAAG,GAAG,CAAC,oBAAoB,EAAE,GAAG,CAAC,kBAAkB,EAAE,gBAAgB,CAAC,CAAC;IACpF,MAAM,KAAK,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IACnC,OAAO;QACL,MAAM,EAAE,GAAG,CAAC,iBAAiB,EAAE,IAAI,EAAE,IAAI,KAAK,EAAE,MAAM,IAAI,eAAe;QACzE,+FAA+F;QAC/F,aAAa,EAAE,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,IAAI,EAAE,CAAC,IAAI,GAAG,CAAC,iBAAkB,CAAC,IAAI,EAAE,KAAK,KAAK,EAAE,MAAM;QACxG,OAAO;QACP,OAAO,EAAE,IAAI,CAAC,mBAAmB,EAAE,GAAG,CAAC,iBAAiB,CAAC,IAAI,KAAK,EAAE,OAAO;QAC3E,UAAU,EAAE,IAAI,CAAC,uBAAuB,EAAE,GAAG,CAAC,qBAAqB,CAAC,IAAI,KAAK,EAAE,UAAU;QACzF,MAAM,EAAE,GAAG,CAAC,iBAAiB,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,EAAE,MAAM,IAAI,eAAe,CAAC,CAAC,CAAC,GAAG,CAAC,iBAAiB,CAAC,IAAI,EAAE,IAAI,SAAS;QAC1H,WAAW,EAAE,GAAG,CAAC,sBAAsB,EAAE,IAAI,EAAE,IAAI,SAAS;QAC5D,IAAI,EAAE,IAAI,CAAC,gBAAgB,EAAE,GAAG,CAAC,cAAc,CAAC,IAAI,KAAK,EAAE,IAAI;QAC/D,IAAI,EAAE,IAAI,CAAC,gBAAgB,EAAE,GAAG,CAAC,cAAc,CAAC;QAChD,OAAO,EAAE,IAAI,CAAC,oBAAoB,EAAE,GAAG,CAAC,kBAAkB,CAAC,IAAI,gBAAgB;QAC/E,aAAa,EAAE,IAAI,CAAC,0BAA0B,EAAE,GAAG,CAAC,wBAAwB,CAAC,IAAI,KAAK,EAAE,aAAa;QACrG,cAAc,EAAE,MAAM,CAAC,GAAG,CAAC,4BAA4B,EAAE,GAAG,CAAC,0BAA0B,EAAE,CAAC,CAAC,CAAC;QAC5F,iBAAiB,EAAE,MAAM,CAAC,GAAG,CAAC,+BAA+B,EAAE,GAAG,CAAC,6BAA6B,EAAE,KAAK,EAAE,UAAU,IAAI,CAAC,CAAC,CAAC;KAC3H,CAAC;AACJ,CAAC;AAED,8FAA8F;AAC9F,MAAM,UAAU,SAAS,CAAC,OAAe;IACvC,OAAO,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,OAAO,EAAE,CAAC;AAC7F,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,QAAQ,CAAC,OAAe;IACtC,MAAM,GAAG,GAAG,WAAW,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IACzC,IAAI,CAAC,GAAG;QAAE,OAAO,SAAS,CAAC;IAC3B,MAAM,EAAE,GAAG,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAChC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAChE,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,YAAY,CAAC,OAAe;IAC1C,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,sBAAsB,EAAE,IAAI,EAAE,CAAC;IAC5D,IAAI,QAAQ,IAAI,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC;QAAE,OAAO,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACpF,OAAO,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,6BAA6B,CAAC,CAAC,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,yBAAyB,CAAC,CAAC,CAAC,SAAS,CAAC;AACxH,CAAC;AAED,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,OAAe,EAAE,IAAsB,EAAE,KAAa,EAAsB,EAAE;IACxG,MAAM,IAAI,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;IACnC,OAAO,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,IAAI,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AACvD,CAAC,CAAC"}
|
package/dist/curve.d.ts
CHANGED
|
@@ -67,7 +67,7 @@ export declare const reservedTokensFor: (supply: bigint, phantomQuote: bigint, g
|
|
|
67
67
|
* Where the reserved tokens go at graduation. The v4 full-range position is seeded at the curve's terminal price
|
|
68
68
|
* ((phantom + threshold) / reserved) with the real quote raised, which takes reserved × threshold / (phantom + threshold)
|
|
69
69
|
* tokens; the rest, the phantom reserve's share, is permanently locked (GraduationTokensPermanentlyLocked).
|
|
70
|
-
* Adopted parameters:
|
|
70
|
+
* Adopted parameters: 38.46% reserved → ≈ 23.7% of supply seeds the pool, ≈ 14.8% is locked.
|
|
71
71
|
*/
|
|
72
72
|
export declare const graduationSplit: (reservedTokens: bigint, phantomQuote: bigint, graduationThreshold: bigint) => {
|
|
73
73
|
poolSeed: bigint;
|
package/dist/curve.js
CHANGED
|
@@ -80,7 +80,7 @@ export const reservedTokensFor = (supply, phantomQuote, graduationThreshold) =>
|
|
|
80
80
|
* Where the reserved tokens go at graduation. The v4 full-range position is seeded at the curve's terminal price
|
|
81
81
|
* ((phantom + threshold) / reserved) with the real quote raised, which takes reserved × threshold / (phantom + threshold)
|
|
82
82
|
* tokens; the rest, the phantom reserve's share, is permanently locked (GraduationTokensPermanentlyLocked).
|
|
83
|
-
* Adopted parameters:
|
|
83
|
+
* Adopted parameters: 38.46% reserved → ≈ 23.7% of supply seeds the pool, ≈ 14.8% is locked.
|
|
84
84
|
*/
|
|
85
85
|
export const graduationSplit = (reservedTokens, phantomQuote, graduationThreshold) => {
|
|
86
86
|
const denominator = phantomQuote + graduationThreshold;
|
package/dist/docs.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { type Config } from "./config.js";
|
|
2
|
-
/** Adopted product parameters (adopted 2026-09-06; published at https://www.parabolic.family/docs#parameters) and verified Arc facts, as text. */
|
|
2
|
+
/** Adopted product parameters (adopted 2026-09-06, graduation threshold revised to $8,000 on 2026-09-16; published at https://www.parabolic.family/docs#parameters) and verified Arc facts, as text. */
|
|
3
3
|
export declare function docsText(config: Config): string;
|