@cloakely/mcp 0.6.0 → 0.7.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/dist/server.d.ts +1 -1
- package/dist/server.js +70 -18
- package/package.json +2 -2
package/dist/server.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { type Prover } from "@cloakely/sdk";
|
|
|
3
3
|
import type { Settings } from "./config.js";
|
|
4
4
|
export { loadSettings, type Settings, type Limits } from "./config.js";
|
|
5
5
|
export { Spending } from "./limits.js";
|
|
6
|
-
export declare const VERSION = "0.
|
|
6
|
+
export declare const VERSION = "0.7.1";
|
|
7
7
|
export declare const HOSTED_URL = "https://cloak.boblabs.tech/mcp";
|
|
8
8
|
/** Turn an SDK or chain error into one plain sentence an agent can act on. */
|
|
9
9
|
export declare function explain(e: unknown): string;
|
package/dist/server.js
CHANGED
|
@@ -4,11 +4,11 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { createPublicClient, createWalletClient, erc20Abi, formatEther, formatUnits, http, isAddress, parseEther, parseUnits, zeroAddress, } from "viem";
|
|
6
6
|
import { privateKeyToAccount } from "viem/accounts";
|
|
7
|
-
import { Cloak, CloakPrivate, cloakAuctionAbi, cloakPairAbi, cloakVaultsAbi, legacyFactories, createProver, loadProver, privateRouterAbi, relayerSubmitter, shieldedPoolAbi, } from "@cloakely/sdk";
|
|
7
|
+
import { Cloak, CloakPrivate, cloakAuctionAbi, cloakPairAbi, cloakVaultsAbi, legacyFactories, createProver, loadProver, createSharedProver, loadSharedProver, privateRouterAbi, relayerSubmitter, sharedRelayerSubmitter, sharedVaultAbi, SharedVaultClient, shieldedPoolAbi, } from "@cloakely/sdk";
|
|
8
8
|
import { Spending } from "./limits.js";
|
|
9
9
|
export { loadSettings } from "./config.js";
|
|
10
10
|
export { Spending } from "./limits.js";
|
|
11
|
-
export const VERSION = "0.
|
|
11
|
+
export const VERSION = "0.7.1";
|
|
12
12
|
export const HOSTED_URL = "https://cloak.boblabs.tech/mcp";
|
|
13
13
|
const INSTRUCTIONS = `Cloak: sealed-bid token launches and private trading on Kinetiq's Elysium (HYPE is the gas and base currency).
|
|
14
14
|
|
|
@@ -94,6 +94,26 @@ export function createCloakServer(settings, opts = {}) {
|
|
|
94
94
|
(() => (proverP ??= settings.circuitsDir
|
|
95
95
|
? Promise.resolve(createProver(readFileSync(join(settings.circuitsDir, "transaction2x2.wasm")), readFileSync(join(settings.circuitsDir, "transaction2x2_final.zkey"))))
|
|
96
96
|
: loadProver(d.circuitsUrl)));
|
|
97
|
+
let sharedProverP;
|
|
98
|
+
const sharedProver = () => (sharedProverP ??= settings.circuitsDir
|
|
99
|
+
? Promise.resolve(createSharedProver(readFileSync(join(settings.circuitsDir, "transactionMA2x2.wasm")), readFileSync(join(settings.circuitsDir, "transactionMA2x2_final.zkey"))))
|
|
100
|
+
: loadSharedProver(d.circuitsUrl));
|
|
101
|
+
let sharedC;
|
|
102
|
+
/** The shared launch-token vault (new Uniswap launches keep their private balances there). */
|
|
103
|
+
async function shared() {
|
|
104
|
+
if (!d.shared || !d.sharedRouter)
|
|
105
|
+
throw new Error("This deployment has no shared launch-token vault.");
|
|
106
|
+
const p = await priv();
|
|
107
|
+
if (!sharedC || sharedC.priv !== p)
|
|
108
|
+
sharedC = new SharedVaultClient(p, d.shared, d.sharedRouter, sharedProver, sharedRelayerSubmitter(d.relayerUrl), d.sharedBlock ?? 0n, d.sharedBatcher);
|
|
109
|
+
return sharedC;
|
|
110
|
+
}
|
|
111
|
+
/** Private balance of one asset, wherever its vault is. */
|
|
112
|
+
async function balanceOf(a) {
|
|
113
|
+
if (a.kind === "token" && a.shared)
|
|
114
|
+
return (await shared()).balance(a.token);
|
|
115
|
+
return (await priv()).balance(a.pool);
|
|
116
|
+
}
|
|
97
117
|
let privP;
|
|
98
118
|
async function priv() {
|
|
99
119
|
if (!wallet)
|
|
@@ -157,6 +177,18 @@ export function createCloakServer(settings, opts = {}) {
|
|
|
157
177
|
else
|
|
158
178
|
out.push({ kind: "token", symbol, token, pool, pair, decimals: 18 });
|
|
159
179
|
}
|
|
180
|
+
// tokens in the shared launch-token vault (every Uniswap launch from Nov 2 on)
|
|
181
|
+
if (d.shared) {
|
|
182
|
+
const v = { address: d.shared, abi: sharedVaultAbi };
|
|
183
|
+
const m = Number(await pub.readContract({ ...v, functionName: "tokensCount" }));
|
|
184
|
+
for (let i = 0; i < m; i++) {
|
|
185
|
+
const token = await pub.readContract({ ...v, functionName: "tokens", args: [BigInt(i)] });
|
|
186
|
+
if (out.some((a) => a.token.toLowerCase() === token.toLowerCase()))
|
|
187
|
+
continue;
|
|
188
|
+
const [pair, symbol] = await Promise.all([pub.readContract({ ...v, functionName: "marketOf", args: [token] }), symbolOf(token)]);
|
|
189
|
+
out.push({ kind: "token", symbol, token, pool: d.shared, pair, decimals: 18, shared: true });
|
|
190
|
+
}
|
|
191
|
+
}
|
|
160
192
|
return out;
|
|
161
193
|
}
|
|
162
194
|
/** "HYPE", "USDC", a launch token's symbol, or any of their addresses. */
|
|
@@ -185,7 +217,9 @@ export function createCloakServer(settings, opts = {}) {
|
|
|
185
217
|
pub.readContract({ address: t.pair, abi: cloakPairAbi, functionName: "reserveToken" }),
|
|
186
218
|
]);
|
|
187
219
|
const out = await pub.readContract({ address: t.pair, abi: cloakPairAbi, functionName: "getAmountOut", args: side === "buy" ? [net, rH, rT] : [net, rT, rH] });
|
|
188
|
-
const fee =
|
|
220
|
+
const fee = t.shared
|
|
221
|
+
? await (side === "buy" ? sharedRelayerSubmitter(d.relayerUrl).quote("sharedBuy", t.token) : sharedRelayerSubmitter(d.relayerUrl).quote("sharedSell", t.token)).then((q) => q.fee, () => 0n)
|
|
222
|
+
: await submit.quote("privateSwap", poolIn).then((q) => q.fee, () => 0n);
|
|
189
223
|
return { out, net, fee };
|
|
190
224
|
}
|
|
191
225
|
/** What an amount of an asset counts against: HYPE value (launch tokens at the pool's price), or USDC. */
|
|
@@ -575,7 +609,7 @@ export function createCloakServer(settings, opts = {}) {
|
|
|
575
609
|
const out = {};
|
|
576
610
|
const tokens = {};
|
|
577
611
|
for (const a of await vaults()) {
|
|
578
|
-
const b = await
|
|
612
|
+
const b = await balanceOf(a);
|
|
579
613
|
if (b === 0n && !include_zero && a.kind === "token")
|
|
580
614
|
continue;
|
|
581
615
|
if (a.kind === "token")
|
|
@@ -587,13 +621,13 @@ export function createCloakServer(settings, opts = {}) {
|
|
|
587
621
|
}));
|
|
588
622
|
server.registerTool("shield", {
|
|
589
623
|
title: "Shield into a private balance",
|
|
590
|
-
description: "Deposit HYPE or
|
|
591
|
-
inputSchema: { asset: z.string().describe('"HYPE"
|
|
624
|
+
description: "Deposit HYPE, USDC or a new launch token (one in the shared launch-token vault) from the wallet into the private balance. This is the one public step: it lands on a one-time key, so it can't be linked to what comes next.",
|
|
625
|
+
inputSchema: { asset: z.string().describe('"HYPE", "USDC" or a launch token in the shared vault'), amount: amt("Amount"), dry_run: dry },
|
|
592
626
|
annotations: WRITE,
|
|
593
627
|
}, safe(async ({ asset, amount, dry_run }) => {
|
|
594
628
|
const { wallet, spending } = needWallet();
|
|
595
629
|
const a = await resolve(asset);
|
|
596
|
-
if (a.kind === "token")
|
|
630
|
+
if (a.kind === "token" && !a.shared)
|
|
597
631
|
throw new Error("Launch tokens enter a private balance by buying privately (private_swap), not by shielding.");
|
|
598
632
|
const x = amountOf(amount, a.decimals);
|
|
599
633
|
if (x === null)
|
|
@@ -604,9 +638,9 @@ export function createCloakServer(settings, opts = {}) {
|
|
|
604
638
|
return { dryRun: true, wouldShield: fmtAmt(a, x), ...(a.kind === "asset" ? { note: "The first time takes two transactions: an approval, then the deposit." } : {}) };
|
|
605
639
|
return exclusive(async () => {
|
|
606
640
|
const p = await priv();
|
|
607
|
-
const h = a.kind === "hype" ? await p.shieldHype(wallet, x) : await p.shieldToken(wallet, a.token, x);
|
|
641
|
+
const h = a.kind === "hype" ? await p.shieldHype(wallet, x) : a.kind === "token" ? await (await shared()).shield(wallet, a.token, x) : await p.shieldToken(wallet, a.token, x);
|
|
608
642
|
spending.record(c.budget, c.value);
|
|
609
|
-
return { shielded: fmtAmt(a, x), privateBalance: fmtAmt(a, await
|
|
643
|
+
return { shielded: fmtAmt(a, x), privateBalance: fmtAmt(a, await balanceOf(a)), tx: link(h) };
|
|
610
644
|
});
|
|
611
645
|
}));
|
|
612
646
|
server.registerTool("private_swap", {
|
|
@@ -621,7 +655,7 @@ export function createCloakServer(settings, opts = {}) {
|
|
|
621
655
|
throw new Error(`${t.symbol} isn't a launch token: private swaps trade launch tokens against HYPE.`);
|
|
622
656
|
const p = await priv();
|
|
623
657
|
const poolIn = side === "buy" ? d.hypePool : t.pool;
|
|
624
|
-
const x = amountOf(amount, 18) ?? (await p.maxSpendable(poolIn, "privateSwap"));
|
|
658
|
+
const x = amountOf(amount, 18) ?? (t.shared && side === "sell" ? await (await shared()).maxWithdraw(t.token, "sharedSell") : await p.maxSpendable(d.hypePool === poolIn ? d.hypePool : poolIn, "privateSwap"));
|
|
625
659
|
if (x <= 0n)
|
|
626
660
|
throw new Error("Not enough private balance for this, counting the relayer's fee.");
|
|
627
661
|
const c = await cost(side === "buy" ? (await vaults())[0] : t, x);
|
|
@@ -632,6 +666,14 @@ export function createCloakServer(settings, opts = {}) {
|
|
|
632
666
|
if (isDry(dry_run))
|
|
633
667
|
return { dryRun: true, wouldSwap: `${formatEther(x)} ${inSym} for about ${formatEther(q.out)} ${outSym}`, relayerFee: `${formatEther(q.fee)} ${inSym}`, slippage: `${(slippage_bps ?? 100) / 100}%` };
|
|
634
668
|
return exclusive(async () => {
|
|
669
|
+
if (t.shared) {
|
|
670
|
+
const sh = await shared();
|
|
671
|
+
const h = side === "buy" ? await sh.buy(t.token, x, { slippageBps: slippage_bps ?? 100 }) : await sh.sell(t.token, x, { slippageBps: slippage_bps ?? 100 });
|
|
672
|
+
await receipt(h);
|
|
673
|
+
spending.record(c.budget, c.value);
|
|
674
|
+
const hypeA = (await vaults())[0];
|
|
675
|
+
return { swapped: `${formatEther(x)} ${inSym}`, expected: `${formatEther(q.out)} ${outSym}`, relayerFee: `${formatEther(q.fee)} HYPE or ${t.symbol}`, privateBalance: { HYPE: formatEther(await balanceOf(hypeA)), [t.symbol]: formatEther(await balanceOf(t)) }, tx: link(h), sentBy: "the relayer, not this wallet" };
|
|
676
|
+
}
|
|
635
677
|
const r = await (side === "buy" ? p.buy(t.token, x, { slippageBps: slippage_bps }) : p.sell(t.token, x, { slippageBps: slippage_bps }));
|
|
636
678
|
await receipt(r.txHash);
|
|
637
679
|
spending.record(c.budget, c.value);
|
|
@@ -655,11 +697,13 @@ export function createCloakServer(settings, opts = {}) {
|
|
|
655
697
|
const c = await cost(side === "buy" ? (await vaults())[0] : t, x);
|
|
656
698
|
spending.check(c.budget, c.value);
|
|
657
699
|
const p = await priv();
|
|
658
|
-
|
|
700
|
+
if (t.shared && !d.sharedBatcher)
|
|
701
|
+
throw new Error(`${t.symbol} is in the shared launch-token vault, which has no batch rounds on this deployment. Use private_swap.`);
|
|
702
|
+
const round = t.shared ? await (await shared()).roundInfo() : await p.roundInfo();
|
|
659
703
|
if (isDry(dry_run))
|
|
660
704
|
return { dryRun: true, wouldPlace: `${formatEther(x)} ${side === "buy" ? "HYPE" : t.symbol} as standard-size ${side} orders`, roundEndsAt: iso(round.endsAt) };
|
|
661
705
|
return exclusive(async () => {
|
|
662
|
-
const r = await p.batch(t.token, side, x);
|
|
706
|
+
const r = t.shared ? await (await shared()).batch(t.token, side, x) : await p.batch(t.token, side, x);
|
|
663
707
|
const placed = r.orders.reduce((a, o) => a + o.size, 0n);
|
|
664
708
|
const pc = await cost(side === "buy" ? (await vaults())[0] : t, placed);
|
|
665
709
|
spending.record(pc.budget, pc.value);
|
|
@@ -672,7 +716,7 @@ export function createCloakServer(settings, opts = {}) {
|
|
|
672
716
|
}));
|
|
673
717
|
server.registerTool("private_send", {
|
|
674
718
|
title: "Send privately",
|
|
675
|
-
description: "Pay someone's cloak: address from the private balance. The chain sees that a private transaction happened, but not who sent it, who got it or how much.",
|
|
719
|
+
description: "Pay someone's cloak: address from the private balance. The chain sees that a private transaction happened, but not who sent it, who got it or how much. For tokens in the shared launch-token vault it can't even tell which token (the relayer is paid from private HYPE, so keep a little private HYPE).",
|
|
676
720
|
inputSchema: { asset: z.string().describe('"HYPE", "USDC" or a launch token'), to: z.string().describe("Recipient cloak: address"), amount: amt('Amount, or "max"'), dry_run: dry },
|
|
677
721
|
annotations: WRITE,
|
|
678
722
|
}, safe(async ({ asset, to, amount, dry_run }) => {
|
|
@@ -681,14 +725,21 @@ export function createCloakServer(settings, opts = {}) {
|
|
|
681
725
|
throw new Error("The recipient must be a cloak: address (the receiver gets it from unlock or the Portfolio page).");
|
|
682
726
|
const a = await resolve(asset);
|
|
683
727
|
const p = await priv();
|
|
684
|
-
const
|
|
728
|
+
const sharedTok = a.kind === "token" && a.shared;
|
|
729
|
+
const x = amountOf(amount, a.decimals) ?? (sharedTok ? await balanceOf(a) : await p.maxSpendable(a.pool, "transact"));
|
|
685
730
|
if (x <= 0n)
|
|
686
731
|
throw new Error("Not enough private balance for this, counting the relayer's fee.");
|
|
687
732
|
const c = await cost(a, x);
|
|
688
733
|
spending.check(c.budget, c.value);
|
|
689
734
|
if (isDry(dry_run))
|
|
690
|
-
return { dryRun: true, wouldSend: fmtAmt(a, x), to };
|
|
735
|
+
return { dryRun: true, wouldSend: fmtAmt(a, x), to, ...(sharedTok ? { relayerFee: "paid from your private HYPE, so the send doesn't show which token moved" } : {}) };
|
|
691
736
|
return exclusive(async () => {
|
|
737
|
+
if (sharedTok) {
|
|
738
|
+
const h = await (await shared()).send(a.token, x, to);
|
|
739
|
+
await receipt(h);
|
|
740
|
+
spending.record(c.budget, c.value);
|
|
741
|
+
return { sent: fmtAmt(a, x), to, privateBalance: fmtAmt(a, await balanceOf(a)), tx: link(h), visibleOnChain: "a private transaction happened; no sender, recipient, amount, or even which token" };
|
|
742
|
+
}
|
|
692
743
|
const h = await p.send(a.kind === "hype" ? p.hypeMarket.token : a.token, to, x);
|
|
693
744
|
await receipt(h);
|
|
694
745
|
spending.record(c.budget, c.value);
|
|
@@ -707,7 +758,8 @@ export function createCloakServer(settings, opts = {}) {
|
|
|
707
758
|
const a = await resolve(asset);
|
|
708
759
|
const p = await priv();
|
|
709
760
|
const kind = a.kind === "hype" ? "unshieldHype" : "transact";
|
|
710
|
-
const
|
|
761
|
+
const sharedTok = a.kind === "token" && a.shared;
|
|
762
|
+
const x = amountOf(amount, a.decimals) ?? (sharedTok ? await (await shared()).maxWithdraw(a.token) : await p.maxSpendable(a.pool, kind));
|
|
711
763
|
if (x <= 0n)
|
|
712
764
|
throw new Error("Not enough private balance for this, counting the relayer's fee.");
|
|
713
765
|
const c = await cost(a, x);
|
|
@@ -716,10 +768,10 @@ export function createCloakServer(settings, opts = {}) {
|
|
|
716
768
|
if (isDry(dry_run))
|
|
717
769
|
return { dryRun: true, wouldWithdraw: fmtAmt(a, x), arrives: fmtAmt(a, arrives), to };
|
|
718
770
|
return exclusive(async () => {
|
|
719
|
-
const h = a.kind === "hype" ? await p.unshieldHype(to, x) : await p.unshieldToken(a.token, to, x);
|
|
771
|
+
const h = a.kind === "hype" ? await p.unshieldHype(to, x) : sharedTok ? await (await shared()).unshield(a.token, x, to) : await p.unshieldToken(a.token, to, x);
|
|
720
772
|
await receipt(h);
|
|
721
773
|
spending.record(c.budget, c.value);
|
|
722
|
-
return { withdrew: fmtAmt(a, x), arrived: fmtAmt(a, arrives), to, privateBalance: fmtAmt(a, await
|
|
774
|
+
return { withdrew: fmtAmt(a, x), arrived: fmtAmt(a, arrives), to, privateBalance: fmtAmt(a, await balanceOf(a)), tx: link(h) };
|
|
723
775
|
});
|
|
724
776
|
}));
|
|
725
777
|
return server;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloakely/mcp",
|
|
3
3
|
"mcpName": "io.github.NECOKIZZ/cloak",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.7.1",
|
|
5
5
|
"description": "Cloak's MCP server: let an AI agent launch tokens with sealed bids and trade them privately on Elysium, with spending limits, testnet by default and a local signer.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"mcp",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"@modelcontextprotocol/sdk": "1.32.1",
|
|
48
48
|
"viem": "2.21.54",
|
|
49
49
|
"zod": "4.6.7",
|
|
50
|
-
"@cloakely/sdk": "^0.
|
|
50
|
+
"@cloakely/sdk": "^0.7.1"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@types/node": "22.10.2",
|