@parabolicfamily/mcp 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/parabolic.ts CHANGED
@@ -9,7 +9,7 @@ import { DEPLOYMENTS, NATIVE_QUOTE, chainName, explorerUrl, siteBase, type Confi
9
9
  import { deployedAt } from "./deployed.js";
10
10
  import { applySlippage, graduationMarketCap, graduationSplit, marketCap, passesPriceBound, poolIdOf, priceImpact, quoteBuy, quoteSell, reservedTokensFor, spotPrice, toNumber, type CurveTerms } from "./curve.js";
11
11
  import { docsText } from "./docs.js";
12
- import { addrOf, amount, big, bool, endpointHost, num, parseAddress, parseAmount, safeMessage, str } from "./format.js";
12
+ import { addrOf, amount, big, bool, endpointHost, num, parseAddress, parseAmount, safeMessage, shortAddr, str } from "./format.js";
13
13
  import { quoteBlock, quoteUnitOf, defaultQuoteOf, type QuoteBlock, type QuoteUnit } from "./quote-unit.js";
14
14
  import { RestApi, type PairFilter } from "./restapi.js";
15
15
  import { RoutedTrades } from "./routed.js";
@@ -300,7 +300,7 @@ export class Parabolic {
300
300
  const unit = quoteUnitOf(gas.symbol, chainId);
301
301
  return { pairToken: NATIVE_QUOTE, symbol: gas.symbol, decimals: gas.decimals, unit, isNative: true, block: quoteBlock(unit, NATIVE_QUOTE, gas.decimals, true) };
302
302
  }
303
- const short = `${pairToken.slice(0, 6)}…${pairToken.slice(-4)}`;
303
+ const short = shortAddr(pairToken);
304
304
  const raw = await this.read(pairToken, tokenAbi, "decimals").catch(() => undefined);
305
305
  const decimals = num(raw);
306
306
  if (raw === undefined || !Number.isInteger(decimals) || decimals < 0 || decimals > 36) {
@@ -584,7 +584,7 @@ export class Parabolic {
584
584
  private async chainDetail(l: Launch) {
585
585
  const [tk, c, hook, quote] = await Promise.all([
586
586
  this.readMany(l.token, tokenAbi, ["name", "symbol", "totalSupply", "getTokenInfo"]),
587
- this.readMany(l.curve, curveAbi, ["getReserves", "realQuoteReserve", "graduated", "readyToGraduate", "launchedAt", "currentSnipeTaxBps", "snipeTaxStartBps", "snipeTaxSeconds", "feeBps", "creatorTaxBps", "phantomQuote", "reservedTokens", "sellableTokens", "protocolFeeShareBps", "buybackBurnBps", "isNativeQuote"]),
587
+ this.readMany(l.curve, curveAbi, ["getReserves", "realQuoteReserve", "graduated", "readyToGraduate", "launchedAt", "currentSnipeTaxBps", "snipeTaxStartBps", "snipeTaxSeconds", "feeBps", "creatorTaxBps", "phantomQuote", "reservedTokens", "sellableTokens", "protocolFeeShareBps", "buybackBurnBps", "buybackEnabled", "isNativeQuote"]),
588
588
  this.hookAddress(),
589
589
  this.quoteAsset(l.pairToken),
590
590
  ]);
@@ -609,7 +609,12 @@ export class Parabolic {
609
609
  const currentSnipeBps = num(c.currentSnipeTaxBps);
610
610
  const protocolShareBps = num(c.protocolFeeShareBps);
611
611
  const buybackBurnBps = num(c.buybackBurnBps);
612
- const buybackOfFeeBps = Math.round(((10_000 - protocolShareBps) * buybackBurnBps) / 10_000);
612
+ // The creator's switch, read off the curve (MONEY-5): a pre-17-Sep coin whose creator called
613
+ // `setBuybackEnabled(false)` earmarks no buyback slice, so its whole creator bucket is the creator's. The site
614
+ // and /api/v1 pass the same switch to `feeSplit` (web/lib/curve.ts); without it this tool showed 50/30/20 for a
615
+ // coin the site shows as 70/30.
616
+ const buybackEnabled = bool(c.buybackEnabled);
617
+ const buybackOfFeeBps = buybackEnabled ? Math.round(((10_000 - protocolShareBps) * buybackBurnBps) / 10_000) : 0;
613
618
  const poolId = l.poolFee !== undefined && l.tickSpacing !== undefined && hook ? poolIdOf(l.token, l.pairToken, l.poolFee, l.tickSpacing, hook) : undefined;
614
619
  const chainId = this.config.chainId;
615
620
  return {
@@ -644,7 +649,7 @@ export class Parabolic {
644
649
  feeBps: num(c.feeBps),
645
650
  creatorTaxBps: num(c.creatorTaxBps),
646
651
  split: { protocolBps: protocolShareBps, buybackBps: buybackOfFeeBps, creatorBps: 10_000 - protocolShareBps - buybackOfFeeBps, note: "shares of the base fee (and of snipe-tax revenue); the creator tax goes entirely to the creator" },
647
- policy: { protocolFeeShareBps: protocolShareBps, buybackBurnBps },
652
+ policy: { protocolFeeShareBps: protocolShareBps, buybackBurnBps, buybackEnabled },
648
653
  },
649
654
  snipeTax: {
650
655
  currentBps: currentSnipeBps,
package/src/routed.ts CHANGED
@@ -28,7 +28,7 @@ import { VENUES, VENUE_KEYS, isVenueKey, venueOn, type VenueChain, type VenueInf
28
28
  * encodes, argument for argument (test/routed.test.ts holds it against digests produced by the site's encoder, two
29
29
  * of which Foundry's `cast calldata` produced first). Two differences, both deliberate:
30
30
  *
31
- * - **No voucher.** A Pro or Hyper voucher is a site feature signed for one wallet by the site's backend; this server
31
+ * - **No voucher.** A Pro or Ultra voucher is a site feature signed for one wallet by the site's backend; this server
32
32
  * sends the router's own "none" (a zeroed struct and an empty signature) and so pays the router's default fee,
33
33
  * which it reads at every call rather than assuming.
34
34
  * - **A plain allowance instead of a Permit2 signature** for an ERC-20 input (a USDG-quoted buy, every sell). The
@@ -434,7 +434,7 @@ export class RoutedTrades {
434
434
  minOut: { ...amount(minOut, outDecimals), slippageBps: Number(slippage) },
435
435
  ...(q.gross !== undefined ? { grossFromVenue: amount(q.gross, qd) } : {}),
436
436
  fees: {
437
- parabolic: { bps: p.feeBps, ...amount(q.fee, qd), note: `Parabolic's routed fee: the router's defaultFeeBps, read now (the Free tier's; a Pro or Hyper voucher lowers it on the site, and this server sends none). Charged on the ${p.side === "buy" ? "input" : "venue's payout"}, on top of ${p.venue.name}'s own fee.` },
437
+ parabolic: { bps: p.feeBps, ...amount(q.fee, qd), note: `Parabolic's routed fee: the router's defaultFeeBps, read now (the Free tier's; a Pro or Ultra voucher lowers it on the site, and this server sends none). Charged on the ${p.side === "buy" ? "input" : "venue's payout"}, on top of ${p.venue.name}'s own fee.` },
438
438
  venue: { name: p.venue.name, ...(q.venueFee !== undefined ? amount(q.venueFee, qd) : { amount: null, raw: null }), note: p.via === "curve" ? `${p.venue.name}'s own curve fee and creator tax on this fill, read off the curve` : `${p.venue.name}'s hook takes its own fee on the pool swap (about ${p.venue.ownFeeBps} bps), already inside the quoted output` },
439
439
  snipeTax: { bps: q.snipeBps, note: q.snipeBps > 0 ? `${p.venue.name} charges a snipe tax on this buy right now, keyed on the RECIPIENT; it decays every second` : "none now" },
440
440
  },
package/src/server.ts CHANGED
@@ -116,6 +116,12 @@ export function createServer(opts: ServerOptions = {}): McpServer {
116
116
  // Tool arguments are validated strictly: an argument the tool does not know (a misspelling, or
117
117
  // `referral` at the top level instead of inside `memo`) is an error, not something silently dropped
118
118
  // and quietly changing what the tool does.
119
+ /**
120
+ * The creator-tax ceiling both factories answer today (`maxCreatorTaxBps()` 500 on 5042 and 4663, read 6 Oct 2026;
121
+ * CON-23). The schema refuses above it so an agent learns the bound before it builds a launch that reverts; the
122
+ * launch builder still reads the factory's own figure and refuses above that, which is what holds if an owner lowers it.
123
+ */
124
+ const MAX_CREATOR_TAX_BPS = 500;
119
125
  const strictArgs = <T extends z.ZodRawShape>(shape: T) => z.object(shape).strict();
120
126
 
121
127
  const readOnly = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true } as const;
@@ -227,7 +233,7 @@ const readOnly = { readOnlyHint: true, destructiveHint: false, idempotentHint: t
227
233
  socials: z.object({ twitter: z.string().max(256).optional(), telegram: z.string().max(256).optional(), discord: z.string().max(256).optional(), website: z.string().max(256).optional(), farcaster: z.string().max(256).optional() }).optional(),
228
234
  pair: z.enum(["USDC", "EURC"]).optional().describe("Which quote asset to launch against: \"USDC\" means the chain's gas token (native-quoted, which is USDC on Arc and is NOT a dollar on a chain paying gas in ETH), \"EURC\" the configured ERC-20. The answer's `quoteAsset` names what was actually resolved, read off the asset itself."),
229
235
  devBuy: whole.optional().describe("Creator's own first buy, whole quote units; returned as a second, snipe-tax-exempt transaction"),
230
- creatorTaxBps: z.number().int().min(0).max(1000).optional().describe("Creator tax in basis points, 0–500 (5%) on the adopted parameters"),
236
+ creatorTaxBps: z.number().int().min(0).max(MAX_CREATOR_TAX_BPS).optional().describe(`Creator tax in basis points, 0 to ${MAX_CREATOR_TAX_BPS} (5%): both factories' maxCreatorTaxBps() read ${MAX_CREATOR_TAX_BPS}, and a higher figure reverts on chain`),
231
237
  creator: z.string().describe("Wallet that signs the launch; becomes deployer and creatorFeeRecipient"),
232
238
  buybackEnabled: z.boolean().optional().describe("Default true"),
233
239
  memo: z.object({ referral: z.string().optional().describe("Referrer's 16-hex referral code or wallet address"), note: z.string().max(280).optional().describe("Note kept on the coin; defaults to \"mcp\", which lists the coin on parabolic.family/agents") }).nullable().optional().describe("The memo-wrapped variant's referral and note (returned as `memo` beside `tx`); null leaves it out"),