@arcnow/mcp 0.1.1 → 0.2.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/README.md +209 -241
- package/dist/config.d.ts +13 -4
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +27 -16
- package/dist/config.js.map +1 -1
- package/dist/format.d.ts +31 -2
- package/dist/format.d.ts.map +1 -1
- package/dist/format.js +88 -1
- package/dist/format.js.map +1 -1
- package/dist/sdk-port.d.ts +23 -6
- package/dist/sdk-port.d.ts.map +1 -1
- package/dist/sdk-port.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +35 -4
- package/dist/server.js.map +1 -1
- package/dist/tools/errors.d.ts.map +1 -1
- package/dist/tools/errors.js +8 -7
- package/dist/tools/errors.js.map +1 -1
- package/dist/tools/pool.d.ts +24 -18
- package/dist/tools/pool.d.ts.map +1 -1
- package/dist/tools/pool.js +63 -50
- package/dist/tools/pool.js.map +1 -1
- package/dist/tools/read.d.ts +6 -2
- package/dist/tools/read.d.ts.map +1 -1
- package/dist/tools/read.js +110 -96
- package/dist/tools/read.js.map +1 -1
- package/dist/tools/venue.d.ts +3 -3
- package/dist/tools/venue.d.ts.map +1 -1
- package/dist/tools/venue.js +8 -7
- package/dist/tools/venue.js.map +1 -1
- package/dist/tools/write.d.ts.map +1 -1
- package/dist/tools/write.js +85 -63
- package/dist/tools/write.js.map +1 -1
- package/package.json +2 -2
package/dist/tools/read.js
CHANGED
|
@@ -13,16 +13,18 @@
|
|
|
13
13
|
*
|
|
14
14
|
* @module
|
|
15
15
|
*/
|
|
16
|
-
import { Bps, isArcNowError, QuoteAmount, TRADE_FEE_BPS, Tokens, } from "@arcnow/sdk";
|
|
17
|
-
import { addr, curveParamsLine, effectivePrice, money, note, price, priceMove, progress, qty, quoteLine, report, section, share, venueOf, ZERO_ADDRESS, } from "../format.js";
|
|
16
|
+
import { Bps, isArcNowError, POOL_LP_FEE_PIPS, POOL_TOTAL_FEE_BPS, POOL_TRADE_FEE_BPS, QuoteAmount, TRADE_FEE_BPS, Tokens, } from "@arcnow/sdk";
|
|
17
|
+
import { addr, bpsPercent, curveFeeSplitSection, curveParamsLine, curveSplitRows, effectivePrice, lpFee, money, note, poolFeeSplitSection, poolFeesLine, price, priceMove, progress, qty, quoteLine, report, section, share, venueOf, ZERO_ADDRESS, } from "../format.js";
|
|
18
18
|
import { renderError } from "./errors.js";
|
|
19
19
|
import { addressArg, boolArg, decimalArg, defineTool, intArg, metadataUriArg, textArg, z } from "./schema.js";
|
|
20
20
|
import { launchParams } from "./launch-params.js";
|
|
21
|
-
import { buyPaymentLine,
|
|
21
|
+
import { buyPaymentLine, quotePoolBuy, quotePoolSell } from "./pool.js";
|
|
22
22
|
import { chooseQuote, parseQuoteAmount } from "./quote.js";
|
|
23
23
|
import { capText } from "./spend.js";
|
|
24
24
|
import { configuredRouter, resolveMarket, strandedText } from "./venue.js";
|
|
25
25
|
const TRADE_FEE = Bps.of(TRADE_FEE_BPS);
|
|
26
|
+
/** The shipped pool rates, for prose about the network — a pool's own are read from it. */
|
|
27
|
+
const POOL_HOOK_FEE = Bps.of(POOL_TRADE_FEE_BPS);
|
|
26
28
|
/** The tolerances a quote is shown at, so a floor is a choice and not a guess. */
|
|
27
29
|
const TOLERANCES = [50n, 100n, 300n, 1000n];
|
|
28
30
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -97,8 +99,15 @@ const network = defineTool({
|
|
|
97
99
|
: `${addr(config.usdcErc20)}, 6 decimals — the SAME asset through an ERC-20 `
|
|
98
100
|
+ "interface. It does not pay for gas and no arcnow.io contract touches it. "
|
|
99
101
|
+ "The two raw integers differ by 1e12."],
|
|
100
|
-
["trade fee", `${TRADE_FEE.bps} bps — a flat
|
|
101
|
-
+ "quote, not a platform's to change. What a platform
|
|
102
|
+
["trade fee, on a curve", `${TRADE_FEE.bps} bps — a flat ${bpsPercent(TRADE_FEE)} on every `
|
|
103
|
+
+ "buy and sell, in the token's quote, not a platform's to change. What a platform "
|
|
104
|
+
+ "configures is how it is divided, FOUR ways: creator, referrer, platform, protocol. "
|
|
105
|
+
+ "There is no developer share."],
|
|
106
|
+
["trade fee, in a pool", `the same ${bpsPercent(Bps.of(POOL_TOTAL_FEE_BPS))} `
|
|
107
|
+
+ `in all, as two charges: arcnow.io's fee hook takes ${bpsPercent(POOL_HOOK_FEE)} `
|
|
108
|
+
+ `(${POOL_HOOK_FEE.bps} bps) of the trade in the pool's quote, split creator / platform / `
|
|
109
|
+
+ `protocol with no referrer, and the pool keeps ${lpFee(POOL_LP_FEE_PIPS)} as its LP fee. `
|
|
110
|
+
+ "Every pool quote reads both rates off that pool rather than assuming these."],
|
|
102
111
|
]), section("quote tokens — what a token can be priced in, for life", [
|
|
103
112
|
...config.quoteTokens.map((token) => [
|
|
104
113
|
token.symbol,
|
|
@@ -177,8 +186,8 @@ const listTokens = defineTool({
|
|
|
177
186
|
["platform", addr(launch.platform)],
|
|
178
187
|
["graduates to", `${addr(launch.migrator)} — ${venueOf(launch.migrator, ctx.port.config)}`],
|
|
179
188
|
["quote", quoteLine(launch.quoteToken)],
|
|
180
|
-
["at launch", `${money(launch.initialBuy)} initial buy, ${
|
|
181
|
-
+
|
|
189
|
+
["at launch", `${money(launch.initialBuy)} initial buy, ${launchFeeLine(launch.launchFee)}, `
|
|
190
|
+
+ `${qty(launch.tokensOut)} tokens to the creator`],
|
|
182
191
|
["block", `${launch.blockNumber} (tx ${launch.txHash})`],
|
|
183
192
|
];
|
|
184
193
|
if (args.includeState) {
|
|
@@ -219,8 +228,8 @@ const tokenTool = defineTool({
|
|
|
219
228
|
+ "TRADES NOW: its bonding curve, its Uniswap v4 pool, or nowhere.\n\n"
|
|
220
229
|
+ "For a token in its pool this reports the router this server would trade through, "
|
|
221
230
|
+ "whether that router can actually reach the pool, the PoolManager the liquidity is in "
|
|
222
|
-
+ "(a v4 pool has no address of its own), and
|
|
223
|
-
+ "
|
|
231
|
+
+ "(a v4 pool has no address of its own), and what a trade there costs — arcnow.io's fee "
|
|
232
|
+
+ "hook's 0.80% plus the pool's own 0.20% LP fee, 1.00% in all, both read off the pool.\n\n"
|
|
224
233
|
+ "It names the token's QUOTE TOKEN — native USDC or an ERC-20 such as EURC, fixed at "
|
|
225
234
|
+ "launch — with its address and decimals: every amount here, and every amount the trade "
|
|
226
235
|
+ "tools take for this token, is in that quote.\n\n"
|
|
@@ -341,9 +350,11 @@ async function whereItTrades(ctx, market) {
|
|
|
341
350
|
settle(pool.quoteToken()),
|
|
342
351
|
settle(pool.quoteIsCurrency0()),
|
|
343
352
|
]);
|
|
344
|
-
// The SDK checks the hook's VERSION() before it reads the accrual
|
|
345
|
-
// any hook but arcnow/arc-now-fee-hook@
|
|
353
|
+
// The SDK checks the hook's VERSION() before it reads the accrual or the
|
|
354
|
+
// rates, and refuses any hook but arcnow/arc-now-fee-hook@4.x.x as
|
|
355
|
+
// UnknownHookVersion.
|
|
346
356
|
const accrued = await settle(pool.accruedHookFee());
|
|
357
|
+
const fees = await settle(pool.fees());
|
|
347
358
|
return section("where it trades", [
|
|
348
359
|
["venue", "its Uniswap v4 pool — graduated and migrated. The quote and trade tools route "
|
|
349
360
|
+ "there on their own, and every pool quote says it is one."],
|
|
@@ -370,12 +381,17 @@ async function whereItTrades(ctx, market) {
|
|
|
370
381
|
+ `currency${quoteIsCurrency0 ? 1 : 0}, as the SDK reads the key. The key orders its `
|
|
371
382
|
+ "currencies by address, so an ERC-20 quote can be either; every amount here is already "
|
|
372
383
|
+ `in ${quote.symbol}`],
|
|
373
|
-
["
|
|
384
|
+
["fees", fees instanceof Error
|
|
385
|
+
? isArcNowError(fees) && fees.code === "UnknownHookVersion"
|
|
386
|
+
? `refused: UnknownHookVersion — ${fees.message}`
|
|
387
|
+
: `could not be read: ${fees.message}`
|
|
388
|
+
: poolFeesLine(fees, market.state.quoteToken)],
|
|
389
|
+
["fee hook", key instanceof Error
|
|
374
390
|
? `could not be read: ${key.message}`
|
|
375
|
-
: `${
|
|
376
|
-
+ `
|
|
377
|
-
|
|
378
|
-
|
|
391
|
+
: `${addr(key.hooks)} — takes its ${fees instanceof Error ? "share" : bpsPercent(fees.hookFeeBps)} `
|
|
392
|
+
+ `in ${market.state.quoteToken.symbol}, accrues it as a PoolManager claim and pays it out `
|
|
393
|
+
+ "to the creator, the platform and the protocol at the start of a later swap in this "
|
|
394
|
+
+ "pool, or when anyone calls distributeFees. A pool swap has no referrer share."],
|
|
379
395
|
["hook fees accrued", accrued instanceof Error
|
|
380
396
|
? isArcNowError(accrued) && accrued.code === "UnknownHookVersion"
|
|
381
397
|
? `refused: UnknownHookVersion — ${accrued.message}`
|
|
@@ -387,7 +403,7 @@ async function whereItTrades(ctx, market) {
|
|
|
387
403
|
+ "part of a trader's fill"],
|
|
388
404
|
["selling", "needs an ERC-20 approval to the router first, a separate transaction. "
|
|
389
405
|
+ "arcnow_sell grants one only when asked, for exactly the amount sold."],
|
|
390
|
-
]);
|
|
406
|
+
]) + (fees instanceof Error ? "" : `\n\n${poolFeeSplitSection(fees)}`);
|
|
391
407
|
}
|
|
392
408
|
async function settle(promise) {
|
|
393
409
|
try {
|
|
@@ -403,9 +419,9 @@ const quoteBuy = defineTool({
|
|
|
403
419
|
title: "Quote a buy",
|
|
404
420
|
access: "read",
|
|
405
421
|
description: "What a given amount of the token's quote would buy right now, wherever the token trades: "
|
|
406
|
-
+ "tokens out, "
|
|
407
|
-
+ "
|
|
408
|
-
+ "minimum-out floor at several slippage tolerances.\n\n"
|
|
422
|
+
+ "tokens out, every fee and who receives it — on a curve the flat 1% split four ways "
|
|
423
|
+
+ "between creator, platform, referrer and protocol — the average price the order would "
|
|
424
|
+
+ "fill at, and the minimum-out floor at several slippage tolerances.\n\n"
|
|
409
425
|
+ "Costs nothing and signs nothing. Use it before every buy, and use it INSTEAD of a spot "
|
|
410
426
|
+ "price whenever someone asks what an amount would get them — an order moves the price "
|
|
411
427
|
+ "across its own size, so the spot price is only ever its first infinitesimal slice.\n\n"
|
|
@@ -414,10 +430,10 @@ const quoteBuy = defineTool({
|
|
|
414
430
|
+ "that same transaction or never. Read that part before quoting a graduating buy to "
|
|
415
431
|
+ "anyone.\n\n"
|
|
416
432
|
+ "IN A UNISWAP V4 POOL (the token graduated and migrated): the quote says it is a pool "
|
|
417
|
-
+ "quote, priced by simulating the real swap through arcnow.io's router. It shows "
|
|
418
|
-
+ "arcnow.io's
|
|
419
|
-
+ "price against the pool's spot price, and
|
|
420
|
-
+ "not exist on a pool swap and
|
|
433
|
+
+ "quote, priced by simulating the real swap through arcnow.io's router. It shows the two "
|
|
434
|
+
+ "charges apart — arcnow.io's fee hook's 0.80% and the pool's own 0.20% LP fee, 1.00% in "
|
|
435
|
+
+ "all, both read off the pool — the average fill price against the pool's spot price, and "
|
|
436
|
+
+ "the price impact. referrer does not exist on a pool swap and is refused.\n\n"
|
|
421
437
|
+ "A token that graduated but never migrated trades nowhere; this says so and names "
|
|
422
438
|
+ "arcnow_migrate. A quote is a snapshot of one block. Anyone else's trade changes it.\n\n"
|
|
423
439
|
+ "THE AMOUNT IS IN THE TOKEN'S QUOTE: native USDC, or the ERC-20 (such as EURC) it was "
|
|
@@ -432,9 +448,8 @@ const quoteBuy = defineTool({
|
|
|
432
448
|
+ "decimals."),
|
|
433
449
|
referrer: addressArg("CURVE ONLY: the address credited with the referral share of the fee. With no referrer "
|
|
434
450
|
+ "that share goes to the platform instead. Refused for a token in its pool, whose swap "
|
|
435
|
-
+ "has no referrer."
|
|
436
|
-
|
|
437
|
-
+ "goes to the platform. Refused for a token in its pool.").optional(),
|
|
451
|
+
+ "has no referrer. (There is no developer share and no developer argument: the fee has "
|
|
452
|
+
+ "four parties — creator, platform, referrer, protocol.)").optional(),
|
|
438
453
|
},
|
|
439
454
|
async run(args, ctx) {
|
|
440
455
|
const market = await resolveMarket(ctx, args.address);
|
|
@@ -447,14 +462,14 @@ const quoteBuy = defineTool({
|
|
|
447
462
|
return quotePoolBuy(args, ctx, market, symbol);
|
|
448
463
|
const quoteIn = parseQuoteAmount(state.quoteToken, args.quoteIn, "quoteIn");
|
|
449
464
|
const quote = await curve.quoteBuy(quoteIn);
|
|
450
|
-
const split = await curve.previewFeeSplit(quote.fee, args.referrer
|
|
465
|
+
const split = await curve.previewFeeSplit(quote.fee, args.referrer);
|
|
451
466
|
const avg = effectivePrice(quote.quoteSpent, quote.tokensOut);
|
|
452
467
|
return {
|
|
453
468
|
text: report(`Buy quote — ${money(quoteIn)} into ${symbol} (curve ${addr(curve.address)})`, section("what you pay and get", [
|
|
454
469
|
["venue", "its bonding curve"],
|
|
455
470
|
["you send", buyPaymentLine(quoteIn, `the curve ${addr(curve.address)}`)],
|
|
456
|
-
["trade fee", `${money(quote.fee)} — a flat
|
|
457
|
-
+ "reaches the reserve"],
|
|
471
|
+
["trade fee", `${money(quote.fee)} — a flat ${bpsPercent(state.tradeFeeBps)} of the input, `
|
|
472
|
+
+ "taken before anything reaches the reserve and split four ways (below)"],
|
|
458
473
|
["reaches the curve", money(quote.quoteSpent)],
|
|
459
474
|
["tokens out", qty(quote.tokensOut, symbol)],
|
|
460
475
|
["refund", quote.refund.isZero()
|
|
@@ -471,7 +486,7 @@ const quoteBuy = defineTool({
|
|
|
471
486
|
+ `of ${money(state.target)}`],
|
|
472
487
|
["tokens sold", `${qty(state.tokensSold)} → ${qty(quote.newTokensSold)}`],
|
|
473
488
|
["graduates", quote.graduates ? "YES — this is the buy that ends trading" : "no"],
|
|
474
|
-
]),
|
|
489
|
+
]), curveFeeSplitSection(quote.fee, split, args.referrer), section("minimum tokens out, by tolerance", TOLERANCES.map((bps) => {
|
|
475
490
|
const floor = Bps.of(10000n - bps).applyToTokens(quote.tokensOut);
|
|
476
491
|
return [`${bps} bps (${Number(bps) / 100}%)`, qty(floor, symbol)];
|
|
477
492
|
})), quote.graduates ? note(GRADUATING_BUY_WARNING) : undefined, note("This quote is one block old the moment it is returned. Any other trade against "
|
|
@@ -485,8 +500,10 @@ const quoteSell = defineTool({
|
|
|
485
500
|
title: "Quote a sell",
|
|
486
501
|
access: "read",
|
|
487
502
|
description: "What a given quantity of tokens would fetch right now, wherever the token trades: "
|
|
488
|
-
+ "gross proceeds, every fee and who receives it
|
|
489
|
-
+ "
|
|
503
|
+
+ "gross proceeds, every fee and who receives it — on a curve the flat 1% split four ways "
|
|
504
|
+
+ "between creator, platform, referrer and protocol — net proceeds in the token's quote "
|
|
505
|
+
+ "(native USDC or an ERC-20 such as EURC), and the minimum-out floor at several "
|
|
506
|
+
+ "tolerances.\n\n"
|
|
490
507
|
+ "Costs nothing and signs nothing.\n\n"
|
|
491
508
|
+ "ON A BONDING CURVE: selling needs NO approval, ever. The curve pulls the tokens "
|
|
492
509
|
+ "through a privileged path that reads no allowance at all, so a holder who has "
|
|
@@ -495,11 +512,12 @@ const quoteSell = defineTool({
|
|
|
495
512
|
+ "IN A UNISWAP V4 POOL (the token graduated and migrated) it is the other way round, and "
|
|
496
513
|
+ "worth telling a user before they sell: the router pulls the tokens with transferFrom, "
|
|
497
514
|
+ "so a pool sell needs an ERC-20 APPROVAL to the router first — a separate transaction. "
|
|
498
|
-
+ "This quote says it is a pool quote, shows arcnow.io's
|
|
499
|
-
+ "
|
|
500
|
-
+ "how much `holder` has approved the router for
|
|
501
|
-
+ "real holder, so `holder` must actually hold the
|
|
502
|
-
+ "defaults to the signing address. referrer
|
|
515
|
+
+ "This quote says it is a pool quote, shows arcnow.io's fee hook's 0.80% and the pool's "
|
|
516
|
+
+ "own 0.20% LP fee apart (1.00% in all, read off the pool), the average fill against the "
|
|
517
|
+
+ "pool's spot price, the price impact, and how much `holder` has approved the router for "
|
|
518
|
+
+ "already. A pool sell is priced as a real holder, so `holder` must actually hold the "
|
|
519
|
+
+ "tokens; on a writing server it defaults to the signing address. referrer is refused on "
|
|
520
|
+
+ "a pool.\n\n"
|
|
503
521
|
+ "A token that graduated but never migrated trades nowhere; this says so and names "
|
|
504
522
|
+ "arcnow_migrate.",
|
|
505
523
|
input: {
|
|
@@ -510,9 +528,7 @@ const quoteSell = defineTool({
|
|
|
510
528
|
+ "signing address on a writing server; required on a read-only one. Not needed on a "
|
|
511
529
|
+ "bonding curve, where a sell is priced for nobody in particular.").optional(),
|
|
512
530
|
referrer: addressArg("CURVE ONLY: credited with the referral share of the fee. Refused for a token in its "
|
|
513
|
-
+ "pool.").optional(),
|
|
514
|
-
developer: addressArg("CURVE ONLY: credited with the developer share of the fee. Refused for a token in its "
|
|
515
|
-
+ "pool.").optional(),
|
|
531
|
+
+ "pool. There is no developer argument: the fee has four parties.").optional(),
|
|
516
532
|
},
|
|
517
533
|
async run(args, ctx) {
|
|
518
534
|
const market = await resolveMarket(ctx, args.address);
|
|
@@ -525,14 +541,15 @@ const quoteSell = defineTool({
|
|
|
525
541
|
return quotePoolSell(args, ctx, market, symbol);
|
|
526
542
|
const tokensIn = Tokens.parse(args.tokensIn);
|
|
527
543
|
const quote = await curve.quoteSell(tokensIn);
|
|
528
|
-
const split = await curve.previewFeeSplit(quote.fee, args.referrer
|
|
544
|
+
const split = await curve.previewFeeSplit(quote.fee, args.referrer);
|
|
529
545
|
const avg = effectivePrice(quote.gross, tokensIn);
|
|
530
546
|
return {
|
|
531
547
|
text: report(`Sell quote — ${qty(tokensIn, symbol)} into curve ${addr(curve.address)}`, section("what you give and get", [
|
|
532
548
|
["venue", "its bonding curve"],
|
|
533
549
|
["you send", `${qty(tokensIn, symbol)} — no approval needed, and none will be asked for`],
|
|
534
550
|
["gross", money(quote.gross)],
|
|
535
|
-
["trade fee", `${money(quote.fee)} — the same flat
|
|
551
|
+
["trade fee", `${money(quote.fee)} — the same flat ${bpsPercent(state.tradeFeeBps)}, taken `
|
|
552
|
+
+ "from the proceeds and split four ways (below)"],
|
|
536
553
|
["you receive", money(quote.quoteOut.floorToRepresentable())],
|
|
537
554
|
["average fill price", avg === undefined ? "n/a" : price(avg)],
|
|
538
555
|
]), section("the curve after this sell", [
|
|
@@ -540,7 +557,7 @@ const quoteSell = defineTool({
|
|
|
540
557
|
+ `(${priceMove(state.spotPrice, quote.newPrice)})`],
|
|
541
558
|
["real reserve", `${money(state.realReserve)} → ${money(quote.newReserve)}`],
|
|
542
559
|
["tokens sold", `${qty(state.tokensSold)} → ${qty(quote.newTokensSold)}`],
|
|
543
|
-
]),
|
|
560
|
+
]), curveFeeSplitSection(quote.fee, split, args.referrer), section(`minimum ${state.quoteToken.symbol} out, by tolerance`, TOLERANCES.map((bps) => {
|
|
544
561
|
const floor = Bps.of(10000n - bps).applyToQuote(quote.quoteOut);
|
|
545
562
|
return [`${bps} bps (${Number(bps) / 100}%)`, money(floor)];
|
|
546
563
|
})), args.holder === undefined
|
|
@@ -556,17 +573,18 @@ const quoteLaunch = defineTool({
|
|
|
556
573
|
name: "arcnow_quote_launch",
|
|
557
574
|
title: "Quote a launch",
|
|
558
575
|
access: "read",
|
|
559
|
-
description: "What launching a token would cost, before anything is spent: the
|
|
560
|
-
+ "
|
|
561
|
-
+ "
|
|
562
|
-
+ "
|
|
576
|
+
description: "What launching a token would cost, before anything is spent: the launch fee the quote "
|
|
577
|
+
+ "registry sets for the quote (zero on arcnow.io's networks — launching is free — but read "
|
|
578
|
+
+ "live, never assumed), the initial buy, the 1% trade fee the initial buy itself pays, the "
|
|
579
|
+
+ "tokens the creator would receive, and — given a creator address — the exact addresses "
|
|
580
|
+
+ "the token and its curve would land at.\n\n"
|
|
563
581
|
+ "Costs nothing and signs nothing. Always call this before arcnow_launch and show the "
|
|
564
582
|
+ "user the total, because a launch is irreversible: the name, symbol, supply, curve "
|
|
565
583
|
+ "shape and graduation venue are fixed at that transaction and can never be changed "
|
|
566
584
|
+ "afterwards, by anyone, including whoever launched it.\n\n"
|
|
567
585
|
+ "THE QUOTE. A token is priced for life in the quote it launches in: `quote` names it by "
|
|
568
586
|
+ "symbol or address — native USDC by default, or an ERC-20 such as EURC that the quote "
|
|
569
|
-
+ "registry accepts (arcnow_quote_tokens). The
|
|
587
|
+
+ "registry accepts (arcnow_quote_tokens). The initial buy — and any launch fee — is in that "
|
|
570
588
|
+ "quote, and so is every trade in the token afterwards. Native USDC is paid as msg.value; an "
|
|
571
589
|
+ "ERC-20 is pulled by the launchpad after an exact approval.\n\n"
|
|
572
590
|
+ "The total is EXACTLY what a launch requires, not a minimum — for native USDC the launchpad "
|
|
@@ -578,8 +596,9 @@ const quoteLaunch = defineTool({
|
|
|
578
596
|
metadataUri: metadataUriArg(),
|
|
579
597
|
initialBuy: decimalArg("How much of the launch's quote the creator spends buying their own token in the launch "
|
|
580
598
|
+ "transaction, in whole units, with no more decimals than the quote has. It is an "
|
|
581
|
-
+ "ORDINARY buy: it runs through the curve and pays the 1% trade fee
|
|
582
|
-
+ "
|
|
599
|
+
+ "ORDINARY buy: it runs through the curve and pays the 1% trade fee. There is no fee-free "
|
|
600
|
+
+ "entry into a curve, and no launch fee on top of it on arcnow.io's networks. \"0\" is "
|
|
601
|
+
+ "allowed."),
|
|
583
602
|
quote: quoteArg(),
|
|
584
603
|
creator: addressArg("Optional: the address that would launch. Given one, this also predicts the token and "
|
|
585
604
|
+ "curve addresses — valid only for that address's CURRENT launch nonce and exactly "
|
|
@@ -614,10 +633,11 @@ const quoteLaunch = defineTool({
|
|
|
614
633
|
? `${money(quote.totalCost)} — this is the exact value the launch transaction must `
|
|
615
634
|
+ "carry. Overpaying reverts; there is no refund path."
|
|
616
635
|
: `${money(quote.totalCost)} — exactly what the launchpad pulls, no more`],
|
|
617
|
-
[" launch fee", `${
|
|
618
|
-
+
|
|
636
|
+
[" launch fee", `${launchFeeLine(quote.launchFee)} — as the quote registry sets it for `
|
|
637
|
+
+ `${quoteToken.symbol} now`],
|
|
619
638
|
[" initial buy", money(quote.initialBuy)],
|
|
620
|
-
[" of which fee", `${money(quote.tradeFee)} — the initial buy's own 1
|
|
639
|
+
[" of which fee", `${money(quote.tradeFee)} — the initial buy's own 1%, split four ways `
|
|
640
|
+
+ "like every curve trade's"],
|
|
621
641
|
["paid as", quoteToken.isNative
|
|
622
642
|
? `msg.value: exactly ${money(quote.nativeValue)}`
|
|
623
643
|
: `an ERC-20 pull by the launchpad — the transaction carries no value. An exact approve `
|
|
@@ -668,8 +688,8 @@ const quoteLaunch = defineTool({
|
|
|
668
688
|
/** The launch tools' `quote` argument. */
|
|
669
689
|
export function quoteArg() {
|
|
670
690
|
return z.string().min(1).max(64).optional().describe("Optional: the quote token to launch in, by symbol (\"USDC\", \"EURC\") or by address. "
|
|
671
|
-
+ "Defaults to native USDC. The token is priced in it FOR LIFE: its
|
|
672
|
-
+ "
|
|
691
|
+
+ "Defaults to native USDC. The token is priced in it FOR LIFE: its initial buy and every "
|
|
692
|
+
+ "trade in it afterwards. A symbol is looked up in this network's own quote "
|
|
673
693
|
+ "tokens only. arcnow_quote_tokens lists them with whether the quote registry accepts each "
|
|
674
694
|
+ "and this server's spend cap for it; a quote with no cap cannot be launched in here.");
|
|
675
695
|
}
|
|
@@ -678,15 +698,16 @@ const platform = defineTool({
|
|
|
678
698
|
title: "Inspect a platform's fees and defaults",
|
|
679
699
|
access: "read",
|
|
680
700
|
description: "One platform's configuration: who administers it, who receives its cut, how the 1% "
|
|
681
|
-
+ "trade fee is divided
|
|
682
|
-
+ "
|
|
683
|
-
+ "it stamps onto every token it launches.\n\n"
|
|
701
|
+
+ "trade fee is divided FOUR ways — creator, referrer, the platform and the protocol; "
|
|
702
|
+
+ "there is no developer share — which migrator it launches tokens into by default, and "
|
|
703
|
+
+ "the curve template it stamps onto every token it launches.\n\n"
|
|
684
704
|
+ "With no address, reports arcnow.io's own platform — the default a launch uses.\n\n"
|
|
685
705
|
+ "The single easiest thing to misread here is what a share means. Every share is basis "
|
|
686
706
|
+ "points OF THE FEE, never of the trade. A creator share of 3000 bps is 30% of the fee "
|
|
687
707
|
+ "and 0.30% of the trade. This tool prints both, every time. The platform's own share "
|
|
688
708
|
+ "is never configured: it is the residual, whatever is left after the protocol, "
|
|
689
|
-
+ "creator
|
|
709
|
+
+ "creator and referrer shares — arcnow.io's own is creator 3000 / referrer 1000 / "
|
|
710
|
+
+ "platform 3500 / protocol 2500.",
|
|
690
711
|
input: {
|
|
691
712
|
address: addressArg("Optional: the PlatformConfig address. Defaults to arcnow.io's own platform.").optional(),
|
|
692
713
|
},
|
|
@@ -715,16 +736,13 @@ const platform = defineTool({
|
|
|
715
736
|
["admin", addr(settings.admin)],
|
|
716
737
|
["fee recipient", addr(settings.feeRecipient)],
|
|
717
738
|
["certified", "yes — this registry deployed this PlatformConfig itself"],
|
|
718
|
-
]), section("how the 1% trade fee is divided", [
|
|
719
|
-
|
|
720
|
-
["
|
|
721
|
-
["
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
+ "
|
|
725
|
-
+ "demand."],
|
|
726
|
-
["", "A share whose address is zero at swap time — no referrer, no developer — is "
|
|
727
|
-
+ "paid to the platform instead, as is the rounding dust."],
|
|
739
|
+
]), section("how the 1% trade fee is divided — four ways, on a curve", [
|
|
740
|
+
...curveSplitRows(fees, TRADE_FEE),
|
|
741
|
+
["developer", "none — there is no developer share; the fee has these four parties"],
|
|
742
|
+
["", "A share whose address is zero at swap time — no referrer named — is paid to "
|
|
743
|
+
+ "the platform instead, as is the rounding dust. In a graduated token's pool the "
|
|
744
|
+
+ `hook applies its own split of its ${bpsPercent(POOL_HOOK_FEE)}, not this one: `
|
|
745
|
+
+ "arcnow_token shows it."],
|
|
728
746
|
]), section("defaults it stamps on a launch", [
|
|
729
747
|
["migrator", `${addr(settings.defaultMigrator)} — `
|
|
730
748
|
+ `${venueOf(settings.defaultMigrator, ctx.port.config)}. Snapshotted into each `
|
|
@@ -741,8 +759,8 @@ const platform = defineTool({
|
|
|
741
759
|
]), section("the protocol, above every platform", [
|
|
742
760
|
["share", share(protocol.protocolShareBps, TRADE_FEE)],
|
|
743
761
|
["recipient", addr(protocol.protocolRecipient)],
|
|
744
|
-
["launch fee", `${
|
|
745
|
-
+ "
|
|
762
|
+
["launch fee", `${launchFeeLine(protocol.launchFee)} — per launch in native USDC, as the `
|
|
763
|
+
+ "quote registry sets it. Each quote token has its own: arcnow_quote_tokens"],
|
|
746
764
|
])),
|
|
747
765
|
};
|
|
748
766
|
},
|
|
@@ -780,9 +798,9 @@ const listPlatforms = defineTool({
|
|
|
780
798
|
const settings = await ctx.port.platforms.settings(address);
|
|
781
799
|
blocks.push(section(`#${i} ${addr(address)}${isDefault ? " (arcnow.io's own)" : ""}`, [
|
|
782
800
|
["fee recipient", addr(settings.feeRecipient)],
|
|
783
|
-
["creator / ref /
|
|
784
|
-
+ `${settings.refShareBps.bps} / ${settings.
|
|
785
|
-
+
|
|
801
|
+
["creator / ref / platform", `${settings.creatorShareBps.bps} / `
|
|
802
|
+
+ `${settings.refShareBps.bps} / ${settings.platformShareBps.bps} bps of the fee, `
|
|
803
|
+
+ "the protocol's share above all of them; no developer share"],
|
|
786
804
|
["default venue", `${addr(settings.defaultMigrator)} — `
|
|
787
805
|
+ venueOf(settings.defaultMigrator, ctx.port.config)],
|
|
788
806
|
["graduation target", "per quote token — arcnow_platform shows it"],
|
|
@@ -808,8 +826,9 @@ const quoteTokens = defineTool({
|
|
|
808
826
|
+ "is priced in for life: native USDC (the gas currency, 18 decimals, paid as msg.value) and "
|
|
809
827
|
+ "the ERC-20s arcnow.io's quote registry allowlists, such as EURC (6 decimals, pulled with "
|
|
810
828
|
+ "an exact ERC-20 approval). For each: symbol, name, decimals, address, whether it is "
|
|
811
|
-
+ "native, its
|
|
812
|
-
+ "
|
|
829
|
+
+ "native, its launch fee in its own units (zero on arcnow.io's networks — launching is "
|
|
830
|
+
+ "free — but read from the registry, never assumed), whether the registry accepts it for "
|
|
831
|
+
+ "new launches now, and this server's spend cap for it.\n\n"
|
|
813
832
|
+ "SPEND CAPS ARE PER QUOTE, AND FAIL CLOSED. The operator caps native USDC with "
|
|
814
833
|
+ "ARCNOW_MCP_MAX_SPEND_USDC (default 100) and every other quote with "
|
|
815
834
|
+ "ARCNOW_MCP_MAX_SPEND_<SYMBOL>, in that quote's own units (ARCNOW_MCP_MAX_SPEND_EURC=50). "
|
|
@@ -817,8 +836,7 @@ const quoteTokens = defineTool({
|
|
|
817
836
|
+ "list can have no cap at all. Tell the user which quotes they can actually spend here "
|
|
818
837
|
+ "before offering a launch or a buy in one.\n\n"
|
|
819
838
|
+ "Reads the registry in at most three eth_calls. If the chain has no quote registry to ask "
|
|
820
|
-
+ "—
|
|
821
|
-
+ "shows the error, "
|
|
839
|
+
+ "— a deployment older than quote registries has none — this says so, shows the error, "
|
|
822
840
|
+ "and lists this network's own quote-token metadata instead, with NOTHING known to be "
|
|
823
841
|
+ "accepted. Takes no arguments and spends nothing.",
|
|
824
842
|
input: {},
|
|
@@ -832,9 +850,8 @@ const quoteTokens = defineTool({
|
|
|
832
850
|
text: report("The quote registry could not be read, so which quote tokens a launch accepts is NOT "
|
|
833
851
|
+ "known. Below is this network's own quote-token metadata: what a token could be "
|
|
834
852
|
+ "priced in, not what the launchpad takes.", renderError("arcnow_quote_tokens", error), note("Do not tell a user a quote is accepted on the strength of this list. A launch in "
|
|
835
|
-
+ "a quote the registry does not accept reverts QuoteTokenNotSupported;
|
|
836
|
-
+ "
|
|
837
|
-
+ "accept only native USDC."), ...ctx.port.config.quoteTokens.map((token) => section(`${token.symbol} — ${token.name}`, [
|
|
853
|
+
+ "a quote the registry does not accept reverts QuoteTokenNotSupported; a deployment "
|
|
854
|
+
+ "with no quote registry at all accepts only native USDC."), ...ctx.port.config.quoteTokens.map((token) => section(`${token.symbol} — ${token.name}`, [
|
|
838
855
|
...quoteTokenRows(token),
|
|
839
856
|
["launch fee", "unknown — the registry could not be read"],
|
|
840
857
|
["accepted", "unknown — the registry could not be read"],
|
|
@@ -845,7 +862,8 @@ const quoteTokens = defineTool({
|
|
|
845
862
|
return {
|
|
846
863
|
text: report(`${entries.length} quote token(s) registered with this launchpad's quote registry.`, ...entries.map(({ token, launchFee, active }) => section(`${token.symbol} — ${token.name}`, [
|
|
847
864
|
...quoteTokenRows(token),
|
|
848
|
-
["launch fee", `${
|
|
865
|
+
["launch fee", `${launchFeeLine(launchFee)} — per launch, in ${token.symbol}, as the `
|
|
866
|
+
+ "registry sets it"],
|
|
849
867
|
["active", active
|
|
850
868
|
? "yes — a launch may use it"
|
|
851
869
|
: "NO — deregistered: no new launch may use it. Curves already priced in it keep "
|
|
@@ -889,19 +907,15 @@ export function statusLine(graduated, migrated) {
|
|
|
889
907
|
return "GRADUATED but NOT MIGRATED — trading is over and the pool was never created, so it "
|
|
890
908
|
+ "trades nowhere. migrate() is permissionless; anyone can finish it.";
|
|
891
909
|
}
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
: `${money(split.devAmount)} → ${addr(split.dev)}`],
|
|
902
|
-
["protocol", `${money(split.protocolAmount)} → ${addr(split.protocol)}`],
|
|
903
|
-
];
|
|
904
|
-
return section(`fee split — where the ${money(fee)} goes`, rows);
|
|
910
|
+
/**
|
|
911
|
+
* A launch fee, as the registry reports it. Zero on every quote arcnow.io
|
|
912
|
+
* registers — launching is free — and said so; anything else is printed as the
|
|
913
|
+
* amount it is, because the figure is read, not assumed.
|
|
914
|
+
*/
|
|
915
|
+
export function launchFeeLine(fee) {
|
|
916
|
+
return fee.isZero()
|
|
917
|
+
? `${money(fee)} launch fee — launching is free`
|
|
918
|
+
: `${money(fee)} launch fee`;
|
|
905
919
|
}
|
|
906
920
|
function describe(error) {
|
|
907
921
|
return error instanceof Error ? error.message : String(error);
|