midnight-mcp 0.2.9 → 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-WZVWUFLH.js";
28
+ } from "./chunk-C23TNY65.js";
29
29
 
30
30
  // src/tools/search/schemas.ts
31
31
  import { z } from "zod";
@@ -786,11 +786,11 @@ async function analyzeContract(input) {
786
786
  });
787
787
  }
788
788
  }
789
- if (!parsed.imports.includes("std")) {
789
+ if (!parsed.imports.includes("CompactStandardLibrary")) {
790
790
  findings.push({
791
791
  severity: "info",
792
792
  message: "Standard library not imported",
793
- suggestion: `Consider adding 'include "std";' for common utilities`
793
+ suggestion: "Consider adding 'import CompactStandardLibrary;' for common utilities"
794
794
  });
795
795
  }
796
796
  }
@@ -1630,13 +1630,15 @@ Always import the standard library:
1630
1630
  import CompactStandardLibrary;
1631
1631
  \`\`\`
1632
1632
 
1633
- For multi-file contracts, use \`include\`:
1633
+ For modular code, use module imports (not a separate \`include\` directive):
1634
1634
  \`\`\`compact
1635
- include "types";
1636
- include "ledger";
1637
- include "circuits";
1635
+ import "path/to/module";
1636
+ import { SomeType } from "other/module";
1638
1637
  \`\`\`
1639
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
+
1640
1642
  ---
1641
1643
 
1642
1644
  ## 3. Ledger Declarations
@@ -1681,6 +1683,14 @@ ledger privateData: Field; // Private, not exported
1681
1683
  | \`Uint<N>\` | Unsigned integer (N = 8, 16, 32, 64, 128, 256) | \`balance: Uint<64>\` |
1682
1684
  | \`Uint<MIN..MAX>\` | Bounded unsigned integer | \`score: Uint<0..100>\` |
1683
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
+
1684
1694
  ### Collection Types
1685
1695
  | Type | Description | Example |
1686
1696
  |------|-------------|---------|
@@ -1913,41 +1923,60 @@ export circuit check_broken(guess: Field): Boolean {
1913
1923
 
1914
1924
  ### Counter Operations
1915
1925
  \`\`\`compact
1916
- // These work in circuits:
1917
- counter.increment(1);
1918
- counter.decrement(1);
1919
- counter.resetToDefault();
1920
-
1921
- // \u26A0\uFE0F DOES NOT WORK IN CIRCUITS:
1922
- // const val = counter.value(); // ERROR: operation undefined
1923
- // 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()
1924
1934
  \`\`\`
1925
1935
 
1926
1936
  ### Map Operations
1927
1937
  \`\`\`compact
1928
- // These work in circuits:
1929
- balances.insert(address, 100);
1930
- balances.remove(address);
1931
-
1932
- // \u26A0\uFE0F DOES NOT WORK IN CIRCUITS:
1933
- // const balance = balances.lookup(address); // ERROR
1934
- // const exists = balances.member(address); // ERROR
1935
- // Instead, use witnesses to read values:
1936
- 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): []
1937
1956
  \`\`\`
1938
1957
 
1958
+ **TypeScript-only:** \`[Symbol.iterator]()\` for iteration - not available in circuits.
1959
+
1939
1960
  ### Set Operations
1940
1961
  \`\`\`compact
1941
- // These work in circuits:
1942
- members.insert(address);
1943
- members.remove(address);
1944
-
1945
- // \u26A0\uFE0F DOES NOT WORK IN CIRCUITS:
1946
- // const isMember = members.member(address); // ERROR
1947
- // Use witness instead:
1948
- 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): []
1949
1976
  \`\`\`
1950
1977
 
1978
+ **TypeScript-only:** \`[Symbol.iterator]()\` for iteration - not available in circuits.
1979
+
1951
1980
  ### Maybe Operations
1952
1981
  \`\`\`compact
1953
1982
  const opt: Maybe<Field> = some<Field>(42);
@@ -2162,9 +2191,13 @@ npm install @openzeppelin/compact-contracts
2162
2191
  ## Usage Example
2163
2192
 
2164
2193
  \`\`\`compact
2165
- include "std";
2166
- include "@openzeppelin/compact-contracts/token/FungibleToken.compact";
2167
- 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_;
2168
2201
 
2169
2202
  ledger {
2170
2203
  // Inherit from OpenZeppelin contracts
@@ -2199,8 +2232,11 @@ The recommended standard for privacy-preserving tokens on Midnight.
2199
2232
  ## Basic Usage
2200
2233
 
2201
2234
  \`\`\`compact
2202
- include "std";
2203
- 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_;
2204
2240
 
2205
2241
  ledger {
2206
2242
  ...FungibleToken.ledger;
@@ -2241,7 +2277,8 @@ export circuit revealBalance(): Field {
2241
2277
  ## Minting and Burning
2242
2278
 
2243
2279
  \`\`\`compact
2244
- include "@openzeppelin/compact-contracts/access/Ownable.compact";
2280
+ import "@openzeppelin/compact-contracts/src/access/Ownable"
2281
+ prefix Ownable_;
2245
2282
 
2246
2283
  ledger {
2247
2284
  ...FungibleToken.ledger;
@@ -2283,7 +2320,8 @@ Patterns for controlling who can call contract functions.
2283
2320
  Simple single-owner access control.
2284
2321
 
2285
2322
  \`\`\`compact
2286
- include "@openzeppelin/compact-contracts/access/Ownable.compact";
2323
+ import "@openzeppelin/compact-contracts/src/access/Ownable"
2324
+ prefix Ownable_;
2287
2325
 
2288
2326
  ledger {
2289
2327
  ...Ownable.ledger;
@@ -2309,7 +2347,8 @@ export circuit transferOwnership(newOwner: Address): Void {
2309
2347
  For more complex permission systems.
2310
2348
 
2311
2349
  \`\`\`compact
2312
- include "@openzeppelin/compact-contracts/access/AccessControl.compact";
2350
+ import "@openzeppelin/compact-contracts/src/access/AccessControl"
2351
+ prefix AccessControl_;
2313
2352
 
2314
2353
  ledger {
2315
2354
  ...AccessControl.ledger;
@@ -2337,8 +2376,10 @@ export circuit grantMinterRole(account: Address): Void {
2337
2376
  ## Combining Patterns
2338
2377
 
2339
2378
  \`\`\`compact
2340
- include "@openzeppelin/compact-contracts/access/Ownable.compact";
2341
- 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_;
2342
2383
 
2343
2384
  ledger {
2344
2385
  ...Ownable.ledger;
@@ -2366,8 +2407,10 @@ Security utilities for Compact contracts.
2366
2407
  Emergency stop mechanism for contracts.
2367
2408
 
2368
2409
  \`\`\`compact
2369
- include "@openzeppelin/compact-contracts/security/Pausable.compact";
2370
- 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_;
2371
2414
 
2372
2415
  ledger {
2373
2416
  ...Pausable.ledger;
@@ -2773,134 +2816,131 @@ var EMBEDDED_CODE = {
2773
2816
  "midnight://code/examples/counter": `// Counter Example Contract
2774
2817
  // A simple contract demonstrating basic Compact concepts
2775
2818
 
2776
- include "std";
2819
+ pragma language_version >= 0.16 && <= 0.18;
2777
2820
 
2778
- ledger {
2779
- // Public counter - visible to everyone
2780
- counter: Counter;
2781
-
2782
- // Track last modifier (public)
2783
- lastModifier: Opaque<"address">;
2784
- }
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">;
2785
2828
 
2786
2829
  // Increment the counter
2787
- export circuit increment(amount: Field): Field {
2830
+ export circuit increment(amount: Uint<16>): Uint<64> {
2788
2831
  // Validate input
2789
- assert(amount > 0, "Amount must be positive");
2790
- 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");
2791
2834
 
2792
2835
  // Update counter
2793
- ledger.counter.increment(amount);
2836
+ counter.increment(amount);
2794
2837
 
2795
2838
  // Return new value
2796
- return ledger.counter.value();
2839
+ return counter.read();
2797
2840
  }
2798
2841
 
2799
2842
  // Decrement the counter
2800
- export circuit decrement(amount: Field): Field {
2843
+ export circuit decrement(amount: Uint<16>): Uint<64> {
2801
2844
  // Validate input
2802
- assert(amount > 0, "Amount must be positive");
2803
- 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");
2804
2847
 
2805
2848
  // Update counter
2806
- ledger.counter.decrement(amount);
2849
+ counter.decrement(amount);
2807
2850
 
2808
2851
  // Return new value
2809
- return ledger.counter.value();
2852
+ return counter.read();
2810
2853
  }
2811
2854
 
2812
2855
  // Read current value (view function)
2813
- export circuit getValue(): Field {
2814
- 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);
2815
2863
  }
2816
2864
  `,
2817
2865
  "midnight://code/examples/bboard": `// Bulletin Board Example Contract
2818
2866
  // Demonstrates private messaging with selective disclosure
2819
2867
 
2820
- include "std";
2868
+ pragma language_version >= 0.16 && <= 0.18;
2821
2869
 
2822
- ledger {
2823
- // Public: message count and IDs
2824
- messageCount: Counter;
2825
- messageIds: Set<Field>;
2826
-
2827
- // Private: actual message contents
2828
- @private
2829
- messages: Map<Field, Opaque<"string">>;
2830
-
2831
- // Private: message authors
2832
- @private
2833
- authors: Map<Field, Opaque<"address">>;
2834
- }
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">;
2835
2884
 
2836
2885
  // Post a new message (content is private)
2837
- export circuit postMessage(content: Opaque<"string">, author: Opaque<"address">): Field {
2838
- // Generate unique message ID
2839
- 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();
2840
2889
 
2841
2890
  // Store message privately
2842
- ledger.messages.insert(messageId, content);
2843
- ledger.authors.insert(messageId, author);
2891
+ messages.insert(messageId as Field, content);
2892
+ authors.insert(messageId as Field, author);
2844
2893
 
2845
2894
  // Update public counters
2846
- ledger.messageCount.increment(1);
2847
- ledger.messageIds.add(messageId);
2895
+ messageCount.increment(1);
2896
+ messageIds.insert(messageId as Field);
2848
2897
 
2849
2898
  return messageId;
2850
2899
  }
2851
2900
 
2852
- // Witness to fetch message content
2853
- witness getMessageContent(id: Field): Opaque<"string"> {
2854
- return ledger.messages.get(id);
2855
- }
2856
-
2857
2901
  // Reveal a message publicly (owner's choice)
2858
2902
  export circuit revealMessage(id: Field): Opaque<"string"> {
2859
- assert(ledger.messageIds.contains(id), "Message not found");
2903
+ assert(messageIds.member(id), "Message not found");
2860
2904
 
2861
2905
  const content = getMessageContent(id);
2862
2906
  return disclose(content);
2863
2907
  }
2864
2908
 
2865
2909
  // Get total message count
2866
- export circuit getMessageCount(): Field {
2867
- return ledger.messageCount.value();
2910
+ export circuit getMessageCount(): Uint<64> {
2911
+ return messageCount.read();
2868
2912
  }
2869
2913
  `,
2870
2914
  "midnight://code/patterns/state-management": `// State Management Pattern
2871
2915
  // Best practices for managing public and private state
2872
2916
 
2873
- include "std";
2917
+ pragma language_version >= 0.16 && <= 0.18;
2874
2918
 
2875
- ledger {
2876
- // PUBLIC STATE
2877
- // - Use for data that should be transparent
2878
- // - Visible in blockchain explorers
2879
- // - Can be queried by anyone
2880
-
2881
- totalSupply: Counter;
2882
- publicConfig: Field;
2883
-
2884
- // PRIVATE STATE
2885
- // - Use for sensitive user data
2886
- // - Only owner can read
2887
- // - Requires witnesses to access in circuits
2888
-
2889
- @private
2890
- userSecrets: Map<Opaque<"address">, Bytes<32>>;
2891
-
2892
- @private
2893
- privateBalances: Map<Opaque<"address">, Field>;
2894
- }
2919
+ import CompactStandardLibrary;
2895
2920
 
2896
- // Reading public state is straightforward
2897
- export circuit getTotalSupply(): Field {
2898
- return ledger.totalSupply.value();
2899
- }
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
2900
2933
 
2901
- // Reading private state requires a witness
2902
- witness getUserSecret(user: Opaque<"address">): Bytes<32> {
2903
- 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;
2904
2944
  }
2905
2945
 
2906
2946
  // Using private state in a circuit
@@ -2911,7 +2951,7 @@ export circuit proveSecretKnowledge(
2911
2951
  const secret = getUserSecret(user);
2912
2952
 
2913
2953
  // Prove knowledge without revealing secret
2914
- assert(hash(secret) == secretHash);
2954
+ assert(persistentHash(secret) == secretHash, "Invalid secret");
2915
2955
  return true;
2916
2956
  }
2917
2957
 
@@ -2921,53 +2961,45 @@ export circuit revealBalance(user: Opaque<"address">): Field {
2921
2961
  // Explicitly reveal - user's choice
2922
2962
  return disclose(balance);
2923
2963
  }
2924
-
2925
- witness getPrivateBalance(user: Opaque<"address">): Field {
2926
- return ledger.privateBalances.get(user);
2927
- }
2928
2964
  `,
2929
2965
  "midnight://code/patterns/access-control": `// Access Control Pattern
2930
2966
  // Implementing permissions and authorization
2931
2967
 
2932
- include "std";
2968
+ pragma language_version >= 0.16 && <= 0.18;
2969
+
2970
+ import CompactStandardLibrary;
2933
2971
 
2934
- ledger {
2935
- // Role definitions
2936
- owner: Opaque<"address">;
2937
- admins: Set<Opaque<"address">>;
2938
-
2939
- // Access-controlled state
2940
- sensitiveData: Field;
2941
-
2942
- @private
2943
- adminKeys: Map<Opaque<"address">, Bytes<32>>;
2944
- }
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>>;
2945
2981
 
2946
2982
  // Witness to get caller identity
2947
- witness getCaller(): Opaque<"address"> {
2948
- return getCurrentCaller();
2949
- }
2983
+ witness getCaller(): Opaque<"address">;
2950
2984
 
2951
2985
  // Only owner can call
2952
- export circuit onlyOwnerAction(newValue: Field): Void {
2986
+ export circuit onlyOwnerAction(newValue: Field): [] {
2953
2987
  const caller = getCaller();
2954
- assert(caller == ledger.owner, "Not owner");
2988
+ assert(caller == owner, "Not owner");
2955
2989
 
2956
- ledger.sensitiveData = newValue;
2990
+ sensitiveData = newValue;
2957
2991
  }
2958
2992
 
2959
2993
  // Only admins can call
2960
- export circuit onlyAdminAction(data: Field): Void {
2994
+ export circuit onlyAdminAction(data: Field): [] {
2961
2995
  const caller = getCaller();
2962
- assert(ledger.admins.contains(caller), "Not admin");
2996
+ assert(admins.member(caller), "Not admin");
2963
2997
 
2964
2998
  // Admin action here
2965
2999
  }
2966
3000
 
2967
3001
  // Multi-sig pattern (require multiple approvals)
2968
- witness getApprovalCount(action: Bytes<32>): Field {
2969
- return countApprovals(action);
2970
- }
3002
+ witness getApprovalCount(action: Bytes<32>): Field;
2971
3003
 
2972
3004
  export circuit requireMultisig(action: Bytes<32>, threshold: Field): Boolean {
2973
3005
  const approvals = getApprovalCount(action);
@@ -2976,11 +3008,9 @@ export circuit requireMultisig(action: Bytes<32>, threshold: Field): Boolean {
2976
3008
  }
2977
3009
 
2978
3010
  // Time-locked action
2979
- witness getCurrentTime(): Field {
2980
- return getBlockTimestamp();
2981
- }
3011
+ witness getCurrentTime(): Field;
2982
3012
 
2983
- export circuit timeLockedAction(unlockTime: Field): Void {
3013
+ export circuit timeLockedAction(unlockTime: Field): [] {
2984
3014
  const currentTime = getCurrentTime();
2985
3015
  assert(currentTime >= unlockTime, "Action is timelocked");
2986
3016
 
@@ -2990,21 +3020,23 @@ export circuit timeLockedAction(unlockTime: Field): Void {
2990
3020
  "midnight://code/patterns/privacy-preserving": `// Privacy-Preserving Patterns
2991
3021
  // Techniques for maintaining privacy in smart contracts
2992
3022
 
2993
- include "std";
3023
+ pragma language_version >= 0.16 && <= 0.18;
3024
+
3025
+ import CompactStandardLibrary;
2994
3026
 
2995
- ledger {
2996
- // Commitment-based private balance
2997
- balanceCommitments: Map<Opaque<"address">, Field>;
2998
-
2999
- // Nullifier set (prevents double-spending)
3000
- nullifiers: Set<Field>;
3001
-
3002
- @private
3003
- secretBalances: Map<Opaque<"address">, Field>;
3004
-
3005
- @private
3006
- secretNonces: Map<Opaque<"address">, Field>;
3007
- }
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;
3008
3040
 
3009
3041
  // PATTERN 1: Commitment Scheme
3010
3042
  // Store commitments instead of values
@@ -3014,11 +3046,14 @@ export circuit deposit(
3014
3046
  amount: Field,
3015
3047
  nonce: Field
3016
3048
  ): Field {
3017
- // Create commitment: hash(amount, nonce, user)
3018
- 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);
3019
3054
 
3020
3055
  // Store commitment (hides amount)
3021
- ledger.balanceCommitments.insert(user, commitment);
3056
+ balanceCommitments.insert(user, commitment);
3022
3057
 
3023
3058
  return commitment;
3024
3059
  }
@@ -3030,31 +3065,31 @@ export circuit proveBalance(
3030
3065
  minBalance: Field
3031
3066
  ): Boolean {
3032
3067
  // Verify commitment
3033
- const expectedCommitment = hash(amount, nonce, user);
3034
- 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");
3035
3072
 
3036
3073
  // Prove property without revealing value
3037
- assert(amount >= minBalance);
3074
+ assert(amount >= minBalance, "Insufficient balance");
3038
3075
  return true;
3039
3076
  }
3040
3077
 
3041
3078
  // PATTERN 2: Nullifiers (Prevent Double-Spending)
3042
3079
 
3043
- witness generateNullifier(secret: Bytes<32>, action: Field): Field {
3044
- return hash(secret, action);
3045
- }
3080
+ witness generateNullifier(secret: Bytes<32>, action: Field): Field;
3046
3081
 
3047
3082
  export circuit spendOnce(
3048
3083
  secret: Bytes<32>,
3049
3084
  action: Field
3050
- ): Void {
3085
+ ): [] {
3051
3086
  const nullifier = generateNullifier(secret, action);
3052
3087
 
3053
3088
  // Check nullifier hasn't been used
3054
- assert(!ledger.nullifiers.contains(nullifier), "Already spent");
3089
+ assert(!nullifiers.member(nullifier), "Already spent");
3055
3090
 
3056
3091
  // Mark as used
3057
- ledger.nullifiers.add(nullifier);
3092
+ nullifiers.insert(nullifier);
3058
3093
 
3059
3094
  // Perform action
3060
3095
  }
@@ -3062,72 +3097,55 @@ export circuit spendOnce(
3062
3097
  // PATTERN 3: Range Proofs
3063
3098
 
3064
3099
  export circuit proveInRange(
3065
- @private value: Field,
3100
+ value: Field,
3066
3101
  min: Field,
3067
3102
  max: Field
3068
3103
  ): Boolean {
3069
3104
  // Prove value is in range without revealing it
3070
- assert(value >= min);
3071
- assert(value <= max);
3105
+ assert(value >= min, "Below minimum");
3106
+ assert(value <= max, "Above maximum");
3072
3107
  return true;
3073
3108
  }
3074
3109
 
3075
3110
  // PATTERN 4: Private Set Membership
3076
3111
 
3112
+ witness computeMerkleRoot(element: Field, proof: Vector<10, Field>): Field;
3113
+
3077
3114
  export circuit proveMembership(
3078
- @private element: Field,
3115
+ element: Field,
3079
3116
  setRoot: Field,
3080
- @private proof: Array<Field>
3117
+ proof: Vector<10, Field>
3081
3118
  ): Boolean {
3082
3119
  // Prove element is in set without revealing which element
3083
3120
  const computedRoot = computeMerkleRoot(element, proof);
3084
- assert(computedRoot == setRoot);
3121
+ assert(computedRoot == setRoot, "Invalid membership proof");
3085
3122
  return true;
3086
3123
  }
3087
-
3088
- witness computeMerkleRoot(element: Field, proof: Array<Field>): Field {
3089
- // Compute Merkle root from element and proof
3090
- return merkleCompute(element, proof);
3091
- }
3092
3124
  `,
3093
3125
  "midnight://code/templates/token": `// Privacy-Preserving Token Template
3094
3126
  // Starter template for token contracts with privacy features
3095
3127
 
3096
- include "std";
3128
+ pragma language_version >= 0.16 && <= 0.18;
3097
3129
 
3098
- ledger {
3099
- // Public token metadata
3100
- name: Opaque<"string">;
3101
- symbol: Opaque<"string">;
3102
- decimals: Field;
3103
- totalSupply: Counter;
3104
-
3105
- // Private balances
3106
- @private
3107
- balances: Map<Opaque<"address">, Field>;
3108
-
3109
- // Private allowances
3110
- @private
3111
- allowances: Map<Opaque<"address">, Map<Opaque<"address">, Field>>;
3112
- }
3130
+ import CompactStandardLibrary;
3113
3131
 
3114
- // Witnesses for private state access
3115
- witness getBalance(account: Opaque<"address">): Field {
3116
- return ledger.balances.get(account) ?? 0;
3117
- }
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>;
3118
3137
 
3119
- witness getAllowance(owner: Opaque<"address">, spender: Opaque<"address">): Field {
3120
- return ledger.allowances.get(owner)?.get(spender) ?? 0;
3121
- }
3138
+ // Private balances
3139
+ ledger balances: Map<Opaque<"address">, Uint<64>>;
3122
3140
 
3123
- witness getCaller(): Opaque<"address"> {
3124
- return getCurrentCaller();
3125
- }
3141
+ // Witnesses for private state access
3142
+ witness getBalance(account: Opaque<"address">): Uint<64>;
3143
+ witness getCaller(): Opaque<"address">;
3126
3144
 
3127
3145
  // Transfer tokens privately
3128
3146
  export circuit transfer(
3129
3147
  to: Opaque<"address">,
3130
- amount: Field
3148
+ amount: Uint<64>
3131
3149
  ): Boolean {
3132
3150
  const from = getCaller();
3133
3151
  const fromBalance = getBalance(from);
@@ -3137,224 +3155,153 @@ export circuit transfer(
3137
3155
  assert(fromBalance >= amount, "Insufficient balance");
3138
3156
 
3139
3157
  // Update balances privately
3140
- ledger.balances.insert(from, fromBalance - amount);
3141
- ledger.balances.insert(to, getBalance(to) + amount);
3142
-
3143
- return true;
3144
- }
3145
-
3146
- // Approve spender
3147
- export circuit approve(
3148
- spender: Opaque<"address">,
3149
- amount: Field
3150
- ): Boolean {
3151
- const owner = getCaller();
3152
-
3153
- // Get or create allowance map for owner
3154
- // Note: Simplified - actual implementation needs nested map handling
3155
- ledger.allowances.get(owner).insert(spender, amount);
3156
-
3157
- return true;
3158
- }
3159
-
3160
- // Transfer from approved account
3161
- export circuit transferFrom(
3162
- from: Opaque<"address">,
3163
- to: Opaque<"address">,
3164
- amount: Field
3165
- ): Boolean {
3166
- const spender = getCaller();
3167
- const allowance = getAllowance(from, spender);
3168
- const fromBalance = getBalance(from);
3169
-
3170
- // Validate
3171
- assert(amount > 0, "Invalid amount");
3172
- assert(allowance >= amount, "Insufficient allowance");
3173
- assert(fromBalance >= amount, "Insufficient balance");
3174
-
3175
- // Update state
3176
- ledger.balances.insert(from, fromBalance - amount);
3177
- ledger.balances.insert(to, getBalance(to) + amount);
3178
- ledger.allowances.get(from).insert(spender, allowance - amount);
3158
+ balances.insert(from, fromBalance - amount);
3159
+ balances.insert(to, getBalance(to) + amount);
3179
3160
 
3180
3161
  return true;
3181
3162
  }
3182
3163
 
3183
3164
  // Reveal balance (user's choice)
3184
- export circuit revealMyBalance(): Field {
3165
+ export circuit revealMyBalance(): Uint<64> {
3185
3166
  const caller = getCaller();
3186
3167
  const balance = getBalance(caller);
3187
3168
  return disclose(balance);
3188
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
+ }
3189
3183
  `,
3190
3184
  "midnight://code/templates/voting": `// Private Voting Template
3191
3185
  // Starter template for privacy-preserving voting contracts
3192
3186
 
3193
- include "std";
3187
+ pragma language_version >= 0.16 && <= 0.18;
3194
3188
 
3195
- ledger {
3196
- // Public: proposal metadata
3197
- proposalCount: Counter;
3198
- proposals: Map<Field, Opaque<"string">>;
3199
- votingDeadlines: Map<Field, Field>;
3200
-
3201
- // Public: vote tallies (revealed after voting ends)
3202
- finalTallies: Map<Field, Map<Field, Field>>; // proposalId -> optionId -> count
3203
-
3204
- // Private: individual votes
3205
- @private
3206
- votes: Map<Field, Map<Opaque<"address">, Field>>; // proposalId -> voter -> option
3207
-
3208
- // Nullifiers to prevent double voting
3209
- voteNullifiers: Set<Field>;
3210
-
3211
- // Eligible voters
3212
- eligibleVoters: Set<Opaque<"address">>;
3213
- }
3189
+ import CompactStandardLibrary;
3214
3190
 
3215
- // Witnesses
3216
- witness getCaller(): Opaque<"address"> {
3217
- return getCurrentCaller();
3218
- }
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>>;
3219
3195
 
3220
- witness getCurrentTime(): Field {
3221
- return getBlockTimestamp();
3222
- }
3196
+ // Private: individual votes
3197
+ ledger votes: Map<Uint<64>, Map<Opaque<"address">, Uint<8>>>;
3223
3198
 
3224
- witness getVote(proposalId: Field, voter: Opaque<"address">): Field {
3225
- return ledger.votes.get(proposalId)?.get(voter) ?? 0;
3226
- }
3199
+ // Nullifiers to prevent double voting
3200
+ export ledger voteNullifiers: Set<Bytes<32>>;
3227
3201
 
3228
- witness computeNullifier(voter: Opaque<"address">, proposalId: Field): Field {
3229
- return hash(voter, proposalId);
3230
- }
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>;
3231
3210
 
3232
3211
  // Create a new proposal
3233
3212
  export circuit createProposal(
3234
- description: Opaque<"string">,
3235
- deadline: Field,
3236
- options: Field
3237
- ): Field {
3238
- const proposalId = ledger.proposalCount.value();
3239
-
3240
- // Store proposal
3241
- ledger.proposals.insert(proposalId, description);
3242
- ledger.votingDeadlines.insert(proposalId, deadline);
3213
+ description: Bytes<256>,
3214
+ deadline: Uint<64>
3215
+ ): Uint<64> {
3216
+ const proposalId = proposalCount.read();
3243
3217
 
3244
- // Initialize tally for each option
3245
- // (Simplified - actual implementation needs loop)
3218
+ // Store proposal - proposalId is Uint<64> from Counter.read()
3219
+ proposals.insert(proposalId, description);
3220
+ votingDeadlines.insert(proposalId, deadline);
3246
3221
 
3247
- ledger.proposalCount.increment(1);
3222
+ proposalCount.increment(1);
3248
3223
  return proposalId;
3249
3224
  }
3250
3225
 
3251
3226
  // Cast a private vote
3252
3227
  export circuit vote(
3253
- proposalId: Field,
3254
- option: Field
3228
+ proposalId: Uint<64>,
3229
+ option: Uint<8>
3255
3230
  ): Boolean {
3256
3231
  const voter = getCaller();
3257
3232
  const currentTime = getCurrentTime();
3258
3233
 
3259
3234
  // Check eligibility
3260
- assert(ledger.eligibleVoters.contains(voter), "Not eligible to vote");
3235
+ assert(eligibleVoters.member(voter), "Not eligible to vote");
3261
3236
 
3262
3237
  // Check deadline
3263
- const deadline = ledger.votingDeadlines.get(proposalId);
3238
+ const deadline = votingDeadlines.lookup(proposalId);
3264
3239
  assert(currentTime < deadline, "Voting ended");
3265
3240
 
3266
3241
  // Check for double voting using nullifier
3267
3242
  const nullifier = computeNullifier(voter, proposalId);
3268
- assert(!ledger.voteNullifiers.contains(nullifier), "Already voted");
3269
-
3270
- // Record vote privately
3271
- ledger.votes.get(proposalId).insert(voter, option);
3243
+ assert(!voteNullifiers.member(nullifier), "Already voted");
3272
3244
 
3273
3245
  // Add nullifier to prevent double voting
3274
- ledger.voteNullifiers.add(nullifier);
3246
+ voteNullifiers.insert(nullifier);
3275
3247
 
3276
3248
  return true;
3277
3249
  }
3278
3250
 
3279
3251
  // Reveal individual vote (voter's choice)
3280
- export circuit revealMyVote(proposalId: Field): Field {
3252
+ export circuit revealMyVote(proposalId: Uint<32>): Uint<8> {
3281
3253
  const voter = getCaller();
3282
3254
  const myVote = getVote(proposalId, voter);
3283
3255
  return disclose(myVote);
3284
3256
  }
3285
3257
 
3286
- // Tally votes (after deadline)
3287
- // Note: This is simplified - real implementation would need
3288
- // a mechanism to privately aggregate votes
3289
- export circuit tallyVotes(proposalId: Field): Boolean {
3290
- const currentTime = getCurrentTime();
3291
- const deadline = ledger.votingDeadlines.get(proposalId);
3292
-
3293
- assert(currentTime >= deadline, "Voting still active");
3294
-
3295
- // In a real implementation, votes would be aggregated
3296
- // using homomorphic encryption or MPC
3297
-
3298
- return true;
3299
- }
3300
-
3301
3258
  // Add eligible voter (admin only)
3302
- export circuit addVoter(voter: Opaque<"address">): Void {
3259
+ export circuit addVoter(voter: Opaque<"address">): [] {
3303
3260
  // Add access control in real implementation
3304
- ledger.eligibleVoters.add(voter);
3261
+ eligibleVoters.insert(voter);
3305
3262
  }
3306
3263
  `,
3307
3264
  "midnight://code/examples/nullifier": `// Nullifier Pattern Example
3308
3265
  // Demonstrates how to create and use nullifiers to prevent double-spending/actions
3309
3266
 
3310
- include "std";
3267
+ pragma language_version >= 0.16 && <= 0.18;
3268
+
3269
+ import CompactStandardLibrary;
3311
3270
 
3312
- ledger {
3313
- // Set of used nullifiers - prevents replay attacks
3314
- usedNullifiers: Set<Bytes<32>>;
3315
-
3316
- // Track claimed rewards
3317
- claimedRewards: Counter;
3318
- }
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;
3319
3276
 
3320
3277
  // Hash function for creating nullifiers
3321
3278
  // Combines secret + public data to create unique identifier
3322
- witness computeNullifier(secret: Field, commitment: Field): Bytes<32> {
3323
- // Hash the secret with the commitment
3324
- // The nullifier reveals nothing about the secret
3325
- // but is unique per secret+commitment pair
3326
- return hash(secret, commitment);
3327
- }
3279
+ witness computeNullifier(secret: Field, commitment: Field): Bytes<32>;
3328
3280
 
3329
3281
  // Alternative: nullifier from address and action ID
3330
- witness computeActionNullifier(
3331
- userSecret: Field,
3332
- actionId: Field
3333
- ): Bytes<32> {
3334
- // Create nullifier: hash(secret || actionId)
3335
- return hash(userSecret, actionId);
3336
- }
3282
+ witness computeActionNullifier(userSecret: Field, actionId: Field): Bytes<32>;
3337
3283
 
3338
3284
  // Claim a reward (can only claim once per user)
3285
+ // Note: rewardAmount is Uint<16> to match Counter.increment signature
3339
3286
  export circuit claimReward(
3340
3287
  secret: Field,
3341
3288
  commitment: Field,
3342
- rewardAmount: Field
3289
+ rewardAmount: Uint<16>
3343
3290
  ): Boolean {
3344
3291
  // Compute the nullifier
3345
3292
  const nullifier = computeNullifier(secret, commitment);
3346
3293
 
3347
3294
  // Check nullifier hasn't been used (prevents double-claim)
3348
3295
  assert(
3349
- !ledger.usedNullifiers.contains(nullifier),
3296
+ !usedNullifiers.member(nullifier),
3350
3297
  "Reward already claimed"
3351
3298
  );
3352
3299
 
3353
3300
  // Mark nullifier as used
3354
- ledger.usedNullifiers.add(nullifier);
3301
+ usedNullifiers.insert(nullifier);
3355
3302
 
3356
- // Process reward
3357
- ledger.claimedRewards.increment(rewardAmount);
3303
+ // Process reward (Counter.increment takes Uint<16>)
3304
+ claimedRewards.increment(rewardAmount);
3358
3305
 
3359
3306
  return true;
3360
3307
  }
@@ -3370,12 +3317,12 @@ export circuit voteWithNullifier(
3370
3317
 
3371
3318
  // Ensure hasn't voted on this proposal
3372
3319
  assert(
3373
- !ledger.usedNullifiers.contains(nullifier),
3320
+ !usedNullifiers.member(nullifier),
3374
3321
  "Already voted on this proposal"
3375
3322
  );
3376
3323
 
3377
3324
  // Record nullifier
3378
- ledger.usedNullifiers.add(nullifier);
3325
+ usedNullifiers.insert(nullifier);
3379
3326
 
3380
3327
  // Process vote...
3381
3328
  return true;
@@ -3384,42 +3331,18 @@ export circuit voteWithNullifier(
3384
3331
  "midnight://code/examples/hash": `// Hash Functions in Compact
3385
3332
  // Examples of using hash functions for various purposes
3386
3333
 
3387
- include "std";
3334
+ pragma language_version >= 0.16 && <= 0.18;
3388
3335
 
3389
- ledger {
3390
- commitments: Set<Bytes<32>>;
3391
- hashedData: Map<Field, Bytes<32>>;
3392
- }
3336
+ import CompactStandardLibrary;
3393
3337
 
3394
- // Basic hash function usage
3395
- witness simpleHash(data: Field): Bytes<32> {
3396
- // Hash a single field element
3397
- return hash(data);
3398
- }
3338
+ export ledger commitments: Set<Bytes<32>>;
3339
+ export ledger hashedData: Map<Field, Bytes<32>>;
3399
3340
 
3400
- // Hash multiple values together
3401
- witness hashMultiple(a: Field, b: Field, c: Field): Bytes<32> {
3402
- // Concatenate and hash
3403
- return hash(a, b, c);
3404
- }
3341
+ // Basic hash function usage
3342
+ witness simpleHash(data: Field): Bytes<32>;
3405
3343
 
3406
3344
  // Create a commitment (hash of value + randomness)
3407
- witness createCommitment(value: Field, randomness: Field): Bytes<32> {
3408
- // Pedersen-style commitment: H(value || randomness)
3409
- return hash(value, randomness);
3410
- }
3411
-
3412
- // Hash bytes data
3413
- witness hashBytes(data: Bytes<64>): Bytes<32> {
3414
- return hash(data);
3415
- }
3416
-
3417
- // Create nullifier from secret
3418
- witness createNullifier(secret: Field, publicInput: Field): Bytes<32> {
3419
- // Nullifier = H(secret || publicInput)
3420
- // Reveals nothing about secret, but is deterministic
3421
- return hash(secret, publicInput);
3422
- }
3345
+ witness createCommitment(value: Field, randomness: Field): Bytes<32>;
3423
3346
 
3424
3347
  // Verify a commitment matches
3425
3348
  export circuit verifyCommitment(
@@ -3435,108 +3358,105 @@ export circuit verifyCommitment(
3435
3358
  // Store a hashed value
3436
3359
  export circuit storeHashed(id: Field, data: Field): Bytes<32> {
3437
3360
  const hashed = simpleHash(data);
3438
- ledger.hashedData.insert(id, hashed);
3361
+ hashedData.insert(id, hashed);
3439
3362
  return hashed;
3440
3363
  }
3441
3364
 
3442
3365
  // Commit-reveal pattern
3443
3366
  export circuit commit(commitment: Bytes<32>): Boolean {
3444
- assert(!ledger.commitments.contains(commitment), "Already committed");
3445
- ledger.commitments.add(commitment);
3367
+ assert(!commitments.member(commitment), "Already committed");
3368
+ commitments.insert(commitment);
3446
3369
  return true;
3447
3370
  }
3448
3371
 
3449
3372
  export circuit reveal(value: Field, randomness: Field): Field {
3450
3373
  const commitment = createCommitment(value, randomness);
3451
- assert(ledger.commitments.contains(commitment), "No matching commitment");
3374
+ assert(commitments.member(commitment), "No matching commitment");
3452
3375
  return disclose(value);
3453
3376
  }
3454
3377
  `,
3455
3378
  "midnight://code/examples/simple-counter": `// Simple Counter Contract
3456
3379
  // Minimal example for learning Compact basics
3457
3380
 
3458
- include "std";
3381
+ pragma language_version >= 0.16 && <= 0.18;
3382
+
3383
+ import CompactStandardLibrary;
3459
3384
 
3460
3385
  // Ledger state - stored on chain
3461
- ledger {
3462
- counter: Counter;
3463
- }
3386
+ export ledger counter: Counter;
3464
3387
 
3465
3388
  // Increment the counter by 1
3466
- export circuit increment(): Field {
3467
- ledger.counter.increment(1);
3468
- return ledger.counter.value();
3389
+ export circuit increment(): Uint<64> {
3390
+ counter.increment(1);
3391
+ return counter.read();
3469
3392
  }
3470
3393
 
3471
3394
  // Decrement the counter by 1
3472
- export circuit decrement(): Field {
3473
- assert(ledger.counter.value() > 0, "Cannot go below zero");
3474
- ledger.counter.decrement(1);
3475
- 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();
3476
3399
  }
3477
3400
 
3478
3401
  // Get current value
3479
- export circuit get(): Field {
3480
- 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);
3481
3409
  }
3482
3410
 
3483
- // Reset to zero (add access control in real apps)
3484
- export circuit reset(): Void {
3485
- const current = ledger.counter.value();
3486
- ledger.counter.decrement(current);
3411
+ // Reset to zero
3412
+ export circuit reset(): [] {
3413
+ counter.resetToDefault();
3487
3414
  }
3488
3415
  `,
3489
3416
  "midnight://code/templates/basic": `// Basic Compact Contract Template
3490
3417
  // Starting point for new contracts
3491
3418
 
3492
- include "std";
3419
+ pragma language_version >= 0.16 && <= 0.18;
3420
+
3421
+ import CompactStandardLibrary;
3493
3422
 
3494
3423
  // ============================================
3495
3424
  // LEDGER STATE
3496
3425
  // ============================================
3497
3426
 
3498
- ledger {
3499
- // Public state (visible on-chain)
3500
- initialized: Boolean;
3501
- owner: Opaque<"address">;
3502
-
3503
- // Private state (only owner can see)
3504
- @private
3505
- secretData: Field;
3506
- }
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;
3507
3440
 
3508
3441
  // ============================================
3509
3442
  // INITIALIZATION
3510
3443
  // ============================================
3511
3444
 
3512
3445
  export circuit initialize(ownerAddress: Opaque<"address">): Boolean {
3513
- assert(!ledger.initialized, "Already initialized");
3446
+ assert(!initialized, "Already initialized");
3514
3447
 
3515
- ledger.owner = ownerAddress;
3516
- ledger.initialized = true;
3448
+ owner = ownerAddress;
3449
+ initialized = true;
3517
3450
 
3518
3451
  return true;
3519
3452
  }
3520
3453
 
3521
- // ============================================
3522
- // ACCESS CONTROL
3523
- // ============================================
3524
-
3525
- witness getCaller(): Opaque<"address"> {
3526
- // Returns the transaction sender
3527
- return context.caller;
3528
- }
3529
-
3530
- witness isOwner(): Boolean {
3531
- return getCaller() == ledger.owner;
3532
- }
3533
-
3534
3454
  // ============================================
3535
3455
  // PUBLIC FUNCTIONS
3536
3456
  // ============================================
3537
3457
 
3538
3458
  export circuit publicFunction(input: Field): Field {
3539
- assert(ledger.initialized, "Not initialized");
3459
+ assert(initialized, "Not initialized");
3540
3460
 
3541
3461
  // Your logic here
3542
3462
  return input * 2;
@@ -3546,22 +3466,19 @@ export circuit publicFunction(input: Field): Field {
3546
3466
  // OWNER-ONLY FUNCTIONS
3547
3467
  // ============================================
3548
3468
 
3549
- export circuit setSecret(newSecret: Field): Void {
3550
- assert(isOwner(), "Only owner can set secret");
3551
- 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;
3552
3473
  }
3553
3474
 
3554
3475
  // ============================================
3555
3476
  // PRIVATE DATA ACCESS
3556
3477
  // ============================================
3557
3478
 
3558
- witness getSecret(): Field {
3559
- assert(isOwner(), "Only owner can view");
3560
- return ledger.secretData;
3561
- }
3562
-
3563
3479
  export circuit revealSecret(): Field {
3564
- assert(isOwner(), "Only owner can reveal");
3480
+ const caller = getCaller();
3481
+ assert(caller == owner, "Only owner can reveal");
3565
3482
  return disclose(getSecret());
3566
3483
  }
3567
3484
  `
@@ -4671,10 +4588,11 @@ Key Compact concepts:
4671
4588
  - \`Field\`, \`Boolean\`, \`Uint<N>\`, \`Bytes<N>\` - Primitive types
4672
4589
 
4673
4590
  Always include:
4674
- 1. Proper imports (include "std")
4675
- 2. Clear ledger state definitions
4676
- 3. Access control where appropriate
4677
- 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
4678
4596
 
4679
4597
  Return ONLY the Compact code, no explanations.`;
4680
4598
  const userPrompt = options.baseExample ? `Based on this example contract:
@@ -4859,6 +4777,406 @@ ${code}
4859
4777
  }
4860
4778
  }
4861
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
+
4862
5180
  // src/server.ts
4863
5181
  var SERVER_INFO = {
4864
5182
  name: "midnight-mcp",
@@ -5725,17 +6043,30 @@ var TYPE_COMPATIBILITY = {
5725
6043
  var LEDGER_TYPE_LIMITS = {
5726
6044
  Counter: {
5727
6045
  circuitOperations: [
5728
- { 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
+ },
5729
6051
  {
5730
6052
  method: ".decrement(n)",
5731
6053
  works: true,
5732
- 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>)"
5733
6060
  },
5734
- { method: ".resetToDefault()", works: true, note: "Resets to 0" },
5735
- { 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" }
5736
6067
  ],
5737
6068
  typescriptAccess: "Access counter value via `ledgerState.counter` in TypeScript SDK",
5738
- 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()"
5739
6070
  },
5740
6071
  Map: {
5741
6072
  circuitOperations: [
@@ -5744,20 +6075,40 @@ var LEDGER_TYPE_LIMITS = {
5744
6075
  works: true,
5745
6076
  note: "Adds/updates entry"
5746
6077
  },
6078
+ {
6079
+ method: ".insertDefault(key)",
6080
+ works: true,
6081
+ note: "Inserts default value for key"
6082
+ },
5747
6083
  { method: ".remove(key)", works: true, note: "Removes entry" },
5748
6084
  {
5749
6085
  method: ".lookup(key)",
5750
6086
  works: true,
5751
- note: "Returns Option<ValueType> - use in circuits"
6087
+ note: "Returns value_type - gets value for key"
5752
6088
  },
5753
6089
  {
5754
6090
  method: ".member(key)",
5755
6091
  works: true,
5756
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"
5757
6108
  }
5758
6109
  ],
5759
- typescriptAccess: "Query map via `contractState.data.get(key)` in TypeScript SDK",
5760
- 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."
5761
6112
  },
5762
6113
  Set: {
5763
6114
  circuitOperations: [
@@ -5767,10 +6118,25 @@ var LEDGER_TYPE_LIMITS = {
5767
6118
  method: ".member(value)",
5768
6119
  works: true,
5769
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"
5770
6136
  }
5771
6137
  ],
5772
- typescriptAccess: "Check membership via `contractState.set.has(value)` in TypeScript SDK",
5773
- 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."
5774
6140
  },
5775
6141
  MerkleTree: {
5776
6142
  circuitOperations: [
@@ -5800,11 +6166,16 @@ Or use bounded Uint<0..N> for parameters that need constraints`
5800
6166
  },
5801
6167
  {
5802
6168
  error: 'operation "value" undefined for ledger field type Counter',
5803
- cause: "Trying to read Counter.value() inside a circuit",
5804
- fix: `Counter values cannot be read in circuits. Options:
5805
- 1. Use a witness: witness get_counter_value(): Uint<64>;
5806
- 2. Read from TypeScript SDK: ledgerState.counter
5807
- 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`
5808
6179
  },
5809
6180
  {
5810
6181
  error: "implicit disclosure of witness value",
@@ -6336,7 +6707,7 @@ async function extractContractStructure(input) {
6336
6707
  });
6337
6708
  }
6338
6709
  }
6339
- const hasStdlibImport = imports.includes("CompactStandardLibrary") || code.includes('include "std"');
6710
+ const hasStdlibImport = imports.includes("CompactStandardLibrary") || code.includes("import CompactStandardLibrary");
6340
6711
  if (hasStdlibImport) {
6341
6712
  for (const circuit of circuits) {
6342
6713
  if (stdlibExports.includes(circuit.name)) {
@@ -6447,7 +6818,7 @@ async function extractContractStructure(input) {
6447
6818
  });
6448
6819
  break;
6449
6820
  }
6450
- const counterValuePattern = /(\w+)\.value\b/g;
6821
+ const counterValuePattern = /(\w+)\.value\s*\(/g;
6451
6822
  let counterMatch;
6452
6823
  while ((counterMatch = counterValuePattern.exec(code)) !== null) {
6453
6824
  const varName = counterMatch[1];
@@ -6459,8 +6830,8 @@ async function extractContractStructure(input) {
6459
6830
  potentialIssues.push({
6460
6831
  type: "invalid_counter_access",
6461
6832
  line: lineNum,
6462
- message: `Counter type '${varName}' does not have a '.value' property`,
6463
- 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()`,
6464
6835
  severity: "error"
6465
6836
  });
6466
6837
  }
@@ -7106,6 +7477,96 @@ async function getLatestSyntax(input) {
7106
7477
  } catch {
7107
7478
  }
7108
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);
7109
7570
  return {
7110
7571
  repository: "midnightntwrk/compact",
7111
7572
  version: `${COMPACT_VERSION.min}-${COMPACT_VERSION.max} (current)`,
@@ -7116,6 +7577,29 @@ async function getLatestSyntax(input) {
7116
7577
  maintenanceGuide: "See docs/SYNTAX_MAINTENANCE.md for update instructions"
7117
7578
  },
7118
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
+ },
7119
7603
  // Quick start template - ALWAYS compiles
7120
7604
  quickStartTemplate: `${RECOMMENDED_PRAGMA}
7121
7605
 
@@ -7133,8 +7617,8 @@ export circuit increment(): [] {
7133
7617
  builtinFunctions: BUILTIN_FUNCTIONS,
7134
7618
  // Type compatibility rules
7135
7619
  typeCompatibility: TYPE_COMPATIBILITY,
7136
- // Ledger type limitations in circuits
7137
- ledgerTypeLimits: LEDGER_TYPE_LIMITS,
7620
+ // Ledger type limitations - VALIDATED against indexed docs
7621
+ ledgerTypeLimits: validatedLedgerTypeLimits,
7138
7622
  // Common compilation errors with fixes
7139
7623
  commonErrors: COMMON_ERRORS,
7140
7624
  // Common mistakes that cause compilation failures
@@ -7176,7 +7660,7 @@ export circuit increment(): [] {
7176
7660
  },
7177
7661
  {
7178
7662
  wrong: "counter.value()",
7179
- correct: "// Read via witness or TypeScript SDK",
7663
+ correct: "counter.read() // Returns Uint<64>",
7180
7664
  error: 'operation "value" undefined for Counter'
7181
7665
  },
7182
7666
  {
@@ -7241,7 +7725,7 @@ Use quickStartTemplate as your base. Check commonMistakes BEFORE submitting code
7241
7725
 
7242
7726
  KEY RULES:
7243
7727
  1. public_key() is NOT a builtin - use persistentHash pattern
7244
- 2. Counter.value() NOT available in circuits - use witnesses
7728
+ 2. Counter uses .read() not .value() - all ADT methods work in circuits
7245
7729
  3. Map.lookup()/Set.member() ARE available in circuits (verified)
7246
7730
  4. Arithmetic results need casting: (a + b) as Uint<64>
7247
7731
  5. Uint\u2192Bytes needs two casts: (amount as Field) as Bytes<32>
@@ -9630,4 +10114,4 @@ export {
9630
10114
  startServer,
9631
10115
  startHttpServer
9632
10116
  };
9633
- //# sourceMappingURL=chunk-5PGBYGCG.js.map
10117
+ //# sourceMappingURL=chunk-7KJSVMFI.js.map