midnight-mcp 0.1.20 → 0.1.22

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.
@@ -1294,6 +1294,129 @@ export async function extractContractStructure(input) {
1294
1294
  }
1295
1295
  }
1296
1296
  }
1297
+ // 6. Detect division operator usage (not supported in Compact)
1298
+ const divisionPattern = /[^/]\/[^/*]/g;
1299
+ let divMatch;
1300
+ while ((divMatch = divisionPattern.exec(code)) !== null) {
1301
+ // Skip if inside a comment
1302
+ const beforeDiv = code.substring(0, divMatch.index);
1303
+ const lastLineStart = beforeDiv.lastIndexOf("\n") + 1;
1304
+ const lineContent = beforeDiv.substring(lastLineStart);
1305
+ if (lineContent.includes("//"))
1306
+ continue;
1307
+ const lineNum = lineByIndex[divMatch.index] || 1;
1308
+ potentialIssues.push({
1309
+ type: "unsupported_division",
1310
+ line: lineNum,
1311
+ message: `Division operator '/' is not supported in Compact`,
1312
+ suggestion: `Use a witness-based division pattern: 'witness divideWithRemainder(a, b): [quotient, remainder]' with on-chain verification`,
1313
+ severity: "error",
1314
+ });
1315
+ break; // Only warn once
1316
+ }
1317
+ // 7. Detect Counter.value access (Counter only has .increment())
1318
+ const counterValuePattern = /(\w+)\.value\b/g;
1319
+ let counterMatch;
1320
+ while ((counterMatch = counterValuePattern.exec(code)) !== null) {
1321
+ const varName = counterMatch[1];
1322
+ // Check if this variable is a Counter type
1323
+ const counterLedger = ledgerItems.find((l) => l.name === varName && l.type === "Counter");
1324
+ if (counterLedger) {
1325
+ const lineNum = lineByIndex[counterMatch.index] || 1;
1326
+ potentialIssues.push({
1327
+ type: "invalid_counter_access",
1328
+ line: lineNum,
1329
+ message: `Counter type '${varName}' does not have a '.value' property`,
1330
+ suggestion: `Counter only has '.increment(n)'. Use 'Uint<32>' or 'Uint<64>' instead if you need to read the value`,
1331
+ severity: "error",
1332
+ });
1333
+ }
1334
+ }
1335
+ // 8. Detect potential Uint overflow in multiplication (suggest Field casting)
1336
+ const multiplyPattern = /(\w+)\s*\*\s*(\w+)(?:\s*\+\s*\w+)?\s*(?:as\s+Uint|==)/g;
1337
+ let multMatch;
1338
+ while ((multMatch = multiplyPattern.exec(code)) !== null) {
1339
+ // Check if operands are likely Uint types and not already cast to Field
1340
+ const beforeMult = code.substring(Math.max(0, multMatch.index - 200), multMatch.index);
1341
+ const afterMult = code.substring(multMatch.index, multMatch.index + multMatch[0].length + 50);
1342
+ // Skip if already casting to Field
1343
+ if (afterMult.includes("as Field") || beforeMult.includes("as Field"))
1344
+ continue;
1345
+ // Check if this looks like a verification pattern (common in witness verification)
1346
+ if (/assert|==/.test(afterMult)) {
1347
+ const lineNum = lineByIndex[multMatch.index] || 1;
1348
+ potentialIssues.push({
1349
+ type: "potential_overflow",
1350
+ line: lineNum,
1351
+ message: `Multiplication '${multMatch[1]} * ${multMatch[2]}' may overflow Uint bounds`,
1352
+ suggestion: `Cast operands to Field for safe arithmetic: '(${multMatch[1]} as Field) * (${multMatch[2]} as Field)'`,
1353
+ severity: "warning",
1354
+ });
1355
+ break; // Only warn once
1356
+ }
1357
+ }
1358
+ // 9. Detect witness/private values used in conditionals without disclose()
1359
+ // Look for patterns like: if (witnessVar ...) or if (param == privateValue)
1360
+ const witnessNames = witnesses.map((w) => w.name);
1361
+ const ifPattern = /if\s*\(([^)]+)\)/g;
1362
+ let ifMatch;
1363
+ while ((ifMatch = ifPattern.exec(code)) !== null) {
1364
+ const condition = ifMatch[1];
1365
+ // Check if condition uses a witness value without disclose
1366
+ for (const witnessName of witnessNames) {
1367
+ if (condition.includes(witnessName) &&
1368
+ !condition.includes(`disclose(${witnessName}`) &&
1369
+ !condition.includes("disclose(")) {
1370
+ const lineNum = lineByIndex[ifMatch.index] || 1;
1371
+ potentialIssues.push({
1372
+ type: "undisclosed_witness_conditional",
1373
+ line: lineNum,
1374
+ message: `Witness value '${witnessName}' used in conditional without disclose()`,
1375
+ suggestion: `Wrap witness comparisons in disclose(): 'if (disclose(${witnessName} == expected))'`,
1376
+ severity: "warning",
1377
+ });
1378
+ break;
1379
+ }
1380
+ }
1381
+ }
1382
+ // 10. Detect constructor parameters assigned to ledger without disclose()
1383
+ // Constructor parameters are treated as witness values and need disclose() when written to ledger
1384
+ const constructorMatch = code.match(/constructor\s*\(([^)]*)\)\s*\{([\s\S]*?)(?=\n\s*(?:export|circuit|witness|ledger|constructor|\}|$))/);
1385
+ if (constructorMatch) {
1386
+ const paramsStr = constructorMatch[1];
1387
+ const constructorBody = constructorMatch[2];
1388
+ // Extract constructor parameter names
1389
+ const paramPattern = /(\w+)\s*:\s*[^,)]+/g;
1390
+ const constructorParams = [];
1391
+ let paramMatch;
1392
+ while ((paramMatch = paramPattern.exec(paramsStr)) !== null) {
1393
+ constructorParams.push(paramMatch[1]);
1394
+ }
1395
+ // Check each parameter for direct assignment to ledger without disclose
1396
+ for (const param of constructorParams) {
1397
+ // Look for direct assignment: ledgerField = param (without disclose)
1398
+ const assignmentPattern = new RegExp(`(\\w+)\\s*=\\s*(?!disclose\\s*\\()${param}\\b`, "g");
1399
+ let assignMatch;
1400
+ while ((assignMatch = assignmentPattern.exec(constructorBody)) !== null) {
1401
+ const fieldName = assignMatch[1];
1402
+ // Check if the field is a ledger item
1403
+ const isLedgerField = ledgerItems.some((l) => l.name === fieldName);
1404
+ if (isLedgerField) {
1405
+ // Find the line number
1406
+ const beforeAssign = code.substring(0, constructorMatch.index +
1407
+ constructorMatch[0].indexOf(assignMatch[0]));
1408
+ const lineNum = (beforeAssign.match(/\n/g) || []).length + 1;
1409
+ potentialIssues.push({
1410
+ type: "undisclosed_constructor_param",
1411
+ line: lineNum,
1412
+ message: `Constructor parameter '${param}' assigned to ledger field '${fieldName}' without disclose()`,
1413
+ suggestion: `Wrap in disclose(): '${fieldName} = disclose(${param});'`,
1414
+ severity: "error",
1415
+ });
1416
+ }
1417
+ }
1418
+ }
1419
+ }
1297
1420
  const summary = [];
1298
1421
  if (circuits.length > 0) {
1299
1422
  summary.push(`${circuits.length} circuit(s)`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "midnight-mcp",
3
- "version": "0.1.20",
3
+ "version": "0.1.22",
4
4
  "description": "Model Context Protocol Server for Midnight Blockchain Development",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",