create-fhevm-example 1.4.5 → 1.4.7

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.
Files changed (28) hide show
  1. package/README.md +3 -4
  2. package/contracts/concepts/antipatterns/ControlFlow.sol +160 -0
  3. package/contracts/concepts/antipatterns/OperationsGasNoise.sol +190 -0
  4. package/contracts/concepts/antipatterns/Permissions.sol +254 -0
  5. package/dist/scripts/commands/add-mode.d.ts.map +1 -1
  6. package/dist/scripts/commands/add-mode.js +10 -21
  7. package/dist/scripts/commands/doctor.js +76 -2
  8. package/dist/scripts/commands/generate-config.js +13 -2
  9. package/dist/scripts/commands/generate-docs.js +4 -1
  10. package/dist/scripts/index.js +43 -24
  11. package/dist/scripts/shared/builders.d.ts.map +1 -1
  12. package/dist/scripts/shared/builders.js +8 -14
  13. package/dist/scripts/shared/config.d.ts.map +1 -1
  14. package/dist/scripts/shared/config.js +96 -67
  15. package/dist/scripts/shared/utils.d.ts.map +1 -1
  16. package/dist/scripts/shared/utils.js +5 -4
  17. package/package.json +1 -1
  18. package/test/concepts/antipatterns/ControlFlow.ts +125 -0
  19. package/test/concepts/antipatterns/OperationsGasNoise.ts +187 -0
  20. package/test/concepts/antipatterns/Permissions.ts +327 -0
  21. package/contracts/concepts/FHEAntiPatterns.sol +0 -300
  22. package/test/concepts/FHEAntiPatterns.ts +0 -111
  23. /package/contracts/concepts/{FHEAccessControl.sol → core/FHEAccessControl.sol} +0 -0
  24. /package/contracts/concepts/{FHEHandles.sol → core/FHEHandles.sol} +0 -0
  25. /package/contracts/concepts/{FHEInputProof.sol → core/FHEInputProof.sol} +0 -0
  26. /package/test/concepts/{FHEAccessControl.ts → core/FHEAccessControl.ts} +0 -0
  27. /package/test/concepts/{FHEHandles.ts → core/FHEHandles.ts} +0 -0
  28. /package/test/concepts/{FHEInputProof.ts → core/FHEInputProof.ts} +0 -0
@@ -58,7 +58,7 @@ const utils_1 = require("../shared/utils");
58
58
  function detectHardhatProject(targetDir) {
59
59
  const packageJsonPath = path.join(targetDir, "package.json");
60
60
  const hardhatConfigTs = path.join(targetDir, "hardhat.config.ts");
61
- const hardhatConfigJs = path.join(targetDir, "hardhat.config");
61
+ const hardhatConfigJs = path.join(targetDir, "hardhat.config.js");
62
62
  if (!fs.existsSync(packageJsonPath)) {
63
63
  return false;
64
64
  }
@@ -106,7 +106,7 @@ function updateHardhatConfig(targetDir) {
106
106
  ? configPathJs
107
107
  : null;
108
108
  if (!actualPath) {
109
- throw new Error("hardhat.config.ts or hardhat.config.js not found");
109
+ throw new Error(utils_1.ERROR_MESSAGES.CONFIG_NOT_FOUND);
110
110
  }
111
111
  let content = fs.readFileSync(actualPath, "utf-8");
112
112
  if (content.includes("@fhevm/hardhat-plugin")) {
@@ -139,12 +139,12 @@ function updateHardhatConfig(targetDir) {
139
139
  function addExampleFiles(exampleName, targetDir) {
140
140
  const example = config_1.EXAMPLES[exampleName];
141
141
  if (!example) {
142
- throw new Error(`Unknown example: ${exampleName}`);
142
+ throw new Error(utils_1.ERROR_MESSAGES.UNKNOWN_EXAMPLE(exampleName));
143
143
  }
144
144
  const rootDir = (0, utils_1.getRootDir)();
145
145
  const contractName = (0, utils_1.getContractName)(example.contract);
146
146
  if (!contractName) {
147
- throw new Error("Could not extract contract name");
147
+ throw new Error(utils_1.ERROR_MESSAGES.CONTRACT_NAME_FAILED);
148
148
  }
149
149
  const contractSource = path.join(rootDir, example.contract);
150
150
  const testSource = path.join(rootDir, example.test);
@@ -154,15 +154,9 @@ function addExampleFiles(exampleName, targetDir) {
154
154
  if (!fs.existsSync(contractsDir)) {
155
155
  fs.mkdirSync(contractsDir, { recursive: true });
156
156
  }
157
- if (fs.existsSync(contractDest)) {
158
- // File exists - will be handled by caller with prompts
159
- fs.copyFileSync(contractSource, contractDest);
160
- p.log.success(`Overwritten: ${contractName}.sol`);
161
- }
162
- else {
163
- fs.copyFileSync(contractSource, contractDest);
164
- p.log.success(`Added: ${contractName}.sol`);
165
- }
157
+ const isContractOverwrite = fs.existsSync(contractDest);
158
+ fs.copyFileSync(contractSource, contractDest);
159
+ p.log.success(`${isContractOverwrite ? "Overwritten" : "Added"}: ${contractName}.sol`);
166
160
  // Handle test file
167
161
  const testFileName = path.basename(example.test);
168
162
  const testDest = path.join(targetDir, "test", testFileName);
@@ -170,14 +164,9 @@ function addExampleFiles(exampleName, targetDir) {
170
164
  if (!fs.existsSync(testDir)) {
171
165
  fs.mkdirSync(testDir, { recursive: true });
172
166
  }
173
- if (fs.existsSync(testDest)) {
174
- fs.copyFileSync(testSource, testDest);
175
- p.log.success(`Overwritten: ${testFileName}`);
176
- }
177
- else {
178
- fs.copyFileSync(testSource, testDest);
179
- p.log.success(`Added: ${testFileName}`);
180
- }
167
+ const isTestOverwrite = fs.existsSync(testDest);
168
+ fs.copyFileSync(testSource, testDest);
169
+ p.log.success(`${isTestOverwrite ? "Overwritten" : "Added"}: ${testFileName}`);
181
170
  // Handle contract dependencies
182
171
  if (example.dependencies) {
183
172
  p.log.message("");
@@ -127,12 +127,83 @@ async function checkConfigIntegrity() {
127
127
  };
128
128
  }
129
129
  }
130
+ async function checkSubmoduleStatus() {
131
+ const SUBMODULE_PATH = "fhevm-hardhat-template";
132
+ const SUBMODULE_URL = "https://github.com/zama-ai/fhevm-hardhat-template";
133
+ try {
134
+ // 1. Get local commit hash
135
+ const localStatus = (0, child_process_1.execSync)(`git submodule status ${SUBMODULE_PATH}`, {
136
+ stdio: "pipe",
137
+ encoding: "utf-8",
138
+ }).trim();
139
+ // git submodule status output: " 3e01a81bfca5f0857cba55a099e98863efc79bcb fhevm-hardhat-template (v0.2.0-2-g3e01a81)"
140
+ const localHash = localStatus.split(" ").filter((s) => s.length > 0)[0];
141
+ if (!localHash || localHash.length < 40) {
142
+ return {
143
+ name: "Submodule status",
144
+ status: "fail",
145
+ message: "Not initialized",
146
+ details: [
147
+ `Run ${picocolors_1.default.bold("git submodule update --init")} to initialize.`,
148
+ ],
149
+ };
150
+ }
151
+ // 2. Get remote commit hash
152
+ // We use a timeout to not hang doctor script if internet is slow/down
153
+ const remoteInfo = (0, child_process_1.execSync)(`git ls-remote ${SUBMODULE_URL} HEAD`, {
154
+ stdio: "pipe",
155
+ encoding: "utf-8",
156
+ timeout: 5000, // 5 seconds timeout
157
+ }).trim();
158
+ const remoteHash = remoteInfo.split("\t")[0];
159
+ if (!remoteHash || remoteHash.length < 40) {
160
+ return {
161
+ name: "Submodule status",
162
+ status: "warn",
163
+ message: "Connection error",
164
+ details: ["Could not fetch remote status from GitHub."],
165
+ };
166
+ }
167
+ if (localHash === remoteHash) {
168
+ return {
169
+ name: "Submodule status",
170
+ status: "success",
171
+ message: "Up to date",
172
+ };
173
+ }
174
+ else {
175
+ return {
176
+ name: "Submodule status",
177
+ status: "warn",
178
+ message: "Update available",
179
+ details: [
180
+ `Local: ${picocolors_1.default.dim(localHash.slice(0, 7))}`,
181
+ `Remote: ${picocolors_1.default.dim(remoteHash.slice(0, 7))}`,
182
+ `Run ${picocolors_1.default.bold("git submodule update --remote --merge")} to update.`,
183
+ ],
184
+ };
185
+ }
186
+ }
187
+ catch (e) {
188
+ return {
189
+ name: "Submodule status",
190
+ status: "warn",
191
+ message: "Check skipped",
192
+ details: ["Git error or no internet connection."],
193
+ };
194
+ }
195
+ }
130
196
  // =============================================================================
131
197
  // Main
132
198
  // =============================================================================
133
199
  async function main() {
134
200
  p.intro(picocolors_1.default.cyan("🩺 FHEVM Doctor"));
135
- const checks = [checkNodeVersion(), checkGit(), checkConfigIntegrity()];
201
+ const checks = [
202
+ checkNodeVersion(),
203
+ checkGit(),
204
+ checkConfigIntegrity(),
205
+ checkSubmoduleStatus(),
206
+ ];
136
207
  const results = await Promise.all(checks);
137
208
  let hasFailure = false;
138
209
  for (const result of results) {
@@ -162,4 +233,7 @@ async function main() {
162
233
  p.outro(picocolors_1.default.green("✅ All checks passed! You are ready to develop. 🚀"));
163
234
  }
164
235
  }
165
- main().catch(console.error);
236
+ main().catch((err) => {
237
+ console.error(err);
238
+ process.exit(1);
239
+ });
@@ -191,7 +191,18 @@ function generateCategoriesConfig(contracts) {
191
191
  }
192
192
  categoryMap[contract.category].push(contract);
193
193
  }
194
- const categoryEntries = Object.entries(categoryMap).map(([category, items]) => {
194
+ const sortedEntries = Object.entries(categoryMap).sort(([catA], [catB]) => {
195
+ const idxA = utils_1.CATEGORY_ORDER.indexOf(catA);
196
+ const idxB = utils_1.CATEGORY_ORDER.indexOf(catB);
197
+ if (idxA !== -1 && idxB !== -1)
198
+ return idxA - idxB;
199
+ if (idxA !== -1)
200
+ return -1;
201
+ if (idxB !== -1)
202
+ return 1;
203
+ return catA.localeCompare(catB);
204
+ });
205
+ const categoryEntries = sortedEntries.map(([category, items]) => {
195
206
  const categoryKey = category
196
207
  .toLowerCase()
197
208
  .replace(/\s+/g, "")
@@ -203,7 +214,7 @@ function generateCategoriesConfig(contracts) {
203
214
  }`)
204
215
  .join(",\n");
205
216
  return ` ${categoryKey}: {
206
- name: "${category} Examples",
217
+ name: "${category}",
207
218
  contracts: [
208
219
  ${contracts}
209
220
  ],
@@ -187,5 +187,8 @@ async function main() {
187
187
  }
188
188
  const isMainModule = process.argv[1]?.includes("generate-docs");
189
189
  if (isMainModule) {
190
- main().catch(console.error);
190
+ main().catch((err) => {
191
+ console.error(err);
192
+ process.exit(1);
193
+ });
191
194
  }
@@ -56,6 +56,17 @@ const builders_1 = require("./shared/builders");
56
56
  const ui_1 = require("./shared/ui");
57
57
  const add_mode_1 = require("./commands/add-mode");
58
58
  const generate_docs_1 = require("./commands/generate-docs");
59
+ // =============================================================================
60
+ // INTERACTIVE MODE
61
+ // =============================================================================
62
+ /** Helper to check if user cancelled and show message */
63
+ function checkCancel(value) {
64
+ if (p.isCancel(value)) {
65
+ p.cancel("Operation cancelled.");
66
+ return true;
67
+ }
68
+ return false;
69
+ }
59
70
  /** Prompts user to select mode and returns project configuration */
60
71
  async function getProjectConfig() {
61
72
  // Build options - docs only available in local dev mode
@@ -83,22 +94,16 @@ async function getProjectConfig() {
83
94
  message: "What would you like to create?",
84
95
  options,
85
96
  });
86
- if (p.isCancel(mode)) {
87
- p.cancel("Operation cancelled.");
97
+ if (checkCancel(mode))
88
98
  return null;
89
- }
90
99
  let name;
91
100
  if (mode === "single") {
92
101
  const category = await (0, ui_1.promptSelectCategory)();
93
- if (p.isCancel(category)) {
94
- p.cancel("Operation cancelled.");
102
+ if (checkCancel(category))
95
103
  return null;
96
- }
97
104
  const example = await (0, ui_1.promptSelectExampleFromCategory)(category);
98
- if (p.isCancel(example)) {
99
- p.cancel("Operation cancelled.");
105
+ if (checkCancel(example))
100
106
  return null;
101
- }
102
107
  name = example;
103
108
  }
104
109
  else if (mode === "docs") {
@@ -108,10 +113,8 @@ async function getProjectConfig() {
108
113
  }
109
114
  else {
110
115
  const category = await (0, ui_1.promptSelectCategoryProject)();
111
- if (p.isCancel(category)) {
112
- p.cancel("Operation cancelled.");
116
+ if (checkCancel(category))
113
117
  return null;
114
- }
115
118
  name = category;
116
119
  }
117
120
  const projectName = await p.text({
@@ -119,27 +122,21 @@ async function getProjectConfig() {
119
122
  placeholder: `my-${name}-project`,
120
123
  defaultValue: `my-${name}-project`,
121
124
  });
122
- if (p.isCancel(projectName)) {
123
- p.cancel("Operation cancelled.");
125
+ if (checkCancel(projectName))
124
126
  return null;
125
- }
126
127
  const outputDir = await p.text({
127
128
  message: "Output directory:",
128
129
  placeholder: `./${projectName}`,
129
130
  defaultValue: `./${projectName}`,
130
131
  });
131
- if (p.isCancel(outputDir)) {
132
- p.cancel("Operation cancelled.");
132
+ if (checkCancel(outputDir))
133
133
  return null;
134
- }
135
134
  const shouldInstall = await p.confirm({
136
135
  message: "Install dependencies and run tests?",
137
136
  initialValue: false,
138
137
  });
139
- if (p.isCancel(shouldInstall)) {
140
- p.cancel("Operation cancelled.");
138
+ if (checkCancel(shouldInstall))
141
139
  return null;
142
- }
143
140
  return {
144
141
  mode: mode,
145
142
  name,
@@ -205,12 +202,22 @@ async function runInteractiveMode() {
205
202
  }
206
203
  function parseArgs(args) {
207
204
  const parsed = {};
205
+ // Short flag mappings
206
+ const shortFlags = {
207
+ "-e": "example",
208
+ "-c": "category",
209
+ "-o": "output",
210
+ "-a": "add",
211
+ "-i": "install",
212
+ "-h": "help",
213
+ };
208
214
  for (let i = 0; i < args.length; i++) {
209
215
  const arg = args[i];
216
+ // Handle long flags (--example, --category, etc.)
210
217
  if (arg.startsWith("--")) {
211
218
  const key = arg.slice(2);
212
219
  const nextArg = args[i + 1];
213
- if (nextArg && !nextArg.startsWith("--")) {
220
+ if (nextArg && !nextArg.startsWith("-")) {
214
221
  parsed[key] = nextArg;
215
222
  i++;
216
223
  }
@@ -218,8 +225,20 @@ function parseArgs(args) {
218
225
  parsed[key] = true;
219
226
  }
220
227
  }
221
- else if (arg === "-h") {
222
- parsed["help"] = true;
228
+ // Handle short flags (-e, -c, -o, -a, -i, -h)
229
+ else if (arg.startsWith("-") && shortFlags[arg]) {
230
+ const key = shortFlags[arg];
231
+ const nextArg = args[i + 1];
232
+ // Flags that take values: -e, -c, -o
233
+ if (["example", "category", "output"].includes(key) &&
234
+ nextArg &&
235
+ !nextArg.startsWith("-")) {
236
+ parsed[key] = nextArg;
237
+ i++;
238
+ }
239
+ else {
240
+ parsed[key] = true;
241
+ }
223
242
  }
224
243
  }
225
244
  return parsed;
@@ -1 +1 @@
1
- {"version":3,"file":"builders.d.ts","sourceRoot":"","sources":["../../../scripts/shared/builders.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA6DH;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,GAChB,IAAI,CAkDN;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,GAChB,IAAI,CAwEN;AAMD;;GAEG;AACH,wBAAsB,sBAAsB,CAC1C,YAAY,EAAE,MAAM,EAAE,EACtB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAuEf"}
1
+ {"version":3,"file":"builders.d.ts","sourceRoot":"","sources":["../../../scripts/shared/builders.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAiEH;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,GAChB,IAAI,CAkDN;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,GAChB,IAAI,CAwEN;AAMD;;GAEG;AACH,wBAAsB,sBAAsB,CAC1C,YAAY,EAAE,MAAM,EAAE,EACtB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CA6Df"}
@@ -69,7 +69,8 @@ function copyDependencies(dependencies, outputDir) {
69
69
  }
70
70
  }
71
71
  /**
72
- * Initializes git repository (optional, fails silently)
72
+ * Initializes git repository (optional, logs warning if skipped)
73
+ * @returns true if git init succeeded, false otherwise
73
74
  */
74
75
  function initGitRepo(outputDir) {
75
76
  try {
@@ -77,9 +78,11 @@ function initGitRepo(outputDir) {
77
78
  cwd: outputDir,
78
79
  stdio: "ignore",
79
80
  });
81
+ return true;
80
82
  }
81
83
  catch {
82
- // Git init is optional
84
+ utils_1.log.dim(" ⚠️ Git initialization skipped (git not available)");
85
+ return false;
83
86
  }
84
87
  }
85
88
  // =============================================================================
@@ -208,18 +211,9 @@ async function createLocalTestProject(exampleNames, outputDir) {
208
211
  Object.assign(allNpmDeps, example.npmDependencies);
209
212
  }
210
213
  }
211
- // 3. Copy dependencies
212
- for (const depPath of allContractDeps) {
213
- const depFullPath = path.join(rootDir, depPath);
214
- if (fs.existsSync(depFullPath)) {
215
- const relativePath = depPath.replace(/^contracts\//, "");
216
- const depDestPath = path.join(outputDir, "contracts", relativePath);
217
- const depDestDir = path.dirname(depDestPath);
218
- if (!fs.existsSync(depDestDir)) {
219
- fs.mkdirSync(depDestDir, { recursive: true });
220
- }
221
- fs.copyFileSync(depFullPath, depDestPath);
222
- }
214
+ // 3. Copy dependencies (reuse helper function)
215
+ if (allContractDeps.size > 0) {
216
+ copyDependencies(Array.from(allContractDeps), outputDir);
223
217
  }
224
218
  // 4. Finalize project
225
219
  (0, generators_1.updateProjectPackageJson)(outputDir, "fhevm-test-project", `Testing ${exampleNames.length} examples`, Object.keys(allNpmDeps).length > 0 ? allNpmDeps : undefined);
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../scripts/shared/config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,WAAW,aAAa;IAC5B,yCAAyC;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,uCAAuC;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,gDAAgD;IAChD,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,uCAAuC;IACvC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,yCAAyC;IACzC,WAAW,EAAE,MAAM,CAAC;IACpB,4BAA4B;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,8CAA8C;IAC9C,UAAU,EAAE,MAAM,CAAC;IACnB,8BAA8B;IAC9B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,SAAS,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAClD;AAMD,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CA6RlD,CAAC;AAMF,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAoJrD,CAAC;AAMF;;GAEG;AACH,wBAAgB,eAAe,IAAI,MAAM,EAAE,CAE1C;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,EAAE,CAE3C;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAElE;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAEpE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAE3D"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../scripts/shared/config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,WAAW,aAAa;IAC5B,yCAAyC;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,uCAAuC;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,gDAAgD;IAChD,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,uCAAuC;IACvC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,yCAAyC;IACzC,WAAW,EAAE,MAAM,CAAC;IACpB,4BAA4B;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,8CAA8C;IAC9C,UAAU,EAAE,MAAM,CAAC;IACnB,8BAA8B;IAC9B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,SAAS,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAClD;AAMD,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CA+SlD,CAAC;AAMF,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAiKrD,CAAC;AAMF;;GAEG;AACH,wBAAgB,eAAe,IAAI,MAAM,EAAE,CAE1C;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,EAAE,CAE3C;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAElE;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAEpE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAE3D"}
@@ -144,36 +144,52 @@ exports.EXAMPLES = {
144
144
  docsOutput: "docs/basic/fhe-operations/fhe-if-then-else.md",
145
145
  title: "FHE If Then Else"
146
146
  },
147
+ "control-flow": {
148
+ contract: "contracts/concepts/antipatterns/ControlFlow.sol",
149
+ test: "test/concepts/antipatterns/ControlFlow.ts",
150
+ description: "Control flow anti-patterns in FHE development. Demonstrates common mistakes when using conditional logic and loops with encrypted values.",
151
+ category: "Concepts - Antipatterns",
152
+ docsOutput: "docs/concepts/antipatterns/control-flow.md",
153
+ title: "Control Flow"
154
+ },
155
+ "operations-gas-noise": {
156
+ contract: "contracts/concepts/antipatterns/OperationsGasNoise.sol",
157
+ test: "test/concepts/antipatterns/OperationsGasNoise.ts",
158
+ description: "Operations, gas, and noise anti-patterns in FHE development. Demonstrates performance issues, side-channel leaks, and inefficient encrypted computation patterns.",
159
+ category: "Concepts - Antipatterns",
160
+ docsOutput: "docs/concepts/antipatterns/operations-gas-noise.md",
161
+ title: "Operations Gas Noise"
162
+ },
163
+ "permissions": {
164
+ contract: "contracts/concepts/antipatterns/Permissions.sol",
165
+ test: "test/concepts/antipatterns/Permissions.ts",
166
+ description: "Permission management anti-patterns in FHE development. Demonstrates common mistakes with allowThis, allow, and permission propagation across transfers and contracts.",
167
+ category: "Concepts - Antipatterns",
168
+ docsOutput: "docs/concepts/antipatterns/permissions.md",
169
+ title: "Permissions"
170
+ },
147
171
  "fhe-access-control": {
148
- contract: "contracts/concepts/FHEAccessControl.sol",
149
- test: "test/concepts/FHEAccessControl.ts",
172
+ contract: "contracts/concepts/core/FHEAccessControl.sol",
173
+ test: "test/concepts/core/FHEAccessControl.ts",
150
174
  description: "Master class for FHE permission patterns and access control. Explains the three permission types: allow() for permanent access, allowThis() for contract operations, and allowTransient() for temporary cross-contract calls. Includes correct and incorrect usage examples to prevent common decryption failures.",
151
- category: "Concepts",
152
- docsOutput: "docs/concepts/fhe-access-control.md",
175
+ category: "Concepts - Core",
176
+ docsOutput: "docs/concepts/core/fhe-access-control.md",
153
177
  title: "FHE Access Control"
154
178
  },
155
- "fhe-anti-patterns": {
156
- contract: "contracts/concepts/FHEAntiPatterns.sol",
157
- test: "test/concepts/FHEAntiPatterns.ts",
158
- description: "Comprehensive guide to FHE anti-patterns and their solutions. Covers 9 critical mistakes: using if/else on encrypted values, incorrect permission patterns, require() statements that leak info, unbounded loops, noise accumulation, and deprecated APIs. Each pattern shows both ❌ WRONG and ✅ CORRECT implementations.",
159
- category: "Concepts",
160
- docsOutput: "docs/concepts/fhe-anti-patterns.md",
161
- title: "FHE Anti Patterns"
162
- },
163
179
  "fhe-handles": {
164
- contract: "contracts/concepts/FHEHandles.sol",
165
- test: "test/concepts/FHEHandles.ts",
180
+ contract: "contracts/concepts/core/FHEHandles.sol",
181
+ test: "test/concepts/core/FHEHandles.ts",
166
182
  description: "Deep dive into FHE handles: what they are and how they work. Explains that handles are uint256 pointers to encrypted data, demonstrates three creation methods (fromExternal, asEuint, operations), and emphasizes immutability - every operation creates a NEW handle. Includes gas cost comparisons for different operations.",
167
- category: "Concepts",
168
- docsOutput: "docs/concepts/fhe-handles.md",
183
+ category: "Concepts - Core",
184
+ docsOutput: "docs/concepts/core/fhe-handles.md",
169
185
  title: "FHE Handles"
170
186
  },
171
187
  "fhe-input-proof": {
172
- contract: "contracts/concepts/FHEInputProof.sol",
173
- test: "test/concepts/FHEInputProof.ts",
188
+ contract: "contracts/concepts/core/FHEInputProof.sol",
189
+ test: "test/concepts/core/FHEInputProof.ts",
174
190
  description: "Input proof validation and batching strategies in FHEVM. Explains why proofs are essential (prevent garbage data, wrong types, and replay attacks) and demonstrates the gas-efficient batching pattern where one proof validates multiple encrypted inputs, saving ~50k gas per additional value.",
175
- category: "Concepts",
176
- docsOutput: "docs/concepts/fhe-input-proof.md",
191
+ category: "Concepts - Core",
192
+ docsOutput: "docs/concepts/core/fhe-input-proof.md",
177
193
  title: "FHE Input Proof"
178
194
  },
179
195
  "encrypted-lottery": {
@@ -277,33 +293,25 @@ exports.EXAMPLES = {
277
293
  // Category Configurations
278
294
  // =============================================================================
279
295
  exports.CATEGORIES = {
280
- advanced: {
281
- name: "Advanced Examples",
296
+ basicencryption: {
297
+ name: "Basic - Encryption",
282
298
  contracts: [
283
299
  {
284
- sol: "contracts/advanced/BlindAuction.sol",
285
- test: "test/advanced/BlindAuction.ts",
286
- },
287
- {
288
- sol: "contracts/advanced/EncryptedEscrow.sol",
289
- test: "test/advanced/EncryptedEscrow.ts",
290
- },
291
- {
292
- sol: "contracts/advanced/HiddenVoting.sol",
293
- test: "test/advanced/HiddenVoting.ts",
300
+ sol: "contracts/basic/encryption/EncryptMultipleValues.sol",
301
+ test: "test/basic/encryption/EncryptMultipleValues.ts",
294
302
  },
295
303
  {
296
- sol: "contracts/advanced/PrivateKYC.sol",
297
- test: "test/advanced/PrivateKYC.ts",
304
+ sol: "contracts/basic/encryption/EncryptSingleValue.sol",
305
+ test: "test/basic/encryption/EncryptSingleValue.ts",
298
306
  },
299
307
  {
300
- sol: "contracts/advanced/PrivatePayroll.sol",
301
- test: "test/advanced/PrivatePayroll.ts",
308
+ sol: "contracts/basic/encryption/FHECounter.sol",
309
+ test: "test/basic/encryption/FHECounter.ts",
302
310
  }
303
311
  ],
304
312
  },
305
313
  basicdecryption: {
306
- name: "Basic - Decryption Examples",
314
+ name: "Basic - Decryption",
307
315
  contracts: [
308
316
  {
309
317
  sol: "contracts/basic/decryption/PublicDecryptMultipleValues.sol",
@@ -323,25 +331,8 @@ exports.CATEGORIES = {
323
331
  }
324
332
  ],
325
333
  },
326
- basicencryption: {
327
- name: "Basic - Encryption Examples",
328
- contracts: [
329
- {
330
- sol: "contracts/basic/encryption/EncryptMultipleValues.sol",
331
- test: "test/basic/encryption/EncryptMultipleValues.ts",
332
- },
333
- {
334
- sol: "contracts/basic/encryption/EncryptSingleValue.sol",
335
- test: "test/basic/encryption/EncryptSingleValue.ts",
336
- },
337
- {
338
- sol: "contracts/basic/encryption/FHECounter.sol",
339
- test: "test/basic/encryption/FHECounter.ts",
340
- }
341
- ],
342
- },
343
334
  basicfheoperations: {
344
- name: "Basic - FHE Operations Examples",
335
+ name: "Basic - FHE Operations",
345
336
  contracts: [
346
337
  {
347
338
  sol: "contracts/basic/fhe-operations/FHEAdd.sol",
@@ -361,29 +352,42 @@ exports.CATEGORIES = {
361
352
  }
362
353
  ],
363
354
  },
364
- concepts: {
365
- name: "Concepts Examples",
355
+ conceptscore: {
356
+ name: "Concepts - Core",
366
357
  contracts: [
367
358
  {
368
- sol: "contracts/concepts/FHEAccessControl.sol",
369
- test: "test/concepts/FHEAccessControl.ts",
359
+ sol: "contracts/concepts/core/FHEAccessControl.sol",
360
+ test: "test/concepts/core/FHEAccessControl.ts",
370
361
  },
371
362
  {
372
- sol: "contracts/concepts/FHEAntiPatterns.sol",
373
- test: "test/concepts/FHEAntiPatterns.ts",
363
+ sol: "contracts/concepts/core/FHEHandles.sol",
364
+ test: "test/concepts/core/FHEHandles.ts",
365
+ },
366
+ {
367
+ sol: "contracts/concepts/core/FHEInputProof.sol",
368
+ test: "test/concepts/core/FHEInputProof.ts",
369
+ }
370
+ ],
371
+ },
372
+ conceptsantipatterns: {
373
+ name: "Concepts - Antipatterns",
374
+ contracts: [
375
+ {
376
+ sol: "contracts/concepts/antipatterns/ControlFlow.sol",
377
+ test: "test/concepts/antipatterns/ControlFlow.ts",
374
378
  },
375
379
  {
376
- sol: "contracts/concepts/FHEHandles.sol",
377
- test: "test/concepts/FHEHandles.ts",
380
+ sol: "contracts/concepts/antipatterns/OperationsGasNoise.sol",
381
+ test: "test/concepts/antipatterns/OperationsGasNoise.ts",
378
382
  },
379
383
  {
380
- sol: "contracts/concepts/FHEInputProof.sol",
381
- test: "test/concepts/FHEInputProof.ts",
384
+ sol: "contracts/concepts/antipatterns/Permissions.sol",
385
+ test: "test/concepts/antipatterns/Permissions.ts",
382
386
  }
383
387
  ],
384
388
  },
385
389
  gaming: {
386
- name: "Gaming Examples",
390
+ name: "Gaming",
387
391
  contracts: [
388
392
  {
389
393
  sol: "contracts/gaming/EncryptedLottery.sol",
@@ -399,8 +403,33 @@ exports.CATEGORIES = {
399
403
  }
400
404
  ],
401
405
  },
406
+ advanced: {
407
+ name: "Advanced",
408
+ contracts: [
409
+ {
410
+ sol: "contracts/advanced/BlindAuction.sol",
411
+ test: "test/advanced/BlindAuction.ts",
412
+ },
413
+ {
414
+ sol: "contracts/advanced/EncryptedEscrow.sol",
415
+ test: "test/advanced/EncryptedEscrow.ts",
416
+ },
417
+ {
418
+ sol: "contracts/advanced/HiddenVoting.sol",
419
+ test: "test/advanced/HiddenVoting.ts",
420
+ },
421
+ {
422
+ sol: "contracts/advanced/PrivateKYC.sol",
423
+ test: "test/advanced/PrivateKYC.ts",
424
+ },
425
+ {
426
+ sol: "contracts/advanced/PrivatePayroll.sol",
427
+ test: "test/advanced/PrivatePayroll.ts",
428
+ }
429
+ ],
430
+ },
402
431
  openzeppelin: {
403
- name: "Openzeppelin Examples",
432
+ name: "Openzeppelin",
404
433
  contracts: [
405
434
  {
406
435
  sol: "contracts/openzeppelin/ERC7984.sol",
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../scripts/shared/utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAWH,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,UAAU,CAAC;AAEhD,eAAO,MAAM,aAAa,iBAAO,CAAC;AAElC,qDAAqD;AACrD,eAAO,MAAM,iBAAiB,2BAA2B,CAAC;AAE1D,gDAAgD;AAChD,eAAO,MAAM,sBAAsB,KAAK,CAAC;AAEzC,mDAAmD;AACnD,eAAO,MAAM,YAAY,UAQxB,CAAC;AAEF,wFAAwF;AACxF,eAAO,MAAM,iBAAiB,2QAyB7B,CAAC;AAEF,4CAA4C;AAC5C,eAAO,MAAM,kBAAkB;;;;;;;;;CAS9B,CAAC;AAEF,eAAO,MAAM,cAAc,UAQ1B,CAAC;AAEF,eAAO,MAAM,kBAAkB,yPAS9B,CAAC;AAEF,eAAO,MAAM,cAAc;;;4BAGD,MAAM;6BACL,MAAM;uBACZ,MAAM;;;;CAI1B,CAAC;AAMF,eAAO,MAAM,GAAG;mBACC,MAAM;iBACR,MAAM;gBACP,MAAM;eACP,MAAM;mBACF,MAAM;CACtB,CAAC;AAEF;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,SAAI,GAAG,KAAK,CAI/D;AAMD,sDAAsD;AACtD,wBAAgB,UAAU,IAAI,MAAM,CAUnC;AAED,uCAAuC;AACvC,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,oEAAoE;AACpE,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,WAAW,GAAE,MAAM,EAAiB,GACnC,IAAI,CAoBN;AAMD,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAM/C;AAED,wBAAgB,yBAAyB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAEtE;AAED,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAKhE;AAED,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAK7D;AAED,wBAAgB,eAAe,CAAC,qBAAqB,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAqB5E;AAMD,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAIlD;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAInD;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAIhE"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../scripts/shared/utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAWH,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,UAAU,CAAC;AAEhD,eAAO,MAAM,aAAa,iBAAO,CAAC;AAElC,qDAAqD;AACrD,eAAO,MAAM,iBAAiB,2BAA2B,CAAC;AAE1D,gDAAgD;AAChD,eAAO,MAAM,sBAAsB,KAAK,CAAC;AAEzC,mDAAmD;AACnD,eAAO,MAAM,YAAY,UAQxB,CAAC;AAEF,wFAAwF;AACxF,eAAO,MAAM,iBAAiB,2QAyB7B,CAAC;AAEF,4CAA4C;AAC5C,eAAO,MAAM,kBAAkB;;;;;;;;;CAS9B,CAAC;AAEF,eAAO,MAAM,cAAc,UAS1B,CAAC;AAEF,eAAO,MAAM,kBAAkB,yPAS9B,CAAC;AAEF,eAAO,MAAM,cAAc;;;4BAGD,MAAM;6BACL,MAAM;uBACZ,MAAM;;;;CAI1B,CAAC;AAMF,eAAO,MAAM,GAAG;mBACC,MAAM;iBACR,MAAM;gBACP,MAAM;eACP,MAAM;mBACF,MAAM;CACtB,CAAC;AAEF;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,SAAI,GAAG,KAAK,CAI/D;AAMD,sDAAsD;AACtD,wBAAgB,UAAU,IAAI,MAAM,CAUnC;AAED,uCAAuC;AACvC,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,oEAAoE;AACpE,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,WAAW,GAAE,MAAM,EAAiB,GACnC,IAAI,CAoBN;AAMD,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAM/C;AAED,wBAAgB,yBAAyB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAEtE;AAED,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAKhE;AAED,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAK7D;AAED,wBAAgB,eAAe,CAAC,qBAAqB,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAqB5E;AAMD,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAIlD;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAInD;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAIhE"}
@@ -108,18 +108,19 @@ exports.FHEVM_DEPENDENCIES = {
108
108
  "@fhevm/solidity": "^0.9.1",
109
109
  },
110
110
  devDependencies: {
111
- "@fhevm/hardhat-plugin": "^0.3.0-1",
112
- "@zama-fhe/relayer-sdk": "^0.3.0-5",
111
+ "@fhevm/hardhat-plugin": "^0.3.0-3",
112
+ "@zama-fhe/relayer-sdk": "^0.3.0-6",
113
113
  },
114
114
  };
115
115
  exports.CATEGORY_ORDER = [
116
116
  "Basic - Encryption",
117
117
  "Basic - Decryption",
118
118
  "Basic - FHE Operations",
119
- "Concepts",
119
+ "Concepts - Core",
120
+ "Concepts - Antipatterns",
120
121
  "Gaming",
121
- "Openzeppelin",
122
122
  "Advanced",
123
+ "Openzeppelin",
123
124
  ];
124
125
  exports.TEST_TYPES_CONTENT = `import type { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
125
126