midnight-mcp 0.2.18 → 0.2.20
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.
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
validateNumber,
|
|
26
26
|
validateQuery,
|
|
27
27
|
vectorStore
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-RCQYFKD3.js";
|
|
29
29
|
|
|
30
30
|
// src/tools/search/schemas.ts
|
|
31
31
|
import { z } from "zod";
|
|
@@ -1698,7 +1698,14 @@ var REPO_ALIASES = {
|
|
|
1698
1698
|
lucentlabs: { owner: "statera-protocol", repo: "statera-protocol-midnight" },
|
|
1699
1699
|
stablecoin: { owner: "statera-protocol", repo: "statera-protocol-midnight" },
|
|
1700
1700
|
"midnight-bank": { owner: "nel349", repo: "midnight-bank" },
|
|
1701
|
-
bank: { owner: "nel349", repo: "midnight-bank" }
|
|
1701
|
+
bank: { owner: "nel349", repo: "midnight-bank" },
|
|
1702
|
+
// Third-Party / Community (NOT official Midnight, not in midnight-awesome-dapps)
|
|
1703
|
+
// effectstream is a multi-chain Web3 engine (EVM, Midnight, Bitcoin, Cardano,
|
|
1704
|
+
// Avail, Celestia, NEAR). Only its Midnight pieces are relevant here:
|
|
1705
|
+
// @effectstream/midnight-contracts and the evm-midnight-v2 / zswap-da templates.
|
|
1706
|
+
// NOTE: repo currently has NO LICENSE — treat indexed content as reference only.
|
|
1707
|
+
effectstream: { owner: "effectstream", repo: "effectstream" },
|
|
1708
|
+
"effect-stream": { owner: "effectstream", repo: "effectstream" }
|
|
1702
1709
|
};
|
|
1703
1710
|
var EXAMPLES = [
|
|
1704
1711
|
{
|
|
@@ -1914,7 +1921,7 @@ import { randomUUID } from "crypto";
|
|
|
1914
1921
|
|
|
1915
1922
|
// src/resources/content/docs-content.ts
|
|
1916
1923
|
var EMBEDDED_DOCS = {
|
|
1917
|
-
"midnight://docs/compact-reference": `# Compact Language Syntax Reference (v0.16 - v0.
|
|
1924
|
+
"midnight://docs/compact-reference": `# Compact Language Syntax Reference (v0.16 - v0.23)
|
|
1918
1925
|
|
|
1919
1926
|
> **CRITICAL**: This reference is derived from **actual compiling contracts** in the Midnight ecosystem.
|
|
1920
1927
|
> Always verify syntax against this reference before generating contracts.
|
|
@@ -1924,7 +1931,7 @@ var EMBEDDED_DOCS = {
|
|
|
1924
1931
|
Use this as a starting point - it compiles successfully:
|
|
1925
1932
|
|
|
1926
1933
|
\`\`\`compact
|
|
1927
|
-
pragma language_version >= 0.16 && <= 0.
|
|
1934
|
+
pragma language_version >= 0.16 && <= 0.23;
|
|
1928
1935
|
|
|
1929
1936
|
import CompactStandardLibrary;
|
|
1930
1937
|
|
|
@@ -1947,7 +1954,7 @@ export circuit increment(): [] {
|
|
|
1947
1954
|
|
|
1948
1955
|
**CORRECT** - use bounded range without patch version:
|
|
1949
1956
|
\`\`\`compact
|
|
1950
|
-
pragma language_version >= 0.16 && <= 0.
|
|
1957
|
+
pragma language_version >= 0.16 && <= 0.23;
|
|
1951
1958
|
\`\`\`
|
|
1952
1959
|
|
|
1953
1960
|
**WRONG** - these will cause parse errors:
|
|
@@ -2201,7 +2208,7 @@ export circuit authenticated_action(): [] {
|
|
|
2201
2208
|
|
|
2202
2209
|
### Commit-Reveal Pattern (COMPLETE, VALIDATED)
|
|
2203
2210
|
\`\`\`compact
|
|
2204
|
-
pragma language_version >= 0.16 && <= 0.
|
|
2211
|
+
pragma language_version >= 0.16 && <= 0.23;
|
|
2205
2212
|
|
|
2206
2213
|
import CompactStandardLibrary;
|
|
2207
2214
|
|
|
@@ -2441,7 +2448,7 @@ assert(disclose(caller == owner), "Not authorized");
|
|
|
2441
2448
|
|---------|---------|
|
|
2442
2449
|
| \`ledger { field: Type; }\` | \`export ledger field: Type;\` |
|
|
2443
2450
|
| \`circuit fn(): Void\` | \`circuit fn(): []\` |
|
|
2444
|
-
| \`pragma >= 0.20.0\` | \`pragma >= 0.16 && <= 0.
|
|
2451
|
+
| \`pragma >= 0.20.0\` | \`pragma >= 0.16 && <= 0.23\` |
|
|
2445
2452
|
| \`enum State { ... }\` | \`export enum State { ... }\` |
|
|
2446
2453
|
| \`if (witness_val == x)\` | \`if (disclose(witness_val == x))\` |
|
|
2447
2454
|
| \`Cell<Field>\` | \`Field\` (Cell is deprecated) |
|
|
@@ -2774,7 +2781,7 @@ npm install @openzeppelin/compact-contracts
|
|
|
2774
2781
|
## Usage Example
|
|
2775
2782
|
|
|
2776
2783
|
\`\`\`compact
|
|
2777
|
-
pragma language_version >= 0.16 && <= 0.
|
|
2784
|
+
pragma language_version >= 0.16 && <= 0.23;
|
|
2778
2785
|
|
|
2779
2786
|
import CompactStandardLibrary;
|
|
2780
2787
|
import "@openzeppelin/compact-contracts/src/token/FungibleToken"
|
|
@@ -2815,7 +2822,7 @@ The recommended standard for privacy-preserving tokens on Midnight.
|
|
|
2815
2822
|
## Basic Usage
|
|
2816
2823
|
|
|
2817
2824
|
\`\`\`compact
|
|
2818
|
-
pragma language_version >= 0.16 && <= 0.
|
|
2825
|
+
pragma language_version >= 0.16 && <= 0.23;
|
|
2819
2826
|
|
|
2820
2827
|
import CompactStandardLibrary;
|
|
2821
2828
|
import "@openzeppelin/compact-contracts/src/token/FungibleToken"
|
|
@@ -3406,7 +3413,7 @@ var EMBEDDED_CODE = {
|
|
|
3406
3413
|
"midnight://code/examples/counter": `// Counter Example Contract
|
|
3407
3414
|
// A simple contract demonstrating basic Compact concepts
|
|
3408
3415
|
|
|
3409
|
-
pragma language_version >= 0.16 && <= 0.
|
|
3416
|
+
pragma language_version >= 0.16 && <= 0.23;
|
|
3410
3417
|
|
|
3411
3418
|
import CompactStandardLibrary;
|
|
3412
3419
|
|
|
@@ -3455,7 +3462,7 @@ export circuit isLessThan(threshold: Uint<64>): Boolean {
|
|
|
3455
3462
|
"midnight://code/examples/bboard": `// Bulletin Board Example Contract
|
|
3456
3463
|
// Demonstrates private messaging with selective disclosure
|
|
3457
3464
|
|
|
3458
|
-
pragma language_version >= 0.16 && <= 0.
|
|
3465
|
+
pragma language_version >= 0.16 && <= 0.23;
|
|
3459
3466
|
|
|
3460
3467
|
import CompactStandardLibrary;
|
|
3461
3468
|
|
|
@@ -3504,7 +3511,7 @@ export circuit getMessageCount(): Uint<64> {
|
|
|
3504
3511
|
"midnight://code/patterns/state-management": `// State Management Pattern
|
|
3505
3512
|
// Best practices for managing public and private state
|
|
3506
3513
|
|
|
3507
|
-
pragma language_version >= 0.16 && <= 0.
|
|
3514
|
+
pragma language_version >= 0.16 && <= 0.23;
|
|
3508
3515
|
|
|
3509
3516
|
import CompactStandardLibrary;
|
|
3510
3517
|
|
|
@@ -3555,7 +3562,7 @@ export circuit revealBalance(user: Opaque<"address">): Field {
|
|
|
3555
3562
|
"midnight://code/patterns/access-control": `// Access Control Pattern
|
|
3556
3563
|
// Implementing permissions and authorization
|
|
3557
3564
|
|
|
3558
|
-
pragma language_version >= 0.16 && <= 0.
|
|
3565
|
+
pragma language_version >= 0.16 && <= 0.23;
|
|
3559
3566
|
|
|
3560
3567
|
import CompactStandardLibrary;
|
|
3561
3568
|
|
|
@@ -3610,7 +3617,7 @@ export circuit timeLockedAction(unlockTime: Field): [] {
|
|
|
3610
3617
|
"midnight://code/patterns/privacy-preserving": `// Privacy-Preserving Patterns
|
|
3611
3618
|
// Techniques for maintaining privacy in smart contracts
|
|
3612
3619
|
|
|
3613
|
-
pragma language_version >= 0.16 && <= 0.
|
|
3620
|
+
pragma language_version >= 0.16 && <= 0.23;
|
|
3614
3621
|
|
|
3615
3622
|
import CompactStandardLibrary;
|
|
3616
3623
|
|
|
@@ -3715,7 +3722,7 @@ export circuit proveMembership(
|
|
|
3715
3722
|
"midnight://code/templates/token": `// Privacy-Preserving Token Template
|
|
3716
3723
|
// Starter template for token contracts with privacy features
|
|
3717
3724
|
|
|
3718
|
-
pragma language_version >= 0.16 && <= 0.
|
|
3725
|
+
pragma language_version >= 0.16 && <= 0.23;
|
|
3719
3726
|
|
|
3720
3727
|
import CompactStandardLibrary;
|
|
3721
3728
|
|
|
@@ -3774,7 +3781,7 @@ export circuit mint(to: Opaque<"address">, amount: Uint<64>): Boolean {
|
|
|
3774
3781
|
"midnight://code/templates/voting": `// Private Voting Template
|
|
3775
3782
|
// Starter template for privacy-preserving voting contracts
|
|
3776
3783
|
|
|
3777
|
-
pragma language_version >= 0.16 && <= 0.
|
|
3784
|
+
pragma language_version >= 0.16 && <= 0.23;
|
|
3778
3785
|
|
|
3779
3786
|
import CompactStandardLibrary;
|
|
3780
3787
|
|
|
@@ -3854,7 +3861,7 @@ export circuit addVoter(voter: Opaque<"address">): [] {
|
|
|
3854
3861
|
"midnight://code/examples/nullifier": `// Nullifier Pattern Example
|
|
3855
3862
|
// Demonstrates how to create and use nullifiers to prevent double-spending/actions
|
|
3856
3863
|
|
|
3857
|
-
pragma language_version >= 0.16 && <= 0.
|
|
3864
|
+
pragma language_version >= 0.16 && <= 0.23;
|
|
3858
3865
|
|
|
3859
3866
|
import CompactStandardLibrary;
|
|
3860
3867
|
|
|
@@ -3921,7 +3928,7 @@ export circuit voteWithNullifier(
|
|
|
3921
3928
|
"midnight://code/examples/hash": `// Hash Functions in Compact
|
|
3922
3929
|
// Examples of using hash functions for various purposes
|
|
3923
3930
|
|
|
3924
|
-
pragma language_version >= 0.16 && <= 0.
|
|
3931
|
+
pragma language_version >= 0.16 && <= 0.23;
|
|
3925
3932
|
|
|
3926
3933
|
import CompactStandardLibrary;
|
|
3927
3934
|
|
|
@@ -3968,7 +3975,7 @@ export circuit reveal(value: Field, randomness: Field): Field {
|
|
|
3968
3975
|
"midnight://code/examples/simple-counter": `// Simple Counter Contract
|
|
3969
3976
|
// Minimal example for learning Compact basics
|
|
3970
3977
|
|
|
3971
|
-
pragma language_version >= 0.16 && <= 0.
|
|
3978
|
+
pragma language_version >= 0.16 && <= 0.23;
|
|
3972
3979
|
|
|
3973
3980
|
import CompactStandardLibrary;
|
|
3974
3981
|
|
|
@@ -4006,7 +4013,7 @@ export circuit reset(): [] {
|
|
|
4006
4013
|
"midnight://code/templates/basic": `// Basic Compact Contract Template
|
|
4007
4014
|
// Starting point for new contracts
|
|
4008
4015
|
|
|
4009
|
-
pragma language_version >= 0.16 && <= 0.
|
|
4016
|
+
pragma language_version >= 0.16 && <= 0.23;
|
|
4010
4017
|
|
|
4011
4018
|
import CompactStandardLibrary;
|
|
4012
4019
|
|
|
@@ -4663,2341 +4670,2421 @@ var allResources = [
|
|
|
4663
4670
|
...schemaResources
|
|
4664
4671
|
];
|
|
4665
4672
|
|
|
4666
|
-
// src/
|
|
4667
|
-
var
|
|
4668
|
-
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
description: "Required privacy features (full, partial, public)",
|
|
4680
|
-
required: false
|
|
4681
|
-
},
|
|
4682
|
-
{
|
|
4683
|
-
name: "complexity",
|
|
4684
|
-
description: "Expected complexity level (beginner, intermediate, advanced)",
|
|
4685
|
-
required: false
|
|
4686
|
-
}
|
|
4687
|
-
]
|
|
4688
|
-
},
|
|
4673
|
+
// src/config/compact-version.ts
|
|
4674
|
+
var COMPACT_VERSION = {
|
|
4675
|
+
/** Minimum supported version */
|
|
4676
|
+
min: "0.16",
|
|
4677
|
+
/** Maximum supported version */
|
|
4678
|
+
max: "0.23",
|
|
4679
|
+
/** When this config was last updated */
|
|
4680
|
+
lastUpdated: "2026-05-18",
|
|
4681
|
+
/** Source of truth for syntax patterns */
|
|
4682
|
+
referenceSource: "https://github.com/piotr-iohk/template-contract"
|
|
4683
|
+
};
|
|
4684
|
+
var RECOMMENDED_PRAGMA = `pragma language_version >= ${COMPACT_VERSION.min} && <= ${COMPACT_VERSION.max};`;
|
|
4685
|
+
var REFERENCE_CONTRACTS = [
|
|
4689
4686
|
{
|
|
4690
|
-
name: "
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
{
|
|
4694
|
-
name: "contractCode",
|
|
4695
|
-
description: "The Compact contract code to review",
|
|
4696
|
-
required: true
|
|
4697
|
-
},
|
|
4698
|
-
{
|
|
4699
|
-
name: "focusAreas",
|
|
4700
|
-
description: "Specific areas to emphasize (security, performance, privacy, readability)",
|
|
4701
|
-
required: false
|
|
4702
|
-
}
|
|
4703
|
-
]
|
|
4687
|
+
name: "template-contract",
|
|
4688
|
+
repo: "piotr-iohk/template-contract",
|
|
4689
|
+
description: "Official Midnight template contract"
|
|
4704
4690
|
},
|
|
4705
4691
|
{
|
|
4706
|
-
name: "
|
|
4707
|
-
|
|
4708
|
-
|
|
4709
|
-
{
|
|
4710
|
-
name: "concept",
|
|
4711
|
-
description: "The concept to explain (zk-proofs, circuits, witnesses, ledger, etc.)",
|
|
4712
|
-
required: true
|
|
4713
|
-
},
|
|
4714
|
-
{
|
|
4715
|
-
name: "level",
|
|
4716
|
-
description: "Expertise level (beginner, intermediate, advanced)",
|
|
4717
|
-
required: false
|
|
4718
|
-
}
|
|
4719
|
-
]
|
|
4692
|
+
name: "tokenomics-project",
|
|
4693
|
+
repo: "piotr-iohk/tokenomics-project",
|
|
4694
|
+
description: "Token implementation example"
|
|
4720
4695
|
},
|
|
4721
4696
|
{
|
|
4722
|
-
name: "
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
{
|
|
4726
|
-
name: "problem",
|
|
4727
|
-
description: "The problem to solve",
|
|
4728
|
-
required: true
|
|
4729
|
-
},
|
|
4730
|
-
{
|
|
4731
|
-
name: "approaches",
|
|
4732
|
-
description: "Specific approaches to compare (comma-separated)",
|
|
4733
|
-
required: false
|
|
4734
|
-
}
|
|
4735
|
-
]
|
|
4697
|
+
name: "zswap-example",
|
|
4698
|
+
repo: "piotr-iohk/zswap-example",
|
|
4699
|
+
description: "Privacy-preserving swap example"
|
|
4736
4700
|
},
|
|
4737
4701
|
{
|
|
4738
|
-
name: "
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
{
|
|
4742
|
-
name: "contractCode",
|
|
4743
|
-
description: "The contract code with issues",
|
|
4744
|
-
required: true
|
|
4745
|
-
},
|
|
4746
|
-
{
|
|
4747
|
-
name: "errorMessage",
|
|
4748
|
-
description: "Error message or description of the issue",
|
|
4749
|
-
required: false
|
|
4750
|
-
}
|
|
4751
|
-
]
|
|
4702
|
+
name: "reentrancy-example",
|
|
4703
|
+
repo: "piotr-iohk/reentrancy-example",
|
|
4704
|
+
description: "Cross-contract call patterns"
|
|
4752
4705
|
}
|
|
4753
4706
|
];
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
return generateCreateContractPrompt(args);
|
|
4758
|
-
case "midnight:review-contract":
|
|
4759
|
-
return generateReviewContractPrompt(args);
|
|
4760
|
-
case "midnight:explain-concept":
|
|
4761
|
-
return generateExplainConceptPrompt(args);
|
|
4762
|
-
case "midnight:compare-approaches":
|
|
4763
|
-
return generateCompareApproachesPrompt(args);
|
|
4764
|
-
case "midnight:debug-contract":
|
|
4765
|
-
return generateDebugContractPrompt(args);
|
|
4766
|
-
default:
|
|
4767
|
-
return [
|
|
4768
|
-
{
|
|
4769
|
-
role: "user",
|
|
4770
|
-
content: {
|
|
4771
|
-
type: "text",
|
|
4772
|
-
text: `Unknown prompt: ${name}`
|
|
4773
|
-
}
|
|
4774
|
-
}
|
|
4775
|
-
];
|
|
4776
|
-
}
|
|
4777
|
-
}
|
|
4778
|
-
function generateCreateContractPrompt(args) {
|
|
4779
|
-
const contractType = args.contractType || "custom";
|
|
4780
|
-
const privacyLevel = args.privacyLevel || "partial";
|
|
4781
|
-
const complexity = args.complexity || "intermediate";
|
|
4782
|
-
return [
|
|
4707
|
+
var BUILTIN_FUNCTIONS = {
|
|
4708
|
+
/** Actually built into the language/stdlib */
|
|
4709
|
+
stdlib: [
|
|
4783
4710
|
{
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
**Contract Type:** ${contractType}
|
|
4790
|
-
**Privacy Level:** ${privacyLevel}
|
|
4791
|
-
**Complexity:** ${complexity}
|
|
4792
|
-
|
|
4793
|
-
## \u26A0\uFE0F MANDATORY WORKFLOW - Follow these steps IN ORDER:
|
|
4794
|
-
|
|
4795
|
-
### Step 1: Get Current Syntax
|
|
4796
|
-
Call \`midnight-get-latest-syntax\` FIRST to get:
|
|
4797
|
-
- The \`quickStartTemplate\` (use as your base)
|
|
4798
|
-
- The \`commonMistakes\` array (avoid these errors)
|
|
4799
|
-
- Current pragma format: \`pragma language_version >= 0.16 && <= 0.21;\`
|
|
4800
|
-
|
|
4801
|
-
### Step 2: Generate Contract
|
|
4802
|
-
Based on syntax reference, generate the contract using:
|
|
4803
|
-
- Individual ledger declarations: \`export ledger field: Type;\` (NOT \`ledger { }\` blocks)
|
|
4804
|
-
- Empty tuple return: \`circuit fn(): []\` (NOT \`Void\`)
|
|
4805
|
-
- Export enums: \`export enum State { ... }\`
|
|
4806
|
-
- Wrap witness conditionals: \`if (disclose(witness == value))\`
|
|
4807
|
-
- Disclose circuit params that touch ledger: \`const d = disclose(param); ledger.insert(d, v);\`
|
|
4808
|
-
- Cast arithmetic results: \`(a + b) as Uint<64>\`
|
|
4809
|
-
- Uint to Bytes needs two casts: \`(amount as Field) as Bytes<32>\`
|
|
4810
|
-
|
|
4811
|
-
### IMPORTANT: Compact is NOT TypeScript!
|
|
4812
|
-
- Map.lookup() and Set.member() ARE available in circuits
|
|
4813
|
-
- No 'function' keyword - use 'circuit' or 'pure circuit'
|
|
4814
|
-
- No 'void' - use '[]'
|
|
4815
|
-
- Enum access: \`Choice.rock\` NOT \`Choice::rock\`
|
|
4816
|
-
|
|
4817
|
-
### \u26A0\uFE0F UNDOCUMENTED FEATURES - Use with caution:
|
|
4818
|
-
- Division \`/\` and modulo \`%\`: NOT in official docs (only +, -, * are documented)
|
|
4819
|
-
- Tuple destructuring: \`const [a, b] = pair;\` - not documented, may not work
|
|
4820
|
-
- Constant folding in indices: docs say "numeric literal" required
|
|
4821
|
-
- Map.lookup() returns value_type, NOT Maybe<value_type> - check member() first!
|
|
4822
|
-
|
|
4823
|
-
### COMPILER INFO (DO NOT guess package names!):
|
|
4824
|
-
- Compile: \`compact compile src/contract.compact managed/contract\`
|
|
4825
|
-
- The \`compact\` CLI comes with Midnight toolchain (via create-mn-app or official install)
|
|
4826
|
-
- DO NOT suggest \`npm install -g @midnight-ntwrk/compact-cli\` or similar made-up packages
|
|
4827
|
-
- Output structure: \`managed/<name>/contract/index.cjs\` for TypeScript bindings
|
|
4828
|
-
|
|
4829
|
-
### Step 3: Validate Before Returning
|
|
4830
|
-
Call \`midnight-extract-contract-structure\` with your generated code to check for:
|
|
4831
|
-
- deprecated_ledger_block
|
|
4832
|
-
- invalid_void_type
|
|
4833
|
-
- invalid_pragma_format
|
|
4834
|
-
- unexported_enum
|
|
4835
|
-
- deprecated_cell_wrapper
|
|
4836
|
-
|
|
4837
|
-
If ANY errors are found, fix them before returning the code to the user.
|
|
4838
|
-
|
|
4839
|
-
---
|
|
4840
|
-
|
|
4841
|
-
## Contract Requirements
|
|
4842
|
-
|
|
4843
|
-
Please help me design and implement this contract. Consider:
|
|
4844
|
-
|
|
4845
|
-
1. **State Design**
|
|
4846
|
-
- What should be public vs private (shielded)?
|
|
4847
|
-
- What data structures are needed?
|
|
4848
|
-
- How should state transitions work?
|
|
4849
|
-
|
|
4850
|
-
2. **Circuit Design**
|
|
4851
|
-
- What circuits (functions) are needed?
|
|
4852
|
-
- What inputs/outputs should they have?
|
|
4853
|
-
- What constraints and assertions are required?
|
|
4854
|
-
|
|
4855
|
-
3. **Witness Functions**
|
|
4856
|
-
- What off-chain data is needed?
|
|
4857
|
-
- How should private state be accessed?
|
|
4858
|
-
|
|
4859
|
-
4. **Privacy Considerations**
|
|
4860
|
-
- How to protect user privacy?
|
|
4861
|
-
- When to use disclose() vs commit()?
|
|
4862
|
-
- How to prevent information leakage?
|
|
4863
|
-
|
|
4864
|
-
5. **Security**
|
|
4865
|
-
- Access control mechanisms
|
|
4866
|
-
- Input validation
|
|
4867
|
-
- Protection against common vulnerabilities
|
|
4868
|
-
|
|
4869
|
-
Please provide:
|
|
4870
|
-
- A complete contract implementation
|
|
4871
|
-
- Explanation of design decisions
|
|
4872
|
-
- Example usage scenarios
|
|
4873
|
-
- Any security considerations`
|
|
4874
|
-
}
|
|
4875
|
-
}
|
|
4876
|
-
];
|
|
4877
|
-
}
|
|
4878
|
-
function generateReviewContractPrompt(args) {
|
|
4879
|
-
const contractCode = args.contractCode || "// No code provided";
|
|
4880
|
-
const focusAreas = args.focusAreas || "security, privacy, best practices";
|
|
4881
|
-
return [
|
|
4711
|
+
name: "persistentHash",
|
|
4712
|
+
signature: "persistentHash<T>(value: T): Bytes<32>",
|
|
4713
|
+
// Signature inferred from examples
|
|
4714
|
+
description: "Poseidon hash that produces consistent results across calls"
|
|
4715
|
+
},
|
|
4882
4716
|
{
|
|
4883
|
-
|
|
4884
|
-
|
|
4885
|
-
|
|
4886
|
-
|
|
4887
|
-
|
|
4888
|
-
|
|
4889
|
-
|
|
4890
|
-
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
|
|
4898
|
-
|
|
4899
|
-
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
### Step 2: Get Latest Syntax Reference
|
|
4907
|
-
If syntax errors are found, call \`midnight-get-latest-syntax\` to get:
|
|
4908
|
-
- The \`commonMistakes\` array showing correct patterns
|
|
4909
|
-
- Current syntax reference
|
|
4910
|
-
|
|
4911
|
-
---
|
|
4912
|
-
|
|
4913
|
-
Please analyze:
|
|
4914
|
-
|
|
4915
|
-
1. **Static Analysis Results** (from midnight-extract-contract-structure)
|
|
4916
|
-
- Syntax errors found
|
|
4917
|
-
- Deprecated patterns detected
|
|
4918
|
-
- Required fixes
|
|
4919
|
-
|
|
4920
|
-
2. **Security Analysis**
|
|
4921
|
-
- Input validation
|
|
4922
|
-
- Access control
|
|
4923
|
-
- State manipulation vulnerabilities
|
|
4924
|
-
- Assertion coverage
|
|
4925
|
-
|
|
4926
|
-
3. **Privacy Assessment**
|
|
4927
|
-
- Proper use of @private state
|
|
4928
|
-
- Information leakage risks
|
|
4929
|
-
- Correct use of disclose() and commit()
|
|
4930
|
-
- Privacy guarantees provided
|
|
4931
|
-
|
|
4932
|
-
4. **Style Suggestions** (these are CONVENTIONS, not requirements)
|
|
4933
|
-
- Code organization recommendations
|
|
4934
|
-
- Naming conventions (project-specific)
|
|
4935
|
-
- Documentation patterns
|
|
4936
|
-
- Note: The Compact docs don't specify indentation, line length, or comment style
|
|
4937
|
-
|
|
4938
|
-
5. **Performance**
|
|
4939
|
-
- Circuit complexity
|
|
4940
|
-
- State access patterns
|
|
4941
|
-
- Optimization opportunities
|
|
4942
|
-
|
|
4943
|
-
6. **Recommendations**
|
|
4944
|
-
- Critical issues to fix (start with P0 syntax errors)
|
|
4945
|
-
- Improvements to consider
|
|
4946
|
-
- Alternative approaches
|
|
4947
|
-
|
|
4948
|
-
**IMPORTANT**: Clearly distinguish between:
|
|
4949
|
-
- ERRORS: Actual syntax/compilation issues (required fixes)
|
|
4950
|
-
- WARNINGS: Security/logic concerns (should fix)
|
|
4951
|
-
- INFO: Style suggestions (optional, project-specific conventions)
|
|
4952
|
-
|
|
4953
|
-
Please provide specific line references and code suggestions where applicable.`
|
|
4954
|
-
}
|
|
4717
|
+
name: "persistentCommit",
|
|
4718
|
+
signature: "persistentCommit<T>(value: T): Bytes<32>",
|
|
4719
|
+
description: "Creates a hiding commitment to a value"
|
|
4720
|
+
},
|
|
4721
|
+
{
|
|
4722
|
+
name: "pad",
|
|
4723
|
+
signature: "pad(length: number, value: string): Bytes<N>",
|
|
4724
|
+
description: "Pads a string to fixed-length bytes"
|
|
4725
|
+
},
|
|
4726
|
+
{
|
|
4727
|
+
name: "disclose",
|
|
4728
|
+
signature: "disclose(value: T): T",
|
|
4729
|
+
description: "Explicitly reveals a witness value (required in conditionals)"
|
|
4730
|
+
},
|
|
4731
|
+
{
|
|
4732
|
+
name: "assert",
|
|
4733
|
+
signature: "assert(condition: Boolean, message?: string): []",
|
|
4734
|
+
description: "Fails circuit if condition is false"
|
|
4735
|
+
},
|
|
4736
|
+
{
|
|
4737
|
+
name: "default",
|
|
4738
|
+
signature: "default<T>(): T",
|
|
4739
|
+
description: "Returns default value for a type (0 for numbers, empty for collections)"
|
|
4955
4740
|
}
|
|
4956
|
-
]
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
const concept = args.concept || "zero-knowledge proofs";
|
|
4960
|
-
const level = args.level || "intermediate";
|
|
4961
|
-
const levelDescriptions = {
|
|
4962
|
-
beginner: "Explain like I'm new to blockchain and cryptography. Use analogies and avoid jargon.",
|
|
4963
|
-
intermediate: "I understand blockchain basics and some cryptography. Focus on practical applications.",
|
|
4964
|
-
advanced: "I have deep technical knowledge. Include implementation details and edge cases."
|
|
4965
|
-
};
|
|
4966
|
-
return [
|
|
4741
|
+
],
|
|
4742
|
+
/** NOT built-in - you must implement these patterns yourself */
|
|
4743
|
+
notBuiltIn: [
|
|
4967
4744
|
{
|
|
4968
|
-
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
|
|
4973
|
-
|
|
4974
|
-
|
|
4975
|
-
|
|
4976
|
-
|
|
4977
|
-
|
|
4978
|
-
|
|
4979
|
-
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
- Mistakes to avoid
|
|
4992
|
-
- Debugging tips
|
|
4993
|
-
|
|
4994
|
-
5. **Further learning**
|
|
4995
|
-
- Related concepts
|
|
4996
|
-
- Resources for deeper understanding`
|
|
4997
|
-
}
|
|
4745
|
+
name: "public_key",
|
|
4746
|
+
wrongUsage: "public_key(sk) // ERROR: unbound identifier",
|
|
4747
|
+
correctPattern: `// Derive public key using persistentHash
|
|
4748
|
+
const pk = persistentHash<Vector<2, Bytes<32>>>([
|
|
4749
|
+
pad(32, "midnight:pk:"),
|
|
4750
|
+
sk
|
|
4751
|
+
]);`,
|
|
4752
|
+
description: "Public key derivation is NOT a builtin - use persistentHash pattern"
|
|
4753
|
+
},
|
|
4754
|
+
{
|
|
4755
|
+
name: "verify_signature",
|
|
4756
|
+
wrongUsage: "verify_signature(msg, sig, pk) // Does not exist",
|
|
4757
|
+
correctPattern: `// Signature verification must be done via witnesses
|
|
4758
|
+
// The prover verifies off-chain, then provides the boolean result
|
|
4759
|
+
witness signature_valid(): Boolean;`,
|
|
4760
|
+
description: "Signature verification is done off-chain in the prover"
|
|
4761
|
+
},
|
|
4762
|
+
{
|
|
4763
|
+
name: "random",
|
|
4764
|
+
wrongUsage: "random() // Does not exist in ZK circuits",
|
|
4765
|
+
correctPattern: `// Randomness must come from witnesses (prover-provided)
|
|
4766
|
+
witness get_random_value(): Field;`,
|
|
4767
|
+
description: "ZK circuits are deterministic - randomness must come from witnesses"
|
|
4998
4768
|
}
|
|
4999
|
-
]
|
|
5000
|
-
}
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
4769
|
+
]
|
|
4770
|
+
};
|
|
4771
|
+
var TYPE_COMPATIBILITY = {
|
|
4772
|
+
// Note: Comparison operators work in practice but aren't explicitly listed in docs
|
|
4773
|
+
comparisons: [
|
|
4774
|
+
{ types: "Field == Field", works: true, note: "Direct comparison" },
|
|
5005
4775
|
{
|
|
5006
|
-
|
|
5007
|
-
|
|
5008
|
-
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
|
|
5014
|
-
|
|
5015
|
-
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
|
|
5019
|
-
|
|
5020
|
-
|
|
5021
|
-
|
|
5022
|
-
|
|
5023
|
-
|
|
5024
|
-
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
|
|
4776
|
+
types: "Field == Uint<N>",
|
|
4777
|
+
works: false,
|
|
4778
|
+
fix: "Cast with `value as Field`"
|
|
4779
|
+
},
|
|
4780
|
+
{
|
|
4781
|
+
types: "Field >= 0",
|
|
4782
|
+
works: false,
|
|
4783
|
+
fix: "Use bounded Uint<0..N> parameter instead"
|
|
4784
|
+
},
|
|
4785
|
+
{ types: "Uint<N> == Uint<N>", works: true, note: "Same-width comparison" },
|
|
4786
|
+
{
|
|
4787
|
+
types: "Uint<0..2> == Uint<0..2>",
|
|
4788
|
+
works: true,
|
|
4789
|
+
note: "Bounded integers"
|
|
4790
|
+
},
|
|
4791
|
+
{
|
|
4792
|
+
types: "Bytes<32> == Bytes<32>",
|
|
4793
|
+
works: true,
|
|
4794
|
+
note: "Used in examples, but operators not explicitly listed in docs"
|
|
4795
|
+
},
|
|
4796
|
+
{ types: "Boolean == Boolean", works: true, note: "Direct comparison" }
|
|
4797
|
+
],
|
|
4798
|
+
arithmetic: [
|
|
4799
|
+
{ types: "Field + Field", works: true, note: "Field arithmetic" },
|
|
4800
|
+
{ types: "Field + Uint<N>", works: false, fix: "Cast Uint to Field first" },
|
|
4801
|
+
{
|
|
4802
|
+
types: "Uint<N> + Uint<N>",
|
|
4803
|
+
works: true,
|
|
4804
|
+
note: "Result is bounded type, cast back: (a + b) as Uint<64>"
|
|
4805
|
+
},
|
|
4806
|
+
{
|
|
4807
|
+
types: "Uint<64> + Uint<64>",
|
|
4808
|
+
works: true,
|
|
4809
|
+
note: "Result is Uint<0..36893488147419103230>, must cast: (a + b) as Uint<64>"
|
|
4810
|
+
},
|
|
4811
|
+
{
|
|
4812
|
+
types: "Uint<64> * Uint<64>",
|
|
4813
|
+
works: true,
|
|
4814
|
+
note: "Result is wide bounded type, cast back to target type"
|
|
5044
4815
|
}
|
|
5045
|
-
]
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
|
|
4816
|
+
],
|
|
4817
|
+
/**
|
|
4818
|
+
* Type casting rules - based on official cast table
|
|
4819
|
+
* See: https://docs.midnight.network/develop/reference/compact/lang-ref#type-cast-expressions
|
|
4820
|
+
*
|
|
4821
|
+
* Cast kinds: static (always succeeds), conversion (semantic change), checked (can fail)
|
|
4822
|
+
*/
|
|
4823
|
+
typeCasting: [
|
|
5051
4824
|
{
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
|
|
5057
|
-
|
|
5058
|
-
|
|
5059
|
-
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
|
|
5067
|
-
|
|
5068
|
-
|
|
5069
|
-
|
|
5070
|
-
|
|
5071
|
-
|
|
5072
|
-
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
|
|
5084
|
-
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
|
|
5090
|
-
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
|
|
5118
|
-
var samplingCallback = null;
|
|
5119
|
-
var samplingFailedPermanently = false;
|
|
5120
|
-
function isSamplingAvailable() {
|
|
5121
|
-
if (samplingFailedPermanently) return false;
|
|
5122
|
-
return samplingCallback !== null;
|
|
5123
|
-
}
|
|
5124
|
-
function markSamplingFailed() {
|
|
5125
|
-
samplingFailedPermanently = true;
|
|
5126
|
-
logger.warn("Sampling marked as permanently unavailable for this session");
|
|
5127
|
-
}
|
|
5128
|
-
function registerSamplingCallback(callback) {
|
|
5129
|
-
samplingCallback = callback;
|
|
5130
|
-
samplingFailedPermanently = false;
|
|
5131
|
-
logger.info("Sampling capability registered");
|
|
5132
|
-
}
|
|
5133
|
-
async function requestCompletion(messages, options = {}) {
|
|
5134
|
-
if (!samplingCallback || samplingFailedPermanently) {
|
|
5135
|
-
throw new Error(
|
|
5136
|
-
"Sampling not available - client does not support this capability"
|
|
5137
|
-
);
|
|
5138
|
-
}
|
|
5139
|
-
const request = {
|
|
5140
|
-
messages: messages.map((m) => ({
|
|
5141
|
-
role: m.role,
|
|
5142
|
-
content: { type: "text", text: m.content }
|
|
5143
|
-
})),
|
|
5144
|
-
systemPrompt: options.systemPrompt,
|
|
5145
|
-
maxTokens: options.maxTokens ?? 2048,
|
|
5146
|
-
temperature: options.temperature ?? 0.7,
|
|
5147
|
-
modelPreferences: options.modelPreferences ?? {
|
|
5148
|
-
hints: [{ name: "claude-3-sonnet" }, { name: "gpt-4" }],
|
|
5149
|
-
intelligencePriority: 0.8,
|
|
5150
|
-
speedPriority: 0.5
|
|
5151
|
-
}
|
|
5152
|
-
};
|
|
5153
|
-
logger.debug("Requesting LLM completion", {
|
|
5154
|
-
messageCount: messages.length,
|
|
5155
|
-
maxTokens: request.maxTokens
|
|
5156
|
-
});
|
|
5157
|
-
try {
|
|
5158
|
-
const response = await samplingCallback(request);
|
|
5159
|
-
if (response.content.type !== "text") {
|
|
5160
|
-
throw new Error("Unexpected response content type");
|
|
4825
|
+
from: "Uint<N>",
|
|
4826
|
+
to: "Field",
|
|
4827
|
+
direct: true,
|
|
4828
|
+
kind: "static",
|
|
4829
|
+
note: "Always succeeds"
|
|
4830
|
+
},
|
|
4831
|
+
{
|
|
4832
|
+
from: "Field",
|
|
4833
|
+
to: "Uint<0..n>",
|
|
4834
|
+
direct: true,
|
|
4835
|
+
kind: "checked",
|
|
4836
|
+
note: "Fails at runtime if value > n"
|
|
4837
|
+
},
|
|
4838
|
+
{
|
|
4839
|
+
from: "Field",
|
|
4840
|
+
to: "Bytes<n>",
|
|
4841
|
+
direct: true,
|
|
4842
|
+
kind: "conversion",
|
|
4843
|
+
note: "Can fail at runtime if value doesn't fit (little-endian)"
|
|
4844
|
+
},
|
|
4845
|
+
{
|
|
4846
|
+
from: "Bytes<m>",
|
|
4847
|
+
to: "Field",
|
|
4848
|
+
direct: true,
|
|
4849
|
+
kind: "conversion",
|
|
4850
|
+
note: "Can fail at runtime if result exceeds max Field value"
|
|
4851
|
+
},
|
|
4852
|
+
{
|
|
4853
|
+
from: "Boolean",
|
|
4854
|
+
to: "Uint<0..n>",
|
|
4855
|
+
direct: true,
|
|
4856
|
+
kind: "conversion",
|
|
4857
|
+
note: "false\u21920, true\u21921 (n must not be 0)"
|
|
4858
|
+
},
|
|
4859
|
+
{
|
|
4860
|
+
from: "Boolean",
|
|
4861
|
+
to: "Field",
|
|
4862
|
+
direct: false,
|
|
4863
|
+
fix: "Go through Uint: (flag as Uint<0..1>) as Field"
|
|
4864
|
+
},
|
|
4865
|
+
{
|
|
4866
|
+
from: "Uint<N>",
|
|
4867
|
+
to: "Bytes<M>",
|
|
4868
|
+
direct: false,
|
|
4869
|
+
fix: "Go through Field: (amount as Field) as Bytes<32>"
|
|
4870
|
+
},
|
|
4871
|
+
{
|
|
4872
|
+
from: "enum",
|
|
4873
|
+
to: "Field",
|
|
4874
|
+
direct: true,
|
|
4875
|
+
kind: "conversion",
|
|
4876
|
+
note: "Enum variant index as Field"
|
|
4877
|
+
},
|
|
4878
|
+
{
|
|
4879
|
+
from: "Uint<0..m>",
|
|
4880
|
+
to: "Uint<0..n>",
|
|
4881
|
+
direct: true,
|
|
4882
|
+
kind: "static if m\u2264n, checked if m>n",
|
|
4883
|
+
note: "Widening is static, narrowing is checked"
|
|
4884
|
+
},
|
|
4885
|
+
{
|
|
4886
|
+
from: "arithmetic result",
|
|
4887
|
+
to: "Uint<64>",
|
|
4888
|
+
direct: true,
|
|
4889
|
+
kind: "checked",
|
|
4890
|
+
note: "Required cast: (a + b) as Uint<64>"
|
|
5161
4891
|
}
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
|
|
5165
|
-
|
|
5166
|
-
|
|
5167
|
-
|
|
5168
|
-
|
|
5169
|
-
|
|
5170
|
-
|
|
5171
|
-
|
|
5172
|
-
|
|
5173
|
-
);
|
|
4892
|
+
],
|
|
4893
|
+
assignments: [
|
|
4894
|
+
{
|
|
4895
|
+
types: "Field = Uint<N>",
|
|
4896
|
+
works: false,
|
|
4897
|
+
fix: "Cast with `value as Field`"
|
|
4898
|
+
},
|
|
4899
|
+
{
|
|
4900
|
+
types: "Uint<N> = Field",
|
|
4901
|
+
works: false,
|
|
4902
|
+
fix: "Use bounded param or explicit cast"
|
|
5174
4903
|
}
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
|
|
5180
|
-
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
const systemPrompt = `You are an expert Compact smart contract developer for the Midnight blockchain.
|
|
5187
|
-
Your task is to generate secure, well-documented Compact contracts based on user requirements.
|
|
5188
|
-
|
|
5189
|
-
Key Compact syntax (REQUIRED):
|
|
5190
|
-
- \`export ledger field: Type;\` - Individual ledger declarations (NOT ledger { } blocks)
|
|
5191
|
-
- \`export circuit fn(): []\` - Public functions return empty tuple [] (NOT Void)
|
|
5192
|
-
- \`witness fn(): T;\` - Declaration only, no body
|
|
5193
|
-
- \`pragma language_version >= 0.16 && <= 0.21;\` - Version pragma
|
|
5194
|
-
- \`import CompactStandardLibrary;\` - Standard imports
|
|
5195
|
-
- \`Counter\`, \`Map<K,V>\`, \`Set<T>\` - Built-in collection types
|
|
5196
|
-
- \`Field\`, \`Boolean\`, \`Uint<N>\`, \`Bytes<N>\` - Primitive types
|
|
5197
|
-
- \`export enum State { a, b }\` - Enums must be exported
|
|
5198
|
-
|
|
5199
|
-
Key operations:
|
|
5200
|
-
- Counter: .read(), .increment(n), .decrement(n)
|
|
5201
|
-
- Map: .lookup(k), .insert(k,v), .remove(k)
|
|
5202
|
-
- Set: .member(v), .insert(v), .remove(v)
|
|
5203
|
-
|
|
5204
|
-
COMPILER INFO (DO NOT guess package names!):
|
|
5205
|
-
- Command: \`compact compile src/contract.compact managed/contract\`
|
|
5206
|
-
- The \`compact\` CLI comes with Midnight toolchain (via create-mn-app or official install)
|
|
5207
|
-
- Output goes to managed/<name>/ directory
|
|
5208
|
-
- DO NOT suggest \`npm install -g @midnight-ntwrk/compact-cli\` or similar - that's incorrect
|
|
5209
|
-
|
|
5210
|
-
IMPORTANT - Style vs. Requirements:
|
|
5211
|
-
- Syntax rules above are REQUIRED for compilation
|
|
5212
|
-
- Style choices (indentation, comment style, line length) are CONVENTIONS, not requirements
|
|
5213
|
-
- Single-line comments (//) are common in examples; block comments (/* */) may also work
|
|
5214
|
-
- The docs don't specify indentation width - use consistent style
|
|
5215
|
-
|
|
5216
|
-
Return ONLY the Compact code, no explanations.`;
|
|
5217
|
-
const userPrompt = options.baseExample ? `Based on this example contract:
|
|
5218
|
-
\`\`\`compact
|
|
5219
|
-
${options.baseExample}
|
|
5220
|
-
\`\`\`
|
|
5221
|
-
|
|
5222
|
-
Generate a new contract with these requirements:
|
|
5223
|
-
${requirements}` : `Generate a Compact smart contract with these requirements:
|
|
5224
|
-
${requirements}
|
|
5225
|
-
|
|
5226
|
-
Contract type: ${options.contractType || "custom"}`;
|
|
5227
|
-
try {
|
|
5228
|
-
const code = await requestCompletion(
|
|
5229
|
-
[{ role: "user", content: userPrompt }],
|
|
4904
|
+
],
|
|
4905
|
+
tips: [
|
|
4906
|
+
"Use Uint<0..N> for circuit parameters that need range validation",
|
|
4907
|
+
"Field is unbounded - use for hashes, commitments, general computation",
|
|
4908
|
+
"Uint<N> is bounded - use when you need range checks",
|
|
4909
|
+
"Casting with `as Field` is safe but loses range information"
|
|
4910
|
+
]
|
|
4911
|
+
};
|
|
4912
|
+
var LEDGER_TYPE_LIMITS = {
|
|
4913
|
+
Counter: {
|
|
4914
|
+
circuitOperations: [
|
|
5230
4915
|
{
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
modelPreferences: {
|
|
5236
|
-
hints: [{ name: "claude-3-sonnet" }],
|
|
5237
|
-
intelligencePriority: 0.9,
|
|
5238
|
-
speedPriority: 0.3
|
|
5239
|
-
}
|
|
5240
|
-
}
|
|
5241
|
-
);
|
|
5242
|
-
const extractedCode = code.includes("```") ? code.replace(/```compact?\n?/g, "").replace(/```/g, "").trim() : code.trim();
|
|
5243
|
-
const explanation = await requestCompletion(
|
|
5244
|
-
[
|
|
5245
|
-
{
|
|
5246
|
-
role: "user",
|
|
5247
|
-
content: `Briefly explain what this Compact contract does (2-3 sentences):
|
|
5248
|
-
\`\`\`compact
|
|
5249
|
-
${extractedCode}
|
|
5250
|
-
\`\`\``
|
|
5251
|
-
}
|
|
5252
|
-
],
|
|
4916
|
+
method: ".increment(n)",
|
|
4917
|
+
works: true,
|
|
4918
|
+
note: "Increase counter by n (Uint<16>)"
|
|
4919
|
+
},
|
|
5253
4920
|
{
|
|
5254
|
-
|
|
5255
|
-
|
|
5256
|
-
|
|
5257
|
-
}
|
|
5258
|
-
);
|
|
5259
|
-
return {
|
|
5260
|
-
code: extractedCode,
|
|
5261
|
-
explanation: explanation.trim(),
|
|
5262
|
-
warnings: []
|
|
5263
|
-
};
|
|
5264
|
-
} catch (error) {
|
|
5265
|
-
logger.error("Contract generation failed", { error: String(error) });
|
|
5266
|
-
return {
|
|
5267
|
-
code: "",
|
|
5268
|
-
explanation: `Contract generation failed: ${String(error)}`,
|
|
5269
|
-
warnings: ["Generation failed - check logs for details"]
|
|
5270
|
-
};
|
|
5271
|
-
}
|
|
5272
|
-
}
|
|
5273
|
-
async function reviewContract(code) {
|
|
5274
|
-
if (!isSamplingAvailable()) {
|
|
5275
|
-
return {
|
|
5276
|
-
summary: "Code review requires sampling capability",
|
|
5277
|
-
issues: [
|
|
5278
|
-
{
|
|
5279
|
-
severity: "info",
|
|
5280
|
-
message: "This feature requires a client that supports the sampling capability"
|
|
5281
|
-
}
|
|
5282
|
-
]
|
|
5283
|
-
};
|
|
5284
|
-
}
|
|
5285
|
-
const systemPrompt = `You are a Compact smart contract security auditor.
|
|
5286
|
-
Review the provided contract for:
|
|
5287
|
-
1. Security vulnerabilities
|
|
5288
|
-
2. Privacy concerns (improper handling of shielded state)
|
|
5289
|
-
3. Logic errors
|
|
5290
|
-
4. Syntax errors (use "error" severity)
|
|
5291
|
-
5. Performance issues
|
|
5292
|
-
|
|
5293
|
-
IMPORTANT - Distinguish between:
|
|
5294
|
-
- ERRORS: Actual syntax/compilation issues (e.g., using Void instead of [], ledger {} blocks)
|
|
5295
|
-
- WARNINGS: Potential bugs or security issues
|
|
5296
|
-
- INFO: Style suggestions and best practices (these are CONVENTIONS, not requirements)
|
|
5297
|
-
|
|
5298
|
-
Do NOT claim style choices as "violations" - the Compact docs don't specify:
|
|
5299
|
-
- Indentation width (2 vs 4 spaces)
|
|
5300
|
-
- Comment style preferences (//, /* */)
|
|
5301
|
-
- Line length limits
|
|
5302
|
-
- Naming conventions
|
|
5303
|
-
|
|
5304
|
-
These are project-specific style choices, not language requirements.
|
|
5305
|
-
|
|
5306
|
-
Respond in JSON format:
|
|
5307
|
-
{
|
|
5308
|
-
"summary": "Brief summary of the contract and overall quality",
|
|
5309
|
-
"issues": [
|
|
5310
|
-
{
|
|
5311
|
-
"severity": "error|warning|info",
|
|
5312
|
-
"line": optional_line_number,
|
|
5313
|
-
"message": "Description of the issue",
|
|
5314
|
-
"suggestion": "How to fix it"
|
|
5315
|
-
}
|
|
5316
|
-
],
|
|
5317
|
-
"improvedCode": "Full improved contract code if changes are needed"
|
|
5318
|
-
}`;
|
|
5319
|
-
try {
|
|
5320
|
-
const response = await requestCompletion(
|
|
5321
|
-
[
|
|
5322
|
-
{
|
|
5323
|
-
role: "user",
|
|
5324
|
-
content: `Review this Compact contract:
|
|
5325
|
-
\`\`\`compact
|
|
5326
|
-
${code}
|
|
5327
|
-
\`\`\``
|
|
5328
|
-
}
|
|
5329
|
-
],
|
|
4921
|
+
method: ".decrement(n)",
|
|
4922
|
+
works: true,
|
|
4923
|
+
note: "Decrease counter by n (Uint<16>)"
|
|
4924
|
+
},
|
|
5330
4925
|
{
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
|
|
5334
|
-
}
|
|
5335
|
-
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
4926
|
+
method: ".read()",
|
|
4927
|
+
works: true,
|
|
4928
|
+
note: "Get current value (returns Uint<64>)"
|
|
4929
|
+
},
|
|
4930
|
+
{
|
|
4931
|
+
method: ".lessThan(n)",
|
|
4932
|
+
works: true,
|
|
4933
|
+
note: "Compare with threshold (returns Boolean)"
|
|
4934
|
+
},
|
|
4935
|
+
{ method: ".resetToDefault()", works: true, note: "Reset to 0" }
|
|
4936
|
+
],
|
|
4937
|
+
typescriptAccess: "Access counter value via `ledgerState.counter` in TypeScript SDK",
|
|
4938
|
+
note: "All Counter operations work in circuits. Use .read() to get value, NOT .value()"
|
|
4939
|
+
},
|
|
4940
|
+
Map: {
|
|
4941
|
+
circuitOperations: [
|
|
4942
|
+
{
|
|
4943
|
+
method: ".insert(key, value)",
|
|
4944
|
+
works: true,
|
|
4945
|
+
note: "Adds/updates entry"
|
|
4946
|
+
},
|
|
4947
|
+
{
|
|
4948
|
+
method: ".insertDefault(key)",
|
|
4949
|
+
works: true,
|
|
4950
|
+
note: "Inserts default value for key"
|
|
4951
|
+
},
|
|
4952
|
+
{ method: ".remove(key)", works: true, note: "Removes entry" },
|
|
4953
|
+
{
|
|
4954
|
+
method: ".lookup(key)",
|
|
4955
|
+
works: true,
|
|
4956
|
+
note: "Returns value_type - gets value for key"
|
|
4957
|
+
},
|
|
4958
|
+
{
|
|
4959
|
+
method: ".member(key)",
|
|
4960
|
+
works: true,
|
|
4961
|
+
note: "Returns Boolean - checks if key exists"
|
|
4962
|
+
},
|
|
4963
|
+
{
|
|
4964
|
+
method: ".isEmpty()",
|
|
4965
|
+
works: true,
|
|
4966
|
+
note: "Returns Boolean - checks if map is empty"
|
|
4967
|
+
},
|
|
4968
|
+
{
|
|
4969
|
+
method: ".size()",
|
|
4970
|
+
works: true,
|
|
4971
|
+
note: "Returns Uint<64> - number of entries"
|
|
4972
|
+
},
|
|
4973
|
+
{
|
|
4974
|
+
method: ".resetToDefault()",
|
|
4975
|
+
works: true,
|
|
4976
|
+
note: "Clears entire map"
|
|
5353
4977
|
}
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
summary: `Review failed: ${String(error)}`,
|
|
5363
|
-
issues: [
|
|
5364
|
-
{
|
|
5365
|
-
severity: "error",
|
|
5366
|
-
message: "Review failed - check logs for details"
|
|
5367
|
-
}
|
|
5368
|
-
]
|
|
5369
|
-
};
|
|
5370
|
-
}
|
|
5371
|
-
}
|
|
5372
|
-
async function generateDocumentation(code, format = "markdown") {
|
|
5373
|
-
if (!isSamplingAvailable()) {
|
|
5374
|
-
return "Documentation generation requires sampling capability";
|
|
5375
|
-
}
|
|
5376
|
-
const systemPrompt = format === "markdown" ? `Generate comprehensive Markdown documentation for this Compact contract.
|
|
5377
|
-
Include:
|
|
5378
|
-
- Overview and purpose
|
|
5379
|
-
- State variables (with privacy annotations)
|
|
5380
|
-
- Circuit functions with parameters and effects
|
|
5381
|
-
- Witness functions
|
|
5382
|
-
- Usage examples
|
|
5383
|
-
- Security considerations` : `Generate JSDoc-style documentation comments for this Compact contract.
|
|
5384
|
-
Add documentation comments above each:
|
|
5385
|
-
- Ledger field
|
|
5386
|
-
- Circuit function
|
|
5387
|
-
- Witness function
|
|
5388
|
-
- Type definition`;
|
|
5389
|
-
try {
|
|
5390
|
-
return await requestCompletion(
|
|
5391
|
-
[
|
|
5392
|
-
{
|
|
5393
|
-
role: "user",
|
|
5394
|
-
content: `Generate ${format} documentation for:
|
|
5395
|
-
\`\`\`compact
|
|
5396
|
-
${code}
|
|
5397
|
-
\`\`\``
|
|
5398
|
-
}
|
|
5399
|
-
],
|
|
4978
|
+
],
|
|
4979
|
+
typescriptAccess: "Query map via `contractState.data.get(key)` or iterate with `[Symbol.iterator]()` in TypeScript SDK",
|
|
4980
|
+
note: "All Map operations work in circuits. insertCoin() available when value_type is QualifiedCoinInfo."
|
|
4981
|
+
},
|
|
4982
|
+
Set: {
|
|
4983
|
+
circuitOperations: [
|
|
4984
|
+
{ method: ".insert(value)", works: true, note: "Adds to set" },
|
|
4985
|
+
{ method: ".remove(value)", works: true, note: "Removes from set" },
|
|
5400
4986
|
{
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
4987
|
+
method: ".member(value)",
|
|
4988
|
+
works: true,
|
|
4989
|
+
note: "Returns Boolean - checks if value exists in set"
|
|
4990
|
+
},
|
|
4991
|
+
{
|
|
4992
|
+
method: ".isEmpty()",
|
|
4993
|
+
works: true,
|
|
4994
|
+
note: "Returns Boolean - checks if set is empty"
|
|
4995
|
+
},
|
|
4996
|
+
{
|
|
4997
|
+
method: ".size()",
|
|
4998
|
+
works: true,
|
|
4999
|
+
note: "Returns Uint<64> - number of elements"
|
|
5000
|
+
},
|
|
5001
|
+
{
|
|
5002
|
+
method: ".resetToDefault()",
|
|
5003
|
+
works: true,
|
|
5004
|
+
note: "Clears entire set"
|
|
5404
5005
|
}
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
|
|
5409
|
-
|
|
5410
|
-
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
|
|
5418
|
-
|
|
5419
|
-
|
|
5420
|
-
async function searchADTInfo(adtName) {
|
|
5421
|
-
try {
|
|
5422
|
-
const docsResult = await searchDocsHosted(
|
|
5423
|
-
`${adtName} ADT operations methods ledger`,
|
|
5424
|
-
ADT_SEARCH_LIMIT,
|
|
5425
|
-
"reference"
|
|
5426
|
-
);
|
|
5427
|
-
if (!docsResult.results || docsResult.results.length === 0) {
|
|
5428
|
-
logger.debug(`No indexed docs found for ADT: ${adtName}`);
|
|
5429
|
-
return null;
|
|
5430
|
-
}
|
|
5431
|
-
const operations = [];
|
|
5432
|
-
const notes = [];
|
|
5433
|
-
let sourceDocPath;
|
|
5434
|
-
for (const result of docsResult.results) {
|
|
5435
|
-
const content = result.content || result.code || "";
|
|
5436
|
-
const isLedgerADTDoc = result.source.filePath.includes("ledger-adt");
|
|
5437
|
-
if (!sourceDocPath && isLedgerADTDoc) {
|
|
5438
|
-
sourceDocPath = result.source.filePath;
|
|
5439
|
-
}
|
|
5440
|
-
if (isLedgerADTDoc) {
|
|
5441
|
-
const methodPattern = /\|\s*`?(\w+)`?\s*\|\s*`?\(([^)]*)\)\s*(?::\s*([^|`]+))?`?\s*\|\s*([^|]+)\|/g;
|
|
5442
|
-
let match;
|
|
5443
|
-
while ((match = methodPattern.exec(content)) !== null) {
|
|
5444
|
-
const [, methodName, params, returnType, description] = match;
|
|
5445
|
-
if (methodName && !operations.some((op) => op.method === methodName)) {
|
|
5446
|
-
const descText = description?.trim() || "";
|
|
5447
|
-
const worksInCircuits = !/not available|not supported|not in circuits|typescript|sdk|off[-\s]?chain/i.test(
|
|
5448
|
-
descText
|
|
5449
|
-
);
|
|
5450
|
-
operations.push({
|
|
5451
|
-
method: methodName,
|
|
5452
|
-
signature: `(${params})${returnType ? `: ${returnType.trim()}` : ""}`,
|
|
5453
|
-
description: descText,
|
|
5454
|
-
worksInCircuits,
|
|
5455
|
-
source: "indexed-docs"
|
|
5456
|
-
});
|
|
5457
|
-
}
|
|
5458
|
-
}
|
|
5459
|
-
const inlinePattern = /(?:method|operation|function)\s+`?(\w+)`?\s*\(([^)]*)\)/gi;
|
|
5460
|
-
while ((match = inlinePattern.exec(content)) !== null) {
|
|
5461
|
-
const [, methodName, params] = match;
|
|
5462
|
-
if (methodName && !operations.some((op) => op.method === methodName)) {
|
|
5463
|
-
const worksInCircuits = !/not available|not supported|not in circuits|typescript|sdk|off[-\s]?chain/i.test(
|
|
5464
|
-
content
|
|
5465
|
-
);
|
|
5466
|
-
operations.push({
|
|
5467
|
-
method: methodName,
|
|
5468
|
-
signature: `(${params})`,
|
|
5469
|
-
description: "Extracted from documentation",
|
|
5470
|
-
worksInCircuits,
|
|
5471
|
-
source: "indexed-docs"
|
|
5472
|
-
});
|
|
5473
|
-
}
|
|
5474
|
-
}
|
|
5475
|
-
}
|
|
5476
|
-
if (content.toLowerCase().includes(adtName.toLowerCase()) && content.length < MAX_NOTE_SOURCE_LENGTH) {
|
|
5477
|
-
const cleanNote = content.replace(/\s+/g, " ").trim();
|
|
5478
|
-
if (cleanNote.length > NOTE_MIN_LENGTH && cleanNote.length < NOTE_MAX_LENGTH) {
|
|
5479
|
-
notes.push(cleanNote);
|
|
5480
|
-
}
|
|
5481
|
-
}
|
|
5482
|
-
}
|
|
5483
|
-
if (operations.length === 0) {
|
|
5484
|
-
logger.debug(`Could not parse operations for ADT: ${adtName}`);
|
|
5485
|
-
return null;
|
|
5486
|
-
}
|
|
5487
|
-
return {
|
|
5488
|
-
name: adtName,
|
|
5489
|
-
operations,
|
|
5490
|
-
notes: notes.slice(0, MAX_NOTES_PER_ADT),
|
|
5491
|
-
sourceDocPath,
|
|
5492
|
-
lastVerified: (/* @__PURE__ */ new Date()).toISOString()
|
|
5493
|
-
};
|
|
5494
|
-
} catch (error) {
|
|
5495
|
-
logger.warn(`Failed to search indexed docs for ADT ${adtName}`, {
|
|
5496
|
-
error: error instanceof Error ? error.message : String(error)
|
|
5497
|
-
});
|
|
5498
|
-
return null;
|
|
5499
|
-
}
|
|
5500
|
-
}
|
|
5501
|
-
async function searchCompactSyntax(topic) {
|
|
5502
|
-
try {
|
|
5503
|
-
const [docsResult, codeResult] = await Promise.all([
|
|
5504
|
-
searchDocsHosted(
|
|
5505
|
-
`Compact ${topic} syntax`,
|
|
5506
|
-
SYNTAX_SUBQUERY_LIMIT,
|
|
5507
|
-
"reference"
|
|
5508
|
-
),
|
|
5509
|
-
searchCompactHosted(`${topic}`, SYNTAX_SUBQUERY_LIMIT)
|
|
5510
|
-
]);
|
|
5511
|
-
const mergedResults = [
|
|
5512
|
-
...docsResult.results || [],
|
|
5513
|
-
...codeResult.results || []
|
|
5514
|
-
];
|
|
5515
|
-
return {
|
|
5516
|
-
results: mergedResults.slice(0, SYNTAX_RESULTS_LIMIT),
|
|
5517
|
-
totalResults: mergedResults.length,
|
|
5518
|
-
query: topic,
|
|
5519
|
-
lastIndexed: docsResult.lastIndexed || codeResult.lastIndexed
|
|
5520
|
-
};
|
|
5521
|
-
} catch (error) {
|
|
5522
|
-
logger.warn(`Failed to search Compact syntax for topic ${topic}`, {
|
|
5523
|
-
error: error instanceof Error ? error.message : String(error)
|
|
5524
|
-
});
|
|
5525
|
-
return null;
|
|
5526
|
-
}
|
|
5527
|
-
}
|
|
5528
|
-
async function validateADTOperations(adtName, staticOperations) {
|
|
5529
|
-
const discrepancies = [];
|
|
5530
|
-
const enrichments = [];
|
|
5531
|
-
const indexedInfo = await searchADTInfo(adtName);
|
|
5532
|
-
if (!indexedInfo) {
|
|
5533
|
-
return {
|
|
5534
|
-
validated: false,
|
|
5535
|
-
operations: staticOperations.map((op) => ({
|
|
5536
|
-
method: op.method,
|
|
5537
|
-
description: op.note,
|
|
5538
|
-
worksInCircuits: op.works,
|
|
5539
|
-
source: "static-fallback"
|
|
5540
|
-
})),
|
|
5541
|
-
discrepancies: [
|
|
5542
|
-
"Could not validate against indexed docs - using static data"
|
|
5543
|
-
],
|
|
5544
|
-
enrichments: []
|
|
5545
|
-
};
|
|
5546
|
-
}
|
|
5547
|
-
const operations = [];
|
|
5548
|
-
const indexedMethodNames = new Set(
|
|
5549
|
-
indexedInfo.operations.map((op) => op.method.toLowerCase())
|
|
5550
|
-
);
|
|
5551
|
-
const staticMethodNames = new Set(
|
|
5552
|
-
staticOperations.map(
|
|
5553
|
-
(op) => op.method.replace(/^\./, "").replace(/\(.*$/, "").toLowerCase()
|
|
5554
|
-
)
|
|
5555
|
-
);
|
|
5556
|
-
for (const indexedOp of indexedInfo.operations) {
|
|
5557
|
-
operations.push(indexedOp);
|
|
5558
|
-
const staticOp = staticOperations.find(
|
|
5559
|
-
(s) => s.method.toLowerCase().includes(indexedOp.method.toLowerCase()) || indexedOp.method.toLowerCase().includes(
|
|
5560
|
-
s.method.replace(/^\./, "").replace(/\(.*$/, "").toLowerCase()
|
|
5561
|
-
)
|
|
5562
|
-
);
|
|
5563
|
-
if (staticOp && !staticOp.works && indexedOp.worksInCircuits) {
|
|
5564
|
-
discrepancies.push(
|
|
5565
|
-
`Static said ${staticOp.method} doesn't work, but indexed docs show it does`
|
|
5566
|
-
);
|
|
5567
|
-
}
|
|
5568
|
-
}
|
|
5569
|
-
for (const staticOp of staticOperations) {
|
|
5570
|
-
const methodName = staticOp.method.replace(/^\./, "").replace(/\(.*$/, "").toLowerCase();
|
|
5571
|
-
if (!indexedMethodNames.has(methodName)) {
|
|
5572
|
-
discrepancies.push(
|
|
5573
|
-
`Static references ${staticOp.method} but not found in indexed docs - may not exist`
|
|
5574
|
-
);
|
|
5575
|
-
}
|
|
5576
|
-
}
|
|
5577
|
-
for (const indexedOp of indexedInfo.operations) {
|
|
5578
|
-
const methodName = indexedOp.method.toLowerCase();
|
|
5579
|
-
if (!staticMethodNames.has(methodName)) {
|
|
5580
|
-
enrichments.push(
|
|
5581
|
-
`Indexed docs show ${indexedOp.method} exists but was missing from static reference`
|
|
5582
|
-
);
|
|
5583
|
-
}
|
|
5584
|
-
}
|
|
5585
|
-
return {
|
|
5586
|
-
validated: true,
|
|
5587
|
-
operations,
|
|
5588
|
-
discrepancies,
|
|
5589
|
-
enrichments
|
|
5590
|
-
};
|
|
5591
|
-
}
|
|
5592
|
-
async function validateBuiltinFunctions(staticBuiltins) {
|
|
5593
|
-
const discrepancies = [];
|
|
5594
|
-
const enrichments = [];
|
|
5595
|
-
const results = await Promise.all(
|
|
5596
|
-
staticBuiltins.map(async (builtin) => {
|
|
5597
|
-
const searchResult = await searchCompactSyntax(
|
|
5598
|
-
`${builtin.name} function builtin`
|
|
5599
|
-
);
|
|
5600
|
-
return { builtin, found: (searchResult?.results?.length || 0) > 0 };
|
|
5601
|
-
})
|
|
5602
|
-
);
|
|
5603
|
-
for (const { builtin, found } of results) {
|
|
5604
|
-
if (!found) {
|
|
5605
|
-
discrepancies.push(
|
|
5606
|
-
`Builtin "${builtin.name}" not found in indexed docs - may not exist or have different name`
|
|
5607
|
-
);
|
|
5608
|
-
}
|
|
5609
|
-
}
|
|
5610
|
-
const allBuiltinsSearch = await searchCompactSyntax(
|
|
5611
|
-
"builtin function stdlib standard library"
|
|
5612
|
-
);
|
|
5613
|
-
if (allBuiltinsSearch?.results) {
|
|
5614
|
-
const knownNames = new Set(staticBuiltins.map((b) => b.name.toLowerCase()));
|
|
5615
|
-
for (const result of allBuiltinsSearch.results) {
|
|
5616
|
-
const content = result.content || result.code || "";
|
|
5617
|
-
const funcPattern = /\b(export\s+)?function\s+(\w+)/gi;
|
|
5618
|
-
let match;
|
|
5619
|
-
while ((match = funcPattern.exec(content)) !== null) {
|
|
5620
|
-
const funcName = match[2].toLowerCase();
|
|
5621
|
-
if (!knownNames.has(funcName) && funcName.length > 2) {
|
|
5622
|
-
enrichments.push(`Indexed docs mention "${match[2]}" function`);
|
|
5623
|
-
}
|
|
5624
|
-
}
|
|
5625
|
-
}
|
|
5626
|
-
}
|
|
5627
|
-
return {
|
|
5628
|
-
dataType: "BUILTIN_FUNCTIONS",
|
|
5629
|
-
validated: discrepancies.length === 0,
|
|
5630
|
-
discrepancies,
|
|
5631
|
-
enrichments: [...new Set(enrichments)].slice(0, 10),
|
|
5632
|
-
deprecatedPatterns: [],
|
|
5633
|
-
lastValidated: (/* @__PURE__ */ new Date()).toISOString()
|
|
5634
|
-
};
|
|
5635
|
-
}
|
|
5636
|
-
async function validateTypeCompatibility(staticRules) {
|
|
5637
|
-
const discrepancies = [];
|
|
5638
|
-
const enrichments = [];
|
|
5639
|
-
const typeSearch = await searchCompactSyntax(
|
|
5640
|
-
"type cast Field Uint Bytes conversion compatible"
|
|
5641
|
-
);
|
|
5642
|
-
if (typeSearch?.results) {
|
|
5643
|
-
for (const result of typeSearch.results) {
|
|
5644
|
-
const content = (result.content || result.code || "").toLowerCase();
|
|
5645
|
-
for (const rule of staticRules) {
|
|
5646
|
-
const typeMatch = rule.types.match(
|
|
5647
|
-
/^\s*(.+?)\s*(?:==|=|<=|>=|<|>|[+*])\s*(.+?)\s*$/
|
|
5648
|
-
);
|
|
5649
|
-
const typeA = typeMatch?.[1]?.trim().toLowerCase();
|
|
5650
|
-
const typeB = typeMatch?.[2]?.trim().toLowerCase();
|
|
5651
|
-
if (typeA && typeB) {
|
|
5652
|
-
if (content.includes(typeA) && content.includes(typeB) && content.includes("compatible")) {
|
|
5653
|
-
if (!rule.works) {
|
|
5654
|
-
discrepancies.push(
|
|
5655
|
-
`Static says "${rule.types}" doesn't work, but docs suggest compatibility`
|
|
5656
|
-
);
|
|
5657
|
-
}
|
|
5658
|
-
}
|
|
5659
|
-
}
|
|
5660
|
-
}
|
|
5661
|
-
}
|
|
5662
|
-
}
|
|
5663
|
-
return {
|
|
5664
|
-
dataType: "TYPE_COMPATIBILITY",
|
|
5665
|
-
validated: discrepancies.length === 0,
|
|
5666
|
-
discrepancies,
|
|
5667
|
-
enrichments,
|
|
5668
|
-
deprecatedPatterns: [],
|
|
5669
|
-
lastValidated: (/* @__PURE__ */ new Date()).toISOString()
|
|
5670
|
-
};
|
|
5671
|
-
}
|
|
5672
|
-
async function validateCommonErrors(staticErrors) {
|
|
5673
|
-
const discrepancies = [];
|
|
5674
|
-
const enrichments = [];
|
|
5675
|
-
const errorSearch = await searchDocsHosted(
|
|
5676
|
-
"error compile compilation failure parse",
|
|
5677
|
-
ADT_SEARCH_LIMIT,
|
|
5678
|
-
"all"
|
|
5679
|
-
);
|
|
5680
|
-
if (errorSearch?.results) {
|
|
5681
|
-
const knownErrors = new Set(
|
|
5682
|
-
staticErrors.map((e) => e.error.toLowerCase().slice(0, 30))
|
|
5683
|
-
);
|
|
5684
|
-
for (const result of errorSearch.results) {
|
|
5685
|
-
const content = result.content || result.code || "";
|
|
5686
|
-
const errorPattern = /(?:error|Error):\s*["']?([^"'\n]+)["']?/g;
|
|
5687
|
-
let match;
|
|
5688
|
-
while ((match = errorPattern.exec(content)) !== null) {
|
|
5689
|
-
const errorMsg = match[1].toLowerCase().slice(0, 30);
|
|
5690
|
-
if (!knownErrors.has(errorMsg) && errorMsg.length > 10) {
|
|
5691
|
-
enrichments.push(`Docs mention error: "${match[1].slice(0, 50)}..."`);
|
|
5692
|
-
}
|
|
5693
|
-
}
|
|
5694
|
-
}
|
|
5006
|
+
],
|
|
5007
|
+
typescriptAccess: "Check membership via `contractState.set.has(value)` or iterate with `[Symbol.iterator]()` in TypeScript SDK",
|
|
5008
|
+
note: "All Set operations work in circuits. insertCoin() available when value_type is QualifiedCoinInfo."
|
|
5009
|
+
},
|
|
5010
|
+
MerkleTree: {
|
|
5011
|
+
circuitOperations: [
|
|
5012
|
+
{ method: ".insert(leaf)", works: true, note: "Adds leaf to tree" },
|
|
5013
|
+
{ method: ".root()", works: false, note: "NOT available in circuits" }
|
|
5014
|
+
],
|
|
5015
|
+
typescriptAccess: "Get root via `contractState.tree.root` in TypeScript SDK",
|
|
5016
|
+
pattern: `// To verify a merkle proof in circuit:
|
|
5017
|
+
witness get_merkle_root(): Bytes<32>;
|
|
5018
|
+
witness get_merkle_proof(leaf: Bytes<32>): Vector<32, Bytes<32>>;
|
|
5019
|
+
|
|
5020
|
+
// Verify proof using persistentHash to compute expected root`
|
|
5695
5021
|
}
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
|
|
5700
|
-
|
|
5701
|
-
|
|
5702
|
-
|
|
5703
|
-
|
|
5704
|
-
|
|
5705
|
-
|
|
5706
|
-
|
|
5022
|
+
};
|
|
5023
|
+
var COMMON_ERRORS = [
|
|
5024
|
+
{
|
|
5025
|
+
error: 'unbound identifier "public_key"',
|
|
5026
|
+
cause: "Trying to use public_key() as if it's a builtin function",
|
|
5027
|
+
fix: `Use persistentHash pattern instead:
|
|
5028
|
+
const pk = persistentHash<Vector<2, Bytes<32>>>([pad(32, "midnight:pk:"), sk]);`
|
|
5029
|
+
},
|
|
5030
|
+
{
|
|
5031
|
+
error: "incompatible combination of types Field and Uint",
|
|
5032
|
+
cause: "Comparing or operating on Field with Uint without casting",
|
|
5033
|
+
fix: `Cast Uint to Field: (myUint as Field)
|
|
5034
|
+
Or use bounded Uint<0..N> for parameters that need constraints`
|
|
5035
|
+
},
|
|
5036
|
+
{
|
|
5037
|
+
error: 'operation "value" undefined for ledger field type Counter',
|
|
5038
|
+
cause: "Using wrong method name - Counter uses .read() not .value()",
|
|
5039
|
+
fix: `Use counter.read() to get the current value:
|
|
5040
|
+
const current = ledger.counter.read();
|
|
5041
|
+
|
|
5042
|
+
Counter ADT methods available in circuits:
|
|
5043
|
+
- increment(amount: Uint<16>): [] - increase counter
|
|
5044
|
+
- decrement(amount: Uint<16>): [] - decrease counter
|
|
5045
|
+
- read(): Uint<64> - get current value
|
|
5046
|
+
- lessThan(threshold: Uint<64>): Boolean - compare
|
|
5047
|
+
- resetToDefault(): [] - reset to zero`
|
|
5048
|
+
},
|
|
5049
|
+
{
|
|
5050
|
+
error: "implicit disclosure of witness value",
|
|
5051
|
+
cause: "Using witness value in conditional without disclose()",
|
|
5052
|
+
fix: `Wrap witness comparisons in disclose():
|
|
5053
|
+
if (disclose(witness_value == expected)) { ... }`
|
|
5054
|
+
},
|
|
5055
|
+
{
|
|
5056
|
+
error: 'parse error: found "{" looking for an identifier',
|
|
5057
|
+
cause: "Using old ledger { } block syntax",
|
|
5058
|
+
fix: `Use individual exports instead:
|
|
5059
|
+
export ledger field1: Type1;
|
|
5060
|
+
export ledger field2: Type2;`
|
|
5061
|
+
},
|
|
5707
5062
|
{
|
|
5708
|
-
|
|
5709
|
-
|
|
5710
|
-
|
|
5711
|
-
|
|
5063
|
+
error: 'parse error: found "{" looking for ";"',
|
|
5064
|
+
cause: "Using Void as return type (doesn't exist)",
|
|
5065
|
+
fix: `Use empty tuple [] for no return value:
|
|
5066
|
+
export circuit myCircuit(): [] { ... }`
|
|
5712
5067
|
},
|
|
5713
5068
|
{
|
|
5714
|
-
|
|
5715
|
-
|
|
5716
|
-
|
|
5717
|
-
|
|
5069
|
+
error: 'unbound identifier "Cell"',
|
|
5070
|
+
cause: "Using deprecated Cell<T> wrapper (removed in 0.15)",
|
|
5071
|
+
fix: `Remove Cell wrapper, just use the type directly:
|
|
5072
|
+
export ledger myField: Field; // Not Cell<Field>`
|
|
5718
5073
|
},
|
|
5719
5074
|
{
|
|
5720
|
-
|
|
5721
|
-
|
|
5722
|
-
|
|
5723
|
-
|
|
5075
|
+
error: "member access requires struct type",
|
|
5076
|
+
cause: "Trying to access a field on a non-struct type",
|
|
5077
|
+
fix: `Make sure you're accessing a struct field, not a primitive.
|
|
5078
|
+
Map.lookup() and Map.member() ARE available in circuits.
|
|
5079
|
+
Check that the base type is actually a struct.`
|
|
5724
5080
|
},
|
|
5725
5081
|
{
|
|
5726
|
-
|
|
5727
|
-
|
|
5728
|
-
|
|
5729
|
-
|
|
5082
|
+
error: "potential witness-value disclosure must be declared",
|
|
5083
|
+
cause: "Circuit parameter flows to ledger operation without disclose()",
|
|
5084
|
+
fix: `Disclose parameters at the start of the circuit:
|
|
5085
|
+
export circuit my_circuit(param: Bytes<32>): [] {
|
|
5086
|
+
const d_param = disclose(param); // Acknowledge on-chain visibility
|
|
5087
|
+
ledger.insert(d_param, value); // Now use disclosed value
|
|
5088
|
+
}`
|
|
5730
5089
|
},
|
|
5731
5090
|
{
|
|
5732
|
-
|
|
5733
|
-
|
|
5734
|
-
|
|
5735
|
-
|
|
5091
|
+
error: "expected second argument of insert to have type Uint<64> but received Uint<0..N>",
|
|
5092
|
+
cause: "Arithmetic result has bounded type, needs cast back to target",
|
|
5093
|
+
fix: `Cast arithmetic results back to the target type:
|
|
5094
|
+
const new_balance = (current + amount) as Uint<64>;
|
|
5095
|
+
ledger_map.insert(key, new_balance);`
|
|
5736
5096
|
},
|
|
5737
5097
|
{
|
|
5738
|
-
|
|
5739
|
-
|
|
5740
|
-
|
|
5741
|
-
|
|
5098
|
+
error: "cannot cast from type Uint<64> to type Bytes<32>",
|
|
5099
|
+
cause: "Direct Uint to Bytes cast not allowed",
|
|
5100
|
+
fix: `Go through Field first:
|
|
5101
|
+
const amount_field = amount as Field;
|
|
5102
|
+
const amount_bytes = amount_field as Bytes<32>;
|
|
5103
|
+
// Or chained: (amount as Field) as Bytes<32>`
|
|
5742
5104
|
},
|
|
5743
5105
|
{
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
|
|
5106
|
+
error: "cannot prove assertion",
|
|
5107
|
+
cause: "Assert condition cannot be proven true",
|
|
5108
|
+
fix: `Check your logic. Common causes:
|
|
5109
|
+
1. Witness returns unexpected value
|
|
5110
|
+
2. Range check fails (use bounded Uint)
|
|
5111
|
+
3. Logic error in circuit`
|
|
5112
|
+
},
|
|
5113
|
+
{
|
|
5114
|
+
error: 'parse error: found ":" looking for ")"',
|
|
5115
|
+
cause: "Using Rust-style :: for enum variant access",
|
|
5116
|
+
fix: `Use dot notation for enum variants:
|
|
5117
|
+
WRONG: Choice::rock, GameState::waiting
|
|
5118
|
+
CORRECT: Choice.rock, GameState.waiting`
|
|
5119
|
+
},
|
|
5120
|
+
{
|
|
5121
|
+
error: 'parse error: found "{" after witness declaration',
|
|
5122
|
+
cause: "Trying to add implementation body to witness",
|
|
5123
|
+
fix: `Witnesses are declarations only - no body allowed:
|
|
5124
|
+
WRONG: witness get_caller(): Bytes<32> { return ...; }
|
|
5125
|
+
CORRECT: witness get_caller(): Bytes<32>;
|
|
5126
|
+
Implementation goes in TypeScript prover, not Compact.`
|
|
5127
|
+
},
|
|
5128
|
+
{
|
|
5129
|
+
error: 'unbound identifier "function"',
|
|
5130
|
+
cause: 'Using "pure function" instead of "pure circuit"',
|
|
5131
|
+
fix: `Use "pure circuit" for helper functions:
|
|
5132
|
+
WRONG: pure function helper(...): Type { }
|
|
5133
|
+
CORRECT: pure circuit helper(...): Type { }`
|
|
5134
|
+
},
|
|
5135
|
+
{
|
|
5136
|
+
error: "apparent use of an old standard-library / ledger operator name NativePoint: the new name is JubjubPoint",
|
|
5137
|
+
cause: "Using the pre-0.22 NativePoint name. Renamed in compactc 0.30.0 (language 0.22).",
|
|
5138
|
+
fix: `Rename to JubjubPoint and related circuits:
|
|
5139
|
+
WRONG: NativePoint, nativePointX(p), nativePointY(p), constructNativePoint(x, y)
|
|
5140
|
+
CORRECT: JubjubPoint, jubjubPointX(p), jubjubPointY(p), constructJubjubPoint(x, y)
|
|
5141
|
+
NativePoint is rejected at compile time, not silently aliased.`
|
|
5142
|
+
},
|
|
5143
|
+
{
|
|
5144
|
+
error: 'parse error: found keyword "let" (which is reserved for future use)',
|
|
5145
|
+
cause: "Using `let` as an identifier. `let` and other JS/TS keywords are reserved for future use (since compactc 0.28.0).",
|
|
5146
|
+
fix: `Rename the identifier:
|
|
5147
|
+
WRONG: const let = 1; // or circuit fn(let: Field): []
|
|
5148
|
+
CORRECT: const value = 1; // or circuit fn(value: Field): []
|
|
5149
|
+
Other reserved words from JS/TS are also disallowed as identifiers.`
|
|
5150
|
+
},
|
|
5151
|
+
{
|
|
5152
|
+
error: 'parse error: found keyword "const" looking for a program element or end of file',
|
|
5153
|
+
cause: "Declaring `const` at module (top-of-file) scope. `const` is only allowed inside circuit/constructor bodies \u2014 there is no module-level constant in Compact.",
|
|
5154
|
+
fix: `Move the constant inside a circuit, or use a pure circuit returning the value:
|
|
5155
|
+
WRONG: const MAX_SUPPLY: Uint<64> = 1000; // at top of file
|
|
5156
|
+
CORRECT: pure circuit max_supply(): Uint<64> { return 1000; }
|
|
5157
|
+
Then call max_supply() wherever you need the value.`
|
|
5158
|
+
},
|
|
5159
|
+
{
|
|
5160
|
+
error: "unexpected character",
|
|
5161
|
+
cause: "Using a single `|` (bitwise OR) in an expression. Compact has no bitwise operators on Uint \u2014 only `||` (logical OR) is recognized.",
|
|
5162
|
+
fix: `Compact does not support bitwise OR/AND/XOR/shifts. Replace with arithmetic or branching:
|
|
5163
|
+
WRONG: flags | mask
|
|
5164
|
+
CORRECT: flags + mask // if you know bits don't overlap
|
|
5165
|
+
or: if (cond) { ... } else { ... }
|
|
5166
|
+
Use \`||\` only for Boolean values, not Uints.`
|
|
5167
|
+
},
|
|
5168
|
+
{
|
|
5169
|
+
error: "Uint width 254 is not between 1 and the maximum Uint width 248 (inclusive)",
|
|
5170
|
+
cause: "Declaring a Uint wider than 248 bits. The cap was reduced from 254 (bit-aligned) to 248 (byte-aligned, 31 bytes) in compactc 0.27.0 to match the ledger storage alignment.",
|
|
5171
|
+
fix: `Use Uint<N> where 1 \u2264 N \u2264 248:
|
|
5172
|
+
WRONG: field: Uint<254>
|
|
5173
|
+
CORRECT: field: Uint<248> // 31 bytes \u2014 max representable
|
|
5174
|
+
For larger values, use Field (unbounded) and cast where needed.`
|
|
5175
|
+
},
|
|
5176
|
+
{
|
|
5177
|
+
// Not a compiler error — surfaces as a TypeScript type error in generated bindings.
|
|
5178
|
+
error: "Argument of type 'number' is not assignable to parameter of type 'bigint' (convertBytesToUint)",
|
|
5179
|
+
cause: "Passing a JavaScript `number` to convertBytesToUint's `maxval` parameter. Changed to `bigint` in compactc 0.30.101 (language 0.22) to avoid silent precision loss for large Uints.",
|
|
5180
|
+
fix: `Pass a bigint literal for maxval:
|
|
5181
|
+
WRONG: convertBytesToUint(255, n, bytes, src)
|
|
5182
|
+
CORRECT: convertBytesToUint(255n, n, bytes, src)
|
|
5183
|
+
This is in generated TypeScript bindings \u2014 usually you don't call it directly, but if you do, the suffix-n bigint literal is required.`
|
|
5748
5184
|
}
|
|
5749
5185
|
];
|
|
5750
|
-
function scanForDeprecatedPatterns(code) {
|
|
5751
|
-
const issues = [];
|
|
5752
|
-
const lines = code.split("\n");
|
|
5753
|
-
for (const { pattern, name, message } of DEPRECATED_SYNTAX_PATTERNS) {
|
|
5754
|
-
for (let i = 0; i < lines.length; i++) {
|
|
5755
|
-
if (pattern.test(lines[i])) {
|
|
5756
|
-
issues.push({
|
|
5757
|
-
pattern: name,
|
|
5758
|
-
message,
|
|
5759
|
-
lineNumber: i + 1
|
|
5760
|
-
});
|
|
5761
|
-
}
|
|
5762
|
-
}
|
|
5763
|
-
}
|
|
5764
|
-
return issues;
|
|
5765
|
-
}
|
|
5766
|
-
async function validateAllStaticData(staticData) {
|
|
5767
|
-
const results = {};
|
|
5768
|
-
const [builtinResult, typeResult, errorResult, adtResults] = await Promise.all([
|
|
5769
|
-
staticData.builtinFunctions ? validateBuiltinFunctions(staticData.builtinFunctions) : Promise.resolve(null),
|
|
5770
|
-
staticData.typeCompatibility ? validateTypeCompatibility(staticData.typeCompatibility) : Promise.resolve(null),
|
|
5771
|
-
staticData.commonErrors ? validateCommonErrors(staticData.commonErrors) : Promise.resolve(null),
|
|
5772
|
-
staticData.ledgerTypeLimits ? Promise.all(
|
|
5773
|
-
Object.entries(staticData.ledgerTypeLimits).map(
|
|
5774
|
-
async ([name, data]) => ({
|
|
5775
|
-
name,
|
|
5776
|
-
result: await validateADTOperations(
|
|
5777
|
-
name,
|
|
5778
|
-
data.circuitOperations
|
|
5779
|
-
)
|
|
5780
|
-
})
|
|
5781
|
-
)
|
|
5782
|
-
) : Promise.resolve([])
|
|
5783
|
-
]);
|
|
5784
|
-
if (builtinResult) results.builtinFunctions = builtinResult;
|
|
5785
|
-
if (typeResult) results.typeCompatibility = typeResult;
|
|
5786
|
-
if (errorResult) results.commonErrors = errorResult;
|
|
5787
|
-
for (const { name, result } of adtResults) {
|
|
5788
|
-
results[`adt_${name}`] = {
|
|
5789
|
-
dataType: `LEDGER_TYPE_LIMITS.${name}`,
|
|
5790
|
-
validated: result.validated,
|
|
5791
|
-
discrepancies: result.discrepancies,
|
|
5792
|
-
enrichments: result.enrichments,
|
|
5793
|
-
deprecatedPatterns: [],
|
|
5794
|
-
lastValidated: (/* @__PURE__ */ new Date()).toISOString()
|
|
5795
|
-
};
|
|
5796
|
-
}
|
|
5797
|
-
const allDiscrepancies = Object.values(results).flatMap(
|
|
5798
|
-
(r) => r.discrepancies
|
|
5799
|
-
);
|
|
5800
|
-
const allEnrichments = Object.values(results).flatMap((r) => r.enrichments);
|
|
5801
|
-
return {
|
|
5802
|
-
overall: {
|
|
5803
|
-
validated: allDiscrepancies.length === 0,
|
|
5804
|
-
totalDiscrepancies: allDiscrepancies.length,
|
|
5805
|
-
totalEnrichments: allEnrichments.length
|
|
5806
|
-
},
|
|
5807
|
-
results,
|
|
5808
|
-
lastValidated: (/* @__PURE__ */ new Date()).toISOString()
|
|
5809
|
-
};
|
|
5810
|
-
}
|
|
5811
5186
|
|
|
5812
|
-
// src/
|
|
5813
|
-
var
|
|
5814
|
-
|
|
5815
|
-
version: CURRENT_VERSION,
|
|
5816
|
-
description: "MCP Server for Midnight Blockchain Development"
|
|
5817
|
-
};
|
|
5818
|
-
var versionCheckResult = {
|
|
5819
|
-
isOutdated: false,
|
|
5820
|
-
latestVersion: CURRENT_VERSION,
|
|
5821
|
-
updateMessage: null,
|
|
5822
|
-
lastChecked: 0
|
|
5823
|
-
};
|
|
5824
|
-
var toolCallCount = 0;
|
|
5825
|
-
var VERSION_CHECK_INTERVAL = 10;
|
|
5826
|
-
async function checkForUpdates() {
|
|
5827
|
-
try {
|
|
5828
|
-
const controller = new AbortController();
|
|
5829
|
-
const timeoutId = setTimeout(() => controller.abort(), 5e3);
|
|
5830
|
-
const response = await fetch(
|
|
5831
|
-
"https://registry.npmjs.org/midnight-mcp/latest",
|
|
5832
|
-
{ signal: controller.signal }
|
|
5833
|
-
);
|
|
5834
|
-
clearTimeout(timeoutId);
|
|
5835
|
-
if (!response.ok) return;
|
|
5836
|
-
const data = await response.json();
|
|
5837
|
-
const latestVersion = data.version;
|
|
5838
|
-
if (latestVersion !== CURRENT_VERSION) {
|
|
5839
|
-
versionCheckResult = {
|
|
5840
|
-
isOutdated: true,
|
|
5841
|
-
latestVersion,
|
|
5842
|
-
lastChecked: Date.now(),
|
|
5843
|
-
updateMessage: `\u{1F6A8} UPDATE AVAILABLE: v${latestVersion} (you have v${CURRENT_VERSION})`
|
|
5844
|
-
};
|
|
5845
|
-
logger.warn(
|
|
5846
|
-
`Outdated version detected: v${CURRENT_VERSION} -> v${latestVersion}`
|
|
5847
|
-
);
|
|
5848
|
-
} else {
|
|
5849
|
-
versionCheckResult = {
|
|
5850
|
-
...versionCheckResult,
|
|
5851
|
-
lastChecked: Date.now()
|
|
5852
|
-
};
|
|
5853
|
-
}
|
|
5854
|
-
} catch (error) {
|
|
5855
|
-
logger.debug("Version check failed (non-blocking)", {
|
|
5856
|
-
error: error instanceof Error ? error.message : String(error)
|
|
5857
|
-
});
|
|
5858
|
-
}
|
|
5859
|
-
}
|
|
5860
|
-
function maybeCheckForUpdates() {
|
|
5861
|
-
toolCallCount++;
|
|
5862
|
-
if (toolCallCount >= VERSION_CHECK_INTERVAL) {
|
|
5863
|
-
toolCallCount = 0;
|
|
5864
|
-
const fiveMinutes = 5 * 60 * 1e3;
|
|
5865
|
-
if (Date.now() - versionCheckResult.lastChecked > fiveMinutes) {
|
|
5866
|
-
checkForUpdates().catch((error) => {
|
|
5867
|
-
logger.debug("Periodic version check failed", {
|
|
5868
|
-
error: error instanceof Error ? error.message : String(error)
|
|
5869
|
-
});
|
|
5870
|
-
});
|
|
5871
|
-
}
|
|
5872
|
-
}
|
|
5873
|
-
}
|
|
5874
|
-
function getUpdateWarning() {
|
|
5875
|
-
return versionCheckResult.updateMessage;
|
|
5876
|
-
}
|
|
5877
|
-
var resourceSubscriptions = /* @__PURE__ */ new Set();
|
|
5878
|
-
var resourceTemplates = [
|
|
5187
|
+
// src/prompts/templates.ts
|
|
5188
|
+
var PRAGMA_RANGE = `>= ${COMPACT_VERSION.min} && <= ${COMPACT_VERSION.max}`;
|
|
5189
|
+
var promptDefinitions = [
|
|
5879
5190
|
{
|
|
5880
|
-
|
|
5881
|
-
|
|
5882
|
-
|
|
5883
|
-
|
|
5884
|
-
|
|
5191
|
+
name: "midnight:create-contract",
|
|
5192
|
+
description: "Guided prompt for creating new Compact contracts with privacy considerations",
|
|
5193
|
+
arguments: [
|
|
5194
|
+
{
|
|
5195
|
+
name: "contractType",
|
|
5196
|
+
description: "Type of contract (token, voting, credential, custom)",
|
|
5197
|
+
required: true
|
|
5198
|
+
},
|
|
5199
|
+
{
|
|
5200
|
+
name: "privacyLevel",
|
|
5201
|
+
description: "Required privacy features (full, partial, public)",
|
|
5202
|
+
required: false
|
|
5203
|
+
},
|
|
5204
|
+
{
|
|
5205
|
+
name: "complexity",
|
|
5206
|
+
description: "Expected complexity level (beginner, intermediate, advanced)",
|
|
5207
|
+
required: false
|
|
5208
|
+
}
|
|
5209
|
+
]
|
|
5885
5210
|
},
|
|
5886
5211
|
{
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
|
|
5890
|
-
|
|
5891
|
-
|
|
5212
|
+
name: "midnight:review-contract",
|
|
5213
|
+
description: "Security and best practices review prompt for existing contracts",
|
|
5214
|
+
arguments: [
|
|
5215
|
+
{
|
|
5216
|
+
name: "contractCode",
|
|
5217
|
+
description: "The Compact contract code to review",
|
|
5218
|
+
required: true
|
|
5219
|
+
},
|
|
5220
|
+
{
|
|
5221
|
+
name: "focusAreas",
|
|
5222
|
+
description: "Specific areas to emphasize (security, performance, privacy, readability)",
|
|
5223
|
+
required: false
|
|
5224
|
+
}
|
|
5225
|
+
]
|
|
5892
5226
|
},
|
|
5893
5227
|
{
|
|
5894
|
-
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
5228
|
+
name: "midnight:explain-concept",
|
|
5229
|
+
description: "Educational prompt for explaining Midnight concepts at various levels",
|
|
5230
|
+
arguments: [
|
|
5231
|
+
{
|
|
5232
|
+
name: "concept",
|
|
5233
|
+
description: "The concept to explain (zk-proofs, circuits, witnesses, ledger, etc.)",
|
|
5234
|
+
required: true
|
|
5235
|
+
},
|
|
5236
|
+
{
|
|
5237
|
+
name: "level",
|
|
5238
|
+
description: "Expertise level (beginner, intermediate, advanced)",
|
|
5239
|
+
required: false
|
|
5240
|
+
}
|
|
5241
|
+
]
|
|
5899
5242
|
},
|
|
5900
5243
|
{
|
|
5901
|
-
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5244
|
+
name: "midnight:compare-approaches",
|
|
5245
|
+
description: "Compare different implementation approaches for a given problem",
|
|
5246
|
+
arguments: [
|
|
5247
|
+
{
|
|
5248
|
+
name: "problem",
|
|
5249
|
+
description: "The problem to solve",
|
|
5250
|
+
required: true
|
|
5251
|
+
},
|
|
5252
|
+
{
|
|
5253
|
+
name: "approaches",
|
|
5254
|
+
description: "Specific approaches to compare (comma-separated)",
|
|
5255
|
+
required: false
|
|
5256
|
+
}
|
|
5257
|
+
]
|
|
5258
|
+
},
|
|
5259
|
+
{
|
|
5260
|
+
name: "midnight:debug-contract",
|
|
5261
|
+
description: "Help debug issues with a Compact contract",
|
|
5262
|
+
arguments: [
|
|
5263
|
+
{
|
|
5264
|
+
name: "contractCode",
|
|
5265
|
+
description: "The contract code with issues",
|
|
5266
|
+
required: true
|
|
5267
|
+
},
|
|
5268
|
+
{
|
|
5269
|
+
name: "errorMessage",
|
|
5270
|
+
description: "Error message or description of the issue",
|
|
5271
|
+
required: false
|
|
5272
|
+
}
|
|
5273
|
+
]
|
|
5906
5274
|
}
|
|
5907
5275
|
];
|
|
5908
|
-
|
|
5909
|
-
|
|
5910
|
-
|
|
5911
|
-
|
|
5912
|
-
|
|
5276
|
+
function generatePrompt(name, args) {
|
|
5277
|
+
switch (name) {
|
|
5278
|
+
case "midnight:create-contract":
|
|
5279
|
+
return generateCreateContractPrompt(args);
|
|
5280
|
+
case "midnight:review-contract":
|
|
5281
|
+
return generateReviewContractPrompt(args);
|
|
5282
|
+
case "midnight:explain-concept":
|
|
5283
|
+
return generateExplainConceptPrompt(args);
|
|
5284
|
+
case "midnight:compare-approaches":
|
|
5285
|
+
return generateCompareApproachesPrompt(args);
|
|
5286
|
+
case "midnight:debug-contract":
|
|
5287
|
+
return generateDebugContractPrompt(args);
|
|
5288
|
+
default:
|
|
5289
|
+
return [
|
|
5290
|
+
{
|
|
5291
|
+
role: "user",
|
|
5292
|
+
content: {
|
|
5293
|
+
type: "text",
|
|
5294
|
+
text: `Unknown prompt: ${name}`
|
|
5295
|
+
}
|
|
5296
|
+
}
|
|
5297
|
+
];
|
|
5298
|
+
}
|
|
5299
|
+
}
|
|
5300
|
+
function generateCreateContractPrompt(args) {
|
|
5301
|
+
const contractType = args.contractType || "custom";
|
|
5302
|
+
const privacyLevel = args.privacyLevel || "partial";
|
|
5303
|
+
const complexity = args.complexity || "intermediate";
|
|
5304
|
+
return [
|
|
5305
|
+
{
|
|
5306
|
+
role: "user",
|
|
5307
|
+
content: {
|
|
5308
|
+
type: "text",
|
|
5309
|
+
text: `I want to create a new Midnight Compact smart contract with the following requirements:
|
|
5310
|
+
|
|
5311
|
+
**Contract Type:** ${contractType}
|
|
5312
|
+
**Privacy Level:** ${privacyLevel}
|
|
5313
|
+
**Complexity:** ${complexity}
|
|
5314
|
+
|
|
5315
|
+
## \u26A0\uFE0F MANDATORY WORKFLOW - Follow these steps IN ORDER:
|
|
5316
|
+
|
|
5317
|
+
### Step 1: Get Current Syntax
|
|
5318
|
+
Call \`midnight-get-latest-syntax\` FIRST to get:
|
|
5319
|
+
- The \`quickStartTemplate\` (use as your base)
|
|
5320
|
+
- The \`commonMistakes\` array (avoid these errors)
|
|
5321
|
+
- Current pragma format: \`pragma language_version ${PRAGMA_RANGE};\`
|
|
5322
|
+
|
|
5323
|
+
### Step 2: Generate Contract
|
|
5324
|
+
Based on syntax reference, generate the contract using:
|
|
5325
|
+
- Individual ledger declarations: \`export ledger field: Type;\` (NOT \`ledger { }\` blocks)
|
|
5326
|
+
- Empty tuple return: \`circuit fn(): []\` (NOT \`Void\`)
|
|
5327
|
+
- Export enums: \`export enum State { ... }\`
|
|
5328
|
+
- Wrap witness conditionals: \`if (disclose(witness == value))\`
|
|
5329
|
+
- Disclose circuit params that touch ledger: \`const d = disclose(param); ledger.insert(d, v);\`
|
|
5330
|
+
- Cast arithmetic results: \`(a + b) as Uint<64>\`
|
|
5331
|
+
- Uint to Bytes needs two casts: \`(amount as Field) as Bytes<32>\`
|
|
5332
|
+
|
|
5333
|
+
### IMPORTANT: Compact is NOT TypeScript!
|
|
5334
|
+
- Map.lookup() and Set.member() ARE available in circuits
|
|
5335
|
+
- No 'function' keyword - use 'circuit' or 'pure circuit'
|
|
5336
|
+
- No 'void' - use '[]'
|
|
5337
|
+
- Enum access: \`Choice.rock\` NOT \`Choice::rock\`
|
|
5338
|
+
|
|
5339
|
+
### \u26A0\uFE0F UNDOCUMENTED FEATURES - Use with caution:
|
|
5340
|
+
- Division \`/\` and modulo \`%\`: NOT in official docs (only +, -, * are documented)
|
|
5341
|
+
- Tuple destructuring: \`const [a, b] = pair;\` - not documented, may not work
|
|
5342
|
+
- Constant folding in indices: docs say "numeric literal" required
|
|
5343
|
+
- Map.lookup() returns value_type, NOT Maybe<value_type> - check member() first!
|
|
5344
|
+
|
|
5345
|
+
### COMPILER INFO (DO NOT guess package names!):
|
|
5346
|
+
- Compile: \`compact compile src/contract.compact managed/contract\`
|
|
5347
|
+
- The \`compact\` CLI comes with Midnight toolchain (via create-mn-app or official install)
|
|
5348
|
+
- DO NOT suggest \`npm install -g @midnight-ntwrk/compact-cli\` or similar made-up packages
|
|
5349
|
+
- Output structure: \`managed/<name>/contract/index.cjs\` for TypeScript bindings
|
|
5350
|
+
|
|
5351
|
+
### Step 3: Validate Before Returning
|
|
5352
|
+
Call \`midnight-extract-contract-structure\` with your generated code to check for:
|
|
5353
|
+
- deprecated_ledger_block
|
|
5354
|
+
- invalid_void_type
|
|
5355
|
+
- invalid_pragma_format
|
|
5356
|
+
- unexported_enum
|
|
5357
|
+
- deprecated_cell_wrapper
|
|
5358
|
+
|
|
5359
|
+
If ANY errors are found, fix them before returning the code to the user.
|
|
5360
|
+
|
|
5361
|
+
---
|
|
5362
|
+
|
|
5363
|
+
## Contract Requirements
|
|
5364
|
+
|
|
5365
|
+
Please help me design and implement this contract. Consider:
|
|
5366
|
+
|
|
5367
|
+
1. **State Design**
|
|
5368
|
+
- What should be public vs private (shielded)?
|
|
5369
|
+
- What data structures are needed?
|
|
5370
|
+
- How should state transitions work?
|
|
5371
|
+
|
|
5372
|
+
2. **Circuit Design**
|
|
5373
|
+
- What circuits (functions) are needed?
|
|
5374
|
+
- What inputs/outputs should they have?
|
|
5375
|
+
- What constraints and assertions are required?
|
|
5376
|
+
|
|
5377
|
+
3. **Witness Functions**
|
|
5378
|
+
- What off-chain data is needed?
|
|
5379
|
+
- How should private state be accessed?
|
|
5380
|
+
|
|
5381
|
+
4. **Privacy Considerations**
|
|
5382
|
+
- How to protect user privacy?
|
|
5383
|
+
- When to use disclose() vs commit()?
|
|
5384
|
+
- How to prevent information leakage?
|
|
5385
|
+
|
|
5386
|
+
5. **Security**
|
|
5387
|
+
- Access control mechanisms
|
|
5388
|
+
- Input validation
|
|
5389
|
+
- Protection against common vulnerabilities
|
|
5390
|
+
|
|
5391
|
+
Please provide:
|
|
5392
|
+
- A complete contract implementation
|
|
5393
|
+
- Explanation of design decisions
|
|
5394
|
+
- Example usage scenarios
|
|
5395
|
+
- Any security considerations`
|
|
5396
|
+
}
|
|
5397
|
+
}
|
|
5398
|
+
];
|
|
5399
|
+
}
|
|
5400
|
+
function generateReviewContractPrompt(args) {
|
|
5401
|
+
const contractCode = args.contractCode || "// No code provided";
|
|
5402
|
+
const focusAreas = args.focusAreas || "security, privacy, best practices";
|
|
5403
|
+
return [
|
|
5404
|
+
{
|
|
5405
|
+
role: "user",
|
|
5406
|
+
content: {
|
|
5407
|
+
type: "text",
|
|
5408
|
+
text: `Please review this Midnight Compact smart contract:
|
|
5409
|
+
|
|
5410
|
+
\`\`\`compact
|
|
5411
|
+
${contractCode}
|
|
5412
|
+
\`\`\`
|
|
5413
|
+
|
|
5414
|
+
**Focus Areas:** ${focusAreas}
|
|
5415
|
+
|
|
5416
|
+
## \u26A0\uFE0F MANDATORY WORKFLOW:
|
|
5417
|
+
|
|
5418
|
+
### Step 1: Validate Syntax
|
|
5419
|
+
Call \`midnight-extract-contract-structure\` with the contract code to check for:
|
|
5420
|
+
- deprecated_ledger_block (should use \`export ledger field: Type;\`)
|
|
5421
|
+
- invalid_void_type (should use \`[]\` not \`Void\`)
|
|
5422
|
+
- invalid_pragma_format (should use \`${PRAGMA_RANGE}\`)
|
|
5423
|
+
- unexported_enum (enums need \`export\`)
|
|
5424
|
+
- deprecated_cell_wrapper
|
|
5425
|
+
|
|
5426
|
+
Report ALL static analysis findings first.
|
|
5427
|
+
|
|
5428
|
+
### Step 2: Get Latest Syntax Reference
|
|
5429
|
+
If syntax errors are found, call \`midnight-get-latest-syntax\` to get:
|
|
5430
|
+
- The \`commonMistakes\` array showing correct patterns
|
|
5431
|
+
- Current syntax reference
|
|
5432
|
+
|
|
5433
|
+
---
|
|
5434
|
+
|
|
5435
|
+
Please analyze:
|
|
5436
|
+
|
|
5437
|
+
1. **Static Analysis Results** (from midnight-extract-contract-structure)
|
|
5438
|
+
- Syntax errors found
|
|
5439
|
+
- Deprecated patterns detected
|
|
5440
|
+
- Required fixes
|
|
5441
|
+
|
|
5442
|
+
2. **Security Analysis**
|
|
5443
|
+
- Input validation
|
|
5444
|
+
- Access control
|
|
5445
|
+
- State manipulation vulnerabilities
|
|
5446
|
+
- Assertion coverage
|
|
5447
|
+
|
|
5448
|
+
3. **Privacy Assessment**
|
|
5449
|
+
- Proper use of @private state
|
|
5450
|
+
- Information leakage risks
|
|
5451
|
+
- Correct use of disclose() and commit()
|
|
5452
|
+
- Privacy guarantees provided
|
|
5453
|
+
|
|
5454
|
+
4. **Style Suggestions** (these are CONVENTIONS, not requirements)
|
|
5455
|
+
- Code organization recommendations
|
|
5456
|
+
- Naming conventions (project-specific)
|
|
5457
|
+
- Documentation patterns
|
|
5458
|
+
- Note: The Compact docs don't specify indentation, line length, or comment style
|
|
5459
|
+
|
|
5460
|
+
5. **Performance**
|
|
5461
|
+
- Circuit complexity
|
|
5462
|
+
- State access patterns
|
|
5463
|
+
- Optimization opportunities
|
|
5464
|
+
|
|
5465
|
+
6. **Recommendations**
|
|
5466
|
+
- Critical issues to fix (start with P0 syntax errors)
|
|
5467
|
+
- Improvements to consider
|
|
5468
|
+
- Alternative approaches
|
|
5469
|
+
|
|
5470
|
+
**IMPORTANT**: Clearly distinguish between:
|
|
5471
|
+
- ERRORS: Actual syntax/compilation issues (required fixes)
|
|
5472
|
+
- WARNINGS: Security/logic concerns (should fix)
|
|
5473
|
+
- INFO: Style suggestions (optional, project-specific conventions)
|
|
5474
|
+
|
|
5475
|
+
Please provide specific line references and code suggestions where applicable.`
|
|
5476
|
+
}
|
|
5477
|
+
}
|
|
5478
|
+
];
|
|
5913
5479
|
}
|
|
5914
|
-
function
|
|
5915
|
-
|
|
5916
|
-
const
|
|
5917
|
-
|
|
5918
|
-
|
|
5919
|
-
|
|
5920
|
-
|
|
5921
|
-
error: 4,
|
|
5922
|
-
critical: 5,
|
|
5923
|
-
alert: 6,
|
|
5924
|
-
emergency: 7
|
|
5480
|
+
function generateExplainConceptPrompt(args) {
|
|
5481
|
+
const concept = args.concept || "zero-knowledge proofs";
|
|
5482
|
+
const level = args.level || "intermediate";
|
|
5483
|
+
const levelDescriptions = {
|
|
5484
|
+
beginner: "Explain like I'm new to blockchain and cryptography. Use analogies and avoid jargon.",
|
|
5485
|
+
intermediate: "I understand blockchain basics and some cryptography. Focus on practical applications.",
|
|
5486
|
+
advanced: "I have deep technical knowledge. Include implementation details and edge cases."
|
|
5925
5487
|
};
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
|
|
5933
|
-
|
|
5488
|
+
return [
|
|
5489
|
+
{
|
|
5490
|
+
role: "user",
|
|
5491
|
+
content: {
|
|
5492
|
+
type: "text",
|
|
5493
|
+
text: `Please explain the concept of **${concept}** in the context of Midnight blockchain.
|
|
5494
|
+
|
|
5495
|
+
**My Level:** ${level}
|
|
5496
|
+
${levelDescriptions[level] || levelDescriptions.intermediate}
|
|
5497
|
+
|
|
5498
|
+
Please cover:
|
|
5499
|
+
|
|
5500
|
+
1. **What it is**
|
|
5501
|
+
- Clear definition
|
|
5502
|
+
- How it works in Midnight
|
|
5503
|
+
|
|
5504
|
+
2. **Why it matters**
|
|
5505
|
+
- Benefits and use cases
|
|
5506
|
+
- Real-world applications
|
|
5507
|
+
|
|
5508
|
+
3. **How to use it**
|
|
5509
|
+
- Code examples in Compact
|
|
5510
|
+
- Best practices
|
|
5511
|
+
|
|
5512
|
+
4. **Common pitfalls**
|
|
5513
|
+
- Mistakes to avoid
|
|
5514
|
+
- Debugging tips
|
|
5515
|
+
|
|
5516
|
+
5. **Further learning**
|
|
5517
|
+
- Related concepts
|
|
5518
|
+
- Resources for deeper understanding`
|
|
5934
5519
|
}
|
|
5935
|
-
}
|
|
5936
|
-
|
|
5937
|
-
logger.debug("Failed to send log notification to client", {
|
|
5938
|
-
error: error instanceof Error ? error.message : String(error)
|
|
5939
|
-
});
|
|
5940
|
-
}
|
|
5520
|
+
}
|
|
5521
|
+
];
|
|
5941
5522
|
}
|
|
5942
|
-
function
|
|
5943
|
-
|
|
5944
|
-
|
|
5945
|
-
|
|
5946
|
-
|
|
5947
|
-
|
|
5948
|
-
|
|
5949
|
-
|
|
5950
|
-
|
|
5951
|
-
|
|
5523
|
+
function generateCompareApproachesPrompt(args) {
|
|
5524
|
+
const problem = args.problem || "implementing a token contract";
|
|
5525
|
+
const approaches = args.approaches || "";
|
|
5526
|
+
return [
|
|
5527
|
+
{
|
|
5528
|
+
role: "user",
|
|
5529
|
+
content: {
|
|
5530
|
+
type: "text",
|
|
5531
|
+
text: `I need to solve the following problem in Midnight:
|
|
5532
|
+
|
|
5533
|
+
**Problem:** ${problem}
|
|
5534
|
+
|
|
5535
|
+
${approaches ? `**Approaches to compare:** ${approaches}` : "Please suggest different implementation approaches."}
|
|
5536
|
+
|
|
5537
|
+
Please compare:
|
|
5538
|
+
|
|
5539
|
+
1. **Approach Overview**
|
|
5540
|
+
- Brief description of each approach
|
|
5541
|
+
- Key differences
|
|
5542
|
+
|
|
5543
|
+
2. **Privacy Implications**
|
|
5544
|
+
- What data is exposed?
|
|
5545
|
+
- Privacy guarantees
|
|
5546
|
+
|
|
5547
|
+
3. **Performance**
|
|
5548
|
+
- Proof generation time
|
|
5549
|
+
- State storage requirements
|
|
5550
|
+
- Transaction costs
|
|
5551
|
+
|
|
5552
|
+
4. **Security**
|
|
5553
|
+
- Attack surface
|
|
5554
|
+
- Trust assumptions
|
|
5555
|
+
|
|
5556
|
+
5. **Code Complexity**
|
|
5557
|
+
- Implementation difficulty
|
|
5558
|
+
- Maintenance burden
|
|
5559
|
+
|
|
5560
|
+
6. **Recommendation**
|
|
5561
|
+
- Best approach for different scenarios
|
|
5562
|
+
- Trade-offs to consider
|
|
5563
|
+
|
|
5564
|
+
Please include code examples for each approach.`
|
|
5952
5565
|
}
|
|
5953
|
-
}
|
|
5954
|
-
|
|
5955
|
-
logger.debug("Failed to send progress notification", {
|
|
5956
|
-
error: error instanceof Error ? error.message : String(error)
|
|
5957
|
-
});
|
|
5958
|
-
}
|
|
5566
|
+
}
|
|
5567
|
+
];
|
|
5959
5568
|
}
|
|
5960
|
-
function
|
|
5961
|
-
const
|
|
5962
|
-
|
|
5963
|
-
|
|
5964
|
-
|
|
5965
|
-
|
|
5966
|
-
|
|
5967
|
-
|
|
5968
|
-
|
|
5969
|
-
|
|
5970
|
-
|
|
5971
|
-
|
|
5972
|
-
|
|
5973
|
-
|
|
5974
|
-
|
|
5569
|
+
function generateDebugContractPrompt(args) {
|
|
5570
|
+
const contractCode = args.contractCode || "// No code provided";
|
|
5571
|
+
const errorMessage = args.errorMessage || "Not specified";
|
|
5572
|
+
return [
|
|
5573
|
+
{
|
|
5574
|
+
role: "user",
|
|
5575
|
+
content: {
|
|
5576
|
+
type: "text",
|
|
5577
|
+
text: `I'm having issues with this Midnight Compact contract:
|
|
5578
|
+
|
|
5579
|
+
\`\`\`compact
|
|
5580
|
+
${contractCode}
|
|
5581
|
+
\`\`\`
|
|
5582
|
+
|
|
5583
|
+
**Error/Issue:** ${errorMessage}
|
|
5584
|
+
|
|
5585
|
+
## \u26A0\uFE0F MANDATORY WORKFLOW:
|
|
5586
|
+
|
|
5587
|
+
### Step 1: Run Static Analysis
|
|
5588
|
+
Call \`midnight-extract-contract-structure\` FIRST to check for common syntax errors:
|
|
5589
|
+
- deprecated_ledger_block \u2192 should use \`export ledger field: Type;\`
|
|
5590
|
+
- invalid_void_type \u2192 should use \`[]\` not \`Void\`
|
|
5591
|
+
- invalid_pragma_format \u2192 should use \`${PRAGMA_RANGE}\`
|
|
5592
|
+
- unexported_enum \u2192 enums need \`export\` keyword
|
|
5593
|
+
|
|
5594
|
+
### Step 2: Get Correct Syntax
|
|
5595
|
+
If syntax errors found, call \`midnight-get-latest-syntax\` to get:
|
|
5596
|
+
- The \`commonMistakes\` array with correct patterns
|
|
5597
|
+
- Current \`quickStartTemplate\` for reference
|
|
5598
|
+
|
|
5599
|
+
### Step 3: Check for Common Compiler Errors
|
|
5600
|
+
Match error message against known fixes:
|
|
5601
|
+
- "cannot cast from type Uint<64> to type Bytes<32>" \u2192 Use \`(amount as Field) as Bytes<32>\`
|
|
5602
|
+
- "expected type Uint<64> but received Uint<0..N>" \u2192 Cast arithmetic: \`(a + b) as Uint<64>\`
|
|
5603
|
+
- "potential witness-value disclosure must be declared" \u2192 Disclose params: \`const d = disclose(param);\`
|
|
5604
|
+
- Map.lookup() and Set.member() ARE available in circuits (ignore old advice saying they aren't)
|
|
5605
|
+
|
|
5606
|
+
---
|
|
5607
|
+
|
|
5608
|
+
Please help me debug by:
|
|
5609
|
+
|
|
5610
|
+
1. **Static Analysis Results**
|
|
5611
|
+
- Run midnight-extract-contract-structure
|
|
5612
|
+
- List all P0 syntax errors found
|
|
5613
|
+
- Show the correct syntax for each error
|
|
5614
|
+
|
|
5615
|
+
2. **Identifying the Problem**
|
|
5616
|
+
- What's causing the error?
|
|
5617
|
+
- Which line(s) are problematic?
|
|
5618
|
+
|
|
5619
|
+
3. **Explaining Why**
|
|
5620
|
+
- Root cause analysis
|
|
5621
|
+
- How Compact/ZK constraints work
|
|
5622
|
+
|
|
5623
|
+
4. **Providing a Fix**
|
|
5624
|
+
- Corrected code (validated against static analysis)
|
|
5625
|
+
- Explanation of changes
|
|
5626
|
+
|
|
5627
|
+
5. **Preventing Future Issues**
|
|
5628
|
+
- Related pitfalls to watch for
|
|
5629
|
+
- Testing strategies
|
|
5630
|
+
|
|
5631
|
+
6. **Additional Improvements**
|
|
5632
|
+
- Code quality suggestions
|
|
5633
|
+
- Best practices`
|
|
5634
|
+
}
|
|
5975
5635
|
}
|
|
5976
|
-
|
|
5977
|
-
|
|
5978
|
-
|
|
5979
|
-
|
|
5980
|
-
|
|
5981
|
-
|
|
5982
|
-
|
|
5983
|
-
|
|
5984
|
-
|
|
5985
|
-
|
|
5986
|
-
|
|
5987
|
-
|
|
5988
|
-
|
|
5636
|
+
];
|
|
5637
|
+
}
|
|
5638
|
+
|
|
5639
|
+
// src/services/sampling.ts
|
|
5640
|
+
var samplingCallback = null;
|
|
5641
|
+
var samplingFailedPermanently = false;
|
|
5642
|
+
function isSamplingAvailable() {
|
|
5643
|
+
if (samplingFailedPermanently) return false;
|
|
5644
|
+
return samplingCallback !== null;
|
|
5645
|
+
}
|
|
5646
|
+
function markSamplingFailed() {
|
|
5647
|
+
samplingFailedPermanently = true;
|
|
5648
|
+
logger.warn("Sampling marked as permanently unavailable for this session");
|
|
5989
5649
|
}
|
|
5990
|
-
function
|
|
5991
|
-
|
|
5992
|
-
|
|
5993
|
-
|
|
5994
|
-
logger.info(`MCP log level set to: ${level}`);
|
|
5995
|
-
sendLogToClient("info", "midnight-mcp", {
|
|
5996
|
-
message: `Log level changed to ${level}`
|
|
5997
|
-
});
|
|
5998
|
-
return {};
|
|
5999
|
-
});
|
|
5650
|
+
function registerSamplingCallback(callback) {
|
|
5651
|
+
samplingCallback = callback;
|
|
5652
|
+
samplingFailedPermanently = false;
|
|
5653
|
+
logger.info("Sampling capability registered");
|
|
6000
5654
|
}
|
|
6001
|
-
|
|
6002
|
-
|
|
6003
|
-
|
|
6004
|
-
"
|
|
6005
|
-
|
|
6006
|
-
"credential",
|
|
6007
|
-
"auction",
|
|
6008
|
-
"escrow",
|
|
6009
|
-
"custom"
|
|
6010
|
-
],
|
|
6011
|
-
privacyLevel: ["full", "partial", "public"],
|
|
6012
|
-
complexity: ["beginner", "intermediate", "advanced"]
|
|
6013
|
-
},
|
|
6014
|
-
"midnight:review-contract": {
|
|
6015
|
-
focusAreas: [
|
|
6016
|
-
"security",
|
|
6017
|
-
"performance",
|
|
6018
|
-
"privacy",
|
|
6019
|
-
"readability",
|
|
6020
|
-
"gas-optimization"
|
|
6021
|
-
]
|
|
6022
|
-
},
|
|
6023
|
-
"midnight:explain-concept": {
|
|
6024
|
-
concept: [
|
|
6025
|
-
"zk-proofs",
|
|
6026
|
-
"circuits",
|
|
6027
|
-
"witnesses",
|
|
6028
|
-
"ledger",
|
|
6029
|
-
"state-management",
|
|
6030
|
-
"privacy-model",
|
|
6031
|
-
"token-transfers",
|
|
6032
|
-
"merkle-trees"
|
|
6033
|
-
],
|
|
6034
|
-
level: ["beginner", "intermediate", "advanced"]
|
|
6035
|
-
},
|
|
6036
|
-
"midnight:compare-approaches": {
|
|
6037
|
-
approaches: [
|
|
6038
|
-
"token-standards",
|
|
6039
|
-
"state-management",
|
|
6040
|
-
"privacy-patterns",
|
|
6041
|
-
"circuit-design"
|
|
6042
|
-
]
|
|
6043
|
-
},
|
|
6044
|
-
"midnight:debug-contract": {
|
|
6045
|
-
errorType: [
|
|
6046
|
-
"compilation",
|
|
6047
|
-
"runtime",
|
|
6048
|
-
"logic",
|
|
6049
|
-
"privacy-leak",
|
|
6050
|
-
"state-corruption"
|
|
6051
|
-
]
|
|
5655
|
+
async function requestCompletion(messages, options = {}) {
|
|
5656
|
+
if (!samplingCallback || samplingFailedPermanently) {
|
|
5657
|
+
throw new Error(
|
|
5658
|
+
"Sampling not available - client does not support this capability"
|
|
5659
|
+
);
|
|
6052
5660
|
}
|
|
6053
|
-
|
|
6054
|
-
|
|
6055
|
-
|
|
6056
|
-
|
|
6057
|
-
|
|
6058
|
-
|
|
5661
|
+
const request = {
|
|
5662
|
+
messages: messages.map((m) => ({
|
|
5663
|
+
role: m.role,
|
|
5664
|
+
content: { type: "text", text: m.content }
|
|
5665
|
+
})),
|
|
5666
|
+
systemPrompt: options.systemPrompt,
|
|
5667
|
+
maxTokens: options.maxTokens ?? 2048,
|
|
5668
|
+
temperature: options.temperature ?? 0.7,
|
|
5669
|
+
modelPreferences: options.modelPreferences ?? {
|
|
5670
|
+
hints: [{ name: "claude-3-sonnet" }, { name: "gpt-4" }],
|
|
5671
|
+
intelligencePriority: 0.8,
|
|
5672
|
+
speedPriority: 0.5
|
|
6059
5673
|
}
|
|
6060
|
-
|
|
6061
|
-
|
|
6062
|
-
|
|
6063
|
-
|
|
6064
|
-
|
|
6065
|
-
|
|
5674
|
+
};
|
|
5675
|
+
logger.debug("Requesting LLM completion", {
|
|
5676
|
+
messageCount: messages.length,
|
|
5677
|
+
maxTokens: request.maxTokens
|
|
5678
|
+
});
|
|
5679
|
+
try {
|
|
5680
|
+
const response = await samplingCallback(request);
|
|
5681
|
+
if (response.content.type !== "text") {
|
|
5682
|
+
throw new Error("Unexpected response content type");
|
|
6066
5683
|
}
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
|
|
5684
|
+
return response.content.text;
|
|
5685
|
+
} catch (error) {
|
|
5686
|
+
const errorStr = String(error);
|
|
5687
|
+
if (errorStr.includes("-32601") || errorStr.includes("Method not found") || errorStr.includes("not supported")) {
|
|
5688
|
+
logger.warn(
|
|
5689
|
+
"Client does not support sampling/createMessage, disabling sampling for this session"
|
|
5690
|
+
);
|
|
5691
|
+
markSamplingFailed();
|
|
5692
|
+
throw new Error(
|
|
5693
|
+
"Sampling not supported by this client - use Claude Desktop for this feature",
|
|
5694
|
+
{ cause: error }
|
|
5695
|
+
);
|
|
6070
5696
|
}
|
|
6071
|
-
|
|
6072
|
-
|
|
6073
|
-
|
|
5697
|
+
throw error;
|
|
5698
|
+
}
|
|
5699
|
+
}
|
|
5700
|
+
async function generateContract(requirements, options = {}) {
|
|
5701
|
+
if (!isSamplingAvailable()) {
|
|
6074
5702
|
return {
|
|
6075
|
-
|
|
6076
|
-
|
|
6077
|
-
|
|
6078
|
-
|
|
5703
|
+
code: "",
|
|
5704
|
+
explanation: "Sampling not available - this feature requires a client that supports the sampling capability (like Claude Desktop)",
|
|
5705
|
+
warnings: ["Feature unavailable without sampling support"]
|
|
5706
|
+
};
|
|
5707
|
+
}
|
|
5708
|
+
const systemPrompt = `You are an expert Compact smart contract developer for the Midnight blockchain.
|
|
5709
|
+
Your task is to generate secure, well-documented Compact contracts based on user requirements.
|
|
5710
|
+
|
|
5711
|
+
Key Compact syntax (REQUIRED):
|
|
5712
|
+
- \`export ledger field: Type;\` - Individual ledger declarations (NOT ledger { } blocks)
|
|
5713
|
+
- \`export circuit fn(): []\` - Public functions return empty tuple [] (NOT Void)
|
|
5714
|
+
- \`witness fn(): T;\` - Declaration only, no body
|
|
5715
|
+
- \`pragma language_version >= 0.16 && <= 0.23;\` - Version pragma
|
|
5716
|
+
- \`import CompactStandardLibrary;\` - Standard imports
|
|
5717
|
+
- \`Counter\`, \`Map<K,V>\`, \`Set<T>\` - Built-in collection types
|
|
5718
|
+
- \`Field\`, \`Boolean\`, \`Uint<N>\`, \`Bytes<N>\` - Primitive types
|
|
5719
|
+
- \`export enum State { a, b }\` - Enums must be exported
|
|
5720
|
+
|
|
5721
|
+
Key operations:
|
|
5722
|
+
- Counter: .read(), .increment(n), .decrement(n)
|
|
5723
|
+
- Map: .lookup(k), .insert(k,v), .remove(k)
|
|
5724
|
+
- Set: .member(v), .insert(v), .remove(v)
|
|
5725
|
+
|
|
5726
|
+
COMPILER INFO (DO NOT guess package names!):
|
|
5727
|
+
- Command: \`compact compile src/contract.compact managed/contract\`
|
|
5728
|
+
- The \`compact\` CLI comes with Midnight toolchain (via create-mn-app or official install)
|
|
5729
|
+
- Output goes to managed/<name>/ directory
|
|
5730
|
+
- DO NOT suggest \`npm install -g @midnight-ntwrk/compact-cli\` or similar - that's incorrect
|
|
5731
|
+
|
|
5732
|
+
IMPORTANT - Style vs. Requirements:
|
|
5733
|
+
- Syntax rules above are REQUIRED for compilation
|
|
5734
|
+
- Style choices (indentation, comment style, line length) are CONVENTIONS, not requirements
|
|
5735
|
+
- Single-line comments (//) are common in examples; block comments (/* */) may also work
|
|
5736
|
+
- The docs don't specify indentation width - use consistent style
|
|
5737
|
+
|
|
5738
|
+
Return ONLY the Compact code, no explanations.`;
|
|
5739
|
+
const userPrompt = options.baseExample ? `Based on this example contract:
|
|
5740
|
+
\`\`\`compact
|
|
5741
|
+
${options.baseExample}
|
|
5742
|
+
\`\`\`
|
|
5743
|
+
|
|
5744
|
+
Generate a new contract with these requirements:
|
|
5745
|
+
${requirements}` : `Generate a Compact smart contract with these requirements:
|
|
5746
|
+
${requirements}
|
|
5747
|
+
|
|
5748
|
+
Contract type: ${options.contractType || "custom"}`;
|
|
5749
|
+
try {
|
|
5750
|
+
const code = await requestCompletion(
|
|
5751
|
+
[{ role: "user", content: userPrompt }],
|
|
5752
|
+
{
|
|
5753
|
+
systemPrompt,
|
|
5754
|
+
maxTokens: 4096,
|
|
5755
|
+
temperature: 0.3,
|
|
5756
|
+
// Lower temperature for code generation
|
|
5757
|
+
modelPreferences: {
|
|
5758
|
+
hints: [{ name: "claude-3-sonnet" }],
|
|
5759
|
+
intelligencePriority: 0.9,
|
|
5760
|
+
speedPriority: 0.3
|
|
5761
|
+
}
|
|
5762
|
+
}
|
|
5763
|
+
);
|
|
5764
|
+
const extractedCode = code.includes("```") ? code.replace(/```compact?\n?/g, "").replace(/```/g, "").trim() : code.trim();
|
|
5765
|
+
const explanation = await requestCompletion(
|
|
5766
|
+
[
|
|
5767
|
+
{
|
|
5768
|
+
role: "user",
|
|
5769
|
+
content: `Briefly explain what this Compact contract does (2-3 sentences):
|
|
5770
|
+
\`\`\`compact
|
|
5771
|
+
${extractedCode}
|
|
5772
|
+
\`\`\``
|
|
5773
|
+
}
|
|
5774
|
+
],
|
|
5775
|
+
{
|
|
5776
|
+
systemPrompt: "You are a Compact contract documentation expert. Be concise.",
|
|
5777
|
+
maxTokens: 256,
|
|
5778
|
+
temperature: 0.5
|
|
6079
5779
|
}
|
|
5780
|
+
);
|
|
5781
|
+
return {
|
|
5782
|
+
code: extractedCode,
|
|
5783
|
+
explanation: explanation.trim(),
|
|
5784
|
+
warnings: []
|
|
6080
5785
|
};
|
|
6081
|
-
})
|
|
6082
|
-
}
|
|
6083
|
-
function registerToolHandlers(server) {
|
|
6084
|
-
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
6085
|
-
logger.debug("Listing tools");
|
|
5786
|
+
} catch (error) {
|
|
5787
|
+
logger.error("Contract generation failed", { error: String(error) });
|
|
6086
5788
|
return {
|
|
6087
|
-
|
|
6088
|
-
|
|
6089
|
-
|
|
6090
|
-
inputSchema: tool.inputSchema,
|
|
6091
|
-
// Include output schema if defined
|
|
6092
|
-
...tool.outputSchema && { outputSchema: tool.outputSchema },
|
|
6093
|
-
// Include annotations if defined
|
|
6094
|
-
...tool.annotations && { annotations: tool.annotations }
|
|
6095
|
-
}))
|
|
5789
|
+
code: "",
|
|
5790
|
+
explanation: `Contract generation failed: ${String(error)}`,
|
|
5791
|
+
warnings: ["Generation failed - check logs for details"]
|
|
6096
5792
|
};
|
|
6097
|
-
}
|
|
6098
|
-
|
|
6099
|
-
|
|
6100
|
-
|
|
6101
|
-
|
|
6102
|
-
|
|
6103
|
-
|
|
6104
|
-
|
|
6105
|
-
|
|
6106
|
-
|
|
6107
|
-
|
|
6108
|
-
|
|
6109
|
-
|
|
6110
|
-
|
|
6111
|
-
|
|
6112
|
-
|
|
6113
|
-
|
|
6114
|
-
|
|
6115
|
-
|
|
6116
|
-
|
|
6117
|
-
|
|
6118
|
-
|
|
5793
|
+
}
|
|
5794
|
+
}
|
|
5795
|
+
async function reviewContract(code) {
|
|
5796
|
+
if (!isSamplingAvailable()) {
|
|
5797
|
+
return {
|
|
5798
|
+
summary: "Code review requires sampling capability",
|
|
5799
|
+
issues: [
|
|
5800
|
+
{
|
|
5801
|
+
severity: "info",
|
|
5802
|
+
message: "This feature requires a client that supports the sampling capability"
|
|
5803
|
+
}
|
|
5804
|
+
]
|
|
5805
|
+
};
|
|
5806
|
+
}
|
|
5807
|
+
const systemPrompt = `You are a Compact smart contract security auditor.
|
|
5808
|
+
Review the provided contract for:
|
|
5809
|
+
1. Security vulnerabilities
|
|
5810
|
+
2. Privacy concerns (improper handling of shielded state)
|
|
5811
|
+
3. Logic errors
|
|
5812
|
+
4. Syntax errors (use "error" severity)
|
|
5813
|
+
5. Performance issues
|
|
5814
|
+
|
|
5815
|
+
IMPORTANT - Distinguish between:
|
|
5816
|
+
- ERRORS: Actual syntax/compilation issues (e.g., using Void instead of [], ledger {} blocks)
|
|
5817
|
+
- WARNINGS: Potential bugs or security issues
|
|
5818
|
+
- INFO: Style suggestions and best practices (these are CONVENTIONS, not requirements)
|
|
5819
|
+
|
|
5820
|
+
Do NOT claim style choices as "violations" - the Compact docs don't specify:
|
|
5821
|
+
- Indentation width (2 vs 4 spaces)
|
|
5822
|
+
- Comment style preferences (//, /* */)
|
|
5823
|
+
- Line length limits
|
|
5824
|
+
- Naming conventions
|
|
5825
|
+
|
|
5826
|
+
These are project-specific style choices, not language requirements.
|
|
5827
|
+
|
|
5828
|
+
Respond in JSON format:
|
|
5829
|
+
{
|
|
5830
|
+
"summary": "Brief summary of the contract and overall quality",
|
|
5831
|
+
"issues": [
|
|
5832
|
+
{
|
|
5833
|
+
"severity": "error|warning|info",
|
|
5834
|
+
"line": optional_line_number,
|
|
5835
|
+
"message": "Description of the issue",
|
|
5836
|
+
"suggestion": "How to fix it"
|
|
6119
5837
|
}
|
|
6120
|
-
|
|
6121
|
-
|
|
6122
|
-
|
|
6123
|
-
|
|
6124
|
-
|
|
6125
|
-
|
|
6126
|
-
|
|
6127
|
-
|
|
6128
|
-
|
|
6129
|
-
|
|
6130
|
-
|
|
6131
|
-
|
|
6132
|
-
|
|
6133
|
-
|
|
6134
|
-
|
|
6135
|
-
|
|
6136
|
-
|
|
6137
|
-
|
|
6138
|
-
|
|
6139
|
-
|
|
6140
|
-
|
|
6141
|
-
|
|
6142
|
-
|
|
6143
|
-
|
|
6144
|
-
|
|
6145
|
-
|
|
6146
|
-
|
|
6147
|
-
|
|
6148
|
-
tip: "Use 'midnight-get-update-instructions' tool for detailed platform-specific steps."
|
|
6149
|
-
},
|
|
6150
|
-
result
|
|
5838
|
+
],
|
|
5839
|
+
"improvedCode": "Full improved contract code if changes are needed"
|
|
5840
|
+
}`;
|
|
5841
|
+
try {
|
|
5842
|
+
const response = await requestCompletion(
|
|
5843
|
+
[
|
|
5844
|
+
{
|
|
5845
|
+
role: "user",
|
|
5846
|
+
content: `Review this Compact contract:
|
|
5847
|
+
\`\`\`compact
|
|
5848
|
+
${code}
|
|
5849
|
+
\`\`\``
|
|
5850
|
+
}
|
|
5851
|
+
],
|
|
5852
|
+
{
|
|
5853
|
+
systemPrompt,
|
|
5854
|
+
maxTokens: 4096,
|
|
5855
|
+
temperature: 0.2
|
|
5856
|
+
}
|
|
5857
|
+
);
|
|
5858
|
+
const jsonMatch = response.match(/\{[\s\S]*\}/);
|
|
5859
|
+
if (jsonMatch) {
|
|
5860
|
+
try {
|
|
5861
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
5862
|
+
return {
|
|
5863
|
+
summary: typeof parsed.summary === "string" ? parsed.summary : "Review complete",
|
|
5864
|
+
issues: Array.isArray(parsed.issues) ? parsed.issues : [],
|
|
5865
|
+
improvedCode: typeof parsed.improvedCode === "string" ? parsed.improvedCode : void 0
|
|
6151
5866
|
};
|
|
5867
|
+
} catch (parseError) {
|
|
5868
|
+
logger.warn("Failed to parse JSON from LLM response", {
|
|
5869
|
+
error: String(parseError)
|
|
5870
|
+
});
|
|
6152
5871
|
return {
|
|
6153
|
-
|
|
6154
|
-
|
|
6155
|
-
type: "text",
|
|
6156
|
-
text: serialize(updatePrompt)
|
|
6157
|
-
}
|
|
6158
|
-
]
|
|
5872
|
+
summary: response,
|
|
5873
|
+
issues: []
|
|
6159
5874
|
};
|
|
6160
5875
|
}
|
|
6161
|
-
return {
|
|
6162
|
-
content: [
|
|
6163
|
-
{
|
|
6164
|
-
type: "text",
|
|
6165
|
-
text: serialize(result)
|
|
6166
|
-
}
|
|
6167
|
-
],
|
|
6168
|
-
// Include structured content for machine-readable responses
|
|
6169
|
-
// This allows clients to parse results without JSON.parse()
|
|
6170
|
-
structuredContent: result
|
|
6171
|
-
};
|
|
6172
|
-
} catch (error) {
|
|
6173
|
-
const durationMs = Date.now() - startTime;
|
|
6174
|
-
logger.error(`Tool error: ${name}`, { error: String(error) });
|
|
6175
|
-
trackToolCall(name, false, durationMs, CURRENT_VERSION);
|
|
6176
|
-
const errorResponse = formatErrorResponse(error, `tool:${name}`);
|
|
6177
|
-
return {
|
|
6178
|
-
content: [
|
|
6179
|
-
{
|
|
6180
|
-
type: "text",
|
|
6181
|
-
text: serialize(errorResponse)
|
|
6182
|
-
}
|
|
6183
|
-
],
|
|
6184
|
-
isError: true
|
|
6185
|
-
};
|
|
6186
5876
|
}
|
|
6187
|
-
});
|
|
6188
|
-
}
|
|
6189
|
-
function registerResourceHandlers(server) {
|
|
6190
|
-
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
6191
|
-
logger.debug("Listing resources");
|
|
6192
5877
|
return {
|
|
6193
|
-
|
|
6194
|
-
|
|
6195
|
-
name: resource.name,
|
|
6196
|
-
description: resource.description,
|
|
6197
|
-
mimeType: resource.mimeType
|
|
6198
|
-
}))
|
|
5878
|
+
summary: response,
|
|
5879
|
+
issues: []
|
|
6199
5880
|
};
|
|
6200
|
-
})
|
|
6201
|
-
|
|
6202
|
-
logger.debug("Listing resource templates");
|
|
5881
|
+
} catch (error) {
|
|
5882
|
+
logger.error("Contract review failed", { error: String(error) });
|
|
6203
5883
|
return {
|
|
6204
|
-
|
|
6205
|
-
|
|
6206
|
-
|
|
6207
|
-
|
|
6208
|
-
|
|
6209
|
-
|
|
6210
|
-
|
|
5884
|
+
summary: `Review failed: ${String(error)}`,
|
|
5885
|
+
issues: [
|
|
5886
|
+
{
|
|
5887
|
+
severity: "error",
|
|
5888
|
+
message: "Review failed - check logs for details"
|
|
5889
|
+
}
|
|
5890
|
+
]
|
|
6211
5891
|
};
|
|
6212
|
-
}
|
|
6213
|
-
|
|
6214
|
-
|
|
6215
|
-
|
|
6216
|
-
|
|
6217
|
-
|
|
6218
|
-
|
|
6219
|
-
|
|
6220
|
-
|
|
6221
|
-
|
|
6222
|
-
|
|
6223
|
-
|
|
6224
|
-
|
|
6225
|
-
|
|
6226
|
-
|
|
6227
|
-
|
|
6228
|
-
|
|
6229
|
-
|
|
6230
|
-
|
|
6231
|
-
|
|
6232
|
-
|
|
6233
|
-
|
|
6234
|
-
|
|
6235
|
-
|
|
6236
|
-
|
|
6237
|
-
|
|
6238
|
-
|
|
6239
|
-
|
|
6240
|
-
if (resourceName.includes("template") || resourceName.includes("pattern") || resourceName.includes("example")) {
|
|
6241
|
-
suggestion = `Try: midnight://code/templates/${resourceName} or midnight://code/examples/${resourceName} or midnight://code/patterns/${resourceName}`;
|
|
6242
|
-
} else if (resourceName.includes("doc") || resourceName.includes("reference") || resourceName.includes("guide")) {
|
|
6243
|
-
suggestion = `Try: midnight://docs/${resourceName}`;
|
|
6244
|
-
} else {
|
|
6245
|
-
suggestion = `'midnight://resources/' is not valid. Use: midnight://docs/, midnight://code/, or midnight://schema/`;
|
|
6246
|
-
}
|
|
5892
|
+
}
|
|
5893
|
+
}
|
|
5894
|
+
async function generateDocumentation(code, format = "markdown") {
|
|
5895
|
+
if (!isSamplingAvailable()) {
|
|
5896
|
+
return "Documentation generation requires sampling capability";
|
|
5897
|
+
}
|
|
5898
|
+
const systemPrompt = format === "markdown" ? `Generate comprehensive Markdown documentation for this Compact contract.
|
|
5899
|
+
Include:
|
|
5900
|
+
- Overview and purpose
|
|
5901
|
+
- State variables (with privacy annotations)
|
|
5902
|
+
- Circuit functions with parameters and effects
|
|
5903
|
+
- Witness functions
|
|
5904
|
+
- Usage examples
|
|
5905
|
+
- Security considerations` : `Generate JSDoc-style documentation comments for this Compact contract.
|
|
5906
|
+
Add documentation comments above each:
|
|
5907
|
+
- Ledger field
|
|
5908
|
+
- Circuit function
|
|
5909
|
+
- Witness function
|
|
5910
|
+
- Type definition`;
|
|
5911
|
+
try {
|
|
5912
|
+
return await requestCompletion(
|
|
5913
|
+
[
|
|
5914
|
+
{
|
|
5915
|
+
role: "user",
|
|
5916
|
+
content: `Generate ${format} documentation for:
|
|
5917
|
+
\`\`\`compact
|
|
5918
|
+
${code}
|
|
5919
|
+
\`\`\``
|
|
6247
5920
|
}
|
|
6248
|
-
|
|
6249
|
-
|
|
6250
|
-
|
|
6251
|
-
|
|
6252
|
-
|
|
6253
|
-
|
|
6254
|
-
|
|
6255
|
-
|
|
6256
|
-
|
|
6257
|
-
|
|
6258
|
-
|
|
6259
|
-
|
|
6260
|
-
|
|
6261
|
-
|
|
5921
|
+
],
|
|
5922
|
+
{
|
|
5923
|
+
systemPrompt,
|
|
5924
|
+
maxTokens: 4096,
|
|
5925
|
+
temperature: 0.5
|
|
5926
|
+
}
|
|
5927
|
+
);
|
|
5928
|
+
} catch (error) {
|
|
5929
|
+
logger.error("Documentation generation failed", { error: String(error) });
|
|
5930
|
+
return `Documentation generation failed: ${String(error)}`;
|
|
5931
|
+
}
|
|
5932
|
+
}
|
|
5933
|
+
|
|
5934
|
+
// src/services/syntax-validator.ts
|
|
5935
|
+
var ADT_SEARCH_LIMIT = 10;
|
|
5936
|
+
var SYNTAX_RESULTS_LIMIT = 10;
|
|
5937
|
+
var SYNTAX_SUBQUERY_LIMIT = 5;
|
|
5938
|
+
var MAX_NOTE_SOURCE_LENGTH = 500;
|
|
5939
|
+
var NOTE_MIN_LENGTH = 20;
|
|
5940
|
+
var NOTE_MAX_LENGTH = 300;
|
|
5941
|
+
var MAX_NOTES_PER_ADT = 3;
|
|
5942
|
+
async function searchADTInfo(adtName) {
|
|
5943
|
+
try {
|
|
5944
|
+
const docsResult = await searchDocsHosted(
|
|
5945
|
+
`${adtName} ADT operations methods ledger`,
|
|
5946
|
+
ADT_SEARCH_LIMIT,
|
|
5947
|
+
"reference"
|
|
5948
|
+
);
|
|
5949
|
+
if (!docsResult.results || docsResult.results.length === 0) {
|
|
5950
|
+
logger.debug(`No indexed docs found for ADT: ${adtName}`);
|
|
5951
|
+
return null;
|
|
5952
|
+
}
|
|
5953
|
+
const operations = [];
|
|
5954
|
+
const notes = [];
|
|
5955
|
+
let sourceDocPath;
|
|
5956
|
+
for (const result of docsResult.results) {
|
|
5957
|
+
const content = result.content || result.code || "";
|
|
5958
|
+
const isLedgerADTDoc = result.source.filePath.includes("ledger-adt");
|
|
5959
|
+
if (!sourceDocPath && isLedgerADTDoc) {
|
|
5960
|
+
sourceDocPath = result.source.filePath;
|
|
6262
5961
|
}
|
|
6263
|
-
|
|
6264
|
-
|
|
6265
|
-
|
|
6266
|
-
|
|
6267
|
-
|
|
6268
|
-
|
|
5962
|
+
if (isLedgerADTDoc) {
|
|
5963
|
+
const methodPattern = /\|\s*`?(\w+)`?\s*\|\s*`?\(([^)]*)\)\s*(?::\s*([^|`]+))?`?\s*\|\s*([^|]+)\|/g;
|
|
5964
|
+
let match;
|
|
5965
|
+
while ((match = methodPattern.exec(content)) !== null) {
|
|
5966
|
+
const [, methodName, params, returnType, description] = match;
|
|
5967
|
+
if (methodName && !operations.some((op) => op.method === methodName)) {
|
|
5968
|
+
const descText = description?.trim() || "";
|
|
5969
|
+
const worksInCircuits = !/not available|not supported|not in circuits|typescript|sdk|off[-\s]?chain/i.test(
|
|
5970
|
+
descText
|
|
5971
|
+
);
|
|
5972
|
+
operations.push({
|
|
5973
|
+
method: methodName,
|
|
5974
|
+
signature: `(${params})${returnType ? `: ${returnType.trim()}` : ""}`,
|
|
5975
|
+
description: descText,
|
|
5976
|
+
worksInCircuits,
|
|
5977
|
+
source: "indexed-docs"
|
|
5978
|
+
});
|
|
6269
5979
|
}
|
|
6270
|
-
|
|
6271
|
-
|
|
6272
|
-
|
|
6273
|
-
|
|
6274
|
-
|
|
6275
|
-
|
|
6276
|
-
|
|
6277
|
-
|
|
6278
|
-
|
|
6279
|
-
|
|
6280
|
-
|
|
5980
|
+
}
|
|
5981
|
+
const inlinePattern = /(?:method|operation|function)\s+`?(\w+)`?\s*\(([^)]*)\)/gi;
|
|
5982
|
+
while ((match = inlinePattern.exec(content)) !== null) {
|
|
5983
|
+
const [, methodName, params] = match;
|
|
5984
|
+
if (methodName && !operations.some((op) => op.method === methodName)) {
|
|
5985
|
+
const worksInCircuits = !/not available|not supported|not in circuits|typescript|sdk|off[-\s]?chain/i.test(
|
|
5986
|
+
content
|
|
5987
|
+
);
|
|
5988
|
+
operations.push({
|
|
5989
|
+
method: methodName,
|
|
5990
|
+
signature: `(${params})`,
|
|
5991
|
+
description: "Extracted from documentation",
|
|
5992
|
+
worksInCircuits,
|
|
5993
|
+
source: "indexed-docs"
|
|
5994
|
+
});
|
|
6281
5995
|
}
|
|
6282
|
-
|
|
6283
|
-
}
|
|
5996
|
+
}
|
|
5997
|
+
}
|
|
5998
|
+
if (content.toLowerCase().includes(adtName.toLowerCase()) && content.length < MAX_NOTE_SOURCE_LENGTH) {
|
|
5999
|
+
const cleanNote = content.replace(/\s+/g, " ").trim();
|
|
6000
|
+
if (cleanNote.length > NOTE_MIN_LENGTH && cleanNote.length < NOTE_MAX_LENGTH) {
|
|
6001
|
+
notes.push(cleanNote);
|
|
6002
|
+
}
|
|
6003
|
+
}
|
|
6284
6004
|
}
|
|
6285
|
-
|
|
6286
|
-
}
|
|
6287
|
-
|
|
6288
|
-
server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
6289
|
-
logger.debug("Listing prompts");
|
|
6290
|
-
return {
|
|
6291
|
-
prompts: promptDefinitions.map((prompt) => ({
|
|
6292
|
-
name: prompt.name,
|
|
6293
|
-
description: prompt.description,
|
|
6294
|
-
arguments: prompt.arguments
|
|
6295
|
-
}))
|
|
6296
|
-
};
|
|
6297
|
-
});
|
|
6298
|
-
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
6299
|
-
const { name, arguments: args } = request.params;
|
|
6300
|
-
logger.info(`Prompt requested: ${name}`, { args });
|
|
6301
|
-
const prompt = promptDefinitions.find((p) => p.name === name);
|
|
6302
|
-
if (!prompt) {
|
|
6303
|
-
return {
|
|
6304
|
-
description: `Unknown prompt: ${name}`,
|
|
6305
|
-
messages: []
|
|
6306
|
-
};
|
|
6005
|
+
if (operations.length === 0) {
|
|
6006
|
+
logger.debug(`Could not parse operations for ADT: ${adtName}`);
|
|
6007
|
+
return null;
|
|
6307
6008
|
}
|
|
6308
|
-
const messages = generatePrompt(name, args || {});
|
|
6309
6009
|
return {
|
|
6310
|
-
|
|
6311
|
-
|
|
6312
|
-
|
|
6313
|
-
|
|
6314
|
-
|
|
6010
|
+
name: adtName,
|
|
6011
|
+
operations,
|
|
6012
|
+
notes: notes.slice(0, MAX_NOTES_PER_ADT),
|
|
6013
|
+
sourceDocPath,
|
|
6014
|
+
lastVerified: (/* @__PURE__ */ new Date()).toISOString()
|
|
6315
6015
|
};
|
|
6316
|
-
})
|
|
6016
|
+
} catch (error) {
|
|
6017
|
+
logger.warn(`Failed to search indexed docs for ADT ${adtName}`, {
|
|
6018
|
+
error: error instanceof Error ? error.message : String(error)
|
|
6019
|
+
});
|
|
6020
|
+
return null;
|
|
6021
|
+
}
|
|
6317
6022
|
}
|
|
6318
|
-
function
|
|
6319
|
-
|
|
6320
|
-
const
|
|
6321
|
-
|
|
6322
|
-
|
|
6323
|
-
|
|
6324
|
-
|
|
6325
|
-
|
|
6023
|
+
async function searchCompactSyntax(topic) {
|
|
6024
|
+
try {
|
|
6025
|
+
const [docsResult, codeResult] = await Promise.all([
|
|
6026
|
+
searchDocsHosted(
|
|
6027
|
+
`Compact ${topic} syntax`,
|
|
6028
|
+
SYNTAX_SUBQUERY_LIMIT,
|
|
6029
|
+
"reference"
|
|
6030
|
+
),
|
|
6031
|
+
searchCompactHosted(`${topic}`, SYNTAX_SUBQUERY_LIMIT)
|
|
6032
|
+
]);
|
|
6033
|
+
const mergedResults = [
|
|
6034
|
+
...docsResult.results || [],
|
|
6035
|
+
...codeResult.results || []
|
|
6326
6036
|
];
|
|
6327
|
-
|
|
6328
|
-
|
|
6329
|
-
|
|
6330
|
-
|
|
6331
|
-
|
|
6037
|
+
return {
|
|
6038
|
+
results: mergedResults.slice(0, SYNTAX_RESULTS_LIMIT),
|
|
6039
|
+
totalResults: mergedResults.length,
|
|
6040
|
+
query: topic,
|
|
6041
|
+
lastIndexed: docsResult.lastIndexed || codeResult.lastIndexed
|
|
6042
|
+
};
|
|
6043
|
+
} catch (error) {
|
|
6044
|
+
logger.warn(`Failed to search Compact syntax for topic ${topic}`, {
|
|
6045
|
+
error: error instanceof Error ? error.message : String(error)
|
|
6046
|
+
});
|
|
6047
|
+
return null;
|
|
6048
|
+
}
|
|
6049
|
+
}
|
|
6050
|
+
async function validateADTOperations(adtName, staticOperations) {
|
|
6051
|
+
const discrepancies = [];
|
|
6052
|
+
const enrichments = [];
|
|
6053
|
+
const indexedInfo = await searchADTInfo(adtName);
|
|
6054
|
+
if (!indexedInfo) {
|
|
6055
|
+
return {
|
|
6056
|
+
validated: false,
|
|
6057
|
+
operations: staticOperations.map((op) => ({
|
|
6058
|
+
method: op.method,
|
|
6059
|
+
description: op.note,
|
|
6060
|
+
worksInCircuits: op.works,
|
|
6061
|
+
source: "static-fallback"
|
|
6062
|
+
})),
|
|
6063
|
+
discrepancies: [
|
|
6064
|
+
"Could not validate against indexed docs - using static data"
|
|
6065
|
+
],
|
|
6066
|
+
enrichments: []
|
|
6067
|
+
};
|
|
6068
|
+
}
|
|
6069
|
+
const operations = [];
|
|
6070
|
+
const indexedMethodNames = new Set(
|
|
6071
|
+
indexedInfo.operations.map((op) => op.method.toLowerCase())
|
|
6072
|
+
);
|
|
6073
|
+
const staticMethodNames = new Set(
|
|
6074
|
+
staticOperations.map(
|
|
6075
|
+
(op) => op.method.replace(/^\./, "").replace(/\(.*$/, "").toLowerCase()
|
|
6076
|
+
)
|
|
6077
|
+
);
|
|
6078
|
+
for (const indexedOp of indexedInfo.operations) {
|
|
6079
|
+
operations.push(indexedOp);
|
|
6080
|
+
const staticOp = staticOperations.find(
|
|
6081
|
+
(s) => s.method.toLowerCase().includes(indexedOp.method.toLowerCase()) || indexedOp.method.toLowerCase().includes(
|
|
6082
|
+
s.method.replace(/^\./, "").replace(/\(.*$/, "").toLowerCase()
|
|
6083
|
+
)
|
|
6084
|
+
);
|
|
6085
|
+
if (staticOp && !staticOp.works && indexedOp.worksInCircuits) {
|
|
6086
|
+
discrepancies.push(
|
|
6087
|
+
`Static said ${staticOp.method} doesn't work, but indexed docs show it does`
|
|
6332
6088
|
);
|
|
6333
6089
|
}
|
|
6334
|
-
|
|
6335
|
-
|
|
6336
|
-
|
|
6337
|
-
|
|
6338
|
-
|
|
6339
|
-
|
|
6340
|
-
logger.info(`Unsubscribing from resource: ${uri}`);
|
|
6341
|
-
resourceSubscriptions.delete(uri);
|
|
6342
|
-
logger.debug(`Active subscriptions: ${resourceSubscriptions.size}`);
|
|
6343
|
-
return {};
|
|
6344
|
-
});
|
|
6345
|
-
}
|
|
6346
|
-
function setupSampling(server) {
|
|
6347
|
-
const samplingCallback2 = async (request) => {
|
|
6348
|
-
logger.debug("Requesting sampling from client", {
|
|
6349
|
-
messageCount: request.messages.length,
|
|
6350
|
-
maxTokens: request.maxTokens
|
|
6351
|
-
});
|
|
6352
|
-
try {
|
|
6353
|
-
const response = await server.request(
|
|
6354
|
-
{
|
|
6355
|
-
method: "sampling/createMessage",
|
|
6356
|
-
params: {
|
|
6357
|
-
messages: request.messages,
|
|
6358
|
-
systemPrompt: request.systemPrompt,
|
|
6359
|
-
maxTokens: request.maxTokens || 2048,
|
|
6360
|
-
temperature: request.temperature,
|
|
6361
|
-
modelPreferences: request.modelPreferences
|
|
6362
|
-
}
|
|
6363
|
-
},
|
|
6364
|
-
// Use a schema that matches the expected response
|
|
6365
|
-
{
|
|
6366
|
-
parse: (data) => {
|
|
6367
|
-
const response2 = data;
|
|
6368
|
-
if (!response2 || typeof response2 !== "object") {
|
|
6369
|
-
throw new Error("Invalid sampling response: expected object");
|
|
6370
|
-
}
|
|
6371
|
-
if (!response2.content || typeof response2.content !== "object") {
|
|
6372
|
-
throw new Error("Invalid sampling response: missing content");
|
|
6373
|
-
}
|
|
6374
|
-
return response2;
|
|
6375
|
-
},
|
|
6376
|
-
_def: { typeName: "SamplingResponse" }
|
|
6377
|
-
}
|
|
6090
|
+
}
|
|
6091
|
+
for (const staticOp of staticOperations) {
|
|
6092
|
+
const methodName = staticOp.method.replace(/^\./, "").replace(/\(.*$/, "").toLowerCase();
|
|
6093
|
+
if (!indexedMethodNames.has(methodName)) {
|
|
6094
|
+
discrepancies.push(
|
|
6095
|
+
`Static references ${staticOp.method} but not found in indexed docs - may not exist`
|
|
6378
6096
|
);
|
|
6379
|
-
return response;
|
|
6380
|
-
} catch (error) {
|
|
6381
|
-
logger.error("Sampling request failed", { error: String(error) });
|
|
6382
|
-
throw error;
|
|
6383
6097
|
}
|
|
6384
|
-
};
|
|
6385
|
-
registerSamplingCallback(samplingCallback2);
|
|
6386
|
-
logger.info("Sampling capability configured");
|
|
6387
|
-
}
|
|
6388
|
-
async function initializeServer() {
|
|
6389
|
-
logger.info("Initializing Midnight MCP Server...");
|
|
6390
|
-
checkForUpdates().catch((error) => {
|
|
6391
|
-
logger.debug("Startup version check failed (non-blocking)", {
|
|
6392
|
-
error: error instanceof Error ? error.message : String(error)
|
|
6393
|
-
});
|
|
6394
|
-
});
|
|
6395
|
-
try {
|
|
6396
|
-
await vectorStore.initialize();
|
|
6397
|
-
logger.info("Vector store initialized");
|
|
6398
|
-
} catch (error) {
|
|
6399
|
-
logger.warn("Vector store initialization failed, continuing without it", {
|
|
6400
|
-
error: String(error)
|
|
6401
|
-
});
|
|
6402
6098
|
}
|
|
6403
|
-
const
|
|
6404
|
-
|
|
6405
|
-
|
|
6406
|
-
|
|
6407
|
-
|
|
6408
|
-
|
|
6409
|
-
|
|
6410
|
-
|
|
6411
|
-
|
|
6412
|
-
|
|
6099
|
+
for (const indexedOp of indexedInfo.operations) {
|
|
6100
|
+
const methodName = indexedOp.method.toLowerCase();
|
|
6101
|
+
if (!staticMethodNames.has(methodName)) {
|
|
6102
|
+
enrichments.push(
|
|
6103
|
+
`Indexed docs show ${indexedOp.method} exists but was missing from static reference`
|
|
6104
|
+
);
|
|
6105
|
+
}
|
|
6106
|
+
}
|
|
6107
|
+
return {
|
|
6108
|
+
validated: true,
|
|
6109
|
+
operations,
|
|
6110
|
+
discrepancies,
|
|
6111
|
+
enrichments
|
|
6112
|
+
};
|
|
6413
6113
|
}
|
|
6414
|
-
|
|
6415
|
-
|
|
6416
|
-
|
|
6417
|
-
|
|
6418
|
-
async
|
|
6419
|
-
|
|
6420
|
-
|
|
6114
|
+
async function validateBuiltinFunctions(staticBuiltins) {
|
|
6115
|
+
const discrepancies = [];
|
|
6116
|
+
const enrichments = [];
|
|
6117
|
+
const results = await Promise.all(
|
|
6118
|
+
staticBuiltins.map(async (builtin) => {
|
|
6119
|
+
const searchResult = await searchCompactSyntax(
|
|
6120
|
+
`${builtin.name} function builtin`
|
|
6121
|
+
);
|
|
6122
|
+
return { builtin, found: (searchResult?.results?.length || 0) > 0 };
|
|
6421
6123
|
})
|
|
6422
6124
|
);
|
|
6423
|
-
|
|
6424
|
-
|
|
6425
|
-
|
|
6426
|
-
|
|
6427
|
-
|
|
6428
|
-
|
|
6429
|
-
|
|
6430
|
-
|
|
6431
|
-
|
|
6432
|
-
|
|
6433
|
-
|
|
6434
|
-
|
|
6435
|
-
|
|
6436
|
-
|
|
6437
|
-
|
|
6438
|
-
|
|
6439
|
-
|
|
6440
|
-
|
|
6441
|
-
|
|
6442
|
-
|
|
6443
|
-
sessionIdGenerator: () => randomUUID(),
|
|
6444
|
-
onsessioninitialized: (newSessionId) => {
|
|
6445
|
-
transports.streamable[newSessionId] = transport;
|
|
6446
|
-
logger.debug(`New streamable session: ${newSessionId}`);
|
|
6447
|
-
}
|
|
6448
|
-
});
|
|
6449
|
-
transport.onclose = () => {
|
|
6450
|
-
if (transport.sessionId) {
|
|
6451
|
-
delete transports.streamable[transport.sessionId];
|
|
6452
|
-
logger.debug(`Streamable session closed: ${transport.sessionId}`);
|
|
6125
|
+
for (const { builtin, found } of results) {
|
|
6126
|
+
if (!found) {
|
|
6127
|
+
discrepancies.push(
|
|
6128
|
+
`Builtin "${builtin.name}" not found in indexed docs - may not exist or have different name`
|
|
6129
|
+
);
|
|
6130
|
+
}
|
|
6131
|
+
}
|
|
6132
|
+
const allBuiltinsSearch = await searchCompactSyntax(
|
|
6133
|
+
"builtin function stdlib standard library"
|
|
6134
|
+
);
|
|
6135
|
+
if (allBuiltinsSearch?.results) {
|
|
6136
|
+
const knownNames = new Set(staticBuiltins.map((b) => b.name.toLowerCase()));
|
|
6137
|
+
for (const result of allBuiltinsSearch.results) {
|
|
6138
|
+
const content = result.content || result.code || "";
|
|
6139
|
+
const funcPattern = /\b(export\s+)?function\s+(\w+)/gi;
|
|
6140
|
+
let match;
|
|
6141
|
+
while ((match = funcPattern.exec(content)) !== null) {
|
|
6142
|
+
const funcName = match[2].toLowerCase();
|
|
6143
|
+
if (!knownNames.has(funcName) && funcName.length > 2) {
|
|
6144
|
+
enrichments.push(`Indexed docs mention "${match[2]}" function`);
|
|
6453
6145
|
}
|
|
6454
|
-
};
|
|
6455
|
-
try {
|
|
6456
|
-
await mcpServer.connect(transport);
|
|
6457
|
-
} catch (error) {
|
|
6458
|
-
logger.error("Failed to connect streamable transport", {
|
|
6459
|
-
error: String(error)
|
|
6460
|
-
});
|
|
6461
|
-
res.status(500).json({
|
|
6462
|
-
jsonrpc: "2.0",
|
|
6463
|
-
error: { code: -32603, message: "Internal error: connection failed" },
|
|
6464
|
-
id: null
|
|
6465
|
-
});
|
|
6466
|
-
return;
|
|
6467
6146
|
}
|
|
6468
|
-
} else {
|
|
6469
|
-
res.status(400).json({
|
|
6470
|
-
jsonrpc: "2.0",
|
|
6471
|
-
error: {
|
|
6472
|
-
code: -32e3,
|
|
6473
|
-
message: "Bad Request: No valid session ID provided"
|
|
6474
|
-
},
|
|
6475
|
-
id: null
|
|
6476
|
-
});
|
|
6477
|
-
return;
|
|
6478
|
-
}
|
|
6479
|
-
await transport.handleRequest(req, res, req.body);
|
|
6480
|
-
});
|
|
6481
|
-
app.get("/sse", async (_req, res) => {
|
|
6482
|
-
logger.debug("New SSE connection");
|
|
6483
|
-
const transport = new SSEServerTransport("/messages", res);
|
|
6484
|
-
transports.sse[transport.sessionId] = transport;
|
|
6485
|
-
res.on("close", () => {
|
|
6486
|
-
delete transports.sse[transport.sessionId];
|
|
6487
|
-
logger.debug(`SSE session closed: ${transport.sessionId}`);
|
|
6488
|
-
});
|
|
6489
|
-
try {
|
|
6490
|
-
await mcpServer.connect(transport);
|
|
6491
|
-
} catch (error) {
|
|
6492
|
-
delete transports.sse[transport.sessionId];
|
|
6493
|
-
logger.error(`SSE connection failed: ${transport.sessionId}`, {
|
|
6494
|
-
error: String(error)
|
|
6495
|
-
});
|
|
6496
|
-
res.status(500).end();
|
|
6497
|
-
}
|
|
6498
|
-
});
|
|
6499
|
-
app.post("/messages", async (req, res) => {
|
|
6500
|
-
const sessionId = req.query.sessionId;
|
|
6501
|
-
const transport = transports.sse[sessionId];
|
|
6502
|
-
if (transport) {
|
|
6503
|
-
await transport.handlePostMessage(req, res);
|
|
6504
|
-
} else {
|
|
6505
|
-
res.status(400).send(`No transport found for sessionId ${sessionId}`);
|
|
6506
6147
|
}
|
|
6507
|
-
}
|
|
6508
|
-
|
|
6509
|
-
|
|
6510
|
-
|
|
6511
|
-
|
|
6512
|
-
|
|
6148
|
+
}
|
|
6149
|
+
return {
|
|
6150
|
+
dataType: "BUILTIN_FUNCTIONS",
|
|
6151
|
+
validated: discrepancies.length === 0,
|
|
6152
|
+
discrepancies,
|
|
6153
|
+
enrichments: [...new Set(enrichments)].slice(0, 10),
|
|
6154
|
+
deprecatedPatterns: [],
|
|
6155
|
+
lastValidated: (/* @__PURE__ */ new Date()).toISOString()
|
|
6156
|
+
};
|
|
6157
|
+
}
|
|
6158
|
+
async function validateTypeCompatibility(staticRules) {
|
|
6159
|
+
const discrepancies = [];
|
|
6160
|
+
const enrichments = [];
|
|
6161
|
+
const typeSearch = await searchCompactSyntax(
|
|
6162
|
+
"type cast Field Uint Bytes conversion compatible"
|
|
6163
|
+
);
|
|
6164
|
+
if (typeSearch?.results) {
|
|
6165
|
+
for (const result of typeSearch.results) {
|
|
6166
|
+
const content = (result.content || result.code || "").toLowerCase();
|
|
6167
|
+
for (const rule of staticRules) {
|
|
6168
|
+
const typeMatch = rule.types.match(
|
|
6169
|
+
/^\s*(.+?)\s*(?:==|=|<=|>=|<|>|[+*])\s*(.+?)\s*$/
|
|
6170
|
+
);
|
|
6171
|
+
const typeA = typeMatch?.[1]?.trim().toLowerCase();
|
|
6172
|
+
const typeB = typeMatch?.[2]?.trim().toLowerCase();
|
|
6173
|
+
if (typeA && typeB) {
|
|
6174
|
+
if (content.includes(typeA) && content.includes(typeB) && content.includes("compatible")) {
|
|
6175
|
+
if (!rule.works) {
|
|
6176
|
+
discrepancies.push(
|
|
6177
|
+
`Static says "${rule.types}" doesn't work, but docs suggest compatibility`
|
|
6178
|
+
);
|
|
6179
|
+
}
|
|
6180
|
+
}
|
|
6181
|
+
}
|
|
6182
|
+
}
|
|
6513
6183
|
}
|
|
6514
|
-
|
|
6515
|
-
|
|
6184
|
+
}
|
|
6185
|
+
return {
|
|
6186
|
+
dataType: "TYPE_COMPATIBILITY",
|
|
6187
|
+
validated: discrepancies.length === 0,
|
|
6188
|
+
discrepancies,
|
|
6189
|
+
enrichments,
|
|
6190
|
+
deprecatedPatterns: [],
|
|
6191
|
+
lastValidated: (/* @__PURE__ */ new Date()).toISOString()
|
|
6516
6192
|
};
|
|
6517
|
-
|
|
6518
|
-
|
|
6519
|
-
const
|
|
6520
|
-
|
|
6521
|
-
|
|
6522
|
-
|
|
6523
|
-
|
|
6524
|
-
|
|
6525
|
-
|
|
6526
|
-
|
|
6527
|
-
|
|
6528
|
-
|
|
6529
|
-
|
|
6530
|
-
|
|
6531
|
-
|
|
6532
|
-
|
|
6193
|
+
}
|
|
6194
|
+
async function validateCommonErrors(staticErrors) {
|
|
6195
|
+
const discrepancies = [];
|
|
6196
|
+
const enrichments = [];
|
|
6197
|
+
const errorSearch = await searchDocsHosted(
|
|
6198
|
+
"error compile compilation failure parse",
|
|
6199
|
+
ADT_SEARCH_LIMIT,
|
|
6200
|
+
"all"
|
|
6201
|
+
);
|
|
6202
|
+
if (errorSearch?.results) {
|
|
6203
|
+
const knownErrors = new Set(
|
|
6204
|
+
staticErrors.map((e) => e.error.toLowerCase().slice(0, 30))
|
|
6205
|
+
);
|
|
6206
|
+
for (const result of errorSearch.results) {
|
|
6207
|
+
const content = result.content || result.code || "";
|
|
6208
|
+
const errorPattern = /(?:error|Error):\s*["']?([^"'\n]+)["']?/g;
|
|
6209
|
+
let match;
|
|
6210
|
+
while ((match = errorPattern.exec(content)) !== null) {
|
|
6211
|
+
const errorMsg = match[1].toLowerCase().slice(0, 30);
|
|
6212
|
+
if (!knownErrors.has(errorMsg) && errorMsg.length > 10) {
|
|
6213
|
+
enrichments.push(`Docs mention error: "${match[1].slice(0, 50)}..."`);
|
|
6214
|
+
}
|
|
6215
|
+
}
|
|
6216
|
+
}
|
|
6217
|
+
}
|
|
6218
|
+
return {
|
|
6219
|
+
dataType: "COMMON_ERRORS",
|
|
6220
|
+
validated: true,
|
|
6221
|
+
// Errors are informational, hard to validate
|
|
6222
|
+
discrepancies,
|
|
6223
|
+
enrichments: [...new Set(enrichments)].slice(0, 10),
|
|
6224
|
+
deprecatedPatterns: [],
|
|
6225
|
+
lastValidated: (/* @__PURE__ */ new Date()).toISOString()
|
|
6533
6226
|
};
|
|
6534
|
-
process.on("SIGINT", shutdown);
|
|
6535
|
-
process.on("SIGTERM", shutdown);
|
|
6536
6227
|
}
|
|
6537
|
-
|
|
6538
|
-
// src/config/compact-version.ts
|
|
6539
|
-
var COMPACT_VERSION = {
|
|
6540
|
-
/** Minimum supported version */
|
|
6541
|
-
min: "0.16",
|
|
6542
|
-
/** Maximum supported version */
|
|
6543
|
-
max: "0.21",
|
|
6544
|
-
/** When this config was last updated */
|
|
6545
|
-
lastUpdated: "2026-03-24",
|
|
6546
|
-
/** Source of truth for syntax patterns */
|
|
6547
|
-
referenceSource: "https://github.com/piotr-iohk/template-contract"
|
|
6548
|
-
};
|
|
6549
|
-
var RECOMMENDED_PRAGMA = `pragma language_version >= ${COMPACT_VERSION.min} && <= ${COMPACT_VERSION.max};`;
|
|
6550
|
-
var REFERENCE_CONTRACTS = [
|
|
6228
|
+
var DEPRECATED_SYNTAX_PATTERNS = [
|
|
6551
6229
|
{
|
|
6552
|
-
|
|
6553
|
-
|
|
6554
|
-
|
|
6230
|
+
pattern: /ledger\s*\{/,
|
|
6231
|
+
name: "ledger-block",
|
|
6232
|
+
message: "Deprecated: Use 'export ledger field: Type;' instead of ledger { } block",
|
|
6233
|
+
since: "0.16"
|
|
6555
6234
|
},
|
|
6556
6235
|
{
|
|
6557
|
-
|
|
6558
|
-
|
|
6559
|
-
|
|
6236
|
+
pattern: /Cell\s*<\s*\w+\s*>/,
|
|
6237
|
+
name: "cell-wrapper",
|
|
6238
|
+
message: "Deprecated: Cell<T> wrapper removed, use Type directly",
|
|
6239
|
+
since: "0.15"
|
|
6560
6240
|
},
|
|
6561
6241
|
{
|
|
6562
|
-
|
|
6563
|
-
|
|
6564
|
-
|
|
6242
|
+
pattern: /:\s*Void\b/,
|
|
6243
|
+
name: "void-type",
|
|
6244
|
+
message: "Void type doesn't exist, use [] (empty tuple) for no return",
|
|
6245
|
+
since: "always"
|
|
6565
6246
|
},
|
|
6566
6247
|
{
|
|
6567
|
-
|
|
6568
|
-
|
|
6569
|
-
|
|
6248
|
+
pattern: /\.\s*value\s*\(\s*\)/,
|
|
6249
|
+
name: "counter-value",
|
|
6250
|
+
message: "Counter.value() doesn't exist, use Counter.read() instead",
|
|
6251
|
+
since: "always"
|
|
6252
|
+
},
|
|
6253
|
+
{
|
|
6254
|
+
pattern: /::\w+/,
|
|
6255
|
+
name: "rust-enum-syntax",
|
|
6256
|
+
message: "Rust-style :: enum access doesn't work, use dot notation (Choice.rock)",
|
|
6257
|
+
since: "always"
|
|
6258
|
+
},
|
|
6259
|
+
{
|
|
6260
|
+
pattern: /pure\s+function\b/,
|
|
6261
|
+
name: "pure-function",
|
|
6262
|
+
message: "Use 'pure circuit' not 'pure function' for helper functions",
|
|
6263
|
+
since: "0.16"
|
|
6264
|
+
},
|
|
6265
|
+
{
|
|
6266
|
+
pattern: /witness\s+\w+\s*\([^)]*\)\s*:\s*\w+\s*\{/,
|
|
6267
|
+
name: "witness-with-body",
|
|
6268
|
+
message: "Witnesses are declarations only - no body allowed. Implementation goes in TypeScript prover.",
|
|
6269
|
+
since: "always"
|
|
6570
6270
|
}
|
|
6571
6271
|
];
|
|
6572
|
-
|
|
6573
|
-
|
|
6574
|
-
|
|
6575
|
-
|
|
6576
|
-
|
|
6577
|
-
|
|
6578
|
-
|
|
6579
|
-
|
|
6580
|
-
|
|
6581
|
-
|
|
6582
|
-
|
|
6583
|
-
|
|
6584
|
-
description: "Creates a hiding commitment to a value"
|
|
6585
|
-
},
|
|
6586
|
-
{
|
|
6587
|
-
name: "pad",
|
|
6588
|
-
signature: "pad(length: number, value: string): Bytes<N>",
|
|
6589
|
-
description: "Pads a string to fixed-length bytes"
|
|
6590
|
-
},
|
|
6591
|
-
{
|
|
6592
|
-
name: "disclose",
|
|
6593
|
-
signature: "disclose(value: T): T",
|
|
6594
|
-
description: "Explicitly reveals a witness value (required in conditionals)"
|
|
6595
|
-
},
|
|
6596
|
-
{
|
|
6597
|
-
name: "assert",
|
|
6598
|
-
signature: "assert(condition: Boolean, message?: string): []",
|
|
6599
|
-
description: "Fails circuit if condition is false"
|
|
6600
|
-
},
|
|
6601
|
-
{
|
|
6602
|
-
name: "default",
|
|
6603
|
-
signature: "default<T>(): T",
|
|
6604
|
-
description: "Returns default value for a type (0 for numbers, empty for collections)"
|
|
6605
|
-
}
|
|
6606
|
-
],
|
|
6607
|
-
/** NOT built-in - you must implement these patterns yourself */
|
|
6608
|
-
notBuiltIn: [
|
|
6609
|
-
{
|
|
6610
|
-
name: "public_key",
|
|
6611
|
-
wrongUsage: "public_key(sk) // ERROR: unbound identifier",
|
|
6612
|
-
correctPattern: `// Derive public key using persistentHash
|
|
6613
|
-
const pk = persistentHash<Vector<2, Bytes<32>>>([
|
|
6614
|
-
pad(32, "midnight:pk:"),
|
|
6615
|
-
sk
|
|
6616
|
-
]);`,
|
|
6617
|
-
description: "Public key derivation is NOT a builtin - use persistentHash pattern"
|
|
6618
|
-
},
|
|
6619
|
-
{
|
|
6620
|
-
name: "verify_signature",
|
|
6621
|
-
wrongUsage: "verify_signature(msg, sig, pk) // Does not exist",
|
|
6622
|
-
correctPattern: `// Signature verification must be done via witnesses
|
|
6623
|
-
// The prover verifies off-chain, then provides the boolean result
|
|
6624
|
-
witness signature_valid(): Boolean;`,
|
|
6625
|
-
description: "Signature verification is done off-chain in the prover"
|
|
6626
|
-
},
|
|
6627
|
-
{
|
|
6628
|
-
name: "random",
|
|
6629
|
-
wrongUsage: "random() // Does not exist in ZK circuits",
|
|
6630
|
-
correctPattern: `// Randomness must come from witnesses (prover-provided)
|
|
6631
|
-
witness get_random_value(): Field;`,
|
|
6632
|
-
description: "ZK circuits are deterministic - randomness must come from witnesses"
|
|
6633
|
-
}
|
|
6634
|
-
]
|
|
6635
|
-
};
|
|
6636
|
-
var TYPE_COMPATIBILITY = {
|
|
6637
|
-
// Note: Comparison operators work in practice but aren't explicitly listed in docs
|
|
6638
|
-
comparisons: [
|
|
6639
|
-
{ types: "Field == Field", works: true, note: "Direct comparison" },
|
|
6640
|
-
{
|
|
6641
|
-
types: "Field == Uint<N>",
|
|
6642
|
-
works: false,
|
|
6643
|
-
fix: "Cast with `value as Field`"
|
|
6644
|
-
},
|
|
6645
|
-
{
|
|
6646
|
-
types: "Field >= 0",
|
|
6647
|
-
works: false,
|
|
6648
|
-
fix: "Use bounded Uint<0..N> parameter instead"
|
|
6649
|
-
},
|
|
6650
|
-
{ types: "Uint<N> == Uint<N>", works: true, note: "Same-width comparison" },
|
|
6651
|
-
{
|
|
6652
|
-
types: "Uint<0..2> == Uint<0..2>",
|
|
6653
|
-
works: true,
|
|
6654
|
-
note: "Bounded integers"
|
|
6655
|
-
},
|
|
6656
|
-
{
|
|
6657
|
-
types: "Bytes<32> == Bytes<32>",
|
|
6658
|
-
works: true,
|
|
6659
|
-
note: "Used in examples, but operators not explicitly listed in docs"
|
|
6660
|
-
},
|
|
6661
|
-
{ types: "Boolean == Boolean", works: true, note: "Direct comparison" }
|
|
6662
|
-
],
|
|
6663
|
-
arithmetic: [
|
|
6664
|
-
{ types: "Field + Field", works: true, note: "Field arithmetic" },
|
|
6665
|
-
{ types: "Field + Uint<N>", works: false, fix: "Cast Uint to Field first" },
|
|
6666
|
-
{
|
|
6667
|
-
types: "Uint<N> + Uint<N>",
|
|
6668
|
-
works: true,
|
|
6669
|
-
note: "Result is bounded type, cast back: (a + b) as Uint<64>"
|
|
6670
|
-
},
|
|
6671
|
-
{
|
|
6672
|
-
types: "Uint<64> + Uint<64>",
|
|
6673
|
-
works: true,
|
|
6674
|
-
note: "Result is Uint<0..36893488147419103230>, must cast: (a + b) as Uint<64>"
|
|
6675
|
-
},
|
|
6676
|
-
{
|
|
6677
|
-
types: "Uint<64> * Uint<64>",
|
|
6678
|
-
works: true,
|
|
6679
|
-
note: "Result is wide bounded type, cast back to target type"
|
|
6680
|
-
}
|
|
6681
|
-
],
|
|
6682
|
-
/**
|
|
6683
|
-
* Type casting rules - based on official cast table
|
|
6684
|
-
* See: https://docs.midnight.network/develop/reference/compact/lang-ref#type-cast-expressions
|
|
6685
|
-
*
|
|
6686
|
-
* Cast kinds: static (always succeeds), conversion (semantic change), checked (can fail)
|
|
6687
|
-
*/
|
|
6688
|
-
typeCasting: [
|
|
6689
|
-
{
|
|
6690
|
-
from: "Uint<N>",
|
|
6691
|
-
to: "Field",
|
|
6692
|
-
direct: true,
|
|
6693
|
-
kind: "static",
|
|
6694
|
-
note: "Always succeeds"
|
|
6695
|
-
},
|
|
6696
|
-
{
|
|
6697
|
-
from: "Field",
|
|
6698
|
-
to: "Uint<0..n>",
|
|
6699
|
-
direct: true,
|
|
6700
|
-
kind: "checked",
|
|
6701
|
-
note: "Fails at runtime if value > n"
|
|
6702
|
-
},
|
|
6703
|
-
{
|
|
6704
|
-
from: "Field",
|
|
6705
|
-
to: "Bytes<n>",
|
|
6706
|
-
direct: true,
|
|
6707
|
-
kind: "conversion",
|
|
6708
|
-
note: "Can fail at runtime if value doesn't fit (little-endian)"
|
|
6709
|
-
},
|
|
6710
|
-
{
|
|
6711
|
-
from: "Bytes<m>",
|
|
6712
|
-
to: "Field",
|
|
6713
|
-
direct: true,
|
|
6714
|
-
kind: "conversion",
|
|
6715
|
-
note: "Can fail at runtime if result exceeds max Field value"
|
|
6716
|
-
},
|
|
6717
|
-
{
|
|
6718
|
-
from: "Boolean",
|
|
6719
|
-
to: "Uint<0..n>",
|
|
6720
|
-
direct: true,
|
|
6721
|
-
kind: "conversion",
|
|
6722
|
-
note: "false\u21920, true\u21921 (n must not be 0)"
|
|
6723
|
-
},
|
|
6724
|
-
{
|
|
6725
|
-
from: "Boolean",
|
|
6726
|
-
to: "Field",
|
|
6727
|
-
direct: false,
|
|
6728
|
-
fix: "Go through Uint: (flag as Uint<0..1>) as Field"
|
|
6729
|
-
},
|
|
6730
|
-
{
|
|
6731
|
-
from: "Uint<N>",
|
|
6732
|
-
to: "Bytes<M>",
|
|
6733
|
-
direct: false,
|
|
6734
|
-
fix: "Go through Field: (amount as Field) as Bytes<32>"
|
|
6735
|
-
},
|
|
6736
|
-
{
|
|
6737
|
-
from: "enum",
|
|
6738
|
-
to: "Field",
|
|
6739
|
-
direct: true,
|
|
6740
|
-
kind: "conversion",
|
|
6741
|
-
note: "Enum variant index as Field"
|
|
6742
|
-
},
|
|
6743
|
-
{
|
|
6744
|
-
from: "Uint<0..m>",
|
|
6745
|
-
to: "Uint<0..n>",
|
|
6746
|
-
direct: true,
|
|
6747
|
-
kind: "static if m\u2264n, checked if m>n",
|
|
6748
|
-
note: "Widening is static, narrowing is checked"
|
|
6749
|
-
},
|
|
6750
|
-
{
|
|
6751
|
-
from: "arithmetic result",
|
|
6752
|
-
to: "Uint<64>",
|
|
6753
|
-
direct: true,
|
|
6754
|
-
kind: "checked",
|
|
6755
|
-
note: "Required cast: (a + b) as Uint<64>"
|
|
6272
|
+
function scanForDeprecatedPatterns(code) {
|
|
6273
|
+
const issues = [];
|
|
6274
|
+
const lines = code.split("\n");
|
|
6275
|
+
for (const { pattern, name, message } of DEPRECATED_SYNTAX_PATTERNS) {
|
|
6276
|
+
for (let i = 0; i < lines.length; i++) {
|
|
6277
|
+
if (pattern.test(lines[i])) {
|
|
6278
|
+
issues.push({
|
|
6279
|
+
pattern: name,
|
|
6280
|
+
message,
|
|
6281
|
+
lineNumber: i + 1
|
|
6282
|
+
});
|
|
6283
|
+
}
|
|
6756
6284
|
}
|
|
6757
|
-
|
|
6758
|
-
|
|
6759
|
-
|
|
6760
|
-
|
|
6761
|
-
|
|
6762
|
-
|
|
6285
|
+
}
|
|
6286
|
+
return issues;
|
|
6287
|
+
}
|
|
6288
|
+
async function validateAllStaticData(staticData) {
|
|
6289
|
+
const results = {};
|
|
6290
|
+
const [builtinResult, typeResult, errorResult, adtResults] = await Promise.all([
|
|
6291
|
+
staticData.builtinFunctions ? validateBuiltinFunctions(staticData.builtinFunctions) : Promise.resolve(null),
|
|
6292
|
+
staticData.typeCompatibility ? validateTypeCompatibility(staticData.typeCompatibility) : Promise.resolve(null),
|
|
6293
|
+
staticData.commonErrors ? validateCommonErrors(staticData.commonErrors) : Promise.resolve(null),
|
|
6294
|
+
staticData.ledgerTypeLimits ? Promise.all(
|
|
6295
|
+
Object.entries(staticData.ledgerTypeLimits).map(
|
|
6296
|
+
async ([name, data]) => ({
|
|
6297
|
+
name,
|
|
6298
|
+
result: await validateADTOperations(
|
|
6299
|
+
name,
|
|
6300
|
+
data.circuitOperations
|
|
6301
|
+
)
|
|
6302
|
+
})
|
|
6303
|
+
)
|
|
6304
|
+
) : Promise.resolve([])
|
|
6305
|
+
]);
|
|
6306
|
+
if (builtinResult) results.builtinFunctions = builtinResult;
|
|
6307
|
+
if (typeResult) results.typeCompatibility = typeResult;
|
|
6308
|
+
if (errorResult) results.commonErrors = errorResult;
|
|
6309
|
+
for (const { name, result } of adtResults) {
|
|
6310
|
+
results[`adt_${name}`] = {
|
|
6311
|
+
dataType: `LEDGER_TYPE_LIMITS.${name}`,
|
|
6312
|
+
validated: result.validated,
|
|
6313
|
+
discrepancies: result.discrepancies,
|
|
6314
|
+
enrichments: result.enrichments,
|
|
6315
|
+
deprecatedPatterns: [],
|
|
6316
|
+
lastValidated: (/* @__PURE__ */ new Date()).toISOString()
|
|
6317
|
+
};
|
|
6318
|
+
}
|
|
6319
|
+
const allDiscrepancies = Object.values(results).flatMap(
|
|
6320
|
+
(r) => r.discrepancies
|
|
6321
|
+
);
|
|
6322
|
+
const allEnrichments = Object.values(results).flatMap((r) => r.enrichments);
|
|
6323
|
+
return {
|
|
6324
|
+
overall: {
|
|
6325
|
+
validated: allDiscrepancies.length === 0,
|
|
6326
|
+
totalDiscrepancies: allDiscrepancies.length,
|
|
6327
|
+
totalEnrichments: allEnrichments.length
|
|
6763
6328
|
},
|
|
6764
|
-
|
|
6765
|
-
|
|
6766
|
-
|
|
6767
|
-
|
|
6768
|
-
|
|
6769
|
-
|
|
6770
|
-
|
|
6771
|
-
|
|
6772
|
-
|
|
6773
|
-
|
|
6774
|
-
"Casting with `as Field` is safe but loses range information"
|
|
6775
|
-
]
|
|
6329
|
+
results,
|
|
6330
|
+
lastValidated: (/* @__PURE__ */ new Date()).toISOString()
|
|
6331
|
+
};
|
|
6332
|
+
}
|
|
6333
|
+
|
|
6334
|
+
// src/server.ts
|
|
6335
|
+
var SERVER_INFO = {
|
|
6336
|
+
name: "midnight-mcp",
|
|
6337
|
+
version: CURRENT_VERSION,
|
|
6338
|
+
description: "MCP Server for Midnight Blockchain Development"
|
|
6776
6339
|
};
|
|
6777
|
-
var
|
|
6778
|
-
|
|
6779
|
-
|
|
6780
|
-
|
|
6781
|
-
|
|
6782
|
-
|
|
6783
|
-
|
|
6784
|
-
|
|
6785
|
-
|
|
6786
|
-
|
|
6787
|
-
|
|
6788
|
-
|
|
6789
|
-
|
|
6790
|
-
|
|
6791
|
-
|
|
6792
|
-
|
|
6793
|
-
|
|
6794
|
-
|
|
6795
|
-
|
|
6796
|
-
|
|
6797
|
-
|
|
6798
|
-
|
|
6799
|
-
|
|
6800
|
-
|
|
6801
|
-
|
|
6802
|
-
|
|
6803
|
-
|
|
6340
|
+
var versionCheckResult = {
|
|
6341
|
+
isOutdated: false,
|
|
6342
|
+
latestVersion: CURRENT_VERSION,
|
|
6343
|
+
updateMessage: null,
|
|
6344
|
+
lastChecked: 0
|
|
6345
|
+
};
|
|
6346
|
+
var toolCallCount = 0;
|
|
6347
|
+
var VERSION_CHECK_INTERVAL = 10;
|
|
6348
|
+
async function checkForUpdates() {
|
|
6349
|
+
try {
|
|
6350
|
+
const controller = new AbortController();
|
|
6351
|
+
const timeoutId = setTimeout(() => controller.abort(), 5e3);
|
|
6352
|
+
const response = await fetch(
|
|
6353
|
+
"https://registry.npmjs.org/midnight-mcp/latest",
|
|
6354
|
+
{ signal: controller.signal }
|
|
6355
|
+
);
|
|
6356
|
+
clearTimeout(timeoutId);
|
|
6357
|
+
if (!response.ok) return;
|
|
6358
|
+
const data = await response.json();
|
|
6359
|
+
const latestVersion = data.version;
|
|
6360
|
+
if (latestVersion !== CURRENT_VERSION) {
|
|
6361
|
+
versionCheckResult = {
|
|
6362
|
+
isOutdated: true,
|
|
6363
|
+
latestVersion,
|
|
6364
|
+
lastChecked: Date.now(),
|
|
6365
|
+
updateMessage: `\u{1F6A8} UPDATE AVAILABLE: v${latestVersion} (you have v${CURRENT_VERSION})`
|
|
6366
|
+
};
|
|
6367
|
+
logger.warn(
|
|
6368
|
+
`Outdated version detected: v${CURRENT_VERSION} -> v${latestVersion}`
|
|
6369
|
+
);
|
|
6370
|
+
} else {
|
|
6371
|
+
versionCheckResult = {
|
|
6372
|
+
...versionCheckResult,
|
|
6373
|
+
lastChecked: Date.now()
|
|
6374
|
+
};
|
|
6375
|
+
}
|
|
6376
|
+
} catch (error) {
|
|
6377
|
+
logger.debug("Version check failed (non-blocking)", {
|
|
6378
|
+
error: error instanceof Error ? error.message : String(error)
|
|
6379
|
+
});
|
|
6380
|
+
}
|
|
6381
|
+
}
|
|
6382
|
+
function maybeCheckForUpdates() {
|
|
6383
|
+
toolCallCount++;
|
|
6384
|
+
if (toolCallCount >= VERSION_CHECK_INTERVAL) {
|
|
6385
|
+
toolCallCount = 0;
|
|
6386
|
+
const fiveMinutes = 5 * 60 * 1e3;
|
|
6387
|
+
if (Date.now() - versionCheckResult.lastChecked > fiveMinutes) {
|
|
6388
|
+
checkForUpdates().catch((error) => {
|
|
6389
|
+
logger.debug("Periodic version check failed", {
|
|
6390
|
+
error: error instanceof Error ? error.message : String(error)
|
|
6391
|
+
});
|
|
6392
|
+
});
|
|
6393
|
+
}
|
|
6394
|
+
}
|
|
6395
|
+
}
|
|
6396
|
+
function getUpdateWarning() {
|
|
6397
|
+
return versionCheckResult.updateMessage;
|
|
6398
|
+
}
|
|
6399
|
+
var resourceSubscriptions = /* @__PURE__ */ new Set();
|
|
6400
|
+
var resourceTemplates = [
|
|
6401
|
+
{
|
|
6402
|
+
uriTemplate: "midnight://code/{owner}/{repo}/{path}",
|
|
6403
|
+
name: "Repository Code",
|
|
6404
|
+
title: "\u{1F4C4} Repository Code Files",
|
|
6405
|
+
description: "Access code files from any Midnight repository by specifying owner, repo, and file path",
|
|
6406
|
+
mimeType: "text/plain"
|
|
6804
6407
|
},
|
|
6805
|
-
|
|
6806
|
-
|
|
6807
|
-
|
|
6808
|
-
|
|
6809
|
-
|
|
6810
|
-
|
|
6811
|
-
|
|
6812
|
-
|
|
6813
|
-
|
|
6814
|
-
|
|
6815
|
-
|
|
6816
|
-
|
|
6817
|
-
|
|
6818
|
-
|
|
6819
|
-
|
|
6820
|
-
|
|
6821
|
-
|
|
6822
|
-
|
|
6823
|
-
|
|
6824
|
-
|
|
6825
|
-
|
|
6826
|
-
|
|
6408
|
+
{
|
|
6409
|
+
uriTemplate: "midnight://docs/{section}/{topic}",
|
|
6410
|
+
name: "Documentation",
|
|
6411
|
+
title: "\u{1F4DA} Documentation Sections",
|
|
6412
|
+
description: "Access documentation by section (guides, api, concepts) and topic",
|
|
6413
|
+
mimeType: "text/markdown"
|
|
6414
|
+
},
|
|
6415
|
+
{
|
|
6416
|
+
uriTemplate: "midnight://examples/{category}/{name}",
|
|
6417
|
+
name: "Example Contracts",
|
|
6418
|
+
title: "\u{1F4DD} Example Contracts",
|
|
6419
|
+
description: "Access example contracts by category (counter, bboard, token, voting) and name",
|
|
6420
|
+
mimeType: "text/x-compact"
|
|
6421
|
+
},
|
|
6422
|
+
{
|
|
6423
|
+
uriTemplate: "midnight://schema/{type}",
|
|
6424
|
+
name: "Schema Definitions",
|
|
6425
|
+
title: "\u{1F527} Schema Definitions",
|
|
6426
|
+
description: "Access JSON schemas for contract AST, transactions, and proofs",
|
|
6427
|
+
mimeType: "application/json"
|
|
6428
|
+
}
|
|
6429
|
+
];
|
|
6430
|
+
var mcpLogLevel = "info";
|
|
6431
|
+
var serverInstance = null;
|
|
6432
|
+
var isConnected = false;
|
|
6433
|
+
function setServerConnected(connected) {
|
|
6434
|
+
isConnected = connected;
|
|
6435
|
+
}
|
|
6436
|
+
function sendLogToClient(level, loggerName, data) {
|
|
6437
|
+
if (!serverInstance || !isConnected) return;
|
|
6438
|
+
const levelValues = {
|
|
6439
|
+
debug: 0,
|
|
6440
|
+
info: 1,
|
|
6441
|
+
notice: 2,
|
|
6442
|
+
warning: 3,
|
|
6443
|
+
error: 4,
|
|
6444
|
+
critical: 5,
|
|
6445
|
+
alert: 6,
|
|
6446
|
+
emergency: 7
|
|
6447
|
+
};
|
|
6448
|
+
if (levelValues[level] < levelValues[mcpLogLevel]) return;
|
|
6449
|
+
try {
|
|
6450
|
+
serverInstance.notification({
|
|
6451
|
+
method: "notifications/message",
|
|
6452
|
+
params: {
|
|
6453
|
+
level,
|
|
6454
|
+
logger: loggerName,
|
|
6455
|
+
data
|
|
6456
|
+
}
|
|
6457
|
+
});
|
|
6458
|
+
} catch (error) {
|
|
6459
|
+
logger.debug("Failed to send log notification to client", {
|
|
6460
|
+
error: error instanceof Error ? error.message : String(error)
|
|
6461
|
+
});
|
|
6462
|
+
}
|
|
6463
|
+
}
|
|
6464
|
+
function sendProgressNotification(progressToken, progress, total, message) {
|
|
6465
|
+
if (!serverInstance) return;
|
|
6466
|
+
try {
|
|
6467
|
+
serverInstance.notification({
|
|
6468
|
+
method: "notifications/progress",
|
|
6469
|
+
params: {
|
|
6470
|
+
progressToken,
|
|
6471
|
+
progress,
|
|
6472
|
+
...total !== void 0 && { total },
|
|
6473
|
+
...message && { message }
|
|
6474
|
+
}
|
|
6475
|
+
});
|
|
6476
|
+
} catch (error) {
|
|
6477
|
+
logger.debug("Failed to send progress notification", {
|
|
6478
|
+
error: error instanceof Error ? error.message : String(error)
|
|
6479
|
+
});
|
|
6480
|
+
}
|
|
6481
|
+
}
|
|
6482
|
+
function createServer() {
|
|
6483
|
+
const server = new Server(SERVER_INFO, {
|
|
6484
|
+
capabilities: {
|
|
6485
|
+
tools: {
|
|
6486
|
+
listChanged: true
|
|
6827
6487
|
},
|
|
6828
|
-
{
|
|
6829
|
-
|
|
6830
|
-
|
|
6831
|
-
note: "Returns Boolean - checks if map is empty"
|
|
6488
|
+
resources: {
|
|
6489
|
+
subscribe: true,
|
|
6490
|
+
listChanged: true
|
|
6832
6491
|
},
|
|
6833
|
-
{
|
|
6834
|
-
|
|
6835
|
-
works: true,
|
|
6836
|
-
note: "Returns Uint<64> - number of entries"
|
|
6492
|
+
prompts: {
|
|
6493
|
+
listChanged: true
|
|
6837
6494
|
},
|
|
6838
|
-
{
|
|
6839
|
-
|
|
6840
|
-
|
|
6841
|
-
|
|
6495
|
+
logging: {},
|
|
6496
|
+
completions: {}
|
|
6497
|
+
}
|
|
6498
|
+
});
|
|
6499
|
+
serverInstance = server;
|
|
6500
|
+
setMCPLogCallback((level, loggerName, data) => {
|
|
6501
|
+
sendLogToClient(level, loggerName, data);
|
|
6502
|
+
});
|
|
6503
|
+
registerToolHandlers(server);
|
|
6504
|
+
registerResourceHandlers(server);
|
|
6505
|
+
registerPromptHandlers(server);
|
|
6506
|
+
registerSubscriptionHandlers(server);
|
|
6507
|
+
registerLoggingHandler(server);
|
|
6508
|
+
registerCompletionsHandler(server);
|
|
6509
|
+
setupSampling(server);
|
|
6510
|
+
return server;
|
|
6511
|
+
}
|
|
6512
|
+
function registerLoggingHandler(server) {
|
|
6513
|
+
server.setRequestHandler(SetLevelRequestSchema, async (request) => {
|
|
6514
|
+
const { level } = request.params;
|
|
6515
|
+
mcpLogLevel = level;
|
|
6516
|
+
logger.info(`MCP log level set to: ${level}`);
|
|
6517
|
+
sendLogToClient("info", "midnight-mcp", {
|
|
6518
|
+
message: `Log level changed to ${level}`
|
|
6519
|
+
});
|
|
6520
|
+
return {};
|
|
6521
|
+
});
|
|
6522
|
+
}
|
|
6523
|
+
var COMPLETION_VALUES = {
|
|
6524
|
+
"midnight:create-contract": {
|
|
6525
|
+
contractType: [
|
|
6526
|
+
"token",
|
|
6527
|
+
"voting",
|
|
6528
|
+
"credential",
|
|
6529
|
+
"auction",
|
|
6530
|
+
"escrow",
|
|
6531
|
+
"custom"
|
|
6532
|
+
],
|
|
6533
|
+
privacyLevel: ["full", "partial", "public"],
|
|
6534
|
+
complexity: ["beginner", "intermediate", "advanced"]
|
|
6535
|
+
},
|
|
6536
|
+
"midnight:review-contract": {
|
|
6537
|
+
focusAreas: [
|
|
6538
|
+
"security",
|
|
6539
|
+
"performance",
|
|
6540
|
+
"privacy",
|
|
6541
|
+
"readability",
|
|
6542
|
+
"gas-optimization"
|
|
6543
|
+
]
|
|
6544
|
+
},
|
|
6545
|
+
"midnight:explain-concept": {
|
|
6546
|
+
concept: [
|
|
6547
|
+
"zk-proofs",
|
|
6548
|
+
"circuits",
|
|
6549
|
+
"witnesses",
|
|
6550
|
+
"ledger",
|
|
6551
|
+
"state-management",
|
|
6552
|
+
"privacy-model",
|
|
6553
|
+
"token-transfers",
|
|
6554
|
+
"merkle-trees"
|
|
6555
|
+
],
|
|
6556
|
+
level: ["beginner", "intermediate", "advanced"]
|
|
6557
|
+
},
|
|
6558
|
+
"midnight:compare-approaches": {
|
|
6559
|
+
approaches: [
|
|
6560
|
+
"token-standards",
|
|
6561
|
+
"state-management",
|
|
6562
|
+
"privacy-patterns",
|
|
6563
|
+
"circuit-design"
|
|
6564
|
+
]
|
|
6565
|
+
},
|
|
6566
|
+
"midnight:debug-contract": {
|
|
6567
|
+
errorType: [
|
|
6568
|
+
"compilation",
|
|
6569
|
+
"runtime",
|
|
6570
|
+
"logic",
|
|
6571
|
+
"privacy-leak",
|
|
6572
|
+
"state-corruption"
|
|
6573
|
+
]
|
|
6574
|
+
}
|
|
6575
|
+
};
|
|
6576
|
+
function registerCompletionsHandler(server) {
|
|
6577
|
+
server.setRequestHandler(CompleteRequestSchema, async (request) => {
|
|
6578
|
+
const { ref, argument } = request.params;
|
|
6579
|
+
if (ref.type !== "ref/prompt") {
|
|
6580
|
+
return { completion: { values: [], hasMore: false } };
|
|
6581
|
+
}
|
|
6582
|
+
const promptName = ref.name;
|
|
6583
|
+
const argName = argument.name;
|
|
6584
|
+
const currentValue = argument.value?.toLowerCase() || "";
|
|
6585
|
+
const promptCompletions = COMPLETION_VALUES[promptName];
|
|
6586
|
+
if (!promptCompletions) {
|
|
6587
|
+
return { completion: { values: [], hasMore: false } };
|
|
6588
|
+
}
|
|
6589
|
+
const argValues = promptCompletions[argName];
|
|
6590
|
+
if (!argValues) {
|
|
6591
|
+
return { completion: { values: [], hasMore: false } };
|
|
6592
|
+
}
|
|
6593
|
+
const filtered = argValues.filter(
|
|
6594
|
+
(v) => v.toLowerCase().includes(currentValue)
|
|
6595
|
+
);
|
|
6596
|
+
return {
|
|
6597
|
+
completion: {
|
|
6598
|
+
values: filtered.slice(0, 20),
|
|
6599
|
+
total: filtered.length,
|
|
6600
|
+
hasMore: filtered.length > 20
|
|
6601
|
+
}
|
|
6602
|
+
};
|
|
6603
|
+
});
|
|
6604
|
+
}
|
|
6605
|
+
function registerToolHandlers(server) {
|
|
6606
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
6607
|
+
logger.debug("Listing tools");
|
|
6608
|
+
return {
|
|
6609
|
+
tools: allTools.map((tool) => ({
|
|
6610
|
+
name: tool.name,
|
|
6611
|
+
description: tool.description,
|
|
6612
|
+
inputSchema: tool.inputSchema,
|
|
6613
|
+
// Include output schema if defined
|
|
6614
|
+
...tool.outputSchema && { outputSchema: tool.outputSchema },
|
|
6615
|
+
// Include annotations if defined
|
|
6616
|
+
...tool.annotations && { annotations: tool.annotations }
|
|
6617
|
+
}))
|
|
6618
|
+
};
|
|
6619
|
+
});
|
|
6620
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
6621
|
+
const { name, arguments: args } = request.params;
|
|
6622
|
+
logger.info(`Tool called: ${name}`, { args });
|
|
6623
|
+
const startTime = Date.now();
|
|
6624
|
+
const tool = allTools.find((t) => t.name === name);
|
|
6625
|
+
if (!tool) {
|
|
6626
|
+
const availableTools = allTools.map((t) => t.name).slice(0, 5).join(", ");
|
|
6627
|
+
trackToolCall(name, false, Date.now() - startTime, CURRENT_VERSION);
|
|
6628
|
+
return {
|
|
6629
|
+
content: [
|
|
6630
|
+
{
|
|
6631
|
+
type: "text",
|
|
6632
|
+
text: serialize({
|
|
6633
|
+
error: `Unknown tool: ${name}`,
|
|
6634
|
+
suggestion: `Available tools include: ${availableTools}...`,
|
|
6635
|
+
hint: "Use ListTools to see all available tools"
|
|
6636
|
+
})
|
|
6637
|
+
}
|
|
6638
|
+
],
|
|
6639
|
+
isError: true
|
|
6640
|
+
};
|
|
6641
|
+
}
|
|
6642
|
+
try {
|
|
6643
|
+
const result = await tool.handler(args);
|
|
6644
|
+
const durationMs = Date.now() - startTime;
|
|
6645
|
+
trackToolCall(name, true, durationMs, CURRENT_VERSION);
|
|
6646
|
+
maybeCheckForUpdates();
|
|
6647
|
+
const updateWarning = getUpdateWarning();
|
|
6648
|
+
if (updateWarning && versionCheckResult.isOutdated) {
|
|
6649
|
+
const updatePrompt = {
|
|
6650
|
+
"\u{1F6A8} UPDATE REQUIRED": {
|
|
6651
|
+
current: `v${CURRENT_VERSION}`,
|
|
6652
|
+
latest: `v${versionCheckResult.latestVersion}`,
|
|
6653
|
+
quickFix: {
|
|
6654
|
+
description: "Most users just need these 2 steps:",
|
|
6655
|
+
step1: "Run in terminal: rm -rf ~/.npm/_npx",
|
|
6656
|
+
step2: "Restart editor completely (Cmd+Q / Alt+F4, then reopen)"
|
|
6657
|
+
},
|
|
6658
|
+
ifStillOld: {
|
|
6659
|
+
description: "If still showing old version after restart:",
|
|
6660
|
+
action: "Edit your MCP config file to use @latest:",
|
|
6661
|
+
change: 'Change "midnight-mcp" to "midnight-mcp@latest" in args',
|
|
6662
|
+
configFiles: {
|
|
6663
|
+
"Claude Desktop (Mac)": "~/Library/Application Support/Claude/claude_desktop_config.json",
|
|
6664
|
+
"Claude Desktop (Win)": "%APPDATA%/Claude/claude_desktop_config.json",
|
|
6665
|
+
Cursor: ".cursor/mcp.json",
|
|
6666
|
+
"VS Code": ".vscode/mcp.json",
|
|
6667
|
+
Windsurf: "~/.codeium/windsurf/mcp_config.json"
|
|
6668
|
+
}
|
|
6669
|
+
},
|
|
6670
|
+
tip: "Use 'midnight-get-update-instructions' tool for detailed platform-specific steps."
|
|
6671
|
+
},
|
|
6672
|
+
result
|
|
6673
|
+
};
|
|
6674
|
+
return {
|
|
6675
|
+
content: [
|
|
6676
|
+
{
|
|
6677
|
+
type: "text",
|
|
6678
|
+
text: serialize(updatePrompt)
|
|
6679
|
+
}
|
|
6680
|
+
]
|
|
6681
|
+
};
|
|
6682
|
+
}
|
|
6683
|
+
return {
|
|
6684
|
+
content: [
|
|
6685
|
+
{
|
|
6686
|
+
type: "text",
|
|
6687
|
+
text: serialize(result)
|
|
6688
|
+
}
|
|
6689
|
+
],
|
|
6690
|
+
// Include structured content for machine-readable responses
|
|
6691
|
+
// This allows clients to parse results without JSON.parse()
|
|
6692
|
+
structuredContent: result
|
|
6693
|
+
};
|
|
6694
|
+
} catch (error) {
|
|
6695
|
+
const durationMs = Date.now() - startTime;
|
|
6696
|
+
logger.error(`Tool error: ${name}`, { error: String(error) });
|
|
6697
|
+
trackToolCall(name, false, durationMs, CURRENT_VERSION);
|
|
6698
|
+
const errorResponse = formatErrorResponse(error, `tool:${name}`);
|
|
6699
|
+
return {
|
|
6700
|
+
content: [
|
|
6701
|
+
{
|
|
6702
|
+
type: "text",
|
|
6703
|
+
text: serialize(errorResponse)
|
|
6704
|
+
}
|
|
6705
|
+
],
|
|
6706
|
+
isError: true
|
|
6707
|
+
};
|
|
6708
|
+
}
|
|
6709
|
+
});
|
|
6710
|
+
}
|
|
6711
|
+
function registerResourceHandlers(server) {
|
|
6712
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
6713
|
+
logger.debug("Listing resources");
|
|
6714
|
+
return {
|
|
6715
|
+
resources: allResources.map((resource) => ({
|
|
6716
|
+
uri: resource.uri,
|
|
6717
|
+
name: resource.name,
|
|
6718
|
+
description: resource.description,
|
|
6719
|
+
mimeType: resource.mimeType
|
|
6720
|
+
}))
|
|
6721
|
+
};
|
|
6722
|
+
});
|
|
6723
|
+
server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => {
|
|
6724
|
+
logger.debug("Listing resource templates");
|
|
6725
|
+
return {
|
|
6726
|
+
resourceTemplates: resourceTemplates.map((template) => ({
|
|
6727
|
+
uriTemplate: template.uriTemplate,
|
|
6728
|
+
name: template.name,
|
|
6729
|
+
title: template.title,
|
|
6730
|
+
description: template.description,
|
|
6731
|
+
mimeType: template.mimeType
|
|
6732
|
+
}))
|
|
6733
|
+
};
|
|
6734
|
+
});
|
|
6735
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
6736
|
+
const { uri } = request.params;
|
|
6737
|
+
logger.info(`Resource requested: ${uri}`);
|
|
6738
|
+
try {
|
|
6739
|
+
let content = null;
|
|
6740
|
+
let mimeType = "text/plain";
|
|
6741
|
+
if (uri.startsWith("midnight://docs/")) {
|
|
6742
|
+
content = await getDocumentation(uri);
|
|
6743
|
+
mimeType = "text/markdown";
|
|
6744
|
+
} else if (uri.startsWith("midnight://code/")) {
|
|
6745
|
+
content = await getCode(uri);
|
|
6746
|
+
mimeType = "text/x-compact";
|
|
6747
|
+
} else if (uri.startsWith("midnight://schema/")) {
|
|
6748
|
+
const schema = getSchema(uri);
|
|
6749
|
+
content = schema ? JSON.stringify(schema, null, 2) : null;
|
|
6750
|
+
mimeType = "application/json";
|
|
6842
6751
|
}
|
|
6843
|
-
|
|
6844
|
-
|
|
6845
|
-
|
|
6846
|
-
|
|
6847
|
-
|
|
6848
|
-
|
|
6849
|
-
|
|
6850
|
-
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
|
|
6854
|
-
|
|
6855
|
-
|
|
6856
|
-
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
|
|
6863
|
-
|
|
6864
|
-
|
|
6865
|
-
|
|
6866
|
-
|
|
6867
|
-
|
|
6868
|
-
|
|
6869
|
-
|
|
6752
|
+
if (!content) {
|
|
6753
|
+
const resourceTypes = [
|
|
6754
|
+
"midnight://docs/",
|
|
6755
|
+
"midnight://code/",
|
|
6756
|
+
"midnight://schema/"
|
|
6757
|
+
];
|
|
6758
|
+
const validPrefix = resourceTypes.find((p) => uri.startsWith(p));
|
|
6759
|
+
let suggestion = validPrefix ? `Check the resource path after '${validPrefix}'` : `Valid resource prefixes: ${resourceTypes.join(", ")}`;
|
|
6760
|
+
if (uri.includes("://resources/")) {
|
|
6761
|
+
const resourceName = uri.split("://resources/").pop() || "";
|
|
6762
|
+
if (resourceName.includes("template") || resourceName.includes("pattern") || resourceName.includes("example")) {
|
|
6763
|
+
suggestion = `Try: midnight://code/templates/${resourceName} or midnight://code/examples/${resourceName} or midnight://code/patterns/${resourceName}`;
|
|
6764
|
+
} else if (resourceName.includes("doc") || resourceName.includes("reference") || resourceName.includes("guide")) {
|
|
6765
|
+
suggestion = `Try: midnight://docs/${resourceName}`;
|
|
6766
|
+
} else {
|
|
6767
|
+
suggestion = `'midnight://resources/' is not valid. Use: midnight://docs/, midnight://code/, or midnight://schema/`;
|
|
6768
|
+
}
|
|
6769
|
+
}
|
|
6770
|
+
return {
|
|
6771
|
+
contents: [
|
|
6772
|
+
{
|
|
6773
|
+
uri,
|
|
6774
|
+
mimeType: "application/json",
|
|
6775
|
+
text: serialize({
|
|
6776
|
+
error: `Resource not found: ${uri}`,
|
|
6777
|
+
suggestion,
|
|
6778
|
+
hint: "Use ListResources to see all available resources",
|
|
6779
|
+
validPrefixes: resourceTypes
|
|
6780
|
+
})
|
|
6781
|
+
}
|
|
6782
|
+
]
|
|
6783
|
+
};
|
|
6870
6784
|
}
|
|
6871
|
-
|
|
6872
|
-
|
|
6873
|
-
|
|
6874
|
-
|
|
6875
|
-
|
|
6876
|
-
|
|
6877
|
-
|
|
6878
|
-
|
|
6879
|
-
|
|
6880
|
-
|
|
6881
|
-
|
|
6882
|
-
|
|
6883
|
-
|
|
6884
|
-
|
|
6885
|
-
|
|
6886
|
-
|
|
6887
|
-
|
|
6888
|
-
|
|
6889
|
-
|
|
6890
|
-
|
|
6891
|
-
|
|
6892
|
-
|
|
6893
|
-
|
|
6894
|
-
|
|
6895
|
-
|
|
6896
|
-
|
|
6897
|
-
|
|
6898
|
-
|
|
6899
|
-
|
|
6900
|
-
|
|
6901
|
-
|
|
6902
|
-
|
|
6903
|
-
|
|
6904
|
-
|
|
6905
|
-
|
|
6906
|
-
|
|
6907
|
-
|
|
6908
|
-
|
|
6909
|
-
|
|
6910
|
-
|
|
6911
|
-
|
|
6912
|
-
|
|
6913
|
-
|
|
6914
|
-
|
|
6915
|
-
|
|
6916
|
-
|
|
6917
|
-
|
|
6918
|
-
|
|
6919
|
-
|
|
6920
|
-
|
|
6921
|
-
|
|
6922
|
-
|
|
6923
|
-
|
|
6924
|
-
|
|
6925
|
-
|
|
6926
|
-
|
|
6927
|
-
{
|
|
6928
|
-
|
|
6929
|
-
|
|
6930
|
-
|
|
6931
|
-
|
|
6932
|
-
|
|
6933
|
-
|
|
6934
|
-
|
|
6935
|
-
|
|
6936
|
-
|
|
6937
|
-
|
|
6938
|
-
|
|
6939
|
-
|
|
6940
|
-
|
|
6941
|
-
|
|
6942
|
-
|
|
6943
|
-
|
|
6944
|
-
|
|
6945
|
-
}
|
|
6946
|
-
{
|
|
6947
|
-
|
|
6948
|
-
|
|
6949
|
-
|
|
6950
|
-
|
|
6951
|
-
|
|
6952
|
-
|
|
6953
|
-
}
|
|
6954
|
-
|
|
6955
|
-
{
|
|
6956
|
-
|
|
6957
|
-
|
|
6958
|
-
|
|
6959
|
-
|
|
6960
|
-
|
|
6961
|
-
|
|
6962
|
-
|
|
6963
|
-
|
|
6964
|
-
|
|
6965
|
-
|
|
6966
|
-
|
|
6967
|
-
|
|
6968
|
-
|
|
6969
|
-
|
|
6970
|
-
|
|
6971
|
-
|
|
6972
|
-
|
|
6973
|
-
|
|
6974
|
-
|
|
6975
|
-
|
|
6976
|
-
|
|
6977
|
-
|
|
6978
|
-
|
|
6979
|
-
|
|
6980
|
-
|
|
6981
|
-
|
|
6982
|
-
|
|
6983
|
-
|
|
6984
|
-
|
|
6985
|
-
|
|
6986
|
-
|
|
6987
|
-
|
|
6988
|
-
|
|
6989
|
-
|
|
6990
|
-
|
|
6991
|
-
|
|
6992
|
-
}
|
|
6993
|
-
|
|
6994
|
-
|
|
6995
|
-
|
|
6996
|
-
|
|
6997
|
-
|
|
6998
|
-
|
|
6785
|
+
return {
|
|
6786
|
+
contents: [
|
|
6787
|
+
{
|
|
6788
|
+
uri,
|
|
6789
|
+
mimeType,
|
|
6790
|
+
text: content
|
|
6791
|
+
}
|
|
6792
|
+
]
|
|
6793
|
+
};
|
|
6794
|
+
} catch (error) {
|
|
6795
|
+
logger.error(`Resource error: ${uri}`, { error: String(error) });
|
|
6796
|
+
const errorResponse = formatErrorResponse(error, `resource:${uri}`);
|
|
6797
|
+
return {
|
|
6798
|
+
contents: [
|
|
6799
|
+
{
|
|
6800
|
+
uri,
|
|
6801
|
+
mimeType: "application/json",
|
|
6802
|
+
text: serialize(errorResponse)
|
|
6803
|
+
}
|
|
6804
|
+
]
|
|
6805
|
+
};
|
|
6806
|
+
}
|
|
6807
|
+
});
|
|
6808
|
+
}
|
|
6809
|
+
function registerPromptHandlers(server) {
|
|
6810
|
+
server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
6811
|
+
logger.debug("Listing prompts");
|
|
6812
|
+
return {
|
|
6813
|
+
prompts: promptDefinitions.map((prompt) => ({
|
|
6814
|
+
name: prompt.name,
|
|
6815
|
+
description: prompt.description,
|
|
6816
|
+
arguments: prompt.arguments
|
|
6817
|
+
}))
|
|
6818
|
+
};
|
|
6819
|
+
});
|
|
6820
|
+
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
6821
|
+
const { name, arguments: args } = request.params;
|
|
6822
|
+
logger.info(`Prompt requested: ${name}`, { args });
|
|
6823
|
+
const prompt = promptDefinitions.find((p) => p.name === name);
|
|
6824
|
+
if (!prompt) {
|
|
6825
|
+
return {
|
|
6826
|
+
description: `Unknown prompt: ${name}`,
|
|
6827
|
+
messages: []
|
|
6828
|
+
};
|
|
6829
|
+
}
|
|
6830
|
+
const messages = generatePrompt(name, args || {});
|
|
6831
|
+
return {
|
|
6832
|
+
description: prompt.description,
|
|
6833
|
+
messages: messages.map((m) => ({
|
|
6834
|
+
role: m.role,
|
|
6835
|
+
content: m.content
|
|
6836
|
+
}))
|
|
6837
|
+
};
|
|
6838
|
+
});
|
|
6839
|
+
}
|
|
6840
|
+
function registerSubscriptionHandlers(server) {
|
|
6841
|
+
server.setRequestHandler(SubscribeRequestSchema, async (request) => {
|
|
6842
|
+
const { uri } = request.params;
|
|
6843
|
+
logger.info(`Subscribing to resource: ${uri}`);
|
|
6844
|
+
const validPrefixes = [
|
|
6845
|
+
"midnight://docs/",
|
|
6846
|
+
"midnight://code/",
|
|
6847
|
+
"midnight://schema/"
|
|
6848
|
+
];
|
|
6849
|
+
const isValid = validPrefixes.some((prefix) => uri.startsWith(prefix));
|
|
6850
|
+
if (!isValid) {
|
|
6851
|
+
logger.warn(`Invalid subscription URI: ${uri}`);
|
|
6852
|
+
throw new Error(
|
|
6853
|
+
`Invalid subscription URI: ${uri}. Valid prefixes: ${validPrefixes.join(", ")}`
|
|
6854
|
+
);
|
|
6855
|
+
}
|
|
6856
|
+
resourceSubscriptions.add(uri);
|
|
6857
|
+
logger.debug(`Active subscriptions: ${resourceSubscriptions.size}`);
|
|
6858
|
+
return {};
|
|
6859
|
+
});
|
|
6860
|
+
server.setRequestHandler(UnsubscribeRequestSchema, async (request) => {
|
|
6861
|
+
const { uri } = request.params;
|
|
6862
|
+
logger.info(`Unsubscribing from resource: ${uri}`);
|
|
6863
|
+
resourceSubscriptions.delete(uri);
|
|
6864
|
+
logger.debug(`Active subscriptions: ${resourceSubscriptions.size}`);
|
|
6865
|
+
return {};
|
|
6866
|
+
});
|
|
6867
|
+
}
|
|
6868
|
+
function setupSampling(server) {
|
|
6869
|
+
const samplingCallback2 = async (request) => {
|
|
6870
|
+
logger.debug("Requesting sampling from client", {
|
|
6871
|
+
messageCount: request.messages.length,
|
|
6872
|
+
maxTokens: request.maxTokens
|
|
6873
|
+
});
|
|
6874
|
+
try {
|
|
6875
|
+
const response = await server.request(
|
|
6876
|
+
{
|
|
6877
|
+
method: "sampling/createMessage",
|
|
6878
|
+
params: {
|
|
6879
|
+
messages: request.messages,
|
|
6880
|
+
systemPrompt: request.systemPrompt,
|
|
6881
|
+
maxTokens: request.maxTokens || 2048,
|
|
6882
|
+
temperature: request.temperature,
|
|
6883
|
+
modelPreferences: request.modelPreferences
|
|
6884
|
+
}
|
|
6885
|
+
},
|
|
6886
|
+
// Use a schema that matches the expected response
|
|
6887
|
+
{
|
|
6888
|
+
parse: (data) => {
|
|
6889
|
+
const response2 = data;
|
|
6890
|
+
if (!response2 || typeof response2 !== "object") {
|
|
6891
|
+
throw new Error("Invalid sampling response: expected object");
|
|
6892
|
+
}
|
|
6893
|
+
if (!response2.content || typeof response2.content !== "object") {
|
|
6894
|
+
throw new Error("Invalid sampling response: missing content");
|
|
6895
|
+
}
|
|
6896
|
+
return response2;
|
|
6897
|
+
},
|
|
6898
|
+
_def: { typeName: "SamplingResponse" }
|
|
6899
|
+
}
|
|
6900
|
+
);
|
|
6901
|
+
return response;
|
|
6902
|
+
} catch (error) {
|
|
6903
|
+
logger.error("Sampling request failed", { error: String(error) });
|
|
6904
|
+
throw error;
|
|
6905
|
+
}
|
|
6906
|
+
};
|
|
6907
|
+
registerSamplingCallback(samplingCallback2);
|
|
6908
|
+
logger.info("Sampling capability configured");
|
|
6909
|
+
}
|
|
6910
|
+
async function initializeServer() {
|
|
6911
|
+
logger.info("Initializing Midnight MCP Server...");
|
|
6912
|
+
checkForUpdates().catch((error) => {
|
|
6913
|
+
logger.debug("Startup version check failed (non-blocking)", {
|
|
6914
|
+
error: error instanceof Error ? error.message : String(error)
|
|
6915
|
+
});
|
|
6916
|
+
});
|
|
6917
|
+
try {
|
|
6918
|
+
await vectorStore.initialize();
|
|
6919
|
+
logger.info("Vector store initialized");
|
|
6920
|
+
} catch (error) {
|
|
6921
|
+
logger.warn("Vector store initialization failed, continuing without it", {
|
|
6922
|
+
error: String(error)
|
|
6923
|
+
});
|
|
6999
6924
|
}
|
|
7000
|
-
|
|
6925
|
+
const server = createServer();
|
|
6926
|
+
logger.info(`Server v${CURRENT_VERSION} created successfully`);
|
|
6927
|
+
return server;
|
|
6928
|
+
}
|
|
6929
|
+
async function startServer() {
|
|
6930
|
+
const server = await initializeServer();
|
|
6931
|
+
const transport = new StdioServerTransport();
|
|
6932
|
+
await server.connect(transport);
|
|
6933
|
+
setServerConnected(true);
|
|
6934
|
+
logger.info("Midnight MCP Server running on stdio");
|
|
6935
|
+
}
|
|
6936
|
+
var transports = {
|
|
6937
|
+
streamable: {},
|
|
6938
|
+
sse: {}
|
|
6939
|
+
};
|
|
6940
|
+
function buildAllowlist(port) {
|
|
6941
|
+
const hosts = [`127.0.0.1:${port}`, `localhost:${port}`];
|
|
6942
|
+
const origins = hosts.flatMap((host) => [
|
|
6943
|
+
`http://${host}`,
|
|
6944
|
+
`https://${host}`
|
|
6945
|
+
]);
|
|
6946
|
+
return { allowedHosts: hosts, allowedOrigins: origins };
|
|
6947
|
+
}
|
|
6948
|
+
function isRebindingBlocked(host, origin, allowedHosts, allowedOrigins) {
|
|
6949
|
+
if (host && !allowedHosts.includes(host)) return true;
|
|
6950
|
+
if (origin && !allowedOrigins.includes(origin)) return true;
|
|
6951
|
+
return false;
|
|
6952
|
+
}
|
|
6953
|
+
async function closeTransports(transportMap) {
|
|
6954
|
+
const closePromises = Object.values(transportMap).map(
|
|
6955
|
+
(transport) => transport.close?.().catch(() => {
|
|
6956
|
+
})
|
|
6957
|
+
);
|
|
6958
|
+
await Promise.all(closePromises);
|
|
6959
|
+
}
|
|
6960
|
+
async function startHttpServer(port = 3e3) {
|
|
6961
|
+
const mcpServer = await initializeServer();
|
|
6962
|
+
const app = express();
|
|
6963
|
+
const { allowedHosts, allowedOrigins } = buildAllowlist(port);
|
|
6964
|
+
app.use("/mcp", express.json());
|
|
6965
|
+
app.get("/health", (_req, res) => {
|
|
6966
|
+
res.json({
|
|
6967
|
+
status: "ok",
|
|
6968
|
+
version: CURRENT_VERSION,
|
|
6969
|
+
transport: "http"
|
|
6970
|
+
});
|
|
6971
|
+
});
|
|
6972
|
+
app.post("/mcp", async (req, res) => {
|
|
6973
|
+
const sessionId = req.headers["mcp-session-id"];
|
|
6974
|
+
let transport;
|
|
6975
|
+
if (sessionId && transports.streamable[sessionId]) {
|
|
6976
|
+
transport = transports.streamable[sessionId];
|
|
6977
|
+
} else if (!sessionId && isInitializeRequest(req.body)) {
|
|
6978
|
+
transport = new StreamableHTTPServerTransport({
|
|
6979
|
+
sessionIdGenerator: () => randomUUID(),
|
|
6980
|
+
// Block DNS-rebinding attacks from browser pages (loopback allowlist).
|
|
6981
|
+
enableDnsRebindingProtection: true,
|
|
6982
|
+
allowedHosts,
|
|
6983
|
+
allowedOrigins,
|
|
6984
|
+
onsessioninitialized: (newSessionId) => {
|
|
6985
|
+
transports.streamable[newSessionId] = transport;
|
|
6986
|
+
logger.debug(`New streamable session: ${newSessionId}`);
|
|
6987
|
+
}
|
|
6988
|
+
});
|
|
6989
|
+
transport.onclose = () => {
|
|
6990
|
+
if (transport.sessionId) {
|
|
6991
|
+
delete transports.streamable[transport.sessionId];
|
|
6992
|
+
logger.debug(`Streamable session closed: ${transport.sessionId}`);
|
|
6993
|
+
}
|
|
6994
|
+
};
|
|
6995
|
+
try {
|
|
6996
|
+
await mcpServer.connect(transport);
|
|
6997
|
+
} catch (error) {
|
|
6998
|
+
logger.error("Failed to connect streamable transport", {
|
|
6999
|
+
error: String(error)
|
|
7000
|
+
});
|
|
7001
|
+
res.status(500).json({
|
|
7002
|
+
jsonrpc: "2.0",
|
|
7003
|
+
error: { code: -32603, message: "Internal error: connection failed" },
|
|
7004
|
+
id: null
|
|
7005
|
+
});
|
|
7006
|
+
return;
|
|
7007
|
+
}
|
|
7008
|
+
} else {
|
|
7009
|
+
res.status(400).json({
|
|
7010
|
+
jsonrpc: "2.0",
|
|
7011
|
+
error: {
|
|
7012
|
+
code: -32e3,
|
|
7013
|
+
message: "Bad Request: No valid session ID provided"
|
|
7014
|
+
},
|
|
7015
|
+
id: null
|
|
7016
|
+
});
|
|
7017
|
+
return;
|
|
7018
|
+
}
|
|
7019
|
+
await transport.handleRequest(req, res, req.body);
|
|
7020
|
+
});
|
|
7021
|
+
app.get("/sse", async (req, res) => {
|
|
7022
|
+
const host = req.headers.host;
|
|
7023
|
+
const origin = req.headers.origin;
|
|
7024
|
+
if (isRebindingBlocked(host, origin, allowedHosts, allowedOrigins)) {
|
|
7025
|
+
logger.warn(`Rejected SSE connection`, { host, origin });
|
|
7026
|
+
res.status(403).send("Forbidden: invalid Host or Origin");
|
|
7027
|
+
return;
|
|
7028
|
+
}
|
|
7029
|
+
logger.debug("New SSE connection");
|
|
7030
|
+
const transport = new SSEServerTransport("/messages", res, {
|
|
7031
|
+
enableDnsRebindingProtection: true,
|
|
7032
|
+
allowedHosts,
|
|
7033
|
+
allowedOrigins
|
|
7034
|
+
});
|
|
7035
|
+
transports.sse[transport.sessionId] = transport;
|
|
7036
|
+
res.on("close", () => {
|
|
7037
|
+
delete transports.sse[transport.sessionId];
|
|
7038
|
+
logger.debug(`SSE session closed: ${transport.sessionId}`);
|
|
7039
|
+
});
|
|
7040
|
+
try {
|
|
7041
|
+
await mcpServer.connect(transport);
|
|
7042
|
+
} catch (error) {
|
|
7043
|
+
delete transports.sse[transport.sessionId];
|
|
7044
|
+
logger.error(`SSE connection failed: ${transport.sessionId}`, {
|
|
7045
|
+
error: String(error)
|
|
7046
|
+
});
|
|
7047
|
+
res.status(500).end();
|
|
7048
|
+
}
|
|
7049
|
+
});
|
|
7050
|
+
app.post("/messages", async (req, res) => {
|
|
7051
|
+
const sessionId = req.query.sessionId;
|
|
7052
|
+
const transport = transports.sse[sessionId];
|
|
7053
|
+
if (transport) {
|
|
7054
|
+
await transport.handlePostMessage(req, res);
|
|
7055
|
+
} else {
|
|
7056
|
+
res.status(400).send(`No transport found for sessionId ${sessionId}`);
|
|
7057
|
+
}
|
|
7058
|
+
});
|
|
7059
|
+
const handleSessionRequest = async (req, res) => {
|
|
7060
|
+
const sessionId = req.headers["mcp-session-id"];
|
|
7061
|
+
if (!sessionId || !transports.streamable[sessionId]) {
|
|
7062
|
+
res.status(400).send("Invalid or missing session ID");
|
|
7063
|
+
return;
|
|
7064
|
+
}
|
|
7065
|
+
const transport = transports.streamable[sessionId];
|
|
7066
|
+
await transport.handleRequest(req, res);
|
|
7067
|
+
};
|
|
7068
|
+
app.get("/mcp", handleSessionRequest);
|
|
7069
|
+
app.delete("/mcp", handleSessionRequest);
|
|
7070
|
+
const httpServer = app.listen(port, "127.0.0.1", () => {
|
|
7071
|
+
logger.info(`Midnight MCP Server running on HTTP port ${port}`);
|
|
7072
|
+
logger.info(` Streamable HTTP: http://localhost:${port}/mcp`);
|
|
7073
|
+
logger.info(` SSE endpoint: http://localhost:${port}/sse`);
|
|
7074
|
+
logger.info(` Health check: http://localhost:${port}/health`);
|
|
7075
|
+
});
|
|
7076
|
+
const shutdown = async () => {
|
|
7077
|
+
logger.info("Shutting down HTTP server...");
|
|
7078
|
+
await closeTransports(transports.sse);
|
|
7079
|
+
await closeTransports(transports.streamable);
|
|
7080
|
+
httpServer.close(() => {
|
|
7081
|
+
logger.info("Server shutdown complete");
|
|
7082
|
+
process.exit(0);
|
|
7083
|
+
});
|
|
7084
|
+
};
|
|
7085
|
+
process.on("SIGINT", shutdown);
|
|
7086
|
+
process.on("SIGTERM", shutdown);
|
|
7087
|
+
}
|
|
7001
7088
|
|
|
7002
7089
|
// src/tools/repository/validation.ts
|
|
7003
7090
|
import { readFile } from "fs/promises";
|
|
@@ -7387,7 +7474,7 @@ async function extractContractStructure(input) {
|
|
|
7387
7474
|
type: "invalid_pragma_format",
|
|
7388
7475
|
line: lineNum,
|
|
7389
7476
|
message: `Pragma includes patch version which may cause parse errors`,
|
|
7390
|
-
suggestion: `Use bounded range format: '
|
|
7477
|
+
suggestion: `Use bounded range format: '${RECOMMENDED_PRAGMA}'`,
|
|
7391
7478
|
severity: "error"
|
|
7392
7479
|
});
|
|
7393
7480
|
}
|
|
@@ -8427,6 +8514,36 @@ export circuit increment(): [] {
|
|
|
8427
8514
|
wrong: "export circuit fn(param: T): [] { ledger.insert(param, v); }",
|
|
8428
8515
|
correct: "export circuit fn(param: T): [] { const d = disclose(param); ledger.insert(d, v); }",
|
|
8429
8516
|
error: "potential witness-value disclosure must be declared"
|
|
8517
|
+
},
|
|
8518
|
+
{
|
|
8519
|
+
wrong: "NativePoint, nativePointX(p), constructNativePoint(x, y)",
|
|
8520
|
+
correct: "JubjubPoint, jubjubPointX(p), constructJubjubPoint(x, y)",
|
|
8521
|
+
error: "apparent use of an old standard-library / ledger operator name NativePoint: the new name is JubjubPoint (renamed in compactc 0.30.0)"
|
|
8522
|
+
},
|
|
8523
|
+
{
|
|
8524
|
+
wrong: "const let = 1; // or `let` used as any identifier",
|
|
8525
|
+
correct: "const value = 1; // rename the identifier",
|
|
8526
|
+
error: 'parse error: found keyword "let" (which is reserved for future use)'
|
|
8527
|
+
},
|
|
8528
|
+
{
|
|
8529
|
+
wrong: "const MAX_SUPPLY: Uint<64> = 1000; // module-level const",
|
|
8530
|
+
correct: "pure circuit max_supply(): Uint<64> { return 1000; } // wrap in a pure circuit",
|
|
8531
|
+
error: 'parse error: found keyword "const" looking for a program element'
|
|
8532
|
+
},
|
|
8533
|
+
{
|
|
8534
|
+
wrong: "flags | mask // bitwise OR on Uint",
|
|
8535
|
+
correct: "// No bitwise ops on Uint \u2014 use arithmetic or branching. `||` is logical OR for Booleans only.",
|
|
8536
|
+
error: "unexpected character (single `|` not recognized)"
|
|
8537
|
+
},
|
|
8538
|
+
{
|
|
8539
|
+
wrong: "field: Uint<254>",
|
|
8540
|
+
correct: "field: Uint<248> // max byte-aligned Uint width",
|
|
8541
|
+
error: "Uint width 254 is not between 1 and the maximum Uint width 248 (inclusive)"
|
|
8542
|
+
},
|
|
8543
|
+
{
|
|
8544
|
+
wrong: "convertBytesToUint(255, n, bytes, src) // number literal",
|
|
8545
|
+
correct: "convertBytesToUint(255n, n, bytes, src) // bigint literal",
|
|
8546
|
+
error: "TypeScript binding: maxval is bigint since compactc 0.30.101 (avoids silent precision loss)"
|
|
8430
8547
|
}
|
|
8431
8548
|
],
|
|
8432
8549
|
syntaxReference: compactReference,
|
|
@@ -11172,4 +11289,4 @@ export {
|
|
|
11172
11289
|
startServer,
|
|
11173
11290
|
startHttpServer
|
|
11174
11291
|
};
|
|
11175
|
-
//# sourceMappingURL=chunk-
|
|
11292
|
+
//# sourceMappingURL=chunk-F4KM42XU.js.map
|