midnight-mcp 0.2.8 → 0.2.10

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-VQTI7YUU.js";
28
+ } from "./chunk-C23TNY65.js";
29
29
 
30
30
  // src/tools/search/schemas.ts
31
31
  import { z } from "zod";
@@ -102,7 +102,49 @@ async function tryHostedSearch(searchType, hostedSearchFn, cacheKey, warnings) {
102
102
  ...response,
103
103
  ...warnings.length > 0 && { warnings }
104
104
  };
105
- searchCache.set(cacheKey, finalResponse);
105
+ searchCache.set(cacheKey, {
106
+ results: finalResponse.results.map((item) => {
107
+ const result = item;
108
+ let startLine;
109
+ let endLine;
110
+ if (result.source.lines) {
111
+ const lineParts = result.source.lines.split("-");
112
+ if (lineParts.length === 2) {
113
+ const parsedStart = parseInt(lineParts[0], 10);
114
+ const parsedEnd = parseInt(lineParts[1], 10);
115
+ if (!Number.isNaN(parsedStart)) {
116
+ startLine = parsedStart;
117
+ }
118
+ if (!Number.isNaN(parsedEnd)) {
119
+ endLine = parsedEnd;
120
+ }
121
+ } else if (lineParts.length === 1) {
122
+ const parsed = parseInt(lineParts[0], 10);
123
+ if (!Number.isNaN(parsed)) {
124
+ startLine = endLine = parsed;
125
+ }
126
+ }
127
+ }
128
+ return {
129
+ code: result.code,
130
+ content: result.content,
131
+ relevanceScore: result.relevanceScore,
132
+ source: {
133
+ repository: result.source.repository,
134
+ filePath: result.source.filePath,
135
+ startLine,
136
+ endLine,
137
+ lines: result.source.lines,
138
+ section: result.source.section
139
+ },
140
+ codeType: result.codeType,
141
+ name: result.name,
142
+ isExported: result.isExported
143
+ };
144
+ }),
145
+ totalResults: finalResponse.totalResults ?? finalResponse.results.length,
146
+ warnings: warnings.length > 0 ? warnings : void 0
147
+ });
106
148
  return {
107
149
  result: finalResponse,
108
150
  cached: true
@@ -122,7 +164,11 @@ function finalizeResponse(response, cacheKey, warnings) {
122
164
  ...response,
123
165
  ...warnings.length > 0 && { warnings }
124
166
  };
125
- searchCache.set(cacheKey, finalResponse);
167
+ searchCache.set(cacheKey, {
168
+ results: finalResponse.results,
169
+ totalResults: finalResponse.totalResults ?? finalResponse.results.length,
170
+ warnings: warnings.length > 0 ? warnings : void 0
171
+ });
126
172
  return finalResponse;
127
173
  }
128
174
  async function searchCompact(input) {
@@ -740,11 +786,11 @@ async function analyzeContract(input) {
740
786
  });
741
787
  }
742
788
  }
743
- if (!parsed.imports.includes("std")) {
789
+ if (!parsed.imports.includes("CompactStandardLibrary")) {
744
790
  findings.push({
745
791
  severity: "info",
746
792
  message: "Standard library not imported",
747
- suggestion: `Consider adding 'include "std";' for common utilities`
793
+ suggestion: "Consider adding 'import CompactStandardLibrary;' for common utilities"
748
794
  });
749
795
  }
750
796
  }
@@ -1584,13 +1630,15 @@ Always import the standard library:
1584
1630
  import CompactStandardLibrary;
1585
1631
  \`\`\`
1586
1632
 
1587
- For multi-file contracts, use \`include\`:
1633
+ For modular code, use module imports (not a separate \`include\` directive):
1588
1634
  \`\`\`compact
1589
- include "types";
1590
- include "ledger";
1591
- include "circuits";
1635
+ import "path/to/module";
1636
+ import { SomeType } from "other/module";
1592
1637
  \`\`\`
1593
1638
 
1639
+ > **Note:** The docs describe modules and \`import\` syntax, but do not document
1640
+ > a separate \`include\` directive. Use \`import\` for code organization.
1641
+
1594
1642
  ---
1595
1643
 
1596
1644
  ## 3. Ledger Declarations
@@ -1635,6 +1683,14 @@ ledger privateData: Field; // Private, not exported
1635
1683
  | \`Uint<N>\` | Unsigned integer (N = 8, 16, 32, 64, 128, 256) | \`balance: Uint<64>\` |
1636
1684
  | \`Uint<MIN..MAX>\` | Bounded unsigned integer | \`score: Uint<0..100>\` |
1637
1685
 
1686
+ **\u26A0\uFE0F Uint Type Equivalence:** \`Uint<N>\` (sized) and \`Uint<0..MAX>\` (bounded) are the **SAME type family**.
1687
+ \`Uint<N>\` is exactly equivalent to \`Uint<0..(2^N - 1)>\`:
1688
+ - \`Uint<8>\` = \`Uint<0..255>\` (2^8 - 1 = 255)
1689
+ - \`Uint<16>\` = \`Uint<0..65535>\` (2^16 - 1 = 65535)
1690
+ - \`Uint<64>\` = \`Uint<0..18446744073709551615>\`
1691
+
1692
+ These can be used interchangeably - they are **not** separate types.
1693
+
1638
1694
  ### Collection Types
1639
1695
  | Type | Description | Example |
1640
1696
  |------|-------------|---------|
@@ -1867,41 +1923,60 @@ export circuit check_broken(guess: Field): Boolean {
1867
1923
 
1868
1924
  ### Counter Operations
1869
1925
  \`\`\`compact
1870
- // These work in circuits:
1871
- counter.increment(1);
1872
- counter.decrement(1);
1873
- counter.resetToDefault();
1874
-
1875
- // \u26A0\uFE0F DOES NOT WORK IN CIRCUITS:
1876
- // const val = counter.value(); // ERROR: operation undefined
1877
- // Instead, read counter value in TypeScript SDK: ledgerState.counter
1926
+ // ALL Counter methods work in circuits:
1927
+ counter.increment(1); // Increase by amount (Uint<16>)
1928
+ counter.decrement(1); // Decrease by amount (Uint<16>)
1929
+ const val = counter.read(); // Get current value (returns Uint<64>)
1930
+ const low = counter.lessThan(100); // Compare with threshold (returns Boolean)
1931
+ counter.resetToDefault(); // Reset to zero
1932
+
1933
+ // \u26A0\uFE0F WRONG: counter.value() does NOT exist - use counter.read()
1878
1934
  \`\`\`
1879
1935
 
1880
1936
  ### Map Operations
1881
1937
  \`\`\`compact
1882
- // These work in circuits:
1883
- balances.insert(address, 100);
1884
- balances.remove(address);
1885
-
1886
- // \u26A0\uFE0F DOES NOT WORK IN CIRCUITS:
1887
- // const balance = balances.lookup(address); // ERROR
1888
- // const exists = balances.member(address); // ERROR
1889
- // Instead, use witnesses to read values:
1890
- witness get_balance(addr: Bytes<32>): Uint<64>;
1938
+ // Map<key_type, value_type> - All operations work in circuits unless noted
1939
+
1940
+ // Insert/update operations
1941
+ balances.insert(address, 100); // insert(key, value): []
1942
+ balances.insertDefault(address); // insertDefault(key): [] - inserts default value
1943
+
1944
+ // Query operations (all work in circuits \u2705)
1945
+ const balance = balances.lookup(address); // lookup(key): value_type
1946
+ const exists = balances.member(address); // member(key): Boolean
1947
+ const empty = balances.isEmpty(); // isEmpty(): Boolean
1948
+ const count = balances.size(); // size(): Uint<64>
1949
+
1950
+ // Remove operations
1951
+ balances.remove(address); // remove(key): []
1952
+ balances.resetToDefault(); // resetToDefault(): [] - clears entire map
1953
+
1954
+ // Coin-specific (only when value_type is QualifiedCoinInfo)
1955
+ // coinMap.insertCoin(key, coinInfo, recipient): []
1891
1956
  \`\`\`
1892
1957
 
1958
+ **TypeScript-only:** \`[Symbol.iterator]()\` for iteration - not available in circuits.
1959
+
1893
1960
  ### Set Operations
1894
1961
  \`\`\`compact
1895
- // These work in circuits:
1896
- members.insert(address);
1897
- members.remove(address);
1898
-
1899
- // \u26A0\uFE0F DOES NOT WORK IN CIRCUITS:
1900
- // const isMember = members.member(address); // ERROR
1901
- // Use witness instead:
1902
- witness is_member(addr: Bytes<32>): Boolean;
1962
+ // Set<value_type> - All operations work in circuits unless noted
1963
+
1964
+ // Insert/remove operations
1965
+ members.insert(address); // insert(elem): []
1966
+ members.remove(address); // remove(elem): []
1967
+ members.resetToDefault(); // resetToDefault(): [] - clears entire set
1968
+
1969
+ // Query operations (all work in circuits \u2705)
1970
+ const isMember = members.member(address); // member(elem): Boolean
1971
+ const empty = members.isEmpty(); // isEmpty(): Boolean
1972
+ const count = members.size(); // size(): Uint<64>
1973
+
1974
+ // Coin-specific (only when value_type is QualifiedCoinInfo)
1975
+ // coinSet.insertCoin(coinInfo, recipient): []
1903
1976
  \`\`\`
1904
1977
 
1978
+ **TypeScript-only:** \`[Symbol.iterator]()\` for iteration - not available in circuits.
1979
+
1905
1980
  ### Maybe Operations
1906
1981
  \`\`\`compact
1907
1982
  const opt: Maybe<Field> = some<Field>(42);
@@ -2116,9 +2191,13 @@ npm install @openzeppelin/compact-contracts
2116
2191
  ## Usage Example
2117
2192
 
2118
2193
  \`\`\`compact
2119
- include "std";
2120
- include "@openzeppelin/compact-contracts/token/FungibleToken.compact";
2121
- include "@openzeppelin/compact-contracts/access/Ownable.compact";
2194
+ pragma language_version >= 0.18.0;
2195
+
2196
+ import CompactStandardLibrary;
2197
+ import "@openzeppelin/compact-contracts/src/token/FungibleToken"
2198
+ prefix FungibleToken_;
2199
+ import "@openzeppelin/compact-contracts/src/access/Ownable"
2200
+ prefix Ownable_;
2122
2201
 
2123
2202
  ledger {
2124
2203
  // Inherit from OpenZeppelin contracts
@@ -2153,8 +2232,11 @@ The recommended standard for privacy-preserving tokens on Midnight.
2153
2232
  ## Basic Usage
2154
2233
 
2155
2234
  \`\`\`compact
2156
- include "std";
2157
- include "@openzeppelin/compact-contracts/token/FungibleToken.compact";
2235
+ pragma language_version >= 0.18.0;
2236
+
2237
+ import CompactStandardLibrary;
2238
+ import "@openzeppelin/compact-contracts/src/token/FungibleToken"
2239
+ prefix FungibleToken_;
2158
2240
 
2159
2241
  ledger {
2160
2242
  ...FungibleToken.ledger;
@@ -2195,7 +2277,8 @@ export circuit revealBalance(): Field {
2195
2277
  ## Minting and Burning
2196
2278
 
2197
2279
  \`\`\`compact
2198
- include "@openzeppelin/compact-contracts/access/Ownable.compact";
2280
+ import "@openzeppelin/compact-contracts/src/access/Ownable"
2281
+ prefix Ownable_;
2199
2282
 
2200
2283
  ledger {
2201
2284
  ...FungibleToken.ledger;
@@ -2237,7 +2320,8 @@ Patterns for controlling who can call contract functions.
2237
2320
  Simple single-owner access control.
2238
2321
 
2239
2322
  \`\`\`compact
2240
- include "@openzeppelin/compact-contracts/access/Ownable.compact";
2323
+ import "@openzeppelin/compact-contracts/src/access/Ownable"
2324
+ prefix Ownable_;
2241
2325
 
2242
2326
  ledger {
2243
2327
  ...Ownable.ledger;
@@ -2263,7 +2347,8 @@ export circuit transferOwnership(newOwner: Address): Void {
2263
2347
  For more complex permission systems.
2264
2348
 
2265
2349
  \`\`\`compact
2266
- include "@openzeppelin/compact-contracts/access/AccessControl.compact";
2350
+ import "@openzeppelin/compact-contracts/src/access/AccessControl"
2351
+ prefix AccessControl_;
2267
2352
 
2268
2353
  ledger {
2269
2354
  ...AccessControl.ledger;
@@ -2291,8 +2376,10 @@ export circuit grantMinterRole(account: Address): Void {
2291
2376
  ## Combining Patterns
2292
2377
 
2293
2378
  \`\`\`compact
2294
- include "@openzeppelin/compact-contracts/access/Ownable.compact";
2295
- include "@openzeppelin/compact-contracts/security/Pausable.compact";
2379
+ import "@openzeppelin/compact-contracts/src/access/Ownable"
2380
+ prefix Ownable_;
2381
+ import "@openzeppelin/compact-contracts/src/security/Pausable"
2382
+ prefix Pausable_;
2296
2383
 
2297
2384
  ledger {
2298
2385
  ...Ownable.ledger;
@@ -2320,8 +2407,10 @@ Security utilities for Compact contracts.
2320
2407
  Emergency stop mechanism for contracts.
2321
2408
 
2322
2409
  \`\`\`compact
2323
- include "@openzeppelin/compact-contracts/security/Pausable.compact";
2324
- include "@openzeppelin/compact-contracts/access/Ownable.compact";
2410
+ import "@openzeppelin/compact-contracts/src/security/Pausable"
2411
+ prefix Pausable_;
2412
+ import "@openzeppelin/compact-contracts/src/access/Ownable"
2413
+ prefix Ownable_;
2325
2414
 
2326
2415
  ledger {
2327
2416
  ...Pausable.ledger;
@@ -2727,134 +2816,131 @@ var EMBEDDED_CODE = {
2727
2816
  "midnight://code/examples/counter": `// Counter Example Contract
2728
2817
  // A simple contract demonstrating basic Compact concepts
2729
2818
 
2730
- include "std";
2819
+ pragma language_version >= 0.16 && <= 0.18;
2731
2820
 
2732
- ledger {
2733
- // Public counter - visible to everyone
2734
- counter: Counter;
2735
-
2736
- // Track last modifier (public)
2737
- lastModifier: Opaque<"address">;
2738
- }
2821
+ import CompactStandardLibrary;
2822
+
2823
+ // Public counter - visible to everyone
2824
+ export ledger counter: Counter;
2825
+
2826
+ // Track last modifier (public)
2827
+ export ledger lastModifier: Opaque<"address">;
2739
2828
 
2740
2829
  // Increment the counter
2741
- export circuit increment(amount: Field): Field {
2830
+ export circuit increment(amount: Uint<16>): Uint<64> {
2742
2831
  // Validate input
2743
- assert(amount > 0, "Amount must be positive");
2744
- assert(amount <= 100, "Amount too large");
2832
+ assert(amount > 0 as Uint<16>, "Amount must be positive");
2833
+ assert(amount <= 100 as Uint<16>, "Amount too large");
2745
2834
 
2746
2835
  // Update counter
2747
- ledger.counter.increment(amount);
2836
+ counter.increment(amount);
2748
2837
 
2749
2838
  // Return new value
2750
- return ledger.counter.value();
2839
+ return counter.read();
2751
2840
  }
2752
2841
 
2753
2842
  // Decrement the counter
2754
- export circuit decrement(amount: Field): Field {
2843
+ export circuit decrement(amount: Uint<16>): Uint<64> {
2755
2844
  // Validate input
2756
- assert(amount > 0, "Amount must be positive");
2757
- assert(ledger.counter.value() >= amount, "Counter would go negative");
2845
+ assert(amount > 0 as Uint<16>, "Amount must be positive");
2846
+ assert(counter.read() >= (amount as Uint<64>), "Counter would go negative");
2758
2847
 
2759
2848
  // Update counter
2760
- ledger.counter.decrement(amount);
2849
+ counter.decrement(amount);
2761
2850
 
2762
2851
  // Return new value
2763
- return ledger.counter.value();
2852
+ return counter.read();
2764
2853
  }
2765
2854
 
2766
2855
  // Read current value (view function)
2767
- export circuit getValue(): Field {
2768
- return ledger.counter.value();
2856
+ export circuit getValue(): Uint<64> {
2857
+ return counter.read();
2858
+ }
2859
+
2860
+ // Check if counter is below threshold
2861
+ export circuit isLessThan(threshold: Uint<64>): Boolean {
2862
+ return counter.lessThan(threshold);
2769
2863
  }
2770
2864
  `,
2771
2865
  "midnight://code/examples/bboard": `// Bulletin Board Example Contract
2772
2866
  // Demonstrates private messaging with selective disclosure
2773
2867
 
2774
- include "std";
2868
+ pragma language_version >= 0.16 && <= 0.18;
2775
2869
 
2776
- ledger {
2777
- // Public: message count and IDs
2778
- messageCount: Counter;
2779
- messageIds: Set<Field>;
2780
-
2781
- // Private: actual message contents
2782
- @private
2783
- messages: Map<Field, Opaque<"string">>;
2784
-
2785
- // Private: message authors
2786
- @private
2787
- authors: Map<Field, Opaque<"address">>;
2788
- }
2870
+ import CompactStandardLibrary;
2871
+
2872
+ // Public: message count and IDs
2873
+ export ledger messageCount: Counter;
2874
+ export ledger messageIds: Set<Field>;
2875
+
2876
+ // Private: actual message contents (no export = private)
2877
+ ledger messages: Map<Field, Opaque<"string">>;
2878
+
2879
+ // Private: message authors
2880
+ ledger authors: Map<Field, Opaque<"address">>;
2881
+
2882
+ // Witness to fetch message content
2883
+ witness getMessageContent(id: Field): Opaque<"string">;
2789
2884
 
2790
2885
  // Post a new message (content is private)
2791
- export circuit postMessage(content: Opaque<"string">, author: Opaque<"address">): Field {
2792
- // Generate unique message ID
2793
- const messageId = ledger.messageCount.value();
2886
+ export circuit postMessage(content: Opaque<"string">, author: Opaque<"address">): Uint<64> {
2887
+ // Generate unique message ID using counter read
2888
+ const messageId = messageCount.read();
2794
2889
 
2795
2890
  // Store message privately
2796
- ledger.messages.insert(messageId, content);
2797
- ledger.authors.insert(messageId, author);
2891
+ messages.insert(messageId as Field, content);
2892
+ authors.insert(messageId as Field, author);
2798
2893
 
2799
2894
  // Update public counters
2800
- ledger.messageCount.increment(1);
2801
- ledger.messageIds.add(messageId);
2895
+ messageCount.increment(1);
2896
+ messageIds.insert(messageId as Field);
2802
2897
 
2803
2898
  return messageId;
2804
2899
  }
2805
2900
 
2806
- // Witness to fetch message content
2807
- witness getMessageContent(id: Field): Opaque<"string"> {
2808
- return ledger.messages.get(id);
2809
- }
2810
-
2811
2901
  // Reveal a message publicly (owner's choice)
2812
2902
  export circuit revealMessage(id: Field): Opaque<"string"> {
2813
- assert(ledger.messageIds.contains(id), "Message not found");
2903
+ assert(messageIds.member(id), "Message not found");
2814
2904
 
2815
2905
  const content = getMessageContent(id);
2816
2906
  return disclose(content);
2817
2907
  }
2818
2908
 
2819
2909
  // Get total message count
2820
- export circuit getMessageCount(): Field {
2821
- return ledger.messageCount.value();
2910
+ export circuit getMessageCount(): Uint<64> {
2911
+ return messageCount.read();
2822
2912
  }
2823
2913
  `,
2824
2914
  "midnight://code/patterns/state-management": `// State Management Pattern
2825
2915
  // Best practices for managing public and private state
2826
2916
 
2827
- include "std";
2917
+ pragma language_version >= 0.16 && <= 0.18;
2828
2918
 
2829
- ledger {
2830
- // PUBLIC STATE
2831
- // - Use for data that should be transparent
2832
- // - Visible in blockchain explorers
2833
- // - Can be queried by anyone
2834
-
2835
- totalSupply: Counter;
2836
- publicConfig: Field;
2837
-
2838
- // PRIVATE STATE
2839
- // - Use for sensitive user data
2840
- // - Only owner can read
2841
- // - Requires witnesses to access in circuits
2842
-
2843
- @private
2844
- userSecrets: Map<Opaque<"address">, Bytes<32>>;
2845
-
2846
- @private
2847
- privateBalances: Map<Opaque<"address">, Field>;
2848
- }
2919
+ import CompactStandardLibrary;
2849
2920
 
2850
- // Reading public state is straightforward
2851
- export circuit getTotalSupply(): Field {
2852
- return ledger.totalSupply.value();
2853
- }
2921
+ // PUBLIC STATE
2922
+ // - Use 'export ledger' for data that should be transparent
2923
+ // - Visible in blockchain explorers
2924
+ // - Can be queried by anyone
2925
+
2926
+ export ledger totalSupply: Uint<64>;
2927
+ export ledger publicConfig: Field;
2928
+
2929
+ // PRIVATE STATE
2930
+ // - Use 'ledger' without export for sensitive user data
2931
+ // - Only owner can read
2932
+ // - Requires witnesses to access in circuits
2854
2933
 
2855
- // Reading private state requires a witness
2856
- witness getUserSecret(user: Opaque<"address">): Bytes<32> {
2857
- return ledger.userSecrets.get(user);
2934
+ ledger userSecrets: Map<Opaque<"address">, Bytes<32>>;
2935
+ ledger privateBalances: Map<Opaque<"address">, Field>;
2936
+
2937
+ // Witnesses for private data access
2938
+ witness getUserSecret(user: Opaque<"address">): Bytes<32>;
2939
+ witness getPrivateBalance(user: Opaque<"address">): Field;
2940
+
2941
+ // Reading public state is straightforward
2942
+ export circuit getTotalSupply(): Uint<64> {
2943
+ return ledger.totalSupply;
2858
2944
  }
2859
2945
 
2860
2946
  // Using private state in a circuit
@@ -2865,7 +2951,7 @@ export circuit proveSecretKnowledge(
2865
2951
  const secret = getUserSecret(user);
2866
2952
 
2867
2953
  // Prove knowledge without revealing secret
2868
- assert(hash(secret) == secretHash);
2954
+ assert(persistentHash(secret) == secretHash, "Invalid secret");
2869
2955
  return true;
2870
2956
  }
2871
2957
 
@@ -2875,53 +2961,45 @@ export circuit revealBalance(user: Opaque<"address">): Field {
2875
2961
  // Explicitly reveal - user's choice
2876
2962
  return disclose(balance);
2877
2963
  }
2878
-
2879
- witness getPrivateBalance(user: Opaque<"address">): Field {
2880
- return ledger.privateBalances.get(user);
2881
- }
2882
2964
  `,
2883
2965
  "midnight://code/patterns/access-control": `// Access Control Pattern
2884
2966
  // Implementing permissions and authorization
2885
2967
 
2886
- include "std";
2968
+ pragma language_version >= 0.16 && <= 0.18;
2887
2969
 
2888
- ledger {
2889
- // Role definitions
2890
- owner: Opaque<"address">;
2891
- admins: Set<Opaque<"address">>;
2892
-
2893
- // Access-controlled state
2894
- sensitiveData: Field;
2895
-
2896
- @private
2897
- adminKeys: Map<Opaque<"address">, Bytes<32>>;
2898
- }
2970
+ import CompactStandardLibrary;
2971
+
2972
+ // Role definitions
2973
+ export ledger owner: Opaque<"address">;
2974
+ export ledger admins: Set<Opaque<"address">>;
2975
+
2976
+ // Access-controlled state
2977
+ export ledger sensitiveData: Field;
2978
+
2979
+ // Private admin keys
2980
+ ledger adminKeys: Map<Opaque<"address">, Bytes<32>>;
2899
2981
 
2900
2982
  // Witness to get caller identity
2901
- witness getCaller(): Opaque<"address"> {
2902
- return getCurrentCaller();
2903
- }
2983
+ witness getCaller(): Opaque<"address">;
2904
2984
 
2905
2985
  // Only owner can call
2906
- export circuit onlyOwnerAction(newValue: Field): Void {
2986
+ export circuit onlyOwnerAction(newValue: Field): [] {
2907
2987
  const caller = getCaller();
2908
- assert(caller == ledger.owner, "Not owner");
2988
+ assert(caller == owner, "Not owner");
2909
2989
 
2910
- ledger.sensitiveData = newValue;
2990
+ sensitiveData = newValue;
2911
2991
  }
2912
2992
 
2913
2993
  // Only admins can call
2914
- export circuit onlyAdminAction(data: Field): Void {
2994
+ export circuit onlyAdminAction(data: Field): [] {
2915
2995
  const caller = getCaller();
2916
- assert(ledger.admins.contains(caller), "Not admin");
2996
+ assert(admins.member(caller), "Not admin");
2917
2997
 
2918
2998
  // Admin action here
2919
2999
  }
2920
3000
 
2921
3001
  // Multi-sig pattern (require multiple approvals)
2922
- witness getApprovalCount(action: Bytes<32>): Field {
2923
- return countApprovals(action);
2924
- }
3002
+ witness getApprovalCount(action: Bytes<32>): Field;
2925
3003
 
2926
3004
  export circuit requireMultisig(action: Bytes<32>, threshold: Field): Boolean {
2927
3005
  const approvals = getApprovalCount(action);
@@ -2930,11 +3008,9 @@ export circuit requireMultisig(action: Bytes<32>, threshold: Field): Boolean {
2930
3008
  }
2931
3009
 
2932
3010
  // Time-locked action
2933
- witness getCurrentTime(): Field {
2934
- return getBlockTimestamp();
2935
- }
3011
+ witness getCurrentTime(): Field;
2936
3012
 
2937
- export circuit timeLockedAction(unlockTime: Field): Void {
3013
+ export circuit timeLockedAction(unlockTime: Field): [] {
2938
3014
  const currentTime = getCurrentTime();
2939
3015
  assert(currentTime >= unlockTime, "Action is timelocked");
2940
3016
 
@@ -2944,21 +3020,23 @@ export circuit timeLockedAction(unlockTime: Field): Void {
2944
3020
  "midnight://code/patterns/privacy-preserving": `// Privacy-Preserving Patterns
2945
3021
  // Techniques for maintaining privacy in smart contracts
2946
3022
 
2947
- include "std";
3023
+ pragma language_version >= 0.16 && <= 0.18;
2948
3024
 
2949
- ledger {
2950
- // Commitment-based private balance
2951
- balanceCommitments: Map<Opaque<"address">, Field>;
2952
-
2953
- // Nullifier set (prevents double-spending)
2954
- nullifiers: Set<Field>;
2955
-
2956
- @private
2957
- secretBalances: Map<Opaque<"address">, Field>;
2958
-
2959
- @private
2960
- secretNonces: Map<Opaque<"address">, Field>;
2961
- }
3025
+ import CompactStandardLibrary;
3026
+
3027
+ // Commitment-based private balance
3028
+ export ledger balanceCommitments: Map<Opaque<"address">, Field>;
3029
+
3030
+ // Nullifier set (prevents double-spending)
3031
+ export ledger nullifiers: Set<Field>;
3032
+
3033
+ // Private state
3034
+ ledger secretBalances: Map<Opaque<"address">, Field>;
3035
+ ledger secretNonces: Map<Opaque<"address">, Field>;
3036
+
3037
+ // Witnesses for private state
3038
+ witness getSecretBalance(user: Opaque<"address">): Field;
3039
+ witness getSecretNonce(user: Opaque<"address">): Field;
2962
3040
 
2963
3041
  // PATTERN 1: Commitment Scheme
2964
3042
  // Store commitments instead of values
@@ -2968,11 +3046,14 @@ export circuit deposit(
2968
3046
  amount: Field,
2969
3047
  nonce: Field
2970
3048
  ): Field {
2971
- // Create commitment: hash(amount, nonce, user)
2972
- const commitment = hash(amount, nonce, user);
3049
+ // Create commitment: hash(amount || nonce)
3050
+ // Use a struct to combine values into a single hashable input
3051
+ struct CommitmentInput { amount: Field, nonce: Field }
3052
+ const input = CommitmentInput { amount: amount, nonce: nonce };
3053
+ const commitment = persistentHash<Field>(input);
2973
3054
 
2974
3055
  // Store commitment (hides amount)
2975
- ledger.balanceCommitments.insert(user, commitment);
3056
+ balanceCommitments.insert(user, commitment);
2976
3057
 
2977
3058
  return commitment;
2978
3059
  }
@@ -2984,31 +3065,31 @@ export circuit proveBalance(
2984
3065
  minBalance: Field
2985
3066
  ): Boolean {
2986
3067
  // Verify commitment
2987
- const expectedCommitment = hash(amount, nonce, user);
2988
- assert(ledger.balanceCommitments.get(user) == expectedCommitment);
3068
+ struct CommitmentInput { amount: Field, nonce: Field }
3069
+ const input = CommitmentInput { amount: amount, nonce: nonce };
3070
+ const expectedCommitment = persistentHash<Field>(input);
3071
+ assert(balanceCommitments.lookup(user) == expectedCommitment, "Invalid commitment");
2989
3072
 
2990
3073
  // Prove property without revealing value
2991
- assert(amount >= minBalance);
3074
+ assert(amount >= minBalance, "Insufficient balance");
2992
3075
  return true;
2993
3076
  }
2994
3077
 
2995
3078
  // PATTERN 2: Nullifiers (Prevent Double-Spending)
2996
3079
 
2997
- witness generateNullifier(secret: Bytes<32>, action: Field): Field {
2998
- return hash(secret, action);
2999
- }
3080
+ witness generateNullifier(secret: Bytes<32>, action: Field): Field;
3000
3081
 
3001
3082
  export circuit spendOnce(
3002
3083
  secret: Bytes<32>,
3003
3084
  action: Field
3004
- ): Void {
3085
+ ): [] {
3005
3086
  const nullifier = generateNullifier(secret, action);
3006
3087
 
3007
3088
  // Check nullifier hasn't been used
3008
- assert(!ledger.nullifiers.contains(nullifier), "Already spent");
3089
+ assert(!nullifiers.member(nullifier), "Already spent");
3009
3090
 
3010
3091
  // Mark as used
3011
- ledger.nullifiers.add(nullifier);
3092
+ nullifiers.insert(nullifier);
3012
3093
 
3013
3094
  // Perform action
3014
3095
  }
@@ -3016,72 +3097,55 @@ export circuit spendOnce(
3016
3097
  // PATTERN 3: Range Proofs
3017
3098
 
3018
3099
  export circuit proveInRange(
3019
- @private value: Field,
3100
+ value: Field,
3020
3101
  min: Field,
3021
3102
  max: Field
3022
3103
  ): Boolean {
3023
3104
  // Prove value is in range without revealing it
3024
- assert(value >= min);
3025
- assert(value <= max);
3105
+ assert(value >= min, "Below minimum");
3106
+ assert(value <= max, "Above maximum");
3026
3107
  return true;
3027
3108
  }
3028
3109
 
3029
3110
  // PATTERN 4: Private Set Membership
3030
3111
 
3112
+ witness computeMerkleRoot(element: Field, proof: Vector<10, Field>): Field;
3113
+
3031
3114
  export circuit proveMembership(
3032
- @private element: Field,
3115
+ element: Field,
3033
3116
  setRoot: Field,
3034
- @private proof: Array<Field>
3117
+ proof: Vector<10, Field>
3035
3118
  ): Boolean {
3036
3119
  // Prove element is in set without revealing which element
3037
3120
  const computedRoot = computeMerkleRoot(element, proof);
3038
- assert(computedRoot == setRoot);
3121
+ assert(computedRoot == setRoot, "Invalid membership proof");
3039
3122
  return true;
3040
3123
  }
3041
-
3042
- witness computeMerkleRoot(element: Field, proof: Array<Field>): Field {
3043
- // Compute Merkle root from element and proof
3044
- return merkleCompute(element, proof);
3045
- }
3046
3124
  `,
3047
3125
  "midnight://code/templates/token": `// Privacy-Preserving Token Template
3048
3126
  // Starter template for token contracts with privacy features
3049
3127
 
3050
- include "std";
3128
+ pragma language_version >= 0.16 && <= 0.18;
3051
3129
 
3052
- ledger {
3053
- // Public token metadata
3054
- name: Opaque<"string">;
3055
- symbol: Opaque<"string">;
3056
- decimals: Field;
3057
- totalSupply: Counter;
3058
-
3059
- // Private balances
3060
- @private
3061
- balances: Map<Opaque<"address">, Field>;
3062
-
3063
- // Private allowances
3064
- @private
3065
- allowances: Map<Opaque<"address">, Map<Opaque<"address">, Field>>;
3066
- }
3130
+ import CompactStandardLibrary;
3067
3131
 
3068
- // Witnesses for private state access
3069
- witness getBalance(account: Opaque<"address">): Field {
3070
- return ledger.balances.get(account) ?? 0;
3071
- }
3132
+ // Public token metadata
3133
+ export ledger name: Bytes<32>;
3134
+ export ledger symbol: Bytes<8>;
3135
+ export ledger decimals: Uint<8>;
3136
+ export ledger totalSupply: Uint<64>;
3072
3137
 
3073
- witness getAllowance(owner: Opaque<"address">, spender: Opaque<"address">): Field {
3074
- return ledger.allowances.get(owner)?.get(spender) ?? 0;
3075
- }
3138
+ // Private balances
3139
+ ledger balances: Map<Opaque<"address">, Uint<64>>;
3076
3140
 
3077
- witness getCaller(): Opaque<"address"> {
3078
- return getCurrentCaller();
3079
- }
3141
+ // Witnesses for private state access
3142
+ witness getBalance(account: Opaque<"address">): Uint<64>;
3143
+ witness getCaller(): Opaque<"address">;
3080
3144
 
3081
3145
  // Transfer tokens privately
3082
3146
  export circuit transfer(
3083
3147
  to: Opaque<"address">,
3084
- amount: Field
3148
+ amount: Uint<64>
3085
3149
  ): Boolean {
3086
3150
  const from = getCaller();
3087
3151
  const fromBalance = getBalance(from);
@@ -3091,224 +3155,153 @@ export circuit transfer(
3091
3155
  assert(fromBalance >= amount, "Insufficient balance");
3092
3156
 
3093
3157
  // Update balances privately
3094
- ledger.balances.insert(from, fromBalance - amount);
3095
- ledger.balances.insert(to, getBalance(to) + amount);
3096
-
3097
- return true;
3098
- }
3099
-
3100
- // Approve spender
3101
- export circuit approve(
3102
- spender: Opaque<"address">,
3103
- amount: Field
3104
- ): Boolean {
3105
- const owner = getCaller();
3106
-
3107
- // Get or create allowance map for owner
3108
- // Note: Simplified - actual implementation needs nested map handling
3109
- ledger.allowances.get(owner).insert(spender, amount);
3110
-
3111
- return true;
3112
- }
3113
-
3114
- // Transfer from approved account
3115
- export circuit transferFrom(
3116
- from: Opaque<"address">,
3117
- to: Opaque<"address">,
3118
- amount: Field
3119
- ): Boolean {
3120
- const spender = getCaller();
3121
- const allowance = getAllowance(from, spender);
3122
- const fromBalance = getBalance(from);
3123
-
3124
- // Validate
3125
- assert(amount > 0, "Invalid amount");
3126
- assert(allowance >= amount, "Insufficient allowance");
3127
- assert(fromBalance >= amount, "Insufficient balance");
3128
-
3129
- // Update state
3130
- ledger.balances.insert(from, fromBalance - amount);
3131
- ledger.balances.insert(to, getBalance(to) + amount);
3132
- ledger.allowances.get(from).insert(spender, allowance - amount);
3158
+ balances.insert(from, fromBalance - amount);
3159
+ balances.insert(to, getBalance(to) + amount);
3133
3160
 
3134
3161
  return true;
3135
3162
  }
3136
3163
 
3137
3164
  // Reveal balance (user's choice)
3138
- export circuit revealMyBalance(): Field {
3165
+ export circuit revealMyBalance(): Uint<64> {
3139
3166
  const caller = getCaller();
3140
3167
  const balance = getBalance(caller);
3141
3168
  return disclose(balance);
3142
3169
  }
3170
+
3171
+ // Get total supply
3172
+ witness getTotalSupply(): Uint<64> {
3173
+ return ledger.totalSupply;
3174
+ }
3175
+
3176
+ // Mint new tokens (admin only)
3177
+ export circuit mint(to: Opaque<"address">, amount: Uint<64>): Boolean {
3178
+ // Add access control in production
3179
+ balances.insert(to, getBalance(to) + amount);
3180
+ ledger.totalSupply = getTotalSupply() + amount;
3181
+ return true;
3182
+ }
3143
3183
  `,
3144
3184
  "midnight://code/templates/voting": `// Private Voting Template
3145
3185
  // Starter template for privacy-preserving voting contracts
3146
3186
 
3147
- include "std";
3187
+ pragma language_version >= 0.16 && <= 0.18;
3148
3188
 
3149
- ledger {
3150
- // Public: proposal metadata
3151
- proposalCount: Counter;
3152
- proposals: Map<Field, Opaque<"string">>;
3153
- votingDeadlines: Map<Field, Field>;
3154
-
3155
- // Public: vote tallies (revealed after voting ends)
3156
- finalTallies: Map<Field, Map<Field, Field>>; // proposalId -> optionId -> count
3157
-
3158
- // Private: individual votes
3159
- @private
3160
- votes: Map<Field, Map<Opaque<"address">, Field>>; // proposalId -> voter -> option
3161
-
3162
- // Nullifiers to prevent double voting
3163
- voteNullifiers: Set<Field>;
3164
-
3165
- // Eligible voters
3166
- eligibleVoters: Set<Opaque<"address">>;
3167
- }
3189
+ import CompactStandardLibrary;
3168
3190
 
3169
- // Witnesses
3170
- witness getCaller(): Opaque<"address"> {
3171
- return getCurrentCaller();
3172
- }
3191
+ // Public: proposal metadata
3192
+ export ledger proposalCount: Counter;
3193
+ export ledger proposals: Map<Uint<64>, Bytes<256>>;
3194
+ export ledger votingDeadlines: Map<Uint<64>, Uint<64>>;
3173
3195
 
3174
- witness getCurrentTime(): Field {
3175
- return getBlockTimestamp();
3176
- }
3196
+ // Private: individual votes
3197
+ ledger votes: Map<Uint<64>, Map<Opaque<"address">, Uint<8>>>;
3177
3198
 
3178
- witness getVote(proposalId: Field, voter: Opaque<"address">): Field {
3179
- return ledger.votes.get(proposalId)?.get(voter) ?? 0;
3180
- }
3199
+ // Nullifiers to prevent double voting
3200
+ export ledger voteNullifiers: Set<Bytes<32>>;
3181
3201
 
3182
- witness computeNullifier(voter: Opaque<"address">, proposalId: Field): Field {
3183
- return hash(voter, proposalId);
3184
- }
3202
+ // Eligible voters
3203
+ export ledger eligibleVoters: Set<Opaque<"address">>;
3204
+
3205
+ // Witnesses
3206
+ witness getCaller(): Opaque<"address">;
3207
+ witness getCurrentTime(): Uint<64>;
3208
+ witness getVote(proposalId: Uint<64>, voter: Opaque<"address">): Uint<8>;
3209
+ witness computeNullifier(voter: Opaque<"address">, proposalId: Uint<64>): Bytes<32>;
3185
3210
 
3186
3211
  // Create a new proposal
3187
3212
  export circuit createProposal(
3188
- description: Opaque<"string">,
3189
- deadline: Field,
3190
- options: Field
3191
- ): Field {
3192
- const proposalId = ledger.proposalCount.value();
3213
+ description: Bytes<256>,
3214
+ deadline: Uint<64>
3215
+ ): Uint<64> {
3216
+ const proposalId = proposalCount.read();
3193
3217
 
3194
- // Store proposal
3195
- ledger.proposals.insert(proposalId, description);
3196
- ledger.votingDeadlines.insert(proposalId, deadline);
3218
+ // Store proposal - proposalId is Uint<64> from Counter.read()
3219
+ proposals.insert(proposalId, description);
3220
+ votingDeadlines.insert(proposalId, deadline);
3197
3221
 
3198
- // Initialize tally for each option
3199
- // (Simplified - actual implementation needs loop)
3200
-
3201
- ledger.proposalCount.increment(1);
3222
+ proposalCount.increment(1);
3202
3223
  return proposalId;
3203
3224
  }
3204
3225
 
3205
3226
  // Cast a private vote
3206
3227
  export circuit vote(
3207
- proposalId: Field,
3208
- option: Field
3228
+ proposalId: Uint<64>,
3229
+ option: Uint<8>
3209
3230
  ): Boolean {
3210
3231
  const voter = getCaller();
3211
3232
  const currentTime = getCurrentTime();
3212
3233
 
3213
3234
  // Check eligibility
3214
- assert(ledger.eligibleVoters.contains(voter), "Not eligible to vote");
3235
+ assert(eligibleVoters.member(voter), "Not eligible to vote");
3215
3236
 
3216
3237
  // Check deadline
3217
- const deadline = ledger.votingDeadlines.get(proposalId);
3238
+ const deadline = votingDeadlines.lookup(proposalId);
3218
3239
  assert(currentTime < deadline, "Voting ended");
3219
3240
 
3220
3241
  // Check for double voting using nullifier
3221
3242
  const nullifier = computeNullifier(voter, proposalId);
3222
- assert(!ledger.voteNullifiers.contains(nullifier), "Already voted");
3223
-
3224
- // Record vote privately
3225
- ledger.votes.get(proposalId).insert(voter, option);
3243
+ assert(!voteNullifiers.member(nullifier), "Already voted");
3226
3244
 
3227
3245
  // Add nullifier to prevent double voting
3228
- ledger.voteNullifiers.add(nullifier);
3246
+ voteNullifiers.insert(nullifier);
3229
3247
 
3230
3248
  return true;
3231
3249
  }
3232
3250
 
3233
3251
  // Reveal individual vote (voter's choice)
3234
- export circuit revealMyVote(proposalId: Field): Field {
3252
+ export circuit revealMyVote(proposalId: Uint<32>): Uint<8> {
3235
3253
  const voter = getCaller();
3236
3254
  const myVote = getVote(proposalId, voter);
3237
3255
  return disclose(myVote);
3238
3256
  }
3239
3257
 
3240
- // Tally votes (after deadline)
3241
- // Note: This is simplified - real implementation would need
3242
- // a mechanism to privately aggregate votes
3243
- export circuit tallyVotes(proposalId: Field): Boolean {
3244
- const currentTime = getCurrentTime();
3245
- const deadline = ledger.votingDeadlines.get(proposalId);
3246
-
3247
- assert(currentTime >= deadline, "Voting still active");
3248
-
3249
- // In a real implementation, votes would be aggregated
3250
- // using homomorphic encryption or MPC
3251
-
3252
- return true;
3253
- }
3254
-
3255
3258
  // Add eligible voter (admin only)
3256
- export circuit addVoter(voter: Opaque<"address">): Void {
3259
+ export circuit addVoter(voter: Opaque<"address">): [] {
3257
3260
  // Add access control in real implementation
3258
- ledger.eligibleVoters.add(voter);
3261
+ eligibleVoters.insert(voter);
3259
3262
  }
3260
3263
  `,
3261
3264
  "midnight://code/examples/nullifier": `// Nullifier Pattern Example
3262
3265
  // Demonstrates how to create and use nullifiers to prevent double-spending/actions
3263
3266
 
3264
- include "std";
3267
+ pragma language_version >= 0.16 && <= 0.18;
3265
3268
 
3266
- ledger {
3267
- // Set of used nullifiers - prevents replay attacks
3268
- usedNullifiers: Set<Bytes<32>>;
3269
-
3270
- // Track claimed rewards
3271
- claimedRewards: Counter;
3272
- }
3269
+ import CompactStandardLibrary;
3270
+
3271
+ // Set of used nullifiers - prevents replay attacks
3272
+ export ledger usedNullifiers: Set<Bytes<32>>;
3273
+
3274
+ // Track claimed rewards
3275
+ export ledger claimedRewards: Counter;
3273
3276
 
3274
3277
  // Hash function for creating nullifiers
3275
3278
  // Combines secret + public data to create unique identifier
3276
- witness computeNullifier(secret: Field, commitment: Field): Bytes<32> {
3277
- // Hash the secret with the commitment
3278
- // The nullifier reveals nothing about the secret
3279
- // but is unique per secret+commitment pair
3280
- return hash(secret, commitment);
3281
- }
3279
+ witness computeNullifier(secret: Field, commitment: Field): Bytes<32>;
3282
3280
 
3283
3281
  // Alternative: nullifier from address and action ID
3284
- witness computeActionNullifier(
3285
- userSecret: Field,
3286
- actionId: Field
3287
- ): Bytes<32> {
3288
- // Create nullifier: hash(secret || actionId)
3289
- return hash(userSecret, actionId);
3290
- }
3282
+ witness computeActionNullifier(userSecret: Field, actionId: Field): Bytes<32>;
3291
3283
 
3292
3284
  // Claim a reward (can only claim once per user)
3285
+ // Note: rewardAmount is Uint<16> to match Counter.increment signature
3293
3286
  export circuit claimReward(
3294
3287
  secret: Field,
3295
3288
  commitment: Field,
3296
- rewardAmount: Field
3289
+ rewardAmount: Uint<16>
3297
3290
  ): Boolean {
3298
3291
  // Compute the nullifier
3299
3292
  const nullifier = computeNullifier(secret, commitment);
3300
3293
 
3301
3294
  // Check nullifier hasn't been used (prevents double-claim)
3302
3295
  assert(
3303
- !ledger.usedNullifiers.contains(nullifier),
3296
+ !usedNullifiers.member(nullifier),
3304
3297
  "Reward already claimed"
3305
3298
  );
3306
3299
 
3307
3300
  // Mark nullifier as used
3308
- ledger.usedNullifiers.add(nullifier);
3301
+ usedNullifiers.insert(nullifier);
3309
3302
 
3310
- // Process reward
3311
- ledger.claimedRewards.increment(rewardAmount);
3303
+ // Process reward (Counter.increment takes Uint<16>)
3304
+ claimedRewards.increment(rewardAmount);
3312
3305
 
3313
3306
  return true;
3314
3307
  }
@@ -3324,12 +3317,12 @@ export circuit voteWithNullifier(
3324
3317
 
3325
3318
  // Ensure hasn't voted on this proposal
3326
3319
  assert(
3327
- !ledger.usedNullifiers.contains(nullifier),
3320
+ !usedNullifiers.member(nullifier),
3328
3321
  "Already voted on this proposal"
3329
3322
  );
3330
3323
 
3331
3324
  // Record nullifier
3332
- ledger.usedNullifiers.add(nullifier);
3325
+ usedNullifiers.insert(nullifier);
3333
3326
 
3334
3327
  // Process vote...
3335
3328
  return true;
@@ -3338,42 +3331,18 @@ export circuit voteWithNullifier(
3338
3331
  "midnight://code/examples/hash": `// Hash Functions in Compact
3339
3332
  // Examples of using hash functions for various purposes
3340
3333
 
3341
- include "std";
3334
+ pragma language_version >= 0.16 && <= 0.18;
3342
3335
 
3343
- ledger {
3344
- commitments: Set<Bytes<32>>;
3345
- hashedData: Map<Field, Bytes<32>>;
3346
- }
3336
+ import CompactStandardLibrary;
3347
3337
 
3348
- // Basic hash function usage
3349
- witness simpleHash(data: Field): Bytes<32> {
3350
- // Hash a single field element
3351
- return hash(data);
3352
- }
3338
+ export ledger commitments: Set<Bytes<32>>;
3339
+ export ledger hashedData: Map<Field, Bytes<32>>;
3353
3340
 
3354
- // Hash multiple values together
3355
- witness hashMultiple(a: Field, b: Field, c: Field): Bytes<32> {
3356
- // Concatenate and hash
3357
- return hash(a, b, c);
3358
- }
3341
+ // Basic hash function usage
3342
+ witness simpleHash(data: Field): Bytes<32>;
3359
3343
 
3360
3344
  // Create a commitment (hash of value + randomness)
3361
- witness createCommitment(value: Field, randomness: Field): Bytes<32> {
3362
- // Pedersen-style commitment: H(value || randomness)
3363
- return hash(value, randomness);
3364
- }
3365
-
3366
- // Hash bytes data
3367
- witness hashBytes(data: Bytes<64>): Bytes<32> {
3368
- return hash(data);
3369
- }
3370
-
3371
- // Create nullifier from secret
3372
- witness createNullifier(secret: Field, publicInput: Field): Bytes<32> {
3373
- // Nullifier = H(secret || publicInput)
3374
- // Reveals nothing about secret, but is deterministic
3375
- return hash(secret, publicInput);
3376
- }
3345
+ witness createCommitment(value: Field, randomness: Field): Bytes<32>;
3377
3346
 
3378
3347
  // Verify a commitment matches
3379
3348
  export circuit verifyCommitment(
@@ -3389,108 +3358,105 @@ export circuit verifyCommitment(
3389
3358
  // Store a hashed value
3390
3359
  export circuit storeHashed(id: Field, data: Field): Bytes<32> {
3391
3360
  const hashed = simpleHash(data);
3392
- ledger.hashedData.insert(id, hashed);
3361
+ hashedData.insert(id, hashed);
3393
3362
  return hashed;
3394
3363
  }
3395
3364
 
3396
3365
  // Commit-reveal pattern
3397
3366
  export circuit commit(commitment: Bytes<32>): Boolean {
3398
- assert(!ledger.commitments.contains(commitment), "Already committed");
3399
- ledger.commitments.add(commitment);
3367
+ assert(!commitments.member(commitment), "Already committed");
3368
+ commitments.insert(commitment);
3400
3369
  return true;
3401
3370
  }
3402
3371
 
3403
3372
  export circuit reveal(value: Field, randomness: Field): Field {
3404
3373
  const commitment = createCommitment(value, randomness);
3405
- assert(ledger.commitments.contains(commitment), "No matching commitment");
3374
+ assert(commitments.member(commitment), "No matching commitment");
3406
3375
  return disclose(value);
3407
3376
  }
3408
3377
  `,
3409
3378
  "midnight://code/examples/simple-counter": `// Simple Counter Contract
3410
3379
  // Minimal example for learning Compact basics
3411
3380
 
3412
- include "std";
3381
+ pragma language_version >= 0.16 && <= 0.18;
3382
+
3383
+ import CompactStandardLibrary;
3413
3384
 
3414
3385
  // Ledger state - stored on chain
3415
- ledger {
3416
- counter: Counter;
3417
- }
3386
+ export ledger counter: Counter;
3418
3387
 
3419
3388
  // Increment the counter by 1
3420
- export circuit increment(): Field {
3421
- ledger.counter.increment(1);
3422
- return ledger.counter.value();
3389
+ export circuit increment(): Uint<64> {
3390
+ counter.increment(1);
3391
+ return counter.read();
3423
3392
  }
3424
3393
 
3425
3394
  // Decrement the counter by 1
3426
- export circuit decrement(): Field {
3427
- assert(ledger.counter.value() > 0, "Cannot go below zero");
3428
- ledger.counter.decrement(1);
3429
- return ledger.counter.value();
3395
+ export circuit decrement(): Uint<64> {
3396
+ assert(counter.read() > 0, "Cannot go below zero");
3397
+ counter.decrement(1);
3398
+ return counter.read();
3430
3399
  }
3431
3400
 
3432
3401
  // Get current value
3433
- export circuit get(): Field {
3434
- return ledger.counter.value();
3402
+ export circuit get(): Uint<64> {
3403
+ return counter.read();
3404
+ }
3405
+
3406
+ // Check if below threshold
3407
+ export circuit isBelowLimit(limit: Uint<64>): Boolean {
3408
+ return counter.lessThan(limit);
3435
3409
  }
3436
3410
 
3437
- // Reset to zero (add access control in real apps)
3438
- export circuit reset(): Void {
3439
- const current = ledger.counter.value();
3440
- ledger.counter.decrement(current);
3411
+ // Reset to zero
3412
+ export circuit reset(): [] {
3413
+ counter.resetToDefault();
3441
3414
  }
3442
3415
  `,
3443
3416
  "midnight://code/templates/basic": `// Basic Compact Contract Template
3444
3417
  // Starting point for new contracts
3445
3418
 
3446
- include "std";
3419
+ pragma language_version >= 0.16 && <= 0.18;
3420
+
3421
+ import CompactStandardLibrary;
3447
3422
 
3448
3423
  // ============================================
3449
3424
  // LEDGER STATE
3450
3425
  // ============================================
3451
3426
 
3452
- ledger {
3453
- // Public state (visible on-chain)
3454
- initialized: Boolean;
3455
- owner: Opaque<"address">;
3456
-
3457
- // Private state (only owner can see)
3458
- @private
3459
- secretData: Field;
3460
- }
3427
+ // Public state (visible on-chain)
3428
+ export ledger initialized: Boolean;
3429
+ export ledger owner: Opaque<"address">;
3430
+
3431
+ // Private state (only owner can see)
3432
+ ledger secretData: Field;
3433
+
3434
+ // ============================================
3435
+ // WITNESSES
3436
+ // ============================================
3437
+
3438
+ witness getCaller(): Opaque<"address">;
3439
+ witness getSecret(): Field;
3461
3440
 
3462
3441
  // ============================================
3463
3442
  // INITIALIZATION
3464
3443
  // ============================================
3465
3444
 
3466
3445
  export circuit initialize(ownerAddress: Opaque<"address">): Boolean {
3467
- assert(!ledger.initialized, "Already initialized");
3446
+ assert(!initialized, "Already initialized");
3468
3447
 
3469
- ledger.owner = ownerAddress;
3470
- ledger.initialized = true;
3448
+ owner = ownerAddress;
3449
+ initialized = true;
3471
3450
 
3472
3451
  return true;
3473
3452
  }
3474
3453
 
3475
- // ============================================
3476
- // ACCESS CONTROL
3477
- // ============================================
3478
-
3479
- witness getCaller(): Opaque<"address"> {
3480
- // Returns the transaction sender
3481
- return context.caller;
3482
- }
3483
-
3484
- witness isOwner(): Boolean {
3485
- return getCaller() == ledger.owner;
3486
- }
3487
-
3488
3454
  // ============================================
3489
3455
  // PUBLIC FUNCTIONS
3490
3456
  // ============================================
3491
3457
 
3492
3458
  export circuit publicFunction(input: Field): Field {
3493
- assert(ledger.initialized, "Not initialized");
3459
+ assert(initialized, "Not initialized");
3494
3460
 
3495
3461
  // Your logic here
3496
3462
  return input * 2;
@@ -3500,22 +3466,19 @@ export circuit publicFunction(input: Field): Field {
3500
3466
  // OWNER-ONLY FUNCTIONS
3501
3467
  // ============================================
3502
3468
 
3503
- export circuit setSecret(newSecret: Field): Void {
3504
- assert(isOwner(), "Only owner can set secret");
3505
- ledger.secretData = newSecret;
3469
+ export circuit setSecret(newSecret: Field): [] {
3470
+ const caller = getCaller();
3471
+ assert(caller == owner, "Only owner can set secret");
3472
+ secretData = newSecret;
3506
3473
  }
3507
3474
 
3508
3475
  // ============================================
3509
3476
  // PRIVATE DATA ACCESS
3510
3477
  // ============================================
3511
3478
 
3512
- witness getSecret(): Field {
3513
- assert(isOwner(), "Only owner can view");
3514
- return ledger.secretData;
3515
- }
3516
-
3517
3479
  export circuit revealSecret(): Field {
3518
- assert(isOwner(), "Only owner can reveal");
3480
+ const caller = getCaller();
3481
+ assert(caller == owner, "Only owner can reveal");
3519
3482
  return disclose(getSecret());
3520
3483
  }
3521
3484
  `
@@ -4625,10 +4588,11 @@ Key Compact concepts:
4625
4588
  - \`Field\`, \`Boolean\`, \`Uint<N>\`, \`Bytes<N>\` - Primitive types
4626
4589
 
4627
4590
  Always include:
4628
- 1. Proper imports (include "std")
4629
- 2. Clear ledger state definitions
4630
- 3. Access control where appropriate
4631
- 4. Comprehensive inline comments
4591
+ 1. Proper imports (import CompactStandardLibrary;)
4592
+ 2. pragma language_version >= 0.18.0;
4593
+ 3. Clear ledger state definitions
4594
+ 4. Access control where appropriate
4595
+ 5. Comprehensive inline comments
4632
4596
 
4633
4597
  Return ONLY the Compact code, no explanations.`;
4634
4598
  const userPrompt = options.baseExample ? `Based on this example contract:
@@ -4813,6 +4777,406 @@ ${code}
4813
4777
  }
4814
4778
  }
4815
4779
 
4780
+ // src/services/syntax-validator.ts
4781
+ var ADT_SEARCH_LIMIT = 10;
4782
+ var SYNTAX_RESULTS_LIMIT = 10;
4783
+ var SYNTAX_SUBQUERY_LIMIT = 5;
4784
+ var MAX_NOTE_SOURCE_LENGTH = 500;
4785
+ var NOTE_MIN_LENGTH = 20;
4786
+ var NOTE_MAX_LENGTH = 300;
4787
+ var MAX_NOTES_PER_ADT = 3;
4788
+ async function searchADTInfo(adtName) {
4789
+ try {
4790
+ const docsResult = await searchDocsHosted(
4791
+ `${adtName} ADT operations methods ledger`,
4792
+ ADT_SEARCH_LIMIT,
4793
+ "reference"
4794
+ );
4795
+ if (!docsResult.results || docsResult.results.length === 0) {
4796
+ logger.debug(`No indexed docs found for ADT: ${adtName}`);
4797
+ return null;
4798
+ }
4799
+ const operations = [];
4800
+ const notes = [];
4801
+ let sourceDocPath;
4802
+ for (const result of docsResult.results) {
4803
+ const content = result.content || result.code || "";
4804
+ const isLedgerADTDoc = result.source.filePath.includes("ledger-adt");
4805
+ if (!sourceDocPath && isLedgerADTDoc) {
4806
+ sourceDocPath = result.source.filePath;
4807
+ }
4808
+ if (isLedgerADTDoc) {
4809
+ const methodPattern = /\|\s*`?(\w+)`?\s*\|\s*`?\(([^)]*)\)\s*(?::\s*([^|`]+))?`?\s*\|\s*([^|]+)\|/g;
4810
+ let match;
4811
+ while ((match = methodPattern.exec(content)) !== null) {
4812
+ const [, methodName, params, returnType, description] = match;
4813
+ if (methodName && !operations.some((op) => op.method === methodName)) {
4814
+ const descText = description?.trim() || "";
4815
+ const worksInCircuits = !/not available|not supported|not in circuits|typescript|sdk|off[-\s]?chain/i.test(
4816
+ descText
4817
+ );
4818
+ operations.push({
4819
+ method: methodName,
4820
+ signature: `(${params})${returnType ? `: ${returnType.trim()}` : ""}`,
4821
+ description: descText,
4822
+ worksInCircuits,
4823
+ source: "indexed-docs"
4824
+ });
4825
+ }
4826
+ }
4827
+ const inlinePattern = /(?:method|operation|function)\s+`?(\w+)`?\s*\(([^)]*)\)/gi;
4828
+ while ((match = inlinePattern.exec(content)) !== null) {
4829
+ const [, methodName, params] = match;
4830
+ if (methodName && !operations.some((op) => op.method === methodName)) {
4831
+ const worksInCircuits = !/not available|not supported|not in circuits|typescript|sdk|off[-\s]?chain/i.test(
4832
+ content
4833
+ );
4834
+ operations.push({
4835
+ method: methodName,
4836
+ signature: `(${params})`,
4837
+ description: "Extracted from documentation",
4838
+ worksInCircuits,
4839
+ source: "indexed-docs"
4840
+ });
4841
+ }
4842
+ }
4843
+ }
4844
+ if (content.toLowerCase().includes(adtName.toLowerCase()) && content.length < MAX_NOTE_SOURCE_LENGTH) {
4845
+ const cleanNote = content.replace(/\s+/g, " ").trim();
4846
+ if (cleanNote.length > NOTE_MIN_LENGTH && cleanNote.length < NOTE_MAX_LENGTH) {
4847
+ notes.push(cleanNote);
4848
+ }
4849
+ }
4850
+ }
4851
+ if (operations.length === 0) {
4852
+ logger.debug(`Could not parse operations for ADT: ${adtName}`);
4853
+ return null;
4854
+ }
4855
+ return {
4856
+ name: adtName,
4857
+ operations,
4858
+ notes: notes.slice(0, MAX_NOTES_PER_ADT),
4859
+ sourceDocPath,
4860
+ lastVerified: (/* @__PURE__ */ new Date()).toISOString()
4861
+ };
4862
+ } catch (error) {
4863
+ logger.warn(`Failed to search indexed docs for ADT ${adtName}`, {
4864
+ error: error instanceof Error ? error.message : String(error)
4865
+ });
4866
+ return null;
4867
+ }
4868
+ }
4869
+ async function searchCompactSyntax(topic) {
4870
+ try {
4871
+ const [docsResult, codeResult] = await Promise.all([
4872
+ searchDocsHosted(
4873
+ `Compact ${topic} syntax`,
4874
+ SYNTAX_SUBQUERY_LIMIT,
4875
+ "reference"
4876
+ ),
4877
+ searchCompactHosted(`${topic}`, SYNTAX_SUBQUERY_LIMIT)
4878
+ ]);
4879
+ const mergedResults = [
4880
+ ...docsResult.results || [],
4881
+ ...codeResult.results || []
4882
+ ];
4883
+ return {
4884
+ results: mergedResults.slice(0, SYNTAX_RESULTS_LIMIT),
4885
+ totalResults: mergedResults.length,
4886
+ query: topic,
4887
+ lastIndexed: docsResult.lastIndexed || codeResult.lastIndexed
4888
+ };
4889
+ } catch (error) {
4890
+ logger.warn(`Failed to search Compact syntax for topic ${topic}`, {
4891
+ error: error instanceof Error ? error.message : String(error)
4892
+ });
4893
+ return null;
4894
+ }
4895
+ }
4896
+ async function validateADTOperations(adtName, staticOperations) {
4897
+ const discrepancies = [];
4898
+ const enrichments = [];
4899
+ const indexedInfo = await searchADTInfo(adtName);
4900
+ if (!indexedInfo) {
4901
+ return {
4902
+ validated: false,
4903
+ operations: staticOperations.map((op) => ({
4904
+ method: op.method,
4905
+ description: op.note,
4906
+ worksInCircuits: op.works,
4907
+ source: "static-fallback"
4908
+ })),
4909
+ discrepancies: [
4910
+ "Could not validate against indexed docs - using static data"
4911
+ ],
4912
+ enrichments: []
4913
+ };
4914
+ }
4915
+ const operations = [];
4916
+ const indexedMethodNames = new Set(
4917
+ indexedInfo.operations.map((op) => op.method.toLowerCase())
4918
+ );
4919
+ const staticMethodNames = new Set(
4920
+ staticOperations.map(
4921
+ (op) => op.method.replace(/^\./, "").replace(/\(.*$/, "").toLowerCase()
4922
+ )
4923
+ );
4924
+ for (const indexedOp of indexedInfo.operations) {
4925
+ operations.push(indexedOp);
4926
+ const staticOp = staticOperations.find(
4927
+ (s) => s.method.toLowerCase().includes(indexedOp.method.toLowerCase()) || indexedOp.method.toLowerCase().includes(
4928
+ s.method.replace(/^\./, "").replace(/\(.*$/, "").toLowerCase()
4929
+ )
4930
+ );
4931
+ if (staticOp && !staticOp.works && indexedOp.worksInCircuits) {
4932
+ discrepancies.push(
4933
+ `Static said ${staticOp.method} doesn't work, but indexed docs show it does`
4934
+ );
4935
+ }
4936
+ }
4937
+ for (const staticOp of staticOperations) {
4938
+ const methodName = staticOp.method.replace(/^\./, "").replace(/\(.*$/, "").toLowerCase();
4939
+ if (!indexedMethodNames.has(methodName)) {
4940
+ discrepancies.push(
4941
+ `Static references ${staticOp.method} but not found in indexed docs - may not exist`
4942
+ );
4943
+ }
4944
+ }
4945
+ for (const indexedOp of indexedInfo.operations) {
4946
+ const methodName = indexedOp.method.toLowerCase();
4947
+ if (!staticMethodNames.has(methodName)) {
4948
+ enrichments.push(
4949
+ `Indexed docs show ${indexedOp.method} exists but was missing from static reference`
4950
+ );
4951
+ }
4952
+ }
4953
+ return {
4954
+ validated: true,
4955
+ operations,
4956
+ discrepancies,
4957
+ enrichments
4958
+ };
4959
+ }
4960
+ async function validateBuiltinFunctions(staticBuiltins) {
4961
+ const discrepancies = [];
4962
+ const enrichments = [];
4963
+ const results = await Promise.all(
4964
+ staticBuiltins.map(async (builtin) => {
4965
+ const searchResult = await searchCompactSyntax(
4966
+ `${builtin.name} function builtin`
4967
+ );
4968
+ return { builtin, found: (searchResult?.results?.length || 0) > 0 };
4969
+ })
4970
+ );
4971
+ for (const { builtin, found } of results) {
4972
+ if (!found) {
4973
+ discrepancies.push(
4974
+ `Builtin "${builtin.name}" not found in indexed docs - may not exist or have different name`
4975
+ );
4976
+ }
4977
+ }
4978
+ const allBuiltinsSearch = await searchCompactSyntax(
4979
+ "builtin function stdlib standard library"
4980
+ );
4981
+ if (allBuiltinsSearch?.results) {
4982
+ const knownNames = new Set(staticBuiltins.map((b) => b.name.toLowerCase()));
4983
+ for (const result of allBuiltinsSearch.results) {
4984
+ const content = result.content || result.code || "";
4985
+ const funcPattern = /\b(export\s+)?function\s+(\w+)/gi;
4986
+ let match;
4987
+ while ((match = funcPattern.exec(content)) !== null) {
4988
+ const funcName = match[2].toLowerCase();
4989
+ if (!knownNames.has(funcName) && funcName.length > 2) {
4990
+ enrichments.push(`Indexed docs mention "${match[2]}" function`);
4991
+ }
4992
+ }
4993
+ }
4994
+ }
4995
+ return {
4996
+ dataType: "BUILTIN_FUNCTIONS",
4997
+ validated: discrepancies.length === 0,
4998
+ discrepancies,
4999
+ enrichments: [...new Set(enrichments)].slice(0, 10),
5000
+ deprecatedPatterns: [],
5001
+ lastValidated: (/* @__PURE__ */ new Date()).toISOString()
5002
+ };
5003
+ }
5004
+ async function validateTypeCompatibility(staticRules) {
5005
+ const discrepancies = [];
5006
+ const enrichments = [];
5007
+ const typeSearch = await searchCompactSyntax(
5008
+ "type cast Field Uint Bytes conversion compatible"
5009
+ );
5010
+ if (typeSearch?.results) {
5011
+ for (const result of typeSearch.results) {
5012
+ const content = (result.content || result.code || "").toLowerCase();
5013
+ for (const rule of staticRules) {
5014
+ const typeMatch = rule.types.match(
5015
+ /^\s*(.+?)\s*(?:==|=|<=|>=|<|>|[+*])\s*(.+?)\s*$/
5016
+ );
5017
+ const typeA = typeMatch?.[1]?.trim().toLowerCase();
5018
+ const typeB = typeMatch?.[2]?.trim().toLowerCase();
5019
+ if (typeA && typeB) {
5020
+ if (content.includes(typeA) && content.includes(typeB) && content.includes("compatible")) {
5021
+ if (!rule.works) {
5022
+ discrepancies.push(
5023
+ `Static says "${rule.types}" doesn't work, but docs suggest compatibility`
5024
+ );
5025
+ }
5026
+ }
5027
+ }
5028
+ }
5029
+ }
5030
+ }
5031
+ return {
5032
+ dataType: "TYPE_COMPATIBILITY",
5033
+ validated: discrepancies.length === 0,
5034
+ discrepancies,
5035
+ enrichments,
5036
+ deprecatedPatterns: [],
5037
+ lastValidated: (/* @__PURE__ */ new Date()).toISOString()
5038
+ };
5039
+ }
5040
+ async function validateCommonErrors(staticErrors) {
5041
+ const discrepancies = [];
5042
+ const enrichments = [];
5043
+ const errorSearch = await searchDocsHosted(
5044
+ "error compile compilation failure parse",
5045
+ ADT_SEARCH_LIMIT,
5046
+ "all"
5047
+ );
5048
+ if (errorSearch?.results) {
5049
+ const knownErrors = new Set(
5050
+ staticErrors.map((e) => e.error.toLowerCase().slice(0, 30))
5051
+ );
5052
+ for (const result of errorSearch.results) {
5053
+ const content = result.content || result.code || "";
5054
+ const errorPattern = /(?:error|Error):\s*["']?([^"'\n]+)["']?/g;
5055
+ let match;
5056
+ while ((match = errorPattern.exec(content)) !== null) {
5057
+ const errorMsg = match[1].toLowerCase().slice(0, 30);
5058
+ if (!knownErrors.has(errorMsg) && errorMsg.length > 10) {
5059
+ enrichments.push(`Docs mention error: "${match[1].slice(0, 50)}..."`);
5060
+ }
5061
+ }
5062
+ }
5063
+ }
5064
+ return {
5065
+ dataType: "COMMON_ERRORS",
5066
+ validated: true,
5067
+ // Errors are informational, hard to validate
5068
+ discrepancies,
5069
+ enrichments: [...new Set(enrichments)].slice(0, 10),
5070
+ deprecatedPatterns: [],
5071
+ lastValidated: (/* @__PURE__ */ new Date()).toISOString()
5072
+ };
5073
+ }
5074
+ var DEPRECATED_SYNTAX_PATTERNS = [
5075
+ {
5076
+ pattern: /ledger\s*\{/,
5077
+ name: "ledger-block",
5078
+ message: "Deprecated: Use 'export ledger field: Type;' instead of ledger { } block",
5079
+ since: "0.16"
5080
+ },
5081
+ {
5082
+ pattern: /Cell\s*<\s*\w+\s*>/,
5083
+ name: "cell-wrapper",
5084
+ message: "Deprecated: Cell<T> wrapper removed, use Type directly",
5085
+ since: "0.15"
5086
+ },
5087
+ {
5088
+ pattern: /:\s*Void\b/,
5089
+ name: "void-type",
5090
+ message: "Void type doesn't exist, use [] (empty tuple) for no return",
5091
+ since: "always"
5092
+ },
5093
+ {
5094
+ pattern: /\.\s*value\s*\(\s*\)/,
5095
+ name: "counter-value",
5096
+ message: "Counter.value() doesn't exist, use Counter.read() instead",
5097
+ since: "always"
5098
+ },
5099
+ {
5100
+ pattern: /::\w+/,
5101
+ name: "rust-enum-syntax",
5102
+ message: "Rust-style :: enum access doesn't work, use dot notation (Choice.rock)",
5103
+ since: "always"
5104
+ },
5105
+ {
5106
+ pattern: /pure\s+function\b/,
5107
+ name: "pure-function",
5108
+ message: "Use 'pure circuit' not 'pure function' for helper functions",
5109
+ since: "0.16"
5110
+ },
5111
+ {
5112
+ pattern: /witness\s+\w+\s*\([^)]*\)\s*:\s*\w+\s*\{/,
5113
+ name: "witness-with-body",
5114
+ message: "Witnesses are declarations only - no body allowed. Implementation goes in TypeScript prover.",
5115
+ since: "always"
5116
+ }
5117
+ ];
5118
+ function scanForDeprecatedPatterns(code) {
5119
+ const issues = [];
5120
+ const lines = code.split("\n");
5121
+ for (const { pattern, name, message } of DEPRECATED_SYNTAX_PATTERNS) {
5122
+ for (let i = 0; i < lines.length; i++) {
5123
+ if (pattern.test(lines[i])) {
5124
+ issues.push({
5125
+ pattern: name,
5126
+ message,
5127
+ lineNumber: i + 1
5128
+ });
5129
+ }
5130
+ }
5131
+ }
5132
+ return issues;
5133
+ }
5134
+ async function validateAllStaticData(staticData) {
5135
+ const results = {};
5136
+ const [builtinResult, typeResult, errorResult, adtResults] = await Promise.all([
5137
+ staticData.builtinFunctions ? validateBuiltinFunctions(staticData.builtinFunctions) : Promise.resolve(null),
5138
+ staticData.typeCompatibility ? validateTypeCompatibility(staticData.typeCompatibility) : Promise.resolve(null),
5139
+ staticData.commonErrors ? validateCommonErrors(staticData.commonErrors) : Promise.resolve(null),
5140
+ staticData.ledgerTypeLimits ? Promise.all(
5141
+ Object.entries(staticData.ledgerTypeLimits).map(
5142
+ async ([name, data]) => ({
5143
+ name,
5144
+ result: await validateADTOperations(
5145
+ name,
5146
+ data.circuitOperations
5147
+ )
5148
+ })
5149
+ )
5150
+ ) : Promise.resolve([])
5151
+ ]);
5152
+ if (builtinResult) results.builtinFunctions = builtinResult;
5153
+ if (typeResult) results.typeCompatibility = typeResult;
5154
+ if (errorResult) results.commonErrors = errorResult;
5155
+ for (const { name, result } of adtResults) {
5156
+ results[`adt_${name}`] = {
5157
+ dataType: `LEDGER_TYPE_LIMITS.${name}`,
5158
+ validated: result.validated,
5159
+ discrepancies: result.discrepancies,
5160
+ enrichments: result.enrichments,
5161
+ deprecatedPatterns: [],
5162
+ lastValidated: (/* @__PURE__ */ new Date()).toISOString()
5163
+ };
5164
+ }
5165
+ const allDiscrepancies = Object.values(results).flatMap(
5166
+ (r) => r.discrepancies
5167
+ );
5168
+ const allEnrichments = Object.values(results).flatMap((r) => r.enrichments);
5169
+ return {
5170
+ overall: {
5171
+ validated: allDiscrepancies.length === 0,
5172
+ totalDiscrepancies: allDiscrepancies.length,
5173
+ totalEnrichments: allEnrichments.length
5174
+ },
5175
+ results,
5176
+ lastValidated: (/* @__PURE__ */ new Date()).toISOString()
5177
+ };
5178
+ }
5179
+
4816
5180
  // src/server.ts
4817
5181
  var SERVER_INFO = {
4818
5182
  name: "midnight-mcp",
@@ -5679,17 +6043,30 @@ var TYPE_COMPATIBILITY = {
5679
6043
  var LEDGER_TYPE_LIMITS = {
5680
6044
  Counter: {
5681
6045
  circuitOperations: [
5682
- { method: ".increment(n)", works: true, note: "Adds n to counter" },
6046
+ {
6047
+ method: ".increment(n)",
6048
+ works: true,
6049
+ note: "Increase counter by n (Uint<16>)"
6050
+ },
5683
6051
  {
5684
6052
  method: ".decrement(n)",
5685
6053
  works: true,
5686
- note: "Subtracts n from counter"
6054
+ note: "Decrease counter by n (Uint<16>)"
6055
+ },
6056
+ {
6057
+ method: ".read()",
6058
+ works: true,
6059
+ note: "Get current value (returns Uint<64>)"
5687
6060
  },
5688
- { method: ".resetToDefault()", works: true, note: "Resets to 0" },
5689
- { method: ".value()", works: false, note: "NOT available in circuits" }
6061
+ {
6062
+ method: ".lessThan(n)",
6063
+ works: true,
6064
+ note: "Compare with threshold (returns Boolean)"
6065
+ },
6066
+ { method: ".resetToDefault()", works: true, note: "Reset to 0" }
5690
6067
  ],
5691
6068
  typescriptAccess: "Access counter value via `ledgerState.counter` in TypeScript SDK",
5692
- reason: "ZK circuits cannot read current ledger state - only modify it"
6069
+ note: "All Counter operations work in circuits. Use .read() to get value, NOT .value()"
5693
6070
  },
5694
6071
  Map: {
5695
6072
  circuitOperations: [
@@ -5698,20 +6075,40 @@ var LEDGER_TYPE_LIMITS = {
5698
6075
  works: true,
5699
6076
  note: "Adds/updates entry"
5700
6077
  },
6078
+ {
6079
+ method: ".insertDefault(key)",
6080
+ works: true,
6081
+ note: "Inserts default value for key"
6082
+ },
5701
6083
  { method: ".remove(key)", works: true, note: "Removes entry" },
5702
6084
  {
5703
6085
  method: ".lookup(key)",
5704
6086
  works: true,
5705
- note: "Returns Option<ValueType> - use in circuits"
6087
+ note: "Returns value_type - gets value for key"
5706
6088
  },
5707
6089
  {
5708
6090
  method: ".member(key)",
5709
6091
  works: true,
5710
6092
  note: "Returns Boolean - checks if key exists"
6093
+ },
6094
+ {
6095
+ method: ".isEmpty()",
6096
+ works: true,
6097
+ note: "Returns Boolean - checks if map is empty"
6098
+ },
6099
+ {
6100
+ method: ".size()",
6101
+ works: true,
6102
+ note: "Returns Uint<64> - number of entries"
6103
+ },
6104
+ {
6105
+ method: ".resetToDefault()",
6106
+ works: true,
6107
+ note: "Clears entire map"
5711
6108
  }
5712
6109
  ],
5713
- typescriptAccess: "Query map via `contractState.data.get(key)` in TypeScript SDK",
5714
- note: "Map.lookup() and Map.member() ARE available in circuits (verified with OpenZeppelin contracts)"
6110
+ typescriptAccess: "Query map via `contractState.data.get(key)` or iterate with `[Symbol.iterator]()` in TypeScript SDK",
6111
+ note: "All Map operations work in circuits. insertCoin() available when value_type is QualifiedCoinInfo."
5715
6112
  },
5716
6113
  Set: {
5717
6114
  circuitOperations: [
@@ -5721,10 +6118,25 @@ var LEDGER_TYPE_LIMITS = {
5721
6118
  method: ".member(value)",
5722
6119
  works: true,
5723
6120
  note: "Returns Boolean - checks if value exists in set"
6121
+ },
6122
+ {
6123
+ method: ".isEmpty()",
6124
+ works: true,
6125
+ note: "Returns Boolean - checks if set is empty"
6126
+ },
6127
+ {
6128
+ method: ".size()",
6129
+ works: true,
6130
+ note: "Returns Uint<64> - number of elements"
6131
+ },
6132
+ {
6133
+ method: ".resetToDefault()",
6134
+ works: true,
6135
+ note: "Clears entire set"
5724
6136
  }
5725
6137
  ],
5726
- typescriptAccess: "Check membership via `contractState.set.has(value)` in TypeScript SDK",
5727
- note: "Set.member() IS available in circuits"
6138
+ typescriptAccess: "Check membership via `contractState.set.has(value)` or iterate with `[Symbol.iterator]()` in TypeScript SDK",
6139
+ note: "All Set operations work in circuits. insertCoin() available when value_type is QualifiedCoinInfo."
5728
6140
  },
5729
6141
  MerkleTree: {
5730
6142
  circuitOperations: [
@@ -5754,11 +6166,16 @@ Or use bounded Uint<0..N> for parameters that need constraints`
5754
6166
  },
5755
6167
  {
5756
6168
  error: 'operation "value" undefined for ledger field type Counter',
5757
- cause: "Trying to read Counter.value() inside a circuit",
5758
- fix: `Counter values cannot be read in circuits. Options:
5759
- 1. Use a witness: witness get_counter_value(): Uint<64>;
5760
- 2. Read from TypeScript SDK: ledgerState.counter
5761
- 3. Track the value in a separate Field ledger variable`
6169
+ cause: "Using wrong method name - Counter uses .read() not .value()",
6170
+ fix: `Use counter.read() to get the current value:
6171
+ const current = ledger.counter.read();
6172
+
6173
+ Counter ADT methods available in circuits:
6174
+ - increment(amount: Uint<16>): [] - increase counter
6175
+ - decrement(amount: Uint<16>): [] - decrease counter
6176
+ - read(): Uint<64> - get current value
6177
+ - lessThan(threshold: Uint<64>): Boolean - compare
6178
+ - resetToDefault(): [] - reset to zero`
5762
6179
  },
5763
6180
  {
5764
6181
  error: "implicit disclosure of witness value",
@@ -5852,6 +6269,9 @@ CORRECT: pure circuit helper(...): Type { }`
5852
6269
  import { readFile } from "fs/promises";
5853
6270
  import { basename, isAbsolute, resolve } from "path";
5854
6271
  import { platform } from "process";
6272
+ function escapeRegex(str) {
6273
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6274
+ }
5855
6275
  function validateFilePath(filePath) {
5856
6276
  if (!isAbsolute(filePath)) {
5857
6277
  return {
@@ -6287,7 +6707,7 @@ async function extractContractStructure(input) {
6287
6707
  });
6288
6708
  }
6289
6709
  }
6290
- const hasStdlibImport = imports.includes("CompactStandardLibrary") || code.includes('include "std"');
6710
+ const hasStdlibImport = imports.includes("CompactStandardLibrary") || code.includes("import CompactStandardLibrary");
6291
6711
  if (hasStdlibImport) {
6292
6712
  for (const circuit of circuits) {
6293
6713
  if (stdlibExports.includes(circuit.name)) {
@@ -6311,16 +6731,20 @@ async function extractContractStructure(input) {
6311
6731
  if (sealedFields.length > 0) {
6312
6732
  for (const circuit of circuits) {
6313
6733
  if (circuit.isExport) {
6734
+ const escapedCircuitName = escapeRegex(circuit.name);
6314
6735
  const circuitBodyMatch = code.match(
6315
6736
  new RegExp(
6316
- `(?:export\\s+)?circuit\\s+${circuit.name}\\s*\\([^)]*\\)\\s*:[^{]*\\{([\\s\\S]*?)\\n\\}`,
6737
+ `(?:export\\s+)?circuit\\s+${escapedCircuitName}\\s*\\([^)]*\\)\\s*:[^{]*\\{([\\s\\S]*?)\\n\\}`,
6317
6738
  "m"
6318
6739
  )
6319
6740
  );
6320
6741
  if (circuitBodyMatch) {
6321
6742
  const body = circuitBodyMatch[1];
6322
6743
  for (const field of sealedFields) {
6323
- if (new RegExp(`\\b${field.name}\\s*=`).test(body) || new RegExp(`\\b${field.name}\\s*\\.\\s*\\w+\\s*\\(`).test(body)) {
6744
+ const escapedFieldName = escapeRegex(field.name);
6745
+ if (new RegExp(`\\b${escapedFieldName}\\s*=`).test(body) || new RegExp(`\\b${escapedFieldName}\\s*\\.\\s*\\w+\\s*\\(`).test(
6746
+ body
6747
+ )) {
6324
6748
  potentialIssues.push({
6325
6749
  type: "sealed_export_conflict",
6326
6750
  line: circuit.line,
@@ -6394,7 +6818,7 @@ async function extractContractStructure(input) {
6394
6818
  });
6395
6819
  break;
6396
6820
  }
6397
- const counterValuePattern = /(\w+)\.value\b/g;
6821
+ const counterValuePattern = /(\w+)\.value\s*\(/g;
6398
6822
  let counterMatch;
6399
6823
  while ((counterMatch = counterValuePattern.exec(code)) !== null) {
6400
6824
  const varName = counterMatch[1];
@@ -6406,8 +6830,8 @@ async function extractContractStructure(input) {
6406
6830
  potentialIssues.push({
6407
6831
  type: "invalid_counter_access",
6408
6832
  line: lineNum,
6409
- message: `Counter type '${varName}' does not have a '.value' property`,
6410
- suggestion: `Counter only has '.increment(n)'. Use 'Uint<32>' or 'Uint<64>' instead if you need to read the value`,
6833
+ message: `Counter type '${varName}' does not have '.value()' - use '.read()' instead`,
6834
+ suggestion: `Counter ADT methods: increment(n), decrement(n), read(), lessThan(n), resetToDefault()`,
6411
6835
  severity: "error"
6412
6836
  });
6413
6837
  }
@@ -6469,8 +6893,9 @@ async function extractContractStructure(input) {
6469
6893
  constructorParams.push(paramMatch[1]);
6470
6894
  }
6471
6895
  for (const param of constructorParams) {
6896
+ const escapedParam = escapeRegex(param);
6472
6897
  const assignmentPattern = new RegExp(
6473
- `(\\w+)\\s*=\\s*(?!disclose\\s*\\()${param}\\b`,
6898
+ `(\\w+)\\s*=\\s*(?!disclose\\s*\\()${escapedParam}\\b`,
6474
6899
  "g"
6475
6900
  );
6476
6901
  let assignMatch;
@@ -7052,6 +7477,96 @@ async function getLatestSyntax(input) {
7052
7477
  } catch {
7053
7478
  }
7054
7479
  if (compactReference) {
7480
+ logger.debug(
7481
+ "Starting comprehensive hybrid validation against indexed docs"
7482
+ );
7483
+ let comprehensiveValidation;
7484
+ try {
7485
+ comprehensiveValidation = await validateAllStaticData({
7486
+ builtinFunctions: BUILTIN_FUNCTIONS.stdlib,
7487
+ typeCompatibility: [
7488
+ ...TYPE_COMPATIBILITY.comparisons,
7489
+ ...TYPE_COMPATIBILITY.arithmetic
7490
+ ],
7491
+ commonErrors: COMMON_ERRORS,
7492
+ ledgerTypeLimits: LEDGER_TYPE_LIMITS
7493
+ });
7494
+ logger.debug("Comprehensive validation complete", {
7495
+ validated: comprehensiveValidation.overall.validated,
7496
+ discrepancies: comprehensiveValidation.overall.totalDiscrepancies,
7497
+ enrichments: comprehensiveValidation.overall.totalEnrichments
7498
+ });
7499
+ } catch (err) {
7500
+ logger.warn(
7501
+ "Comprehensive validation failed, falling back to ADT-only validation",
7502
+ {
7503
+ error: err instanceof Error ? err.message : String(err)
7504
+ }
7505
+ );
7506
+ }
7507
+ const validationWarnings = [];
7508
+ const validationEnrichments = [];
7509
+ if (comprehensiveValidation) {
7510
+ for (const [key, result] of Object.entries(
7511
+ comprehensiveValidation.results
7512
+ )) {
7513
+ if (result.discrepancies.length > 0) {
7514
+ validationWarnings.push(
7515
+ `${key}: ${result.discrepancies.join("; ")}`
7516
+ );
7517
+ }
7518
+ if (result.enrichments.length > 0) {
7519
+ validationEnrichments.push(
7520
+ `${key}: ${result.enrichments.join("; ")}`
7521
+ );
7522
+ }
7523
+ }
7524
+ }
7525
+ let indexedSyntaxPatterns;
7526
+ try {
7527
+ const syntaxSearch = await searchCompactSyntax(
7528
+ "circuit witness ledger pragma"
7529
+ );
7530
+ if (syntaxSearch && syntaxSearch.results.length > 0) {
7531
+ indexedSyntaxPatterns = {
7532
+ resultCount: syntaxSearch.results.length,
7533
+ topSources: syntaxSearch.results.slice(0, 3).map((r) => ({
7534
+ path: r.source.filePath,
7535
+ repo: r.source.repository
7536
+ }))
7537
+ };
7538
+ }
7539
+ } catch {
7540
+ }
7541
+ const validatedLedgerTypeLimits = {};
7542
+ for (const [adtName, staticInfo] of Object.entries(LEDGER_TYPE_LIMITS)) {
7543
+ const typedStaticInfo = staticInfo;
7544
+ const adtValidation = comprehensiveValidation?.results[`adt_${adtName}`];
7545
+ if (adtValidation?.validated) {
7546
+ validatedLedgerTypeLimits[adtName] = {
7547
+ ...typedStaticInfo,
7548
+ validatedAgainstDocs: true,
7549
+ validationDiscrepancies: adtValidation.discrepancies,
7550
+ validationEnrichments: adtValidation.enrichments
7551
+ };
7552
+ } else {
7553
+ validatedLedgerTypeLimits[adtName] = {
7554
+ ...typedStaticInfo,
7555
+ validatedAgainstDocs: false,
7556
+ warning: "Could not validate against indexed docs - using static reference"
7557
+ };
7558
+ }
7559
+ }
7560
+ const quickStartTemplate = `${RECOMMENDED_PRAGMA}
7561
+
7562
+ import CompactStandardLibrary;
7563
+
7564
+ export ledger counter: Counter;
7565
+
7566
+ export circuit increment(): [] {
7567
+ counter.increment(1);
7568
+ }`;
7569
+ const deprecatedInTemplate = scanForDeprecatedPatterns(quickStartTemplate);
7055
7570
  return {
7056
7571
  repository: "midnightntwrk/compact",
7057
7572
  version: `${COMPACT_VERSION.min}-${COMPACT_VERSION.max} (current)`,
@@ -7062,6 +7577,29 @@ async function getLatestSyntax(input) {
7062
7577
  maintenanceGuide: "See docs/SYNTAX_MAINTENANCE.md for update instructions"
7063
7578
  },
7064
7579
  ...versionWarning && { versionWarning },
7580
+ // Comprehensive hybrid validation status
7581
+ hybridValidation: {
7582
+ enabled: true,
7583
+ comprehensive: true,
7584
+ // Overall validation stats
7585
+ overall: comprehensiveValidation?.overall || {
7586
+ validated: false,
7587
+ totalDiscrepancies: 0,
7588
+ totalEnrichments: 0
7589
+ },
7590
+ // Which data types were validated
7591
+ validatedDataTypes: comprehensiveValidation ? Object.keys(comprehensiveValidation.results) : [],
7592
+ // All warnings from ALL static data validation
7593
+ warnings: validationWarnings.length > 0 ? validationWarnings : void 0,
7594
+ // All enrichments discovered from indexed docs
7595
+ enrichments: validationEnrichments.length > 0 ? validationEnrichments : void 0,
7596
+ // Indexed syntax pattern sources
7597
+ indexedSyntaxPatterns,
7598
+ // Deprecated patterns found in quick start template (should be none!)
7599
+ deprecatedPatternsInTemplate: deprecatedInTemplate.length > 0 ? deprecatedInTemplate : void 0,
7600
+ // Last validation timestamp
7601
+ lastValidated: comprehensiveValidation?.lastValidated || (/* @__PURE__ */ new Date()).toISOString()
7602
+ },
7065
7603
  // Quick start template - ALWAYS compiles
7066
7604
  quickStartTemplate: `${RECOMMENDED_PRAGMA}
7067
7605
 
@@ -7079,8 +7617,8 @@ export circuit increment(): [] {
7079
7617
  builtinFunctions: BUILTIN_FUNCTIONS,
7080
7618
  // Type compatibility rules
7081
7619
  typeCompatibility: TYPE_COMPATIBILITY,
7082
- // Ledger type limitations in circuits
7083
- ledgerTypeLimits: LEDGER_TYPE_LIMITS,
7620
+ // Ledger type limitations - VALIDATED against indexed docs
7621
+ ledgerTypeLimits: validatedLedgerTypeLimits,
7084
7622
  // Common compilation errors with fixes
7085
7623
  commonErrors: COMMON_ERRORS,
7086
7624
  // Common mistakes that cause compilation failures
@@ -7122,7 +7660,7 @@ export circuit increment(): [] {
7122
7660
  },
7123
7661
  {
7124
7662
  wrong: "counter.value()",
7125
- correct: "// Read via witness or TypeScript SDK",
7663
+ correct: "counter.read() // Returns Uint<64>",
7126
7664
  error: 'operation "value" undefined for Counter'
7127
7665
  },
7128
7666
  {
@@ -7187,7 +7725,7 @@ Use quickStartTemplate as your base. Check commonMistakes BEFORE submitting code
7187
7725
 
7188
7726
  KEY RULES:
7189
7727
  1. public_key() is NOT a builtin - use persistentHash pattern
7190
- 2. Counter.value() NOT available in circuits - use witnesses
7728
+ 2. Counter uses .read() not .value() - all ADT methods work in circuits
7191
7729
  3. Map.lookup()/Set.member() ARE available in circuits (verified)
7192
7730
  4. Arithmetic results need casting: (a + b) as Uint<64>
7193
7731
  5. Uint\u2192Bytes needs two casts: (amount as Field) as Bytes<32>
@@ -9576,4 +10114,4 @@ export {
9576
10114
  startServer,
9577
10115
  startHttpServer
9578
10116
  };
9579
- //# sourceMappingURL=chunk-QPQOD3S6.js.map
10117
+ //# sourceMappingURL=chunk-7KJSVMFI.js.map