dexe-mcp 0.3.0 → 0.4.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.
@@ -0,0 +1,174 @@
1
+ import { z } from "zod";
2
+ import { isAddress, getAddress } from "ethers";
3
+ import { buildAddressMerkleTree, buildMerkleTree, computeLeafHash, verifyProof, } from "../lib/merkleTree.js";
4
+ /**
5
+ * Merkle utility tools — produce roots and proofs compatible with
6
+ * OpenZeppelin's `StandardMerkleTree` and the on-chain verifier in
7
+ * `TokenSaleProposalBuy.sol::_checkMerkleProofs`.
8
+ *
9
+ * Default leaf encoding is `["address"]` (the only shape used by the
10
+ * frontend's OTC token-sale flow). Set `leafEncoding` + `entries` for
11
+ * advanced trees (e.g. `["address","uint256"]`).
12
+ */
13
+ function errorResult(message) {
14
+ return { content: [{ type: "text", text: message }], isError: true };
15
+ }
16
+ function jsonResult(value) {
17
+ return {
18
+ content: [{ type: "text", text: JSON.stringify(value, null, 2) }],
19
+ structuredContent: value,
20
+ };
21
+ }
22
+ function normaliseAddresses(addresses) {
23
+ const seen = new Set();
24
+ const result = [];
25
+ for (const raw of addresses) {
26
+ if (!isAddress(raw))
27
+ throw new Error(`Invalid address: ${raw}`);
28
+ const checksummed = getAddress(raw);
29
+ if (seen.has(checksummed.toLowerCase()))
30
+ continue;
31
+ seen.add(checksummed.toLowerCase());
32
+ result.push(checksummed);
33
+ }
34
+ return result;
35
+ }
36
+ export function registerMerkleTools(server, _ctx) {
37
+ server.registerTool("dexe_merkle_build", {
38
+ title: "Build a merkle tree (OZ StandardMerkleTree compatible)",
39
+ description: "Builds a merkle tree compatible with OpenZeppelin StandardMerkleTree (used by DeXe's TokenSaleProposal merkle whitelists). Default leaf shape is a single address; pass `leafEncoding` + `entries` for richer leaves (e.g. address + amount). Returns root, leaf hashes, and per-input-index proofs.",
40
+ inputSchema: {
41
+ addresses: z
42
+ .array(z.string())
43
+ .min(1)
44
+ .optional()
45
+ .describe("Convenience: addresses for an `address`-only merkle whitelist. Mutually exclusive with `entries`."),
46
+ entries: z
47
+ .array(z.array(z.union([z.string(), z.number()])))
48
+ .min(1)
49
+ .optional()
50
+ .describe("Advanced: per-leaf raw values matching `leafEncoding` order. Mutually exclusive with `addresses`."),
51
+ leafEncoding: z
52
+ .array(z.string())
53
+ .min(1)
54
+ .default(["address"])
55
+ .describe("ABI types for each leaf column. Default `['address']` matches the OTC frontend."),
56
+ },
57
+ outputSchema: {
58
+ root: z.string(),
59
+ leafHashes: z.array(z.string()),
60
+ proofs: z.array(z.array(z.string())),
61
+ addresses: z.array(z.string()).optional(),
62
+ },
63
+ }, async ({ addresses, entries, leafEncoding = ["address"] }) => {
64
+ try {
65
+ if (!addresses && !entries) {
66
+ return errorResult("Must provide either `addresses` or `entries`.");
67
+ }
68
+ if (addresses && entries) {
69
+ return errorResult("Provide either `addresses` or `entries`, not both.");
70
+ }
71
+ if (addresses) {
72
+ const normalised = normaliseAddresses(addresses);
73
+ const result = buildAddressMerkleTree(normalised);
74
+ return jsonResult({
75
+ root: result.root,
76
+ leafHashes: result.leafHashes,
77
+ proofs: result.proofs,
78
+ addresses: normalised,
79
+ });
80
+ }
81
+ const result = buildMerkleTree(entries, leafEncoding);
82
+ return jsonResult({
83
+ root: result.root,
84
+ leafHashes: result.leafHashes,
85
+ proofs: result.proofs,
86
+ });
87
+ }
88
+ catch (err) {
89
+ return errorResult(err instanceof Error ? err.message : String(err));
90
+ }
91
+ });
92
+ server.registerTool("dexe_merkle_proof", {
93
+ title: "Compute a merkle proof for one address (or leaf)",
94
+ description: "Builds the same tree as `dexe_merkle_build` and returns the proof for a single target. Useful for buyer-side flows where the full whitelist is known but only one proof is needed. Default shape: address-only.",
95
+ inputSchema: {
96
+ addresses: z
97
+ .array(z.string())
98
+ .min(1)
99
+ .optional()
100
+ .describe("Address-only whitelist (mutually exclusive with `entries`)."),
101
+ entries: z
102
+ .array(z.array(z.union([z.string(), z.number()])))
103
+ .min(1)
104
+ .optional(),
105
+ leafEncoding: z.array(z.string()).min(1).default(["address"]),
106
+ target: z.string().describe("Address (when using `addresses`)."),
107
+ targetEntry: z
108
+ .array(z.union([z.string(), z.number()]))
109
+ .optional()
110
+ .describe("Raw leaf values when using `entries`."),
111
+ },
112
+ outputSchema: {
113
+ root: z.string(),
114
+ leaf: z.string(),
115
+ proof: z.array(z.string()),
116
+ verified: z.boolean(),
117
+ },
118
+ }, async ({ addresses, entries, leafEncoding = ["address"], target, targetEntry }) => {
119
+ try {
120
+ if (!addresses && !entries) {
121
+ return errorResult("Must provide either `addresses` or `entries`.");
122
+ }
123
+ if (addresses) {
124
+ if (!isAddress(target))
125
+ return errorResult(`Invalid target address: ${target}`);
126
+ const normalised = normaliseAddresses(addresses);
127
+ const checksummed = getAddress(target);
128
+ const idx = normalised.findIndex((a) => a.toLowerCase() === checksummed.toLowerCase());
129
+ if (idx === -1) {
130
+ return errorResult(`Target ${checksummed} not present in whitelist.`);
131
+ }
132
+ const tree = buildAddressMerkleTree(normalised);
133
+ const leaf = computeLeafHash([checksummed], ["address"]);
134
+ const proof = tree.proofs[idx] ?? [];
135
+ return jsonResult({
136
+ root: tree.root,
137
+ leaf,
138
+ proof,
139
+ verified: verifyProof(proof, tree.root, leaf),
140
+ });
141
+ }
142
+ if (!entries) {
143
+ return errorResult("Must provide `entries` when not using `addresses`.");
144
+ }
145
+ if (!targetEntry) {
146
+ return errorResult("Provide `targetEntry` when using `entries`.");
147
+ }
148
+ const tree = buildMerkleTree(entries, leafEncoding);
149
+ const leaf = computeLeafHash(targetEntry, leafEncoding);
150
+ const idx = tree.leafHashes.findIndex((h) => h.toLowerCase() === leaf.toLowerCase());
151
+ if (idx === -1) {
152
+ return errorResult("Target entry leaf not in tree.");
153
+ }
154
+ let proof = [];
155
+ for (let i = 0; i < entries.length; i++) {
156
+ const candidateLeaf = computeLeafHash(entries[i], leafEncoding);
157
+ if (candidateLeaf.toLowerCase() === leaf.toLowerCase()) {
158
+ proof = tree.proofs[i] ?? [];
159
+ break;
160
+ }
161
+ }
162
+ return jsonResult({
163
+ root: tree.root,
164
+ leaf,
165
+ proof,
166
+ verified: verifyProof(proof, tree.root, leaf),
167
+ });
168
+ }
169
+ catch (err) {
170
+ return errorResult(err instanceof Error ? err.message : String(err));
171
+ }
172
+ });
173
+ }
174
+ //# sourceMappingURL=merkle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merkle.js","sourceRoot":"","sources":["../../src/tools/merkle.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { ToolContext } from "./context.js";
3
+ import { SignerManager } from "../lib/signer.js";
4
+ export declare function registerOtcTools(server: McpServer, ctx: ToolContext, signer: SignerManager): void;
5
+ //# sourceMappingURL=otc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"otc.d.ts","sourceRoot":"","sources":["../../src/tools/otc.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AA+DjD,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,SAAS,EACjB,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,aAAa,GACpB,IAAI,CAohBN"}