midnight-mcp 0.1.32 → 0.1.34
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 +15 -2
- package/dist/config/compact-version.d.ts +68 -0
- package/dist/config/compact-version.js +95 -0
- package/dist/prompts/templates.js +86 -12
- package/dist/resources/content/docs-content.js +293 -167
- package/dist/server.d.ts +4 -0
- package/dist/server.js +13 -2
- package/dist/tools/repository/handlers.d.ts +17 -1
- package/dist/tools/repository/handlers.js +74 -20
- package/dist/tools/repository/tools.js +6 -4
- package/dist/tools/repository/validation.js +72 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -93,7 +93,7 @@ Or manually update your config:
|
|
|
93
93
|
| Category | Tools | Description |
|
|
94
94
|
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |
|
|
95
95
|
| **Search** | `search-compact`, `search-typescript`, `search-docs` | Semantic search across Midnight codebase |
|
|
96
|
-
| **Analysis** | `analyze-contract`, `explain-circuit`, `extract-contract-structure` | Static analysis
|
|
96
|
+
| **Analysis** | `analyze-contract`, `explain-circuit`, `extract-contract-structure` | Static analysis with 15+ checks (P0-P2 severity) |
|
|
97
97
|
| **Repository** | `get-file`, `list-examples`, `get-latest-updates` | Access files and examples |
|
|
98
98
|
| **Versioning** | `get-version-info`, `check-breaking-changes`, `get-migration-guide`, `get-file-at-version`, `compare-syntax`, `get-latest-syntax` | Version tracking and migration |
|
|
99
99
|
| **AI Generation** | `generate-contract`, `review-contract`, `document-contract` | AI-powered code generation _(requires sampling)_ |
|
|
@@ -119,13 +119,26 @@ All tools are prefixed with `midnight-` (e.g., `midnight-search-compact`).
|
|
|
119
119
|
|
|
120
120
|
Quick references available offline:
|
|
121
121
|
|
|
122
|
-
- Compact syntax guide
|
|
122
|
+
- Compact syntax guide (v0.16-0.18)
|
|
123
123
|
- SDK API reference
|
|
124
124
|
- OpenZeppelin contracts
|
|
125
125
|
- Tokenomics overview
|
|
126
126
|
- Wallet integration
|
|
127
127
|
- Common errors & solutions
|
|
128
128
|
|
|
129
|
+
### Static Analysis
|
|
130
|
+
|
|
131
|
+
`extract-contract-structure` catches common mistakes before compilation:
|
|
132
|
+
|
|
133
|
+
| Check | Severity | Description |
|
|
134
|
+
| ------------------------- | -------- | ------------------------------------------------------- |
|
|
135
|
+
| `deprecated_ledger_block` | P0 | Catches `ledger { }` → use `export ledger field: Type;` |
|
|
136
|
+
| `invalid_void_type` | P0 | Catches `Void` → use `[]` (empty tuple) |
|
|
137
|
+
| `invalid_pragma_format` | P0 | Catches old pragma → use `>= 0.16 && <= 0.18` |
|
|
138
|
+
| `unexported_enum` | P1 | Enums need `export` for TypeScript access |
|
|
139
|
+
| `module_level_const` | P0 | Use `pure circuit` instead |
|
|
140
|
+
| + 10 more checks | P1-P2 | Overflow, division, assertions, etc. |
|
|
141
|
+
|
|
129
142
|
### 5 Prompts
|
|
130
143
|
|
|
131
144
|
- `create-contract` — Generate new contracts
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compact Language Version Configuration
|
|
3
|
+
*
|
|
4
|
+
* MAINTAINER: Update these values when Compact language syntax changes!
|
|
5
|
+
* See docs/SYNTAX_MAINTENANCE.md for the full update checklist.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Supported Compact language version range
|
|
9
|
+
* Update when new compiler versions are released
|
|
10
|
+
*/
|
|
11
|
+
export declare const COMPACT_VERSION: {
|
|
12
|
+
/** Minimum supported version */
|
|
13
|
+
min: string;
|
|
14
|
+
/** Maximum supported version */
|
|
15
|
+
max: string;
|
|
16
|
+
/** When this config was last updated */
|
|
17
|
+
lastUpdated: string;
|
|
18
|
+
/** Source of truth for syntax patterns */
|
|
19
|
+
referenceSource: string;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Current pragma format that should be used in contracts
|
|
23
|
+
*/
|
|
24
|
+
export declare const RECOMMENDED_PRAGMA: string;
|
|
25
|
+
/**
|
|
26
|
+
* Known deprecated patterns (add new ones here when Compact evolves)
|
|
27
|
+
*/
|
|
28
|
+
export declare const DEPRECATED_PATTERNS: {
|
|
29
|
+
/** Deprecated in: 0.16 */
|
|
30
|
+
ledgerBlock: {
|
|
31
|
+
pattern: RegExp;
|
|
32
|
+
since: string;
|
|
33
|
+
replacement: string;
|
|
34
|
+
description: string;
|
|
35
|
+
};
|
|
36
|
+
/** Deprecated in: 0.15 */
|
|
37
|
+
cellWrapper: {
|
|
38
|
+
pattern: RegExp;
|
|
39
|
+
since: string;
|
|
40
|
+
replacement: string;
|
|
41
|
+
description: string;
|
|
42
|
+
};
|
|
43
|
+
/** Never existed */
|
|
44
|
+
voidType: {
|
|
45
|
+
pattern: RegExp;
|
|
46
|
+
since: string;
|
|
47
|
+
replacement: string;
|
|
48
|
+
description: string;
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Reference contracts known to compile successfully
|
|
53
|
+
* Use these to verify syntax is still correct
|
|
54
|
+
*/
|
|
55
|
+
export declare const REFERENCE_CONTRACTS: {
|
|
56
|
+
name: string;
|
|
57
|
+
repo: string;
|
|
58
|
+
description: string;
|
|
59
|
+
}[];
|
|
60
|
+
/**
|
|
61
|
+
* Get the version info as a string for display
|
|
62
|
+
*/
|
|
63
|
+
export declare function getVersionInfo(): string;
|
|
64
|
+
/**
|
|
65
|
+
* Check if a version is within supported range
|
|
66
|
+
*/
|
|
67
|
+
export declare function isVersionSupported(version: string): boolean;
|
|
68
|
+
//# sourceMappingURL=compact-version.d.ts.map
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compact Language Version Configuration
|
|
3
|
+
*
|
|
4
|
+
* MAINTAINER: Update these values when Compact language syntax changes!
|
|
5
|
+
* See docs/SYNTAX_MAINTENANCE.md for the full update checklist.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Supported Compact language version range
|
|
9
|
+
* Update when new compiler versions are released
|
|
10
|
+
*/
|
|
11
|
+
export const COMPACT_VERSION = {
|
|
12
|
+
/** Minimum supported version */
|
|
13
|
+
min: "0.16",
|
|
14
|
+
/** Maximum supported version */
|
|
15
|
+
max: "0.18",
|
|
16
|
+
/** When this config was last updated */
|
|
17
|
+
lastUpdated: "2025-01-26",
|
|
18
|
+
/** Source of truth for syntax patterns */
|
|
19
|
+
referenceSource: "https://github.com/piotr-iohk/template-contract",
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Current pragma format that should be used in contracts
|
|
23
|
+
*/
|
|
24
|
+
export const RECOMMENDED_PRAGMA = `pragma language_version >= ${COMPACT_VERSION.min} && <= ${COMPACT_VERSION.max};`;
|
|
25
|
+
/**
|
|
26
|
+
* Known deprecated patterns (add new ones here when Compact evolves)
|
|
27
|
+
*/
|
|
28
|
+
export const DEPRECATED_PATTERNS = {
|
|
29
|
+
/** Deprecated in: 0.16 */
|
|
30
|
+
ledgerBlock: {
|
|
31
|
+
pattern: /ledger\s*\{/,
|
|
32
|
+
since: "0.16",
|
|
33
|
+
replacement: "export ledger fieldName: Type;",
|
|
34
|
+
description: "Block-style ledger declarations",
|
|
35
|
+
},
|
|
36
|
+
/** Deprecated in: 0.15 */
|
|
37
|
+
cellWrapper: {
|
|
38
|
+
pattern: /Cell\s*<\s*\w+\s*>/,
|
|
39
|
+
since: "0.15",
|
|
40
|
+
replacement: "Type (without Cell wrapper)",
|
|
41
|
+
description: "Cell<T> type wrapper",
|
|
42
|
+
},
|
|
43
|
+
/** Never existed */
|
|
44
|
+
voidType: {
|
|
45
|
+
pattern: /:\s*Void\b/,
|
|
46
|
+
since: "always",
|
|
47
|
+
replacement: "[] (empty tuple)",
|
|
48
|
+
description: "Void return type",
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Reference contracts known to compile successfully
|
|
53
|
+
* Use these to verify syntax is still correct
|
|
54
|
+
*/
|
|
55
|
+
export const REFERENCE_CONTRACTS = [
|
|
56
|
+
{
|
|
57
|
+
name: "template-contract",
|
|
58
|
+
repo: "piotr-iohk/template-contract",
|
|
59
|
+
description: "Official Midnight template contract",
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: "tokenomics-project",
|
|
63
|
+
repo: "piotr-iohk/tokenomics-project",
|
|
64
|
+
description: "Token implementation example",
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: "zswap-example",
|
|
68
|
+
repo: "piotr-iohk/zswap-example",
|
|
69
|
+
description: "Privacy-preserving swap example",
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: "reentrancy-example",
|
|
73
|
+
repo: "piotr-iohk/reentrancy-example",
|
|
74
|
+
description: "Cross-contract call patterns",
|
|
75
|
+
},
|
|
76
|
+
];
|
|
77
|
+
/**
|
|
78
|
+
* Get the version info as a string for display
|
|
79
|
+
*/
|
|
80
|
+
export function getVersionInfo() {
|
|
81
|
+
return `Compact ${COMPACT_VERSION.min}-${COMPACT_VERSION.max} (updated ${COMPACT_VERSION.lastUpdated})`;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Check if a version is within supported range
|
|
85
|
+
*/
|
|
86
|
+
export function isVersionSupported(version) {
|
|
87
|
+
const [major, minor] = version.split(".").map(Number);
|
|
88
|
+
const [minMajor, minMinor] = COMPACT_VERSION.min.split(".").map(Number);
|
|
89
|
+
const [maxMajor, maxMinor] = COMPACT_VERSION.max.split(".").map(Number);
|
|
90
|
+
const versionNum = major * 100 + minor;
|
|
91
|
+
const minNum = minMajor * 100 + minMinor;
|
|
92
|
+
const maxNum = maxMajor * 100 + maxMinor;
|
|
93
|
+
return versionNum >= minNum && versionNum <= maxNum;
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=compact-version.js.map
|
|
@@ -128,6 +128,35 @@ function generateCreateContractPrompt(args) {
|
|
|
128
128
|
**Privacy Level:** ${privacyLevel}
|
|
129
129
|
**Complexity:** ${complexity}
|
|
130
130
|
|
|
131
|
+
## ⚠️ MANDATORY WORKFLOW - Follow these steps IN ORDER:
|
|
132
|
+
|
|
133
|
+
### Step 1: Get Current Syntax
|
|
134
|
+
Call \`midnight-get-latest-syntax\` FIRST to get:
|
|
135
|
+
- The \`quickStartTemplate\` (use as your base)
|
|
136
|
+
- The \`commonMistakes\` array (avoid these errors)
|
|
137
|
+
- Current pragma format: \`pragma language_version >= 0.16 && <= 0.18;\`
|
|
138
|
+
|
|
139
|
+
### Step 2: Generate Contract
|
|
140
|
+
Based on syntax reference, generate the contract using:
|
|
141
|
+
- Individual ledger declarations: \`export ledger field: Type;\` (NOT \`ledger { }\` blocks)
|
|
142
|
+
- Empty tuple return: \`circuit fn(): []\` (NOT \`Void\`)
|
|
143
|
+
- Export enums: \`export enum State { ... }\`
|
|
144
|
+
- Wrap witness conditionals: \`if (disclose(witness == value))\`
|
|
145
|
+
|
|
146
|
+
### Step 3: Validate Before Returning
|
|
147
|
+
Call \`midnight-extract-contract-structure\` with your generated code to check for:
|
|
148
|
+
- deprecated_ledger_block
|
|
149
|
+
- invalid_void_type
|
|
150
|
+
- invalid_pragma_format
|
|
151
|
+
- unexported_enum
|
|
152
|
+
- deprecated_cell_wrapper
|
|
153
|
+
|
|
154
|
+
If ANY errors are found, fix them before returning the code to the user.
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## Contract Requirements
|
|
159
|
+
|
|
131
160
|
Please help me design and implement this contract. Consider:
|
|
132
161
|
|
|
133
162
|
1. **State Design**
|
|
@@ -179,33 +208,57 @@ ${contractCode}
|
|
|
179
208
|
|
|
180
209
|
**Focus Areas:** ${focusAreas}
|
|
181
210
|
|
|
211
|
+
## ⚠️ MANDATORY WORKFLOW:
|
|
212
|
+
|
|
213
|
+
### Step 1: Validate Syntax
|
|
214
|
+
Call \`midnight-extract-contract-structure\` with the contract code to check for:
|
|
215
|
+
- deprecated_ledger_block (should use \`export ledger field: Type;\`)
|
|
216
|
+
- invalid_void_type (should use \`[]\` not \`Void\`)
|
|
217
|
+
- invalid_pragma_format (should use \`>= 0.16 && <= 0.18\`)
|
|
218
|
+
- unexported_enum (enums need \`export\`)
|
|
219
|
+
- deprecated_cell_wrapper
|
|
220
|
+
|
|
221
|
+
Report ALL static analysis findings first.
|
|
222
|
+
|
|
223
|
+
### Step 2: Get Latest Syntax Reference
|
|
224
|
+
If syntax errors are found, call \`midnight-get-latest-syntax\` to get:
|
|
225
|
+
- The \`commonMistakes\` array showing correct patterns
|
|
226
|
+
- Current syntax reference
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
182
230
|
Please analyze:
|
|
183
231
|
|
|
184
|
-
1. **
|
|
232
|
+
1. **Static Analysis Results** (from midnight-extract-contract-structure)
|
|
233
|
+
- Syntax errors found
|
|
234
|
+
- Deprecated patterns detected
|
|
235
|
+
- Required fixes
|
|
236
|
+
|
|
237
|
+
2. **Security Analysis**
|
|
185
238
|
- Input validation
|
|
186
239
|
- Access control
|
|
187
240
|
- State manipulation vulnerabilities
|
|
188
241
|
- Assertion coverage
|
|
189
242
|
|
|
190
|
-
|
|
243
|
+
3. **Privacy Assessment**
|
|
191
244
|
- Proper use of @private state
|
|
192
245
|
- Information leakage risks
|
|
193
246
|
- Correct use of disclose() and commit()
|
|
194
247
|
- Privacy guarantees provided
|
|
195
248
|
|
|
196
|
-
|
|
249
|
+
4. **Best Practices**
|
|
197
250
|
- Code organization
|
|
198
251
|
- Naming conventions
|
|
199
252
|
- Documentation
|
|
200
253
|
- Error messages
|
|
201
254
|
|
|
202
|
-
|
|
255
|
+
5. **Performance**
|
|
203
256
|
- Circuit complexity
|
|
204
257
|
- State access patterns
|
|
205
258
|
- Optimization opportunities
|
|
206
259
|
|
|
207
|
-
|
|
208
|
-
- Critical issues to fix
|
|
260
|
+
6. **Recommendations**
|
|
261
|
+
- Critical issues to fix (start with P0 syntax errors)
|
|
209
262
|
- Improvements to consider
|
|
210
263
|
- Alternative approaches
|
|
211
264
|
|
|
@@ -319,25 +372,46 @@ ${contractCode}
|
|
|
319
372
|
|
|
320
373
|
**Error/Issue:** ${errorMessage}
|
|
321
374
|
|
|
375
|
+
## ⚠️ MANDATORY WORKFLOW:
|
|
376
|
+
|
|
377
|
+
### Step 1: Run Static Analysis
|
|
378
|
+
Call \`midnight-extract-contract-structure\` FIRST to check for common syntax errors:
|
|
379
|
+
- deprecated_ledger_block → should use \`export ledger field: Type;\`
|
|
380
|
+
- invalid_void_type → should use \`[]\` not \`Void\`
|
|
381
|
+
- invalid_pragma_format → should use \`>= 0.16 && <= 0.18\`
|
|
382
|
+
- unexported_enum → enums need \`export\` keyword
|
|
383
|
+
|
|
384
|
+
### Step 2: Get Correct Syntax
|
|
385
|
+
If syntax errors found, call \`midnight-get-latest-syntax\` to get:
|
|
386
|
+
- The \`commonMistakes\` array with correct patterns
|
|
387
|
+
- Current \`quickStartTemplate\` for reference
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
322
391
|
Please help me debug by:
|
|
323
392
|
|
|
324
|
-
1. **
|
|
393
|
+
1. **Static Analysis Results**
|
|
394
|
+
- Run midnight-extract-contract-structure
|
|
395
|
+
- List all P0 syntax errors found
|
|
396
|
+
- Show the correct syntax for each error
|
|
397
|
+
|
|
398
|
+
2. **Identifying the Problem**
|
|
325
399
|
- What's causing the error?
|
|
326
400
|
- Which line(s) are problematic?
|
|
327
401
|
|
|
328
|
-
|
|
402
|
+
3. **Explaining Why**
|
|
329
403
|
- Root cause analysis
|
|
330
404
|
- How Compact/ZK constraints work
|
|
331
405
|
|
|
332
|
-
|
|
333
|
-
- Corrected code
|
|
406
|
+
4. **Providing a Fix**
|
|
407
|
+
- Corrected code (validated against static analysis)
|
|
334
408
|
- Explanation of changes
|
|
335
409
|
|
|
336
|
-
|
|
410
|
+
5. **Preventing Future Issues**
|
|
337
411
|
- Related pitfalls to watch for
|
|
338
412
|
- Testing strategies
|
|
339
413
|
|
|
340
|
-
|
|
414
|
+
6. **Additional Improvements**
|
|
341
415
|
- Code quality suggestions
|
|
342
416
|
- Best practices`,
|
|
343
417
|
},
|
|
@@ -11,262 +11,388 @@
|
|
|
11
11
|
* use the search_docs tool which queries the Vector DB.
|
|
12
12
|
*/
|
|
13
13
|
export const EMBEDDED_DOCS = {
|
|
14
|
-
"midnight://docs/compact-reference": `# Compact Language
|
|
14
|
+
"midnight://docs/compact-reference": `# Compact Language Syntax Reference (v0.16 - v0.18)
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
> **CRITICAL**: This reference is derived from **actual compiling contracts** in the Midnight ecosystem.
|
|
17
|
+
> Always verify syntax against this reference before generating contracts.
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
## Quick Start Template
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
Use this as a starting point - it compiles successfully:
|
|
21
22
|
|
|
22
23
|
\`\`\`compact
|
|
23
|
-
pragma language_version >= 0.
|
|
24
|
+
pragma language_version >= 0.16 && <= 0.18;
|
|
24
25
|
|
|
25
|
-
|
|
26
|
+
import CompactStandardLibrary;
|
|
27
|
+
|
|
28
|
+
// Ledger state (individual declarations, NOT a block)
|
|
29
|
+
export ledger counter: Counter;
|
|
30
|
+
export ledger owner: Bytes<32>;
|
|
31
|
+
|
|
32
|
+
// Witness for private/off-chain data
|
|
33
|
+
witness local_secret_key(): Bytes<32>;
|
|
34
|
+
|
|
35
|
+
// Circuit (returns [] not Void)
|
|
36
|
+
export circuit increment(): [] {
|
|
37
|
+
counter.increment(1);
|
|
38
|
+
}
|
|
39
|
+
\`\`\`
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## 1. Pragma (Version Declaration)
|
|
44
|
+
|
|
45
|
+
**CORRECT** - use bounded range without patch version:
|
|
46
|
+
\`\`\`compact
|
|
47
|
+
pragma language_version >= 0.16 && <= 0.18;
|
|
48
|
+
\`\`\`
|
|
49
|
+
|
|
50
|
+
**WRONG** - these will cause parse errors:
|
|
51
|
+
\`\`\`compact
|
|
52
|
+
pragma language_version >= 0.14.0; // ❌ patch version not needed
|
|
53
|
+
pragma language_version >= 0.16.0 < 0.19.0; // ❌ wrong operator format
|
|
54
|
+
\`\`\`
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## 2. Imports
|
|
59
|
+
|
|
60
|
+
Always import the standard library:
|
|
61
|
+
\`\`\`compact
|
|
62
|
+
import CompactStandardLibrary;
|
|
63
|
+
\`\`\`
|
|
64
|
+
|
|
65
|
+
For multi-file contracts, use \`include\`:
|
|
66
|
+
\`\`\`compact
|
|
67
|
+
include "types";
|
|
68
|
+
include "ledger";
|
|
69
|
+
include "circuits";
|
|
70
|
+
\`\`\`
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## 3. Ledger Declarations
|
|
75
|
+
|
|
76
|
+
**CORRECT** - individual declarations with \`export ledger\`:
|
|
77
|
+
\`\`\`compact
|
|
78
|
+
export ledger counter: Counter;
|
|
79
|
+
export ledger owner: Bytes<32>;
|
|
80
|
+
export ledger balances: Map<Bytes<32>, Uint<64>>;
|
|
81
|
+
|
|
82
|
+
// Private state (off-chain only)
|
|
83
|
+
ledger secretValue: Field; // no export = private
|
|
84
|
+
\`\`\`
|
|
26
85
|
|
|
86
|
+
**WRONG** - block syntax is deprecated:
|
|
87
|
+
\`\`\`compact
|
|
88
|
+
// ❌ This causes parse error: found "{" looking for an identifier
|
|
27
89
|
ledger {
|
|
28
|
-
// Public state (on-chain, visible to everyone)
|
|
29
90
|
counter: Counter;
|
|
30
|
-
|
|
31
|
-
// Private state (off-chain, only owner can see)
|
|
32
|
-
@private
|
|
33
|
-
secretValue: Field;
|
|
91
|
+
owner: Bytes<32>;
|
|
34
92
|
}
|
|
93
|
+
\`\`\`
|
|
35
94
|
|
|
36
|
-
|
|
37
|
-
export circuit increment(amount: Field): Void {
|
|
38
|
-
assert(amount > 0);
|
|
39
|
-
ledger.counter.increment(amount);
|
|
40
|
-
}
|
|
95
|
+
### Ledger Modifiers
|
|
41
96
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
97
|
+
\`\`\`compact
|
|
98
|
+
export ledger publicData: Field; // Public, readable by anyone
|
|
99
|
+
export sealed ledger immutableData: Field; // Set once in constructor, cannot change
|
|
100
|
+
ledger privateData: Field; // Private, not exported
|
|
46
101
|
\`\`\`
|
|
47
102
|
|
|
48
|
-
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## 4. Data Types
|
|
49
106
|
|
|
50
107
|
### Primitive Types
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
108
|
+
| Type | Description | Example |
|
|
109
|
+
|------|-------------|---------|
|
|
110
|
+
| \`Field\` | Finite field element (basic numeric) | \`amount: Field\` |
|
|
111
|
+
| \`Boolean\` | True or false | \`isActive: Boolean\` |
|
|
112
|
+
| \`Bytes<N>\` | Fixed-size byte array | \`hash: Bytes<32>\` |
|
|
113
|
+
| \`Uint<N>\` | Unsigned integer (N = 8, 16, 32, 64, 128, 256) | \`balance: Uint<64>\` |
|
|
114
|
+
| \`Uint<MIN..MAX>\` | Bounded unsigned integer | \`score: Uint<0..100>\` |
|
|
55
115
|
|
|
56
116
|
### Collection Types
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
117
|
+
| Type | Description | Example |
|
|
118
|
+
|------|-------------|---------|
|
|
119
|
+
| \`Counter\` | Incrementable/decrementable | \`count: Counter\` |
|
|
120
|
+
| \`Map<K, V>\` | Key-value mapping | \`Map<Bytes<32>, Uint<64>>\` |
|
|
121
|
+
| \`Set<T>\` | Unique value collection | \`Set<Bytes<32>>\` |
|
|
122
|
+
| \`Vector<N, T>\` | Fixed-size array | \`Vector<3, Field>\` |
|
|
123
|
+
| \`List<T>\` | Dynamic list | \`List<Bytes<32>>\` |
|
|
124
|
+
| \`Maybe<T>\` | Optional value | \`Maybe<Bytes<32>>\` |
|
|
125
|
+
| \`Either<L, R>\` | Union type | \`Either<Field, Bytes<32>>\` |
|
|
126
|
+
| \`Opaque<"type">\` | External type from TypeScript | \`Opaque<"string">\` |
|
|
127
|
+
|
|
128
|
+
### Custom Types
|
|
129
|
+
|
|
130
|
+
**Enums** - must use \`export\` to access from TypeScript:
|
|
131
|
+
\`\`\`compact
|
|
132
|
+
export enum GameState { waiting, playing, finished }
|
|
133
|
+
export enum Choice { rock, paper, scissors }
|
|
134
|
+
\`\`\`
|
|
65
135
|
|
|
136
|
+
**Structs**:
|
|
66
137
|
\`\`\`compact
|
|
67
|
-
export
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
// State modifications
|
|
73
|
-
ledger.balance.decrement(amount);
|
|
138
|
+
export struct PlayerConfig {
|
|
139
|
+
name: Opaque<"string">,
|
|
140
|
+
score: Uint<32>,
|
|
141
|
+
isActive: Boolean,
|
|
74
142
|
}
|
|
75
143
|
\`\`\`
|
|
76
144
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
- Cannot access external data directly (use witnesses)
|
|
81
|
-
- Assertions become ZK constraints
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## 5. Circuits
|
|
82
148
|
|
|
83
|
-
|
|
149
|
+
Circuits are on-chain functions that generate ZK proofs.
|
|
84
150
|
|
|
85
|
-
|
|
151
|
+
**CRITICAL**: Return type is \`[]\` (empty tuple), NOT \`Void\`:
|
|
86
152
|
|
|
87
153
|
\`\`\`compact
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
154
|
+
// CORRECT - returns []
|
|
155
|
+
export circuit increment(): [] {
|
|
156
|
+
counter.increment(1);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// CORRECT - with parameters
|
|
160
|
+
export circuit transfer(to: Bytes<32>, amount: Uint<64>): [] {
|
|
161
|
+
assert(amount > 0, "Amount must be positive");
|
|
162
|
+
// ... logic
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// CORRECT - with return value
|
|
166
|
+
export circuit getBalance(addr: Bytes<32>): Uint<64> {
|
|
167
|
+
return balances.lookup(addr);
|
|
91
168
|
}
|
|
92
169
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
// ... execute purchase
|
|
170
|
+
// WRONG - Void does not exist
|
|
171
|
+
export circuit broken(): Void { // ❌ Parse error
|
|
172
|
+
counter.increment(1);
|
|
97
173
|
}
|
|
98
174
|
\`\`\`
|
|
99
175
|
|
|
100
|
-
###
|
|
101
|
-
- Run locally, not on-chain
|
|
102
|
-
- Can access external APIs, databases
|
|
103
|
-
- Cannot modify ledger state directly
|
|
104
|
-
- Results are private unless disclosed
|
|
176
|
+
### Circuit Modifiers
|
|
105
177
|
|
|
106
|
-
|
|
178
|
+
\`\`\`compact
|
|
179
|
+
export circuit publicFn(): [] // Callable externally
|
|
180
|
+
circuit internalFn(): [] // Internal only, not exported
|
|
181
|
+
export pure circuit hash(x: Field): Bytes<32> // No state access
|
|
182
|
+
\`\`\`
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## 6. Witnesses
|
|
187
|
+
|
|
188
|
+
Witnesses provide off-chain/private data to circuits. They run locally, not on-chain.
|
|
107
189
|
|
|
108
|
-
### Public State
|
|
109
190
|
\`\`\`compact
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
191
|
+
// Basic witness - returns private data
|
|
192
|
+
witness local_secret_key(): Bytes<32>;
|
|
193
|
+
|
|
194
|
+
// Witness with parameters
|
|
195
|
+
witness get_merkle_path(leaf: Bytes<32>): MerkleTreePath<10, Bytes<32>>;
|
|
196
|
+
|
|
197
|
+
// Witness that returns nothing (side effect only)
|
|
198
|
+
witness store_locally(data: Field): [];
|
|
199
|
+
|
|
200
|
+
// Witness returning optional
|
|
201
|
+
witness find_user(id: Bytes<32>): Maybe<UserData>;
|
|
114
202
|
\`\`\`
|
|
115
203
|
|
|
116
|
-
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## 7. Constructor
|
|
207
|
+
|
|
208
|
+
Optional - initializes sealed ledger fields at deploy time:
|
|
209
|
+
|
|
117
210
|
\`\`\`compact
|
|
118
|
-
ledger
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
211
|
+
export sealed ledger owner: Bytes<32>;
|
|
212
|
+
export sealed ledger nonce: Bytes<32>;
|
|
213
|
+
|
|
214
|
+
constructor(initNonce: Bytes<32>) {
|
|
215
|
+
owner = disclose(public_key(local_secret_key()));
|
|
216
|
+
nonce = disclose(initNonce);
|
|
124
217
|
}
|
|
125
218
|
\`\`\`
|
|
126
219
|
|
|
127
|
-
|
|
220
|
+
---
|
|
128
221
|
|
|
129
|
-
|
|
222
|
+
## 8. Common Patterns
|
|
223
|
+
|
|
224
|
+
### Authentication Pattern
|
|
130
225
|
\`\`\`compact
|
|
131
|
-
|
|
132
|
-
owner: Opaque<"address">;
|
|
133
|
-
}
|
|
226
|
+
witness local_secret_key(): Bytes<32>;
|
|
134
227
|
|
|
135
|
-
|
|
136
|
-
return
|
|
228
|
+
circuit get_public_key(sk: Bytes<32>): Bytes<32> {
|
|
229
|
+
return persistentHash<Vector<2, Bytes<32>>>([pad(32, "myapp:pk:"), sk]);
|
|
137
230
|
}
|
|
138
231
|
|
|
139
|
-
export circuit
|
|
140
|
-
|
|
232
|
+
export circuit authenticated_action(): [] {
|
|
233
|
+
const sk = local_secret_key();
|
|
234
|
+
const caller = get_public_key(sk);
|
|
235
|
+
assert(disclose(caller == owner), "Not authorized");
|
|
236
|
+
// ... action
|
|
141
237
|
}
|
|
142
238
|
\`\`\`
|
|
143
239
|
|
|
144
|
-
###
|
|
240
|
+
### Commit-Reveal Pattern
|
|
145
241
|
\`\`\`compact
|
|
146
|
-
export
|
|
147
|
-
|
|
148
|
-
|
|
242
|
+
export ledger commitment: Bytes<32>;
|
|
243
|
+
export ledger revealed: Boolean;
|
|
244
|
+
|
|
245
|
+
witness get_stored_value(): Field;
|
|
246
|
+
witness store_value(v: Field): [];
|
|
247
|
+
|
|
248
|
+
export circuit commit(value: Field): [] {
|
|
249
|
+
const sk = local_secret_key();
|
|
250
|
+
store_value(value);
|
|
251
|
+
commitment = disclose(persistentHash<Vector<2, Bytes<32>>>([
|
|
252
|
+
value as Bytes<32>,
|
|
253
|
+
sk
|
|
254
|
+
]));
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export circuit reveal(): Field {
|
|
258
|
+
const sk = local_secret_key();
|
|
259
|
+
const value = get_stored_value();
|
|
260
|
+
const expected = persistentHash<Vector<2, Bytes<32>>>([
|
|
261
|
+
value as Bytes<32>,
|
|
262
|
+
sk
|
|
263
|
+
]);
|
|
264
|
+
assert(disclose(expected == commitment), "Mismatch");
|
|
265
|
+
revealed = true;
|
|
266
|
+
return disclose(value);
|
|
149
267
|
}
|
|
150
268
|
\`\`\`
|
|
151
269
|
|
|
152
|
-
### Disclosure in Conditionals
|
|
153
|
-
When
|
|
270
|
+
### Disclosure in Conditionals
|
|
271
|
+
When branching on witness values, wrap comparisons in \`disclose()\`:
|
|
154
272
|
|
|
155
273
|
\`\`\`compact
|
|
156
|
-
//
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
if (guess == secret) { // ERROR: implicit disclosure
|
|
274
|
+
// CORRECT
|
|
275
|
+
export circuit check(guess: Field): Boolean {
|
|
276
|
+
const secret = get_secret(); // witness
|
|
277
|
+
if (disclose(guess == secret)) {
|
|
161
278
|
return true;
|
|
162
279
|
}
|
|
163
280
|
return false;
|
|
164
281
|
}
|
|
165
282
|
|
|
166
|
-
//
|
|
167
|
-
export circuit
|
|
168
|
-
const secret =
|
|
169
|
-
if (
|
|
283
|
+
// WRONG - will not compile
|
|
284
|
+
export circuit check_broken(guess: Field): Boolean {
|
|
285
|
+
const secret = get_secret();
|
|
286
|
+
if (guess == secret) { // ❌ implicit disclosure error
|
|
170
287
|
return true;
|
|
171
288
|
}
|
|
172
289
|
return false;
|
|
173
290
|
}
|
|
174
|
-
|
|
175
|
-
// ✅ Multiple comparisons
|
|
176
|
-
export circuit giveHint(guess: Field): Hint {
|
|
177
|
-
const secret = getSecret();
|
|
178
|
-
if (disclose(guess == secret)) {
|
|
179
|
-
return Hint.correct;
|
|
180
|
-
} else if (disclose(guess > secret)) {
|
|
181
|
-
return Hint.too_high;
|
|
182
|
-
} else {
|
|
183
|
-
return Hint.too_low;
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
291
|
\`\`\`
|
|
187
292
|
|
|
188
|
-
|
|
293
|
+
---
|
|
189
294
|
|
|
190
|
-
|
|
295
|
+
## 9. Common Operations
|
|
296
|
+
|
|
297
|
+
### Counter Operations
|
|
191
298
|
\`\`\`compact
|
|
192
|
-
|
|
193
|
-
|
|
299
|
+
counter.increment(1);
|
|
300
|
+
counter.decrement(1);
|
|
301
|
+
const val = counter.value(); // Note: returns as Field
|
|
194
302
|
\`\`\`
|
|
195
303
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
### 1. Cell<T> Wrapper (Deprecated)
|
|
304
|
+
### Map Operations
|
|
199
305
|
\`\`\`compact
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
const x = myValue.read();
|
|
204
|
-
|
|
205
|
-
// ✅ CURRENT SYNTAX (0.16+) - direct declaration
|
|
206
|
-
export ledger myValue: Field;
|
|
207
|
-
myValue = 42;
|
|
208
|
-
const x = myValue;
|
|
306
|
+
balances.insert(address, 100);
|
|
307
|
+
const balance = balances.lookup(address);
|
|
308
|
+
const exists = balances.member(address);
|
|
209
309
|
\`\`\`
|
|
210
310
|
|
|
211
|
-
###
|
|
311
|
+
### Set Operations
|
|
212
312
|
\`\`\`compact
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
message = "hello"; // ERROR!
|
|
217
|
-
}
|
|
313
|
+
members.insert(address);
|
|
314
|
+
const isMember = members.member(address);
|
|
315
|
+
\`\`\`
|
|
218
316
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
status = Status.approved; // Works!
|
|
224
|
-
}
|
|
317
|
+
### Maybe Operations
|
|
318
|
+
\`\`\`compact
|
|
319
|
+
const opt: Maybe<Field> = some<Field>(42);
|
|
320
|
+
const empty: Maybe<Field> = none<Field>();
|
|
225
321
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
message = msg; // msg comes from TypeScript
|
|
322
|
+
if (opt.is_some) {
|
|
323
|
+
const val = opt.value;
|
|
229
324
|
}
|
|
230
325
|
\`\`\`
|
|
231
326
|
|
|
232
|
-
###
|
|
327
|
+
### Type Casting
|
|
233
328
|
\`\`\`compact
|
|
234
|
-
// Counter
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
count.decrement(1);
|
|
238
|
-
const val = count.value();
|
|
239
|
-
|
|
240
|
-
// Field uses direct assignment
|
|
241
|
-
export ledger amount: Field;
|
|
242
|
-
amount = amount + 1;
|
|
243
|
-
const val = amount;
|
|
329
|
+
const f: Field = counter.value(); // Counter to Field
|
|
330
|
+
const bytes: Bytes<32> = f as Bytes<32>; // Field to Bytes
|
|
331
|
+
const num: Uint<64> = f as Uint<64>; // Field to Uint
|
|
244
332
|
\`\`\`
|
|
245
333
|
|
|
246
|
-
###
|
|
334
|
+
### Hashing
|
|
247
335
|
\`\`\`compact
|
|
248
|
-
//
|
|
249
|
-
|
|
336
|
+
// Persistent hash (same input = same output across calls)
|
|
337
|
+
const hash = persistentHash<Vector<2, Bytes<32>>>([data1, data2]);
|
|
250
338
|
|
|
251
|
-
//
|
|
252
|
-
const
|
|
253
|
-
balances.insert(address, 100);
|
|
339
|
+
// Transient hash (includes randomness)
|
|
340
|
+
const commit = transientCommit<Field>(value, randomness);
|
|
254
341
|
\`\`\`
|
|
255
342
|
|
|
256
|
-
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
## 10. Assertions
|
|
346
|
+
|
|
257
347
|
\`\`\`compact
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
}
|
|
348
|
+
assert(condition, "Error message");
|
|
349
|
+
assert(amount > 0, "Amount must be positive");
|
|
350
|
+
assert(disclose(caller == owner), "Not authorized");
|
|
351
|
+
\`\`\`
|
|
263
352
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
353
|
+
---
|
|
354
|
+
|
|
355
|
+
## 11. Common Mistakes to Avoid
|
|
356
|
+
|
|
357
|
+
| Mistake | Correct |
|
|
358
|
+
|---------|---------|
|
|
359
|
+
| \`ledger { field: Type; }\` | \`export ledger field: Type;\` |
|
|
360
|
+
| \`circuit fn(): Void\` | \`circuit fn(): []\` |
|
|
361
|
+
| \`pragma >= 0.16.0\` | \`pragma >= 0.16 && <= 0.18\` |
|
|
362
|
+
| \`enum State { ... }\` | \`export enum State { ... }\` |
|
|
363
|
+
| \`if (witness_val == x)\` | \`if (disclose(witness_val == x))\` |
|
|
364
|
+
| \`Cell<Field>\` | \`Field\` (Cell is deprecated) |
|
|
365
|
+
| \`myValue.read()\` / \`.write()\` | Direct assignment: \`myValue = x\` |
|
|
366
|
+
|
|
367
|
+
---
|
|
368
|
+
|
|
369
|
+
## 12. Exports for TypeScript
|
|
370
|
+
|
|
371
|
+
To use types/values in TypeScript, they must be exported:
|
|
372
|
+
|
|
373
|
+
\`\`\`compact
|
|
374
|
+
// These are accessible from TypeScript
|
|
375
|
+
export enum GameState { waiting, playing }
|
|
376
|
+
export struct Config { value: Field }
|
|
377
|
+
export ledger counter: Counter;
|
|
378
|
+
export circuit play(): []
|
|
379
|
+
|
|
380
|
+
// Standard library re-exports (if needed in TS)
|
|
381
|
+
export { Maybe, Either, CoinInfo };
|
|
269
382
|
\`\`\`
|
|
383
|
+
|
|
384
|
+
---
|
|
385
|
+
|
|
386
|
+
## Reference Contracts
|
|
387
|
+
|
|
388
|
+
These contracts compile successfully and demonstrate correct patterns:
|
|
389
|
+
|
|
390
|
+
1. **Counter** (beginner): \`midnightntwrk/example-counter\`
|
|
391
|
+
2. **Bulletin Board** (intermediate): \`midnightntwrk/example-bboard\`
|
|
392
|
+
3. **Naval Battle Game** (advanced): \`ErickRomeroDev/naval-battle-game_v2\`
|
|
393
|
+
4. **Sea Battle** (advanced): \`bricktowers/midnight-seabattle\`
|
|
394
|
+
|
|
395
|
+
When in doubt, reference these repos for working syntax.
|
|
270
396
|
`,
|
|
271
397
|
"midnight://docs/sdk-api": `# Midnight TypeScript SDK Quick Reference
|
|
272
398
|
|
package/dist/server.d.ts
CHANGED
|
@@ -8,6 +8,10 @@ export declare function getUpdateWarning(): string | null;
|
|
|
8
8
|
* Clear all subscriptions (useful for server restart/testing)
|
|
9
9
|
*/
|
|
10
10
|
export declare function clearSubscriptions(): void;
|
|
11
|
+
/**
|
|
12
|
+
* Mark server as connected (safe to send notifications)
|
|
13
|
+
*/
|
|
14
|
+
export declare function setServerConnected(connected: boolean): void;
|
|
11
15
|
/**
|
|
12
16
|
* Send a log message to the MCP client
|
|
13
17
|
* This allows clients to see server logs for debugging
|
package/dist/server.js
CHANGED
|
@@ -8,7 +8,7 @@ import { allResources, getDocumentation, getCode, getSchema, } from "./resources
|
|
|
8
8
|
import { promptDefinitions, generatePrompt } from "./prompts/index.js";
|
|
9
9
|
import { registerSamplingCallback } from "./services/index.js";
|
|
10
10
|
// Server information - version should match package.json
|
|
11
|
-
const CURRENT_VERSION = "0.1.
|
|
11
|
+
const CURRENT_VERSION = "0.1.33";
|
|
12
12
|
const SERVER_INFO = {
|
|
13
13
|
name: "midnight-mcp",
|
|
14
14
|
version: CURRENT_VERSION,
|
|
@@ -101,12 +101,21 @@ const resourceTemplates = [
|
|
|
101
101
|
let mcpLogLevel = "info";
|
|
102
102
|
// Server instance for sending notifications
|
|
103
103
|
let serverInstance = null;
|
|
104
|
+
// Track if server is connected (can send notifications)
|
|
105
|
+
let isConnected = false;
|
|
106
|
+
/**
|
|
107
|
+
* Mark server as connected (safe to send notifications)
|
|
108
|
+
*/
|
|
109
|
+
export function setServerConnected(connected) {
|
|
110
|
+
isConnected = connected;
|
|
111
|
+
}
|
|
104
112
|
/**
|
|
105
113
|
* Send a log message to the MCP client
|
|
106
114
|
* This allows clients to see server logs for debugging
|
|
107
115
|
*/
|
|
108
116
|
export function sendLogToClient(level, loggerName, data) {
|
|
109
|
-
if
|
|
117
|
+
// Only send if server exists AND is connected
|
|
118
|
+
if (!serverInstance || !isConnected)
|
|
110
119
|
return;
|
|
111
120
|
// Map levels to numeric values for comparison
|
|
112
121
|
const levelValues = {
|
|
@@ -667,6 +676,8 @@ export async function startServer() {
|
|
|
667
676
|
const server = await initializeServer();
|
|
668
677
|
const transport = new StdioServerTransport();
|
|
669
678
|
await server.connect(transport);
|
|
679
|
+
// Now safe to send notifications
|
|
680
|
+
setServerConnected(true);
|
|
670
681
|
logger.info("Midnight MCP Server running on stdio");
|
|
671
682
|
}
|
|
672
683
|
//# sourceMappingURL=server.js.map
|
|
@@ -158,13 +158,29 @@ export declare function compareSyntax(input: CompareSyntaxInput): Promise<{
|
|
|
158
158
|
* This is the source of truth for writing valid, compilable contracts
|
|
159
159
|
*/
|
|
160
160
|
export declare function getLatestSyntax(input: GetLatestSyntaxInput): Promise<{
|
|
161
|
+
quickStartTemplate: string;
|
|
162
|
+
commonMistakes: {
|
|
163
|
+
wrong: string;
|
|
164
|
+
correct: string;
|
|
165
|
+
error: string;
|
|
166
|
+
}[];
|
|
161
167
|
syntaxReference: string;
|
|
162
168
|
sections: string[];
|
|
163
|
-
|
|
169
|
+
referenceContracts: {
|
|
170
|
+
name: string;
|
|
171
|
+
repo: string;
|
|
172
|
+
description: string;
|
|
173
|
+
}[];
|
|
164
174
|
note: string;
|
|
165
175
|
versionWarning?: string | undefined;
|
|
166
176
|
repository: string;
|
|
167
177
|
version: string;
|
|
178
|
+
versionConfig: {
|
|
179
|
+
min: string;
|
|
180
|
+
max: string;
|
|
181
|
+
lastUpdated: string;
|
|
182
|
+
maintenanceGuide: string;
|
|
183
|
+
};
|
|
168
184
|
warning?: undefined;
|
|
169
185
|
syntaxFiles?: undefined;
|
|
170
186
|
examplePaths?: undefined;
|
|
@@ -8,6 +8,7 @@ import { logger, DEFAULT_REPOSITORIES, SelfCorrectionHints, } from "../../utils/
|
|
|
8
8
|
import { sendProgressNotification } from "../../server.js";
|
|
9
9
|
import { REPO_ALIASES, EXAMPLES } from "./constants.js";
|
|
10
10
|
import { EMBEDDED_DOCS } from "../../resources/content/docs-content.js";
|
|
11
|
+
import { COMPACT_VERSION, RECOMMENDED_PRAGMA, REFERENCE_CONTRACTS, } from "../../config/compact-version.js";
|
|
11
12
|
// Re-export validation handlers from validation.ts
|
|
12
13
|
export { extractContractStructure } from "./validation.js";
|
|
13
14
|
/**
|
|
@@ -310,7 +311,7 @@ export async function getLatestSyntax(input) {
|
|
|
310
311
|
if (repoName === "compact" || repoName === "midnight-compact") {
|
|
311
312
|
const compactReference = EMBEDDED_DOCS["midnight://docs/compact-reference"];
|
|
312
313
|
// Check if there's a newer release we might not have documented
|
|
313
|
-
|
|
314
|
+
// Version config is centralized in src/config/compact-version.ts
|
|
314
315
|
let versionWarning;
|
|
315
316
|
try {
|
|
316
317
|
const versionInfo = await releaseTracker.getVersionInfo("midnightntwrk", "compact");
|
|
@@ -322,12 +323,9 @@ export async function getLatestSyntax(input) {
|
|
|
322
323
|
.split(".")
|
|
323
324
|
.slice(0, 2)
|
|
324
325
|
.join(".");
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
.
|
|
328
|
-
if (latestVersion !== embeddedMajorMinor &&
|
|
329
|
-
parseFloat(latestVersion) > parseFloat(embeddedMajorMinor)) {
|
|
330
|
-
versionWarning = `⚠️ Compact ${latestTag} is available. This reference is based on ${EMBEDDED_DOCS_VERSION}. Some syntax may have changed - check release notes for breaking changes.`;
|
|
326
|
+
if (latestVersion !== COMPACT_VERSION.max &&
|
|
327
|
+
parseFloat(latestVersion) > parseFloat(COMPACT_VERSION.max)) {
|
|
328
|
+
versionWarning = `⚠️ Compact ${latestTag} is available. This reference is based on ${COMPACT_VERSION.max}. Some syntax may have changed - check release notes for breaking changes. See docs/SYNTAX_MAINTENANCE.md for update instructions.`;
|
|
331
329
|
}
|
|
332
330
|
}
|
|
333
331
|
}
|
|
@@ -337,27 +335,83 @@ export async function getLatestSyntax(input) {
|
|
|
337
335
|
if (compactReference) {
|
|
338
336
|
return {
|
|
339
337
|
repository: "midnightntwrk/compact",
|
|
340
|
-
version:
|
|
338
|
+
version: `${COMPACT_VERSION.min}-${COMPACT_VERSION.max} (current)`,
|
|
339
|
+
versionConfig: {
|
|
340
|
+
min: COMPACT_VERSION.min,
|
|
341
|
+
max: COMPACT_VERSION.max,
|
|
342
|
+
lastUpdated: COMPACT_VERSION.lastUpdated,
|
|
343
|
+
maintenanceGuide: "See docs/SYNTAX_MAINTENANCE.md for update instructions",
|
|
344
|
+
},
|
|
341
345
|
...(versionWarning && { versionWarning }),
|
|
346
|
+
// Quick start template - ALWAYS compiles
|
|
347
|
+
quickStartTemplate: `${RECOMMENDED_PRAGMA}
|
|
348
|
+
|
|
349
|
+
import CompactStandardLibrary;
|
|
350
|
+
|
|
351
|
+
export ledger counter: Counter;
|
|
352
|
+
export ledger owner: Bytes<32>;
|
|
353
|
+
|
|
354
|
+
witness local_secret_key(): Bytes<32>;
|
|
355
|
+
|
|
356
|
+
export circuit increment(): [] {
|
|
357
|
+
counter.increment(1);
|
|
358
|
+
}`,
|
|
359
|
+
// Common mistakes that cause compilation failures
|
|
360
|
+
commonMistakes: [
|
|
361
|
+
{
|
|
362
|
+
wrong: "ledger { field: Type; }",
|
|
363
|
+
correct: "export ledger field: Type;",
|
|
364
|
+
error: 'parse error: found "{" looking for an identifier',
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
wrong: "circuit fn(): Void",
|
|
368
|
+
correct: "circuit fn(): []",
|
|
369
|
+
error: 'parse error: found "{" looking for ";"',
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
wrong: "pragma language_version >= 0.14.0;",
|
|
373
|
+
correct: RECOMMENDED_PRAGMA,
|
|
374
|
+
error: "version mismatch or parse error",
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
wrong: "enum State { a, b }",
|
|
378
|
+
correct: "export enum State { a, b }",
|
|
379
|
+
error: "enum not accessible from TypeScript",
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
wrong: "if (witness_val == x)",
|
|
383
|
+
correct: "if (disclose(witness_val == x))",
|
|
384
|
+
error: "implicit disclosure error",
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
wrong: "Cell<Field>",
|
|
388
|
+
correct: "Field",
|
|
389
|
+
error: "unbound identifier Cell (deprecated)",
|
|
390
|
+
},
|
|
391
|
+
],
|
|
342
392
|
syntaxReference: compactReference,
|
|
343
393
|
sections: [
|
|
344
|
-
"
|
|
394
|
+
"Quick Start Template",
|
|
395
|
+
"Pragma (Version Declaration)",
|
|
396
|
+
"Imports",
|
|
397
|
+
"Ledger Declarations",
|
|
345
398
|
"Data Types",
|
|
346
399
|
"Circuits",
|
|
347
400
|
"Witnesses",
|
|
348
|
-
"
|
|
401
|
+
"Constructor",
|
|
349
402
|
"Common Patterns",
|
|
350
|
-
"
|
|
351
|
-
"
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
"
|
|
355
|
-
'Cannot assign string literals to Opaque<"string"> - use enum or parameters',
|
|
356
|
-
"Must disclose() comparisons used in if/else conditions",
|
|
357
|
-
"Counter uses .increment()/.value(), Field uses direct assignment",
|
|
358
|
-
"Boolean returns from witnesses need disclose()",
|
|
403
|
+
"Common Operations",
|
|
404
|
+
"Assertions",
|
|
405
|
+
"Common Mistakes to Avoid",
|
|
406
|
+
"Exports for TypeScript",
|
|
407
|
+
"Reference Contracts",
|
|
359
408
|
],
|
|
360
|
-
|
|
409
|
+
referenceContracts: REFERENCE_CONTRACTS.map((rc) => ({
|
|
410
|
+
name: rc.name,
|
|
411
|
+
repo: rc.repo,
|
|
412
|
+
description: rc.description,
|
|
413
|
+
})),
|
|
414
|
+
note: `CRITICAL: Use quickStartTemplate as your base. Check commonMistakes before submitting code. This reference is for Compact ${COMPACT_VERSION.min}-${COMPACT_VERSION.max} (last updated: ${COMPACT_VERSION.lastUpdated}).`,
|
|
361
415
|
};
|
|
362
416
|
}
|
|
363
417
|
}
|
|
@@ -359,10 +359,12 @@ export const repositoryTools = [
|
|
|
359
359
|
{
|
|
360
360
|
name: "midnight-extract-contract-structure",
|
|
361
361
|
description: "Extract and analyze Compact contract structure (circuits, witnesses, ledger). " +
|
|
362
|
-
"
|
|
363
|
-
"
|
|
364
|
-
"
|
|
365
|
-
"
|
|
362
|
+
"CRITICAL CHECKS: deprecated 'ledger { }' block syntax, 'Void' return type (should be []), " +
|
|
363
|
+
"old pragma format, unexported enums, deprecated Cell<T> wrapper. " +
|
|
364
|
+
"Also detects: module-level const, stdlib name collisions, division operator, " +
|
|
365
|
+
"Counter.value access, missing disclose() calls, potential overflow. " +
|
|
366
|
+
"Use BEFORE generating contracts to catch syntax errors. " +
|
|
367
|
+
"Note: Static analysis only - catches common patterns but not semantic errors.",
|
|
366
368
|
inputSchema: {
|
|
367
369
|
type: "object",
|
|
368
370
|
properties: {
|
|
@@ -440,6 +440,77 @@ export async function extractContractStructure(input) {
|
|
|
440
440
|
"Opaque",
|
|
441
441
|
"Vector",
|
|
442
442
|
];
|
|
443
|
+
// ========== CRITICAL SYNTAX CHECKS (P0 - causes immediate compilation failure) ==========
|
|
444
|
+
// P0-1. Detect deprecated ledger block syntax
|
|
445
|
+
const ledgerBlockPattern = /ledger\s*\{/g;
|
|
446
|
+
let ledgerBlockMatch;
|
|
447
|
+
while ((ledgerBlockMatch = ledgerBlockPattern.exec(code)) !== null) {
|
|
448
|
+
const lineNum = lineByIndex[ledgerBlockMatch.index] || 1;
|
|
449
|
+
potentialIssues.push({
|
|
450
|
+
type: "deprecated_ledger_block",
|
|
451
|
+
line: lineNum,
|
|
452
|
+
message: `Deprecated ledger block syntax 'ledger { }' - causes parse error`,
|
|
453
|
+
suggestion: `Use individual declarations: 'export ledger fieldName: Type;'`,
|
|
454
|
+
severity: "error",
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
// P0-2. Detect Void return type (doesn't exist in Compact)
|
|
458
|
+
const voidReturnPattern = /circuit\s+\w+\s*\([^)]*\)\s*:\s*Void\b/g;
|
|
459
|
+
let voidMatch;
|
|
460
|
+
while ((voidMatch = voidReturnPattern.exec(code)) !== null) {
|
|
461
|
+
const lineNum = lineByIndex[voidMatch.index] || 1;
|
|
462
|
+
potentialIssues.push({
|
|
463
|
+
type: "invalid_void_type",
|
|
464
|
+
line: lineNum,
|
|
465
|
+
message: `Invalid return type 'Void' - Void does not exist in Compact`,
|
|
466
|
+
suggestion: `Use '[]' (empty tuple) for circuits that return nothing: 'circuit fn(): []'`,
|
|
467
|
+
severity: "error",
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
// P0-3. Detect old pragma format with patch version
|
|
471
|
+
const oldPragmaPattern = /pragma\s+language_version\s*>=?\s*\d+\.\d+\.\d+/g;
|
|
472
|
+
let oldPragmaMatch;
|
|
473
|
+
while ((oldPragmaMatch = oldPragmaPattern.exec(code)) !== null) {
|
|
474
|
+
const lineNum = lineByIndex[oldPragmaMatch.index] || 1;
|
|
475
|
+
potentialIssues.push({
|
|
476
|
+
type: "invalid_pragma_format",
|
|
477
|
+
line: lineNum,
|
|
478
|
+
message: `Pragma includes patch version which may cause parse errors`,
|
|
479
|
+
suggestion: `Use bounded range format: 'pragma language_version >= 0.16 && <= 0.18;'`,
|
|
480
|
+
severity: "error",
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
// P0-4. Detect missing export on enums (won't be accessible from TypeScript)
|
|
484
|
+
const unexportedEnumPattern = /(?<!export\s+)enum\s+(\w+)\s*\{/g;
|
|
485
|
+
let unexportedEnumMatch;
|
|
486
|
+
while ((unexportedEnumMatch = unexportedEnumPattern.exec(code)) !== null) {
|
|
487
|
+
// Double check it's not preceded by export
|
|
488
|
+
const before = code.substring(Math.max(0, unexportedEnumMatch.index - 10), unexportedEnumMatch.index);
|
|
489
|
+
if (!before.includes("export")) {
|
|
490
|
+
const lineNum = lineByIndex[unexportedEnumMatch.index] || 1;
|
|
491
|
+
potentialIssues.push({
|
|
492
|
+
type: "unexported_enum",
|
|
493
|
+
line: lineNum,
|
|
494
|
+
message: `Enum '${unexportedEnumMatch[1]}' is not exported - won't be accessible from TypeScript`,
|
|
495
|
+
suggestion: `Add 'export' keyword: 'export enum ${unexportedEnumMatch[1]} { ... }'`,
|
|
496
|
+
severity: "warning",
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
// P0-5. Detect Cell<T> wrapper (deprecated since 0.15)
|
|
501
|
+
const cellPattern = /Cell\s*<\s*\w+\s*>/g;
|
|
502
|
+
let cellMatch;
|
|
503
|
+
while ((cellMatch = cellPattern.exec(code)) !== null) {
|
|
504
|
+
const lineNum = lineByIndex[cellMatch.index] || 1;
|
|
505
|
+
potentialIssues.push({
|
|
506
|
+
type: "deprecated_cell_wrapper",
|
|
507
|
+
line: lineNum,
|
|
508
|
+
message: `'Cell<T>' wrapper is deprecated since Compact 0.15`,
|
|
509
|
+
suggestion: `Use the type directly: 'Field' instead of 'Cell<Field>'`,
|
|
510
|
+
severity: "error",
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
// ========== EXISTING CHECKS ==========
|
|
443
514
|
// 1. Detect module-level const (not supported in Compact)
|
|
444
515
|
const constPattern = /^const\s+(\w+)\s*:/gm;
|
|
445
516
|
let constMatch;
|
|
@@ -693,19 +764,7 @@ export async function extractContractStructure(input) {
|
|
|
693
764
|
severity: "error",
|
|
694
765
|
});
|
|
695
766
|
}
|
|
696
|
-
// 12.
|
|
697
|
-
const voidReturnPattern = /circuit\s+\w+\s*\([^)]*\)\s*:\s*Void\b/g;
|
|
698
|
-
let voidMatch;
|
|
699
|
-
while ((voidMatch = voidReturnPattern.exec(code)) !== null) {
|
|
700
|
-
const lineNum = lineByIndex[voidMatch.index] || 1;
|
|
701
|
-
potentialIssues.push({
|
|
702
|
-
type: "invalid_void_type",
|
|
703
|
-
line: lineNum,
|
|
704
|
-
message: `'Void' is not a valid type in Compact`,
|
|
705
|
-
suggestion: `Use empty tuple '[]' for circuits with no return value: 'circuit name(): []'`,
|
|
706
|
-
severity: "error",
|
|
707
|
-
});
|
|
708
|
-
}
|
|
767
|
+
// 12. (Moved to P0-2 above - Void return type check)
|
|
709
768
|
const summary = [];
|
|
710
769
|
if (circuits.length > 0) {
|
|
711
770
|
summary.push(`${circuits.length} circuit(s)`);
|