create-izi-noir 0.2.2 → 0.2.6
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.
- package/dist/index.js +1185 -131
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { Command } from "commander";
|
|
|
6
6
|
// src/commands/init.ts
|
|
7
7
|
import path2 from "path";
|
|
8
8
|
import { execSync } from "child_process";
|
|
9
|
-
import
|
|
9
|
+
import pc3 from "picocolors";
|
|
10
10
|
|
|
11
11
|
// src/prompts/project.ts
|
|
12
12
|
import prompts from "prompts";
|
|
@@ -17,8 +17,7 @@ var TEMPLATES = [
|
|
|
17
17
|
{ title: "Balance Proof only", value: "balance-proof" }
|
|
18
18
|
];
|
|
19
19
|
var NETWORKS = [
|
|
20
|
-
{ title: "Solana
|
|
21
|
-
{ title: "EVM (Ethereum, Base, etc.)", value: "evm", description: "Uses UltraHonk proofs" }
|
|
20
|
+
{ title: "Solana", value: "solana", description: "Uses Groth16 for on-chain verification" }
|
|
22
21
|
];
|
|
23
22
|
function networkToProvider(network) {
|
|
24
23
|
return network === "solana" ? "arkworks" : "barretenberg";
|
|
@@ -69,6 +68,16 @@ async function promptProjectOptions(defaults) {
|
|
|
69
68
|
name: "initGit",
|
|
70
69
|
message: "Initialize git repository?",
|
|
71
70
|
initial: !defaults.skipGit
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
type: "select",
|
|
74
|
+
name: "aiTool",
|
|
75
|
+
message: "Which AI coding assistant do you use?",
|
|
76
|
+
choices: [
|
|
77
|
+
{ title: "Claude Code", value: "claude", description: "Install IZI-NOIR circuit patterns skill" },
|
|
78
|
+
{ title: "None / Other", value: "none", description: "Skip AI assistant configuration" }
|
|
79
|
+
],
|
|
80
|
+
initial: 0
|
|
72
81
|
}
|
|
73
82
|
);
|
|
74
83
|
try {
|
|
@@ -83,7 +92,8 @@ async function promptProjectOptions(defaults) {
|
|
|
83
92
|
template: response.template || defaults.template || "default",
|
|
84
93
|
provider: networkToProvider(network),
|
|
85
94
|
skipInstall: response.installDeps === false,
|
|
86
|
-
skipGit: response.initGit === false
|
|
95
|
+
skipGit: response.initGit === false,
|
|
96
|
+
aiTool: response.aiTool || "none"
|
|
87
97
|
};
|
|
88
98
|
} catch {
|
|
89
99
|
return null;
|
|
@@ -117,44 +127,8 @@ async function isDirectoryEmpty(dir) {
|
|
|
117
127
|
}
|
|
118
128
|
}
|
|
119
129
|
|
|
120
|
-
// src/utils/spinner.ts
|
|
121
|
-
import pc2 from "picocolors";
|
|
122
|
-
var frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
123
|
-
var Spinner = class {
|
|
124
|
-
message;
|
|
125
|
-
interval = null;
|
|
126
|
-
frameIndex = 0;
|
|
127
|
-
constructor(message) {
|
|
128
|
-
this.message = message;
|
|
129
|
-
}
|
|
130
|
-
start() {
|
|
131
|
-
process.stdout.write("\x1B[?25l");
|
|
132
|
-
this.interval = setInterval(() => {
|
|
133
|
-
const frame = pc2.cyan(frames[this.frameIndex]);
|
|
134
|
-
process.stdout.write(`\r${frame} ${this.message}`);
|
|
135
|
-
this.frameIndex = (this.frameIndex + 1) % frames.length;
|
|
136
|
-
}, 80);
|
|
137
|
-
}
|
|
138
|
-
stop(success = true) {
|
|
139
|
-
if (this.interval) {
|
|
140
|
-
clearInterval(this.interval);
|
|
141
|
-
this.interval = null;
|
|
142
|
-
}
|
|
143
|
-
process.stdout.write("\x1B[?25h");
|
|
144
|
-
const icon = success ? pc2.green("\u2713") : pc2.red("\u2717");
|
|
145
|
-
process.stdout.write(`\r${icon} ${this.message}
|
|
146
|
-
`);
|
|
147
|
-
}
|
|
148
|
-
update(message) {
|
|
149
|
-
this.message = message;
|
|
150
|
-
}
|
|
151
|
-
};
|
|
152
|
-
function createSpinner(message) {
|
|
153
|
-
return new Spinner(message);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
130
|
// src/utils/progress.ts
|
|
157
|
-
import
|
|
131
|
+
import pc2 from "picocolors";
|
|
158
132
|
var THINKING_PHRASES = [
|
|
159
133
|
"Initializing ZK environment",
|
|
160
134
|
"Configuring proof system",
|
|
@@ -191,15 +165,15 @@ var ProgressReporter = class {
|
|
|
191
165
|
this.showThinking();
|
|
192
166
|
}
|
|
193
167
|
showThinking() {
|
|
194
|
-
const
|
|
168
|
+
const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
195
169
|
let frameIndex = 0;
|
|
196
170
|
let dotCount = 0;
|
|
197
171
|
this.thinkingInterval = setInterval(() => {
|
|
198
|
-
const frame =
|
|
172
|
+
const frame = pc2.cyan(frames[frameIndex]);
|
|
199
173
|
const phrase = THINKING_PHRASES[this.thinkingIndex % THINKING_PHRASES.length];
|
|
200
174
|
const dots = ".".repeat(dotCount % 4);
|
|
201
|
-
process.stdout.write(`\r${frame} ${
|
|
202
|
-
frameIndex = (frameIndex + 1) %
|
|
175
|
+
process.stdout.write(`\r${frame} ${pc2.dim(phrase)}${dots} `);
|
|
176
|
+
frameIndex = (frameIndex + 1) % frames.length;
|
|
203
177
|
dotCount++;
|
|
204
178
|
if (dotCount % 12 === 0) {
|
|
205
179
|
this.thinkingIndex++;
|
|
@@ -215,29 +189,29 @@ var ProgressReporter = class {
|
|
|
215
189
|
}
|
|
216
190
|
async reportFile(filename, isLast = false) {
|
|
217
191
|
const icon = getFileIcon(filename);
|
|
218
|
-
const line = ` ${icon} ${
|
|
219
|
-
process.stdout.write(" " + icon + " " +
|
|
192
|
+
const line = ` ${icon} ${pc2.dim("created")} ${pc2.white(filename)}`;
|
|
193
|
+
process.stdout.write(" " + icon + " " + pc2.dim("created") + " ");
|
|
220
194
|
for (const char of filename) {
|
|
221
|
-
process.stdout.write(
|
|
195
|
+
process.stdout.write(pc2.white(char));
|
|
222
196
|
await sleep(8 + Math.random() * 12);
|
|
223
197
|
}
|
|
224
198
|
process.stdout.write("\n");
|
|
225
199
|
this.currentLine = line;
|
|
226
200
|
}
|
|
227
201
|
async reportDirectory(dirname) {
|
|
228
|
-
process.stdout.write(` \u{1F4C1} ${
|
|
202
|
+
process.stdout.write(` \u{1F4C1} ${pc2.dim("mkdir")} ${pc2.blue(dirname)}/
|
|
229
203
|
`);
|
|
230
204
|
await sleep(30);
|
|
231
205
|
}
|
|
232
206
|
showSuccess(message) {
|
|
233
207
|
process.stdout.write("\x1B[?25h");
|
|
234
208
|
console.log();
|
|
235
|
-
console.log(
|
|
209
|
+
console.log(pc2.green("\u2713") + " " + message);
|
|
236
210
|
}
|
|
237
211
|
showError(message) {
|
|
238
212
|
process.stdout.write("\x1B[?25h");
|
|
239
213
|
console.log();
|
|
240
|
-
console.log(
|
|
214
|
+
console.log(pc2.red("\u2717") + " " + message);
|
|
241
215
|
}
|
|
242
216
|
};
|
|
243
217
|
function createProgressReporter() {
|
|
@@ -260,18 +234,18 @@ var InstallProgress = class {
|
|
|
260
234
|
start() {
|
|
261
235
|
process.stdout.write("\x1B[?25l");
|
|
262
236
|
const barWidth = 30;
|
|
263
|
-
const
|
|
237
|
+
const frames = ["\u25D0", "\u25D3", "\u25D1", "\u25D2"];
|
|
264
238
|
let frameIndex = 0;
|
|
265
239
|
this.interval = setInterval(() => {
|
|
266
|
-
const frame =
|
|
240
|
+
const frame = pc2.cyan(frames[frameIndex]);
|
|
267
241
|
const filled = Math.floor(this.progress / 100 * barWidth);
|
|
268
242
|
const empty = barWidth - filled;
|
|
269
|
-
const bar =
|
|
243
|
+
const bar = pc2.green("\u2588".repeat(filled)) + pc2.dim("\u2591".repeat(empty));
|
|
270
244
|
const pkg = this.packages[this.currentPackage % this.packages.length];
|
|
271
245
|
process.stdout.write(
|
|
272
|
-
`\r${frame} Installing dependencies ${bar} ${
|
|
246
|
+
`\r${frame} Installing dependencies ${bar} ${pc2.dim(pkg)} `
|
|
273
247
|
);
|
|
274
|
-
frameIndex = (frameIndex + 1) %
|
|
248
|
+
frameIndex = (frameIndex + 1) % frames.length;
|
|
275
249
|
if (this.progress < 95) {
|
|
276
250
|
this.progress += Math.random() * 3;
|
|
277
251
|
if (this.progress > (this.currentPackage + 1) * 12) {
|
|
@@ -287,7 +261,7 @@ var InstallProgress = class {
|
|
|
287
261
|
}
|
|
288
262
|
process.stdout.write("\x1B[?25h");
|
|
289
263
|
process.stdout.write("\r\x1B[K");
|
|
290
|
-
const icon = success ?
|
|
264
|
+
const icon = success ? pc2.green("\u2713") : pc2.red("\u2717");
|
|
291
265
|
const message = success ? "Dependencies installed" : "Failed to install dependencies";
|
|
292
266
|
console.log(`${icon} ${message}`);
|
|
293
267
|
}
|
|
@@ -295,12 +269,54 @@ var InstallProgress = class {
|
|
|
295
269
|
function createInstallProgress() {
|
|
296
270
|
return new InstallProgress();
|
|
297
271
|
}
|
|
272
|
+
var GitProgress = class {
|
|
273
|
+
interval = null;
|
|
274
|
+
phases = [
|
|
275
|
+
"Initializing repository",
|
|
276
|
+
"Setting up version control",
|
|
277
|
+
"Creating .git directory",
|
|
278
|
+
"Configuring git objects",
|
|
279
|
+
"Preparing initial branch"
|
|
280
|
+
];
|
|
281
|
+
phaseIndex = 0;
|
|
282
|
+
dotCount = 0;
|
|
283
|
+
start() {
|
|
284
|
+
process.stdout.write("\x1B[?25l");
|
|
285
|
+
const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
286
|
+
let frameIndex = 0;
|
|
287
|
+
this.interval = setInterval(() => {
|
|
288
|
+
const frame = pc2.cyan(frames[frameIndex]);
|
|
289
|
+
const phase = this.phases[this.phaseIndex % this.phases.length];
|
|
290
|
+
const dots = ".".repeat(this.dotCount % 4);
|
|
291
|
+
process.stdout.write(`\r${frame} ${pc2.dim(phase)}${dots} `);
|
|
292
|
+
frameIndex = (frameIndex + 1) % frames.length;
|
|
293
|
+
this.dotCount++;
|
|
294
|
+
if (this.dotCount % 8 === 0) {
|
|
295
|
+
this.phaseIndex++;
|
|
296
|
+
}
|
|
297
|
+
}, 80);
|
|
298
|
+
}
|
|
299
|
+
stop(success = true) {
|
|
300
|
+
if (this.interval) {
|
|
301
|
+
clearInterval(this.interval);
|
|
302
|
+
this.interval = null;
|
|
303
|
+
}
|
|
304
|
+
process.stdout.write("\x1B[?25h");
|
|
305
|
+
process.stdout.write("\r\x1B[K");
|
|
306
|
+
const icon = success ? pc2.green("\u2713") : pc2.red("\u2717");
|
|
307
|
+
const message = success ? "Git repository initialized" : "Failed to initialize git";
|
|
308
|
+
console.log(`${icon} ${message}`);
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
function createGitProgress() {
|
|
312
|
+
return new GitProgress();
|
|
313
|
+
}
|
|
298
314
|
|
|
299
315
|
// src/generators/package-json.ts
|
|
300
316
|
function generatePackageJson(options) {
|
|
301
317
|
const isSolana = options.provider === "arkworks";
|
|
302
318
|
const dependencies = {
|
|
303
|
-
"@izi-noir/sdk": "^0.1.
|
|
319
|
+
"@izi-noir/sdk": "^0.1.5",
|
|
304
320
|
"@noir-lang/acvm_js": "1.0.0-beta.13-1d260df.nightly",
|
|
305
321
|
"@noir-lang/noirc_abi": "1.0.0-beta.13-1d260df.nightly",
|
|
306
322
|
"react": "^18.3.1",
|
|
@@ -599,9 +615,17 @@ export default defineConfig({
|
|
|
599
615
|
},
|
|
600
616
|
define: {
|
|
601
617
|
"global": "globalThis",
|
|
618
|
+
"process.env": {},
|
|
602
619
|
},
|
|
603
620
|
optimizeDeps: {
|
|
604
|
-
exclude: [
|
|
621
|
+
exclude: [
|
|
622
|
+
"@noir-lang/noir_wasm",
|
|
623
|
+
"@noir-lang/noir_js",
|
|
624
|
+
"@noir-lang/acvm_js",
|
|
625
|
+
"@noir-lang/noirc_abi",
|
|
626
|
+
"@aztec/bb.js",
|
|
627
|
+
"@izi-noir/sdk",
|
|
628
|
+
],
|
|
605
629
|
include: ["buffer", "util"],
|
|
606
630
|
},
|
|
607
631
|
build: {
|
|
@@ -636,6 +660,9 @@ function generateIndexHtml(options) {
|
|
|
636
660
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
637
661
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
638
662
|
<title>${options.projectName} - ZK Proof Demo</title>
|
|
663
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
664
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
665
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
|
639
666
|
</head>
|
|
640
667
|
<body>
|
|
641
668
|
<div id="root"></div>
|
|
@@ -768,10 +795,17 @@ import { Chain, Network } from '@izi-noir/sdk';` : "";
|
|
|
768
795
|
</div>
|
|
769
796
|
|
|
770
797
|
<div className="verify-box">
|
|
798
|
+
{publicInputs && (
|
|
799
|
+
<div className="public-inputs-display">
|
|
800
|
+
<span>Public inputs:</span>
|
|
801
|
+
<code>{JSON.stringify(publicInputs)}</code>
|
|
802
|
+
</div>
|
|
803
|
+
)}
|
|
771
804
|
<button
|
|
772
805
|
onClick={handleVerify}
|
|
773
806
|
disabled={!vkAccount || isVerifying}
|
|
774
807
|
className="btn btn-primary"
|
|
808
|
+
style={{ marginTop: publicInputs ? '0.5rem' : 0 }}
|
|
775
809
|
>
|
|
776
810
|
{isVerifying ? 'Verifying...' : verified ? 'Verified!' : 'Verify On-Chain'}
|
|
777
811
|
</button>
|
|
@@ -839,6 +873,7 @@ function App() {
|
|
|
839
873
|
const [proofTime, setProofTime] = useState<number | null>(null);
|
|
840
874
|
const [proofError, setProofError] = useState<string | null>(null);
|
|
841
875
|
const [localVerified, setLocalVerified] = useState<boolean | null>(null);
|
|
876
|
+
const [publicInputs, setPublicInputs] = useState<string[] | null>(null);
|
|
842
877
|
|
|
843
878
|
// IziNoir instance
|
|
844
879
|
const [iziInstance, setIziInstance] = useState<IziNoir | null>(null);
|
|
@@ -855,6 +890,7 @@ ${solanaState}
|
|
|
855
890
|
setProof(null);
|
|
856
891
|
setProofTime(null);
|
|
857
892
|
setLocalVerified(null);
|
|
893
|
+
setPublicInputs(null);
|
|
858
894
|
setNoirCode(null);
|
|
859
895
|
}
|
|
860
896
|
}, [selectedCircuit]);
|
|
@@ -927,6 +963,7 @@ ${solanaState}
|
|
|
927
963
|
const endTime = performance.now();
|
|
928
964
|
setProof(proofBytes);
|
|
929
965
|
setProofTime(Math.round(endTime - startTime));
|
|
966
|
+
setPublicInputs(publicInputsHex);
|
|
930
967
|
} catch (error) {
|
|
931
968
|
console.error('Proof generation error:', error);
|
|
932
969
|
setProofError((error as Error).message);
|
|
@@ -940,7 +977,7 @@ ${solanaHandlers}
|
|
|
940
977
|
<div className="app">
|
|
941
978
|
<header>
|
|
942
979
|
<h1>${options.projectName}</h1>
|
|
943
|
-
<p>
|
|
980
|
+
<p>Built with IZI-NOIR</p>
|
|
944
981
|
</header>
|
|
945
982
|
|
|
946
983
|
<main>
|
|
@@ -1103,29 +1140,65 @@ function capitalizeFirst(str) {
|
|
|
1103
1140
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
1104
1141
|
}
|
|
1105
1142
|
function generateAppCss() {
|
|
1106
|
-
return
|
|
1143
|
+
return `/* ================================
|
|
1144
|
+
CSS Variables - IZI-NOIR Theme
|
|
1145
|
+
================================ */
|
|
1146
|
+
:root {
|
|
1147
|
+
--solana-purple: #9945FF;
|
|
1148
|
+
--solana-green: #14F195;
|
|
1149
|
+
--noir-primary: #050505;
|
|
1150
|
+
--noir-elevated: #0A0A0A;
|
|
1151
|
+
--noir-orange: #FF6B35;
|
|
1152
|
+
--izi-cyan: #00D4FF;
|
|
1153
|
+
--text: #ffffff;
|
|
1154
|
+
--text-muted: #888888;
|
|
1155
|
+
--border: rgba(255, 255, 255, 0.1);
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
/* ================================
|
|
1159
|
+
Reset & Base Styles
|
|
1160
|
+
================================ */
|
|
1161
|
+
* {
|
|
1107
1162
|
box-sizing: border-box;
|
|
1108
1163
|
margin: 0;
|
|
1109
1164
|
padding: 0;
|
|
1110
1165
|
}
|
|
1111
1166
|
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
--green: #14F195;
|
|
1115
|
-
--bg: #0a0a0a;
|
|
1116
|
-
--bg-elevated: #111111;
|
|
1117
|
-
--border: #222222;
|
|
1118
|
-
--text: #ffffff;
|
|
1119
|
-
--text-muted: #888888;
|
|
1167
|
+
html {
|
|
1168
|
+
scroll-behavior: smooth;
|
|
1120
1169
|
}
|
|
1121
1170
|
|
|
1122
1171
|
body {
|
|
1123
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI',
|
|
1124
|
-
background: var(--
|
|
1172
|
+
font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
1173
|
+
background-color: var(--noir-primary);
|
|
1174
|
+
/* Noise texture + radial gradients like the main frontend */
|
|
1175
|
+
background-image:
|
|
1176
|
+
url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)' opacity='0.015'/%3E%3C/svg%3E"),
|
|
1177
|
+
radial-gradient(
|
|
1178
|
+
ellipse 60% 40% at 50% 30%,
|
|
1179
|
+
rgba(153, 69, 255, 0.06) 0%,
|
|
1180
|
+
transparent 50%
|
|
1181
|
+
),
|
|
1182
|
+
radial-gradient(
|
|
1183
|
+
ellipse 80% 50% at 50% 110%,
|
|
1184
|
+
rgba(20, 241, 149, 0.04) 0%,
|
|
1185
|
+
transparent 40%
|
|
1186
|
+
);
|
|
1187
|
+
background-attachment: fixed;
|
|
1125
1188
|
color: var(--text);
|
|
1126
1189
|
line-height: 1.6;
|
|
1190
|
+
-webkit-font-smoothing: antialiased;
|
|
1191
|
+
-moz-osx-font-smoothing: grayscale;
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
::selection {
|
|
1195
|
+
background: rgba(153, 69, 255, 0.3);
|
|
1196
|
+
color: white;
|
|
1127
1197
|
}
|
|
1128
1198
|
|
|
1199
|
+
/* ================================
|
|
1200
|
+
Layout
|
|
1201
|
+
================================ */
|
|
1129
1202
|
.app {
|
|
1130
1203
|
min-height: 100vh;
|
|
1131
1204
|
display: flex;
|
|
@@ -1136,11 +1209,14 @@ header {
|
|
|
1136
1209
|
padding: 2rem;
|
|
1137
1210
|
text-align: center;
|
|
1138
1211
|
border-bottom: 1px solid var(--border);
|
|
1212
|
+
backdrop-filter: blur(8px);
|
|
1213
|
+
background: rgba(0, 0, 0, 0.3);
|
|
1139
1214
|
}
|
|
1140
1215
|
|
|
1141
1216
|
header h1 {
|
|
1142
1217
|
font-size: 2rem;
|
|
1143
|
-
|
|
1218
|
+
font-weight: 700;
|
|
1219
|
+
background: linear-gradient(135deg, var(--solana-purple) 0%, var(--noir-orange) 100%);
|
|
1144
1220
|
-webkit-background-clip: text;
|
|
1145
1221
|
-webkit-text-fill-color: transparent;
|
|
1146
1222
|
background-clip: text;
|
|
@@ -1159,34 +1235,51 @@ main {
|
|
|
1159
1235
|
width: 100%;
|
|
1160
1236
|
}
|
|
1161
1237
|
|
|
1238
|
+
/* ================================
|
|
1239
|
+
Section Cards - Glassmorphism
|
|
1240
|
+
================================ */
|
|
1162
1241
|
.section {
|
|
1163
1242
|
margin-bottom: 2rem;
|
|
1164
1243
|
padding: 1.5rem;
|
|
1165
|
-
background:
|
|
1244
|
+
background: rgba(0, 0, 0, 0.3);
|
|
1245
|
+
backdrop-filter: blur(8px);
|
|
1166
1246
|
border: 1px solid var(--border);
|
|
1167
|
-
border-radius:
|
|
1247
|
+
border-radius: 16px;
|
|
1248
|
+
transition: border-color 0.3s ease, box-shadow 0.3s ease;
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
.section:hover {
|
|
1252
|
+
border-color: rgba(255, 255, 255, 0.15);
|
|
1253
|
+
box-shadow: 0 0 30px rgba(153, 69, 255, 0.1);
|
|
1168
1254
|
}
|
|
1169
1255
|
|
|
1170
1256
|
.section h2 {
|
|
1171
1257
|
font-size: 1.25rem;
|
|
1258
|
+
font-weight: 600;
|
|
1172
1259
|
margin-bottom: 1rem;
|
|
1173
1260
|
color: var(--text);
|
|
1174
1261
|
}
|
|
1175
1262
|
|
|
1263
|
+
/* ================================
|
|
1264
|
+
Form Controls
|
|
1265
|
+
================================ */
|
|
1176
1266
|
.select {
|
|
1177
1267
|
width: 100%;
|
|
1178
|
-
padding: 0.75rem;
|
|
1179
|
-
background:
|
|
1268
|
+
padding: 0.75rem 1rem;
|
|
1269
|
+
background: rgba(0, 0, 0, 0.4);
|
|
1180
1270
|
border: 1px solid var(--border);
|
|
1181
|
-
border-radius:
|
|
1271
|
+
border-radius: 12px;
|
|
1182
1272
|
color: var(--text);
|
|
1273
|
+
font-family: inherit;
|
|
1183
1274
|
font-size: 1rem;
|
|
1184
1275
|
cursor: pointer;
|
|
1276
|
+
transition: border-color 0.3s ease, box-shadow 0.3s ease;
|
|
1185
1277
|
}
|
|
1186
1278
|
|
|
1187
1279
|
.select:focus {
|
|
1188
1280
|
outline: none;
|
|
1189
|
-
border-color: var(--purple);
|
|
1281
|
+
border-color: var(--solana-purple);
|
|
1282
|
+
box-shadow: 0 0 20px rgba(153, 69, 255, 0.2);
|
|
1190
1283
|
}
|
|
1191
1284
|
|
|
1192
1285
|
.inputs-grid {
|
|
@@ -1211,71 +1304,93 @@ main {
|
|
|
1211
1304
|
|
|
1212
1305
|
.input-badge {
|
|
1213
1306
|
font-size: 0.625rem;
|
|
1214
|
-
padding: 0.125rem 0.
|
|
1215
|
-
border-radius:
|
|
1307
|
+
padding: 0.125rem 0.5rem;
|
|
1308
|
+
border-radius: 6px;
|
|
1216
1309
|
text-transform: uppercase;
|
|
1217
1310
|
font-weight: 600;
|
|
1311
|
+
letter-spacing: 0.05em;
|
|
1218
1312
|
}
|
|
1219
1313
|
|
|
1220
1314
|
.input-badge.public {
|
|
1221
|
-
background:
|
|
1222
|
-
color:
|
|
1315
|
+
background: rgba(20, 241, 149, 0.2);
|
|
1316
|
+
color: var(--solana-green);
|
|
1317
|
+
border: 1px solid rgba(20, 241, 149, 0.3);
|
|
1223
1318
|
}
|
|
1224
1319
|
|
|
1225
1320
|
.input-badge.private {
|
|
1226
|
-
background:
|
|
1227
|
-
color:
|
|
1321
|
+
background: rgba(153, 69, 255, 0.2);
|
|
1322
|
+
color: var(--solana-purple);
|
|
1323
|
+
border: 1px solid rgba(153, 69, 255, 0.3);
|
|
1228
1324
|
}
|
|
1229
1325
|
|
|
1230
1326
|
.input-group input {
|
|
1231
|
-
padding: 0.75rem;
|
|
1232
|
-
background:
|
|
1327
|
+
padding: 0.75rem 1rem;
|
|
1328
|
+
background: rgba(0, 0, 0, 0.4);
|
|
1233
1329
|
border: 1px solid var(--border);
|
|
1234
|
-
border-radius:
|
|
1330
|
+
border-radius: 12px;
|
|
1235
1331
|
color: var(--text);
|
|
1332
|
+
font-family: 'JetBrains Mono', 'Fira Code', monospace;
|
|
1236
1333
|
font-size: 1rem;
|
|
1334
|
+
transition: border-color 0.3s ease, box-shadow 0.3s ease;
|
|
1237
1335
|
}
|
|
1238
1336
|
|
|
1239
1337
|
.input-group input:focus {
|
|
1240
1338
|
outline: none;
|
|
1241
|
-
border-color: var(--purple);
|
|
1339
|
+
border-color: var(--solana-purple);
|
|
1340
|
+
box-shadow: 0 0 20px rgba(153, 69, 255, 0.2);
|
|
1242
1341
|
}
|
|
1243
1342
|
|
|
1343
|
+
/* ================================
|
|
1344
|
+
Buttons - Animated Gradient
|
|
1345
|
+
================================ */
|
|
1244
1346
|
.btn {
|
|
1245
1347
|
padding: 0.75rem 1.5rem;
|
|
1246
1348
|
font-size: 1rem;
|
|
1247
1349
|
font-weight: 600;
|
|
1248
1350
|
border: none;
|
|
1249
|
-
border-radius:
|
|
1351
|
+
border-radius: 12px;
|
|
1250
1352
|
cursor: pointer;
|
|
1251
|
-
transition: all 0.
|
|
1353
|
+
transition: all 0.3s ease;
|
|
1252
1354
|
}
|
|
1253
1355
|
|
|
1254
1356
|
.btn:disabled {
|
|
1255
|
-
opacity: 0.
|
|
1357
|
+
opacity: 0.4;
|
|
1256
1358
|
cursor: not-allowed;
|
|
1257
1359
|
}
|
|
1258
1360
|
|
|
1259
1361
|
.btn-primary {
|
|
1260
|
-
background: linear-gradient(
|
|
1362
|
+
background: linear-gradient(
|
|
1363
|
+
90deg,
|
|
1364
|
+
var(--solana-purple) 0%,
|
|
1365
|
+
var(--solana-purple) 50%,
|
|
1366
|
+
var(--solana-green) 100%
|
|
1367
|
+
);
|
|
1368
|
+
background-size: 200% 100%;
|
|
1369
|
+
background-position: 0% 0%;
|
|
1261
1370
|
color: white;
|
|
1371
|
+
box-shadow: 0 0 20px rgba(153, 69, 255, 0.3);
|
|
1262
1372
|
}
|
|
1263
1373
|
|
|
1264
1374
|
.btn-primary:hover:not(:disabled) {
|
|
1375
|
+
background-position: 100% 0%;
|
|
1265
1376
|
transform: translateY(-2px);
|
|
1266
|
-
box-shadow: 0
|
|
1377
|
+
box-shadow: 0 0 30px rgba(20, 241, 149, 0.3);
|
|
1267
1378
|
}
|
|
1268
1379
|
|
|
1269
1380
|
.btn-secondary {
|
|
1270
|
-
background:
|
|
1381
|
+
background: rgba(0, 0, 0, 0.4);
|
|
1271
1382
|
border: 1px solid var(--border);
|
|
1272
1383
|
color: var(--text);
|
|
1273
1384
|
}
|
|
1274
1385
|
|
|
1275
1386
|
.btn-secondary:hover:not(:disabled) {
|
|
1276
|
-
border-color: var(--purple);
|
|
1387
|
+
border-color: var(--solana-purple);
|
|
1388
|
+
box-shadow: 0 0 20px rgba(153, 69, 255, 0.15);
|
|
1277
1389
|
}
|
|
1278
1390
|
|
|
1391
|
+
/* ================================
|
|
1392
|
+
Results Grid
|
|
1393
|
+
================================ */
|
|
1279
1394
|
.results {
|
|
1280
1395
|
display: grid;
|
|
1281
1396
|
grid-template-columns: repeat(3, 1fr);
|
|
@@ -1283,22 +1398,36 @@ main {
|
|
|
1283
1398
|
margin-top: 1rem;
|
|
1284
1399
|
}
|
|
1285
1400
|
|
|
1401
|
+
@media (max-width: 640px) {
|
|
1402
|
+
.results {
|
|
1403
|
+
grid-template-columns: 1fr;
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1286
1407
|
.result-card {
|
|
1287
|
-
padding:
|
|
1288
|
-
background:
|
|
1408
|
+
padding: 1.25rem;
|
|
1409
|
+
background: rgba(0, 0, 0, 0.3);
|
|
1289
1410
|
border: 1px solid var(--border);
|
|
1290
|
-
border-radius:
|
|
1411
|
+
border-radius: 12px;
|
|
1291
1412
|
text-align: center;
|
|
1413
|
+
transition: border-color 0.3s ease, transform 0.3s ease, box-shadow 0.3s ease;
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
.result-card:hover {
|
|
1417
|
+
border-color: rgba(255, 255, 255, 0.15);
|
|
1418
|
+
transform: translateY(-4px);
|
|
1419
|
+
box-shadow: 0 0 25px rgba(153, 69, 255, 0.15);
|
|
1292
1420
|
}
|
|
1293
1421
|
|
|
1294
1422
|
.result-value {
|
|
1295
|
-
font-size: 1.
|
|
1296
|
-
font-weight:
|
|
1423
|
+
font-size: 1.5rem;
|
|
1424
|
+
font-weight: 700;
|
|
1425
|
+
font-family: 'JetBrains Mono', monospace;
|
|
1297
1426
|
display: block;
|
|
1298
1427
|
}
|
|
1299
1428
|
|
|
1300
1429
|
.result-value.success {
|
|
1301
|
-
color: var(--green);
|
|
1430
|
+
color: var(--solana-green);
|
|
1302
1431
|
}
|
|
1303
1432
|
|
|
1304
1433
|
.result-value.error {
|
|
@@ -1309,22 +1438,40 @@ main {
|
|
|
1309
1438
|
font-size: 0.75rem;
|
|
1310
1439
|
color: var(--text-muted);
|
|
1311
1440
|
text-transform: uppercase;
|
|
1312
|
-
|
|
1441
|
+
letter-spacing: 0.1em;
|
|
1442
|
+
margin-top: 0.5rem;
|
|
1313
1443
|
display: block;
|
|
1314
1444
|
}
|
|
1315
1445
|
|
|
1446
|
+
/* ================================
|
|
1447
|
+
Messages
|
|
1448
|
+
================================ */
|
|
1316
1449
|
.error {
|
|
1317
|
-
color: #
|
|
1450
|
+
color: #ff6b6b;
|
|
1318
1451
|
font-size: 0.875rem;
|
|
1319
1452
|
margin-top: 0.5rem;
|
|
1453
|
+
padding: 0.75rem 1rem;
|
|
1454
|
+
background: rgba(255, 107, 107, 0.1);
|
|
1455
|
+
border: 1px solid rgba(255, 107, 107, 0.2);
|
|
1456
|
+
border-radius: 8px;
|
|
1457
|
+
word-break: break-word;
|
|
1458
|
+
overflow-wrap: break-word;
|
|
1459
|
+
max-width: 100%;
|
|
1320
1460
|
}
|
|
1321
1461
|
|
|
1322
1462
|
.success {
|
|
1323
|
-
color: var(--green);
|
|
1463
|
+
color: var(--solana-green);
|
|
1324
1464
|
font-size: 0.875rem;
|
|
1325
1465
|
margin-top: 0.5rem;
|
|
1466
|
+
padding: 0.75rem 1rem;
|
|
1467
|
+
background: rgba(20, 241, 149, 0.1);
|
|
1468
|
+
border: 1px solid rgba(20, 241, 149, 0.2);
|
|
1469
|
+
border-radius: 8px;
|
|
1326
1470
|
}
|
|
1327
1471
|
|
|
1472
|
+
/* ================================
|
|
1473
|
+
Noir Code Details
|
|
1474
|
+
================================ */
|
|
1328
1475
|
.noir-details {
|
|
1329
1476
|
margin-top: 1rem;
|
|
1330
1477
|
}
|
|
@@ -1333,12 +1480,21 @@ main {
|
|
|
1333
1480
|
cursor: pointer;
|
|
1334
1481
|
color: var(--text-muted);
|
|
1335
1482
|
font-size: 0.875rem;
|
|
1483
|
+
padding: 0.5rem 0;
|
|
1484
|
+
transition: color 0.3s ease;
|
|
1336
1485
|
}
|
|
1337
1486
|
|
|
1338
1487
|
.noir-details summary:hover {
|
|
1339
|
-
color: var(--
|
|
1488
|
+
color: var(--solana-purple);
|
|
1340
1489
|
}
|
|
1341
1490
|
|
|
1491
|
+
.noir-details[open] summary {
|
|
1492
|
+
color: var(--solana-purple);
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
/* ================================
|
|
1496
|
+
Deploy & Verify Section
|
|
1497
|
+
================================ */
|
|
1342
1498
|
.deploy-verify-row {
|
|
1343
1499
|
display: grid;
|
|
1344
1500
|
grid-template-columns: 1fr 1fr;
|
|
@@ -1346,38 +1502,118 @@ main {
|
|
|
1346
1502
|
margin-top: 1rem;
|
|
1347
1503
|
}
|
|
1348
1504
|
|
|
1505
|
+
@media (max-width: 640px) {
|
|
1506
|
+
.deploy-verify-row {
|
|
1507
|
+
grid-template-columns: 1fr;
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1349
1511
|
.deploy-box,
|
|
1350
1512
|
.verify-box {
|
|
1351
|
-
padding:
|
|
1352
|
-
background:
|
|
1513
|
+
padding: 1.25rem;
|
|
1514
|
+
background: rgba(0, 0, 0, 0.3);
|
|
1515
|
+
border: 1px solid var(--border);
|
|
1516
|
+
border-radius: 12px;
|
|
1517
|
+
overflow: hidden;
|
|
1518
|
+
word-break: break-word;
|
|
1519
|
+
transition: border-color 0.3s ease, box-shadow 0.3s ease;
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
.deploy-box:hover,
|
|
1523
|
+
.verify-box:hover {
|
|
1524
|
+
border-color: rgba(255, 255, 255, 0.15);
|
|
1525
|
+
box-shadow: 0 0 20px rgba(153, 69, 255, 0.1);
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
.public-inputs-display {
|
|
1529
|
+
margin-top: 0.75rem;
|
|
1530
|
+
font-size: 0.75rem;
|
|
1531
|
+
color: var(--text-muted);
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
.public-inputs-display code {
|
|
1535
|
+
display: block;
|
|
1536
|
+
margin-top: 0.5rem;
|
|
1537
|
+
padding: 0.75rem;
|
|
1538
|
+
background: rgba(0, 0, 0, 0.4);
|
|
1353
1539
|
border: 1px solid var(--border);
|
|
1354
1540
|
border-radius: 8px;
|
|
1541
|
+
font-family: 'JetBrains Mono', monospace;
|
|
1542
|
+
font-size: 0.7rem;
|
|
1543
|
+
overflow-x: auto;
|
|
1544
|
+
max-width: 100%;
|
|
1355
1545
|
}
|
|
1356
1546
|
|
|
1547
|
+
/* ================================
|
|
1548
|
+
Footer
|
|
1549
|
+
================================ */
|
|
1357
1550
|
footer {
|
|
1358
1551
|
padding: 1.5rem;
|
|
1359
1552
|
text-align: center;
|
|
1360
1553
|
border-top: 1px solid var(--border);
|
|
1554
|
+
backdrop-filter: blur(8px);
|
|
1555
|
+
background: rgba(0, 0, 0, 0.3);
|
|
1361
1556
|
color: var(--text-muted);
|
|
1362
1557
|
font-size: 0.875rem;
|
|
1363
1558
|
}
|
|
1364
1559
|
|
|
1365
1560
|
footer a {
|
|
1366
|
-
color: var(--purple);
|
|
1561
|
+
color: var(--solana-purple);
|
|
1367
1562
|
text-decoration: none;
|
|
1563
|
+
transition: color 0.3s ease;
|
|
1368
1564
|
}
|
|
1369
1565
|
|
|
1370
1566
|
footer a:hover {
|
|
1371
|
-
|
|
1567
|
+
color: var(--solana-green);
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
/* ================================
|
|
1571
|
+
Custom Scrollbar
|
|
1572
|
+
================================ */
|
|
1573
|
+
::-webkit-scrollbar {
|
|
1574
|
+
width: 6px;
|
|
1575
|
+
height: 6px;
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
::-webkit-scrollbar-track {
|
|
1579
|
+
background: var(--noir-primary);
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1582
|
+
::-webkit-scrollbar-thumb {
|
|
1583
|
+
background: rgba(255, 255, 255, 0.1);
|
|
1584
|
+
border-radius: 9999px;
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
::-webkit-scrollbar-thumb:hover {
|
|
1588
|
+
background: rgba(255, 255, 255, 0.2);
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
/* ================================
|
|
1592
|
+
Accessibility - Reduced Motion
|
|
1593
|
+
================================ */
|
|
1594
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1595
|
+
*,
|
|
1596
|
+
*::before,
|
|
1597
|
+
*::after {
|
|
1598
|
+
animation-duration: 0.01ms !important;
|
|
1599
|
+
animation-iteration-count: 1 !important;
|
|
1600
|
+
transition-duration: 0.01ms !important;
|
|
1601
|
+
}
|
|
1372
1602
|
}
|
|
1373
1603
|
`;
|
|
1374
1604
|
}
|
|
1375
1605
|
function generateIndexCss() {
|
|
1376
|
-
return
|
|
1606
|
+
return `/* Base styles - fonts are loaded in index.html */
|
|
1607
|
+
body {
|
|
1377
1608
|
margin: 0;
|
|
1378
1609
|
-webkit-font-smoothing: antialiased;
|
|
1379
1610
|
-moz-osx-font-smoothing: grayscale;
|
|
1380
1611
|
}
|
|
1612
|
+
|
|
1613
|
+
/* Ensure mono font is applied to code elements */
|
|
1614
|
+
code, pre, .mono {
|
|
1615
|
+
font-family: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', monospace;
|
|
1616
|
+
}
|
|
1381
1617
|
`;
|
|
1382
1618
|
}
|
|
1383
1619
|
function generateViteEnvDts() {
|
|
@@ -1535,6 +1771,810 @@ export function EditableCodeBlock({
|
|
|
1535
1771
|
`;
|
|
1536
1772
|
}
|
|
1537
1773
|
|
|
1774
|
+
// src/generators/claude-skills.ts
|
|
1775
|
+
function generateClaudeSkills() {
|
|
1776
|
+
return {
|
|
1777
|
+
".claude/skills/izi-noir-circuit-patterns/SKILL.md": SKILL_MD,
|
|
1778
|
+
".claude/skills/izi-noir-circuit-patterns/assets/valid-examples.ts": VALID_EXAMPLES_TS,
|
|
1779
|
+
".claude/skills/izi-noir-circuit-patterns/assets/operator-mapping.md": OPERATOR_MAPPING_MD
|
|
1780
|
+
};
|
|
1781
|
+
}
|
|
1782
|
+
var SKILL_MD = `---
|
|
1783
|
+
name: izi-noir-circuit-patterns
|
|
1784
|
+
description: >
|
|
1785
|
+
Patterns for writing JS/TS code that transpiles to Noir ZK circuits.
|
|
1786
|
+
Trigger: When writing circuit functions, IziNoir API, Solana deployment, Provider/Chain selection, or JS-to-Noir transformations.
|
|
1787
|
+
license: MIT
|
|
1788
|
+
metadata:
|
|
1789
|
+
author: izi-noir
|
|
1790
|
+
version: "2.0"
|
|
1791
|
+
scope: [sdk, frontend, solana-contracts]
|
|
1792
|
+
auto_invoke:
|
|
1793
|
+
- "circuit function"
|
|
1794
|
+
- "createProof"
|
|
1795
|
+
- "IziNoir"
|
|
1796
|
+
- "assert statement"
|
|
1797
|
+
- "JS to Noir"
|
|
1798
|
+
- "zero knowledge"
|
|
1799
|
+
- "Solana proof"
|
|
1800
|
+
- "Chain.Solana"
|
|
1801
|
+
- "Provider.Arkworks"
|
|
1802
|
+
allowed-tools: Read, Glob, Grep
|
|
1803
|
+
---
|
|
1804
|
+
|
|
1805
|
+
# IZI-NOIR Circuit Patterns
|
|
1806
|
+
|
|
1807
|
+
Patterns for writing JavaScript/TypeScript code that the SDK can parse and transpile 1:1 to Noir ZK circuits, with Solana on-chain verification support.
|
|
1808
|
+
|
|
1809
|
+
## When to Use
|
|
1810
|
+
|
|
1811
|
+
- Writing circuit functions for \`createProof()\`
|
|
1812
|
+
- Using the IziNoir class API (init, compile, prove, deploy, verifyOnChain)
|
|
1813
|
+
- Deploying proofs to Solana
|
|
1814
|
+
- Choosing between providers (Arkworks, Barretenberg)
|
|
1815
|
+
- Debugging parsing or Noir generation errors
|
|
1816
|
+
- Understanding JS \u2192 Noir type mapping
|
|
1817
|
+
|
|
1818
|
+
## Architecture Overview
|
|
1819
|
+
|
|
1820
|
+
\`\`\`
|
|
1821
|
+
JS Function \u2192 AcornParser \u2192 NoirGenerator \u2192 Noir WASM \u2192 ArkworksWasm \u2192 Solana
|
|
1822
|
+
\`\`\`
|
|
1823
|
+
|
|
1824
|
+
**Pipeline layers:**
|
|
1825
|
+
1. **Domain Layer** - Core types: Circuit, Proof, VerifyingKey, Provider, Chain, Network
|
|
1826
|
+
2. **Application Layer** - Services: NoirGenerator, createProof orchestration
|
|
1827
|
+
3. **Infrastructure Layer** - Parsers (Acorn), Compilers (Noir WASM), Provers (Arkworks/BB)
|
|
1828
|
+
|
|
1829
|
+
## IziNoir Class API
|
|
1830
|
+
|
|
1831
|
+
### Initialization
|
|
1832
|
+
|
|
1833
|
+
\`\`\`typescript
|
|
1834
|
+
import { IziNoir, Provider, Chain, Network } from '@izi-noir/sdk';
|
|
1835
|
+
|
|
1836
|
+
// Basic initialization (development)
|
|
1837
|
+
const izi = await IziNoir.init({
|
|
1838
|
+
provider: Provider.Arkworks,
|
|
1839
|
+
});
|
|
1840
|
+
|
|
1841
|
+
// With Solana chain targeting
|
|
1842
|
+
const izi = await IziNoir.init({
|
|
1843
|
+
provider: Provider.Arkworks,
|
|
1844
|
+
chain: Chain.Solana,
|
|
1845
|
+
network: Network.Devnet,
|
|
1846
|
+
});
|
|
1847
|
+
\`\`\`
|
|
1848
|
+
|
|
1849
|
+
### Providers
|
|
1850
|
+
|
|
1851
|
+
| Provider | Use Case | Proof Size | Notes |
|
|
1852
|
+
|----------|----------|------------|-------|
|
|
1853
|
+
| \`Provider.Arkworks\` | **Solana production** | 256 bytes | Groth16, native syscalls |
|
|
1854
|
+
| \`Provider.Barretenberg\` | Development/testing | ~2KB | UltraPlonk, faster setup |
|
|
1855
|
+
|
|
1856
|
+
**Rule:** Always use \`Provider.Arkworks\` for Solana on-chain verification.
|
|
1857
|
+
|
|
1858
|
+
### Chain and Network
|
|
1859
|
+
|
|
1860
|
+
\`\`\`typescript
|
|
1861
|
+
// Chain enum
|
|
1862
|
+
Chain.Solana // Format proofs for Solana verification
|
|
1863
|
+
Chain.EVM // Format proofs for EVM verification (future)
|
|
1864
|
+
|
|
1865
|
+
// Network enum
|
|
1866
|
+
Network.Devnet // Solana devnet
|
|
1867
|
+
Network.Mainnet // Solana mainnet
|
|
1868
|
+
Network.Localnet // Local validator
|
|
1869
|
+
\`\`\`
|
|
1870
|
+
|
|
1871
|
+
### Creating Proofs
|
|
1872
|
+
|
|
1873
|
+
\`\`\`typescript
|
|
1874
|
+
// Define circuit function
|
|
1875
|
+
const balanceProof = (
|
|
1876
|
+
[threshold]: [number], // public inputs
|
|
1877
|
+
[balance]: [number] // private inputs
|
|
1878
|
+
) => {
|
|
1879
|
+
assert(balance >= threshold);
|
|
1880
|
+
};
|
|
1881
|
+
|
|
1882
|
+
// Generate proof
|
|
1883
|
+
const result = await izi.createProof(
|
|
1884
|
+
balanceProof,
|
|
1885
|
+
[100], // public: threshold
|
|
1886
|
+
[1500] // private: actual balance
|
|
1887
|
+
);
|
|
1888
|
+
|
|
1889
|
+
console.log(result.verified); // true
|
|
1890
|
+
console.log(result.proof); // Uint8Array (256 bytes for Arkworks)
|
|
1891
|
+
\`\`\`
|
|
1892
|
+
|
|
1893
|
+
### Solana Deployment
|
|
1894
|
+
|
|
1895
|
+
\`\`\`typescript
|
|
1896
|
+
// Deploy verifying key to Solana
|
|
1897
|
+
const deployment = await izi.deploy(balanceProof, {
|
|
1898
|
+
payer: wallet,
|
|
1899
|
+
network: Network.Devnet,
|
|
1900
|
+
});
|
|
1901
|
+
|
|
1902
|
+
console.log(deployment.vkAccount); // PublicKey of VK account
|
|
1903
|
+
console.log(deployment.programId); // Verifier program ID
|
|
1904
|
+
|
|
1905
|
+
// Verify proof on-chain
|
|
1906
|
+
const txSignature = await izi.verifyOnChain(result.proof, {
|
|
1907
|
+
vkAccount: deployment.vkAccount,
|
|
1908
|
+
payer: wallet,
|
|
1909
|
+
});
|
|
1910
|
+
\`\`\`
|
|
1911
|
+
|
|
1912
|
+
### SolanaProofData Structure
|
|
1913
|
+
|
|
1914
|
+
When \`chain: Chain.Solana\`, proof data is formatted as:
|
|
1915
|
+
|
|
1916
|
+
\`\`\`typescript
|
|
1917
|
+
interface SolanaProofData {
|
|
1918
|
+
verifyingKey: {
|
|
1919
|
+
nr_pubinputs: number;
|
|
1920
|
+
vk_alpha_g1: number[]; // 64 bytes
|
|
1921
|
+
vk_beta_g2: number[]; // 128 bytes
|
|
1922
|
+
vk_gamma_g2: number[]; // 128 bytes
|
|
1923
|
+
vk_delta_g2: number[]; // 128 bytes
|
|
1924
|
+
vk_ic: number[][]; // (nr_pubinputs + 1) \xD7 64 bytes
|
|
1925
|
+
};
|
|
1926
|
+
proof: {
|
|
1927
|
+
ar: number[]; // 64 bytes - A point (G1)
|
|
1928
|
+
bs: number[]; // 128 bytes - B point (G2)
|
|
1929
|
+
krs: number[]; // 64 bytes - C point (G1)
|
|
1930
|
+
};
|
|
1931
|
+
publicInputs: number[][]; // Each input as 32-byte array
|
|
1932
|
+
}
|
|
1933
|
+
\`\`\`
|
|
1934
|
+
|
|
1935
|
+
## Critical Patterns
|
|
1936
|
+
|
|
1937
|
+
### 1. Function Signature (REQUIRED)
|
|
1938
|
+
|
|
1939
|
+
\`\`\`typescript
|
|
1940
|
+
// MUST use array destructuring for both parameters
|
|
1941
|
+
([publicInputs], [privateInputs]) => {
|
|
1942
|
+
// body with assert statements
|
|
1943
|
+
}
|
|
1944
|
+
|
|
1945
|
+
// Example with multiple inputs
|
|
1946
|
+
([expected, threshold], [secret, salt]) => {
|
|
1947
|
+
assert(secret * secret == expected);
|
|
1948
|
+
assert(secret > threshold);
|
|
1949
|
+
}
|
|
1950
|
+
\`\`\`
|
|
1951
|
+
|
|
1952
|
+
- First array: **public inputs** (marked \`pub\` in Noir)
|
|
1953
|
+
- Second array: **private inputs** (witness values)
|
|
1954
|
+
- Arrow function or function expression
|
|
1955
|
+
- Body: block statement \`{ }\` or single expression
|
|
1956
|
+
|
|
1957
|
+
### 2. Mutability Convention
|
|
1958
|
+
|
|
1959
|
+
\`\`\`typescript
|
|
1960
|
+
// Immutable (default)
|
|
1961
|
+
let x = a + b; // \u2192 let x: Field = a + b;
|
|
1962
|
+
|
|
1963
|
+
// Mutable (use mut_ prefix)
|
|
1964
|
+
let mut_sum = 0; // \u2192 let mut sum: Field = 0;
|
|
1965
|
+
mut_sum = mut_sum + 1; // \u2192 sum = sum + 1;
|
|
1966
|
+
\`\`\`
|
|
1967
|
+
|
|
1968
|
+
**Rule:** Prefix variable name with \`mut_\` for mutable variables. The prefix is stripped in generated Noir.
|
|
1969
|
+
|
|
1970
|
+
### 3. Operators
|
|
1971
|
+
|
|
1972
|
+
| JavaScript | Noir | Notes |
|
|
1973
|
+
|------------|------|-------|
|
|
1974
|
+
| \`==\` / \`===\` | \`==\` | Both map to equality |
|
|
1975
|
+
| \`!=\` / \`!==\` | \`!=\` | Both map to inequality |
|
|
1976
|
+
| \`+\` | \`+\` | Addition |
|
|
1977
|
+
| \`-\` | \`-\` | Subtraction |
|
|
1978
|
+
| \`*\` | \`*\` | Multiplication |
|
|
1979
|
+
| \`/\` | \`/\` | Division |
|
|
1980
|
+
| \`%\` | \`%\` | Modulo |
|
|
1981
|
+
| \`<\` | \`<\` | Less than |
|
|
1982
|
+
| \`>\` | \`>\` | Greater than |
|
|
1983
|
+
| \`<=\` | \`<=\` | Less than or equal |
|
|
1984
|
+
| \`>=\` | \`>=\` | Greater than or equal |
|
|
1985
|
+
| \`&&\` | \`&\` | **Converted to bitwise AND** |
|
|
1986
|
+
| \`\\|\\|\` | \`\\|\` | **Converted to bitwise OR** |
|
|
1987
|
+
| \`!\` | \`!\` | Logical NOT |
|
|
1988
|
+
| \`-x\` | \`-x\` | Negation |
|
|
1989
|
+
|
|
1990
|
+
**Warning:** \`&&\` and \`||\` are converted to bitwise operators in Noir!
|
|
1991
|
+
|
|
1992
|
+
### 4. Statements
|
|
1993
|
+
|
|
1994
|
+
#### Assert
|
|
1995
|
+
\`\`\`typescript
|
|
1996
|
+
assert(condition); // Basic assertion
|
|
1997
|
+
assert(condition, "message"); // With error message
|
|
1998
|
+
\`\`\`
|
|
1999
|
+
|
|
2000
|
+
#### Variable Declaration
|
|
2001
|
+
\`\`\`typescript
|
|
2002
|
+
let x = a + b; // Immutable
|
|
2003
|
+
let mut_counter = 0; // Mutable (mut_ prefix)
|
|
2004
|
+
const y = 10; // Immutable (const supported)
|
|
2005
|
+
\`\`\`
|
|
2006
|
+
|
|
2007
|
+
#### Assignment (mutable only)
|
|
2008
|
+
\`\`\`typescript
|
|
2009
|
+
mut_x = mut_x + 1; // Only valid for mut_ variables
|
|
2010
|
+
\`\`\`
|
|
2011
|
+
|
|
2012
|
+
#### If/Else
|
|
2013
|
+
\`\`\`typescript
|
|
2014
|
+
if (condition) {
|
|
2015
|
+
// consequent
|
|
2016
|
+
} else {
|
|
2017
|
+
// alternate
|
|
2018
|
+
}
|
|
2019
|
+
\`\`\`
|
|
2020
|
+
|
|
2021
|
+
#### For Loop
|
|
2022
|
+
\`\`\`typescript
|
|
2023
|
+
// Exclusive range (i < end)
|
|
2024
|
+
for (let i = 0; i < 10; i++) { }
|
|
2025
|
+
// \u2192 for i in 0..10 { }
|
|
2026
|
+
|
|
2027
|
+
// Inclusive range (i <= end)
|
|
2028
|
+
for (let i = 1; i <= 5; i++) { }
|
|
2029
|
+
// \u2192 for i in 1..=5 { }
|
|
2030
|
+
|
|
2031
|
+
// Variable bounds
|
|
2032
|
+
for (let i = start; i < end; i++) { }
|
|
2033
|
+
// \u2192 for i in start..end { }
|
|
2034
|
+
\`\`\`
|
|
2035
|
+
|
|
2036
|
+
**Loop constraints:**
|
|
2037
|
+
- Init: \`let i = start\`
|
|
2038
|
+
- Test: \`i < end\` or \`i <= end\`
|
|
2039
|
+
- Update: \`i++\`, \`++i\`, or \`i = i + 1\`
|
|
2040
|
+
|
|
2041
|
+
### 5. Expressions
|
|
2042
|
+
|
|
2043
|
+
#### Literals
|
|
2044
|
+
\`\`\`typescript
|
|
2045
|
+
5 // number \u2192 Field
|
|
2046
|
+
100n // bigint \u2192 Field
|
|
2047
|
+
"string" // string literal
|
|
2048
|
+
0x1234 // hex value
|
|
2049
|
+
true / false // boolean \u2192 bool
|
|
2050
|
+
\`\`\`
|
|
2051
|
+
|
|
2052
|
+
#### Arrays
|
|
2053
|
+
\`\`\`typescript
|
|
2054
|
+
let arr = [a, b, c]; // \u2192 [Field; 3]
|
|
2055
|
+
arr[0] // static index
|
|
2056
|
+
arr[i] // dynamic index
|
|
2057
|
+
arr.length // \u2192 arr.len()
|
|
2058
|
+
\`\`\`
|
|
2059
|
+
|
|
2060
|
+
#### Ternary (conditional)
|
|
2061
|
+
\`\`\`typescript
|
|
2062
|
+
let result = cond ? a : b;
|
|
2063
|
+
// \u2192 let result: Field = if cond { a } else { b };
|
|
2064
|
+
\`\`\`
|
|
2065
|
+
|
|
2066
|
+
#### Method Calls
|
|
2067
|
+
\`\`\`typescript
|
|
2068
|
+
arr.len() // Array length
|
|
2069
|
+
\`\`\`
|
|
2070
|
+
|
|
2071
|
+
### 6. Type Mapping
|
|
2072
|
+
|
|
2073
|
+
| JavaScript | Noir Type | Notes |
|
|
2074
|
+
|------------|-----------|-------|
|
|
2075
|
+
| \`number\` | \`Field\` | Default for all numerics |
|
|
2076
|
+
| \`bigint\` | \`Field\` | Converted to string |
|
|
2077
|
+
| \`boolean\` | \`bool\` | Only \`true\`/\`false\` literals |
|
|
2078
|
+
| \`[a,b,c]\` | \`[Field; 3]\` | Fixed-size array |
|
|
2079
|
+
|
|
2080
|
+
**Default type:** Everything is \`Field\` unless explicitly boolean.
|
|
2081
|
+
|
|
2082
|
+
## NOT Supported
|
|
2083
|
+
|
|
2084
|
+
These JavaScript features **cannot** be parsed:
|
|
2085
|
+
|
|
2086
|
+
- Object literals \`{ key: value }\`
|
|
2087
|
+
- Destructuring (except function parameters)
|
|
2088
|
+
- Spread operator \`...\`
|
|
2089
|
+
- Rest parameters \`...args\`
|
|
2090
|
+
- Template literals \`\\\`\${x}\\\`\`
|
|
2091
|
+
- Async/await
|
|
2092
|
+
- While/do-while loops
|
|
2093
|
+
- Switch statements
|
|
2094
|
+
- Function declarations inside circuit
|
|
2095
|
+
- Closures over external variables
|
|
2096
|
+
- Class methods
|
|
2097
|
+
- Regular expressions
|
|
2098
|
+
- Try/catch
|
|
2099
|
+
- Break/continue
|
|
2100
|
+
- Return statements
|
|
2101
|
+
- Computed property assignment \`arr[i] = x\`
|
|
2102
|
+
|
|
2103
|
+
## Code Examples
|
|
2104
|
+
|
|
2105
|
+
See [assets/valid-examples.ts](assets/valid-examples.ts) for complete working examples.
|
|
2106
|
+
|
|
2107
|
+
## CLI: create-izi-noir
|
|
2108
|
+
|
|
2109
|
+
Scaffold new projects quickly:
|
|
2110
|
+
|
|
2111
|
+
\`\`\`bash
|
|
2112
|
+
# Interactive mode
|
|
2113
|
+
npx create-izi-noir
|
|
2114
|
+
|
|
2115
|
+
# Quick setup
|
|
2116
|
+
npx create-izi-noir my-project --yes
|
|
2117
|
+
|
|
2118
|
+
# With specific template
|
|
2119
|
+
npx create-izi-noir my-project --template balance-proof
|
|
2120
|
+
|
|
2121
|
+
# With specific provider
|
|
2122
|
+
npx create-izi-noir my-project --provider arkworks
|
|
2123
|
+
\`\`\`
|
|
2124
|
+
|
|
2125
|
+
**Templates:**
|
|
2126
|
+
- \`default\` - Balance proof + age verification circuits
|
|
2127
|
+
- \`minimal\` - Blank canvas with empty circuit
|
|
2128
|
+
- \`balance-proof\` - Just balance proof circuit
|
|
2129
|
+
|
|
2130
|
+
**Options:**
|
|
2131
|
+
- \`-t, --template <template>\` - Template to use
|
|
2132
|
+
- \`-p, --provider <provider>\` - Proving provider (arkworks/barretenberg)
|
|
2133
|
+
- \`-y, --yes\` - Skip prompts
|
|
2134
|
+
- \`--skip-install\` - Skip npm install
|
|
2135
|
+
- \`--skip-git\` - Skip git init
|
|
2136
|
+
|
|
2137
|
+
## Frontend Integration (Vite)
|
|
2138
|
+
|
|
2139
|
+
\`\`\`typescript
|
|
2140
|
+
// vite.config.ts
|
|
2141
|
+
export default defineConfig({
|
|
2142
|
+
optimizeDeps: {
|
|
2143
|
+
exclude: ['@noir-lang/noirc_abi', '@noir-lang/acvm_js'],
|
|
2144
|
+
},
|
|
2145
|
+
});
|
|
2146
|
+
|
|
2147
|
+
// main.ts - Initialize WASM before use
|
|
2148
|
+
import initNoirC from '@noir-lang/noirc_abi';
|
|
2149
|
+
import initACVM from '@noir-lang/acvm_js';
|
|
2150
|
+
import acvm from '@noir-lang/acvm_js/web/acvm_js_bg.wasm?url';
|
|
2151
|
+
import noirc from '@noir-lang/noirc_abi/web/noirc_abi_wasm_bg.wasm?url';
|
|
2152
|
+
import { markWasmInitialized, IziNoir, Provider } from '@izi-noir/sdk';
|
|
2153
|
+
|
|
2154
|
+
await Promise.all([initACVM(fetch(acvm)), initNoirC(fetch(noirc))]);
|
|
2155
|
+
markWasmInitialized();
|
|
2156
|
+
|
|
2157
|
+
// Now IziNoir is ready
|
|
2158
|
+
const izi = await IziNoir.init({ provider: Provider.Arkworks });
|
|
2159
|
+
\`\`\`
|
|
2160
|
+
|
|
2161
|
+
## Resources
|
|
2162
|
+
|
|
2163
|
+
- [Operator Mapping Table](assets/operator-mapping.md)
|
|
2164
|
+
- Parser: \`packages/sdk/src/infra/parser/AcornParser.ts\`
|
|
2165
|
+
- Generator: \`packages/sdk/src/application/services/NoirGenerator.ts\`
|
|
2166
|
+
- IziNoir Class: \`packages/sdk/src/IziNoir.ts\`
|
|
2167
|
+
`;
|
|
2168
|
+
var VALID_EXAMPLES_TS = `/**
|
|
2169
|
+
* Valid JS/TS Circuit Patterns for IZI-NOIR
|
|
2170
|
+
*
|
|
2171
|
+
* Each example shows the JS input and the generated Noir output.
|
|
2172
|
+
* All examples are valid and can be used with createProof().
|
|
2173
|
+
*/
|
|
2174
|
+
|
|
2175
|
+
// Declare assert for TypeScript (SDK parses it, not executed)
|
|
2176
|
+
declare function assert(condition: boolean, message?: string): void;
|
|
2177
|
+
|
|
2178
|
+
// =============================================================================
|
|
2179
|
+
// Example 1: Basic Assertion (simplest circuit)
|
|
2180
|
+
// =============================================================================
|
|
2181
|
+
/**
|
|
2182
|
+
* JS Input:
|
|
2183
|
+
*/
|
|
2184
|
+
export const basicAssertion = ([expected]: number[], [secret]: number[]) => {
|
|
2185
|
+
assert(secret * secret == expected);
|
|
2186
|
+
};
|
|
2187
|
+
/**
|
|
2188
|
+
* Generated Noir:
|
|
2189
|
+
* \`\`\`noir
|
|
2190
|
+
* fn main(secret: Field, expected: pub Field) {
|
|
2191
|
+
* assert(secret * secret == expected);
|
|
2192
|
+
* }
|
|
2193
|
+
* \`\`\`
|
|
2194
|
+
*/
|
|
2195
|
+
|
|
2196
|
+
// =============================================================================
|
|
2197
|
+
// Example 2: Multiple Inputs with Arithmetic
|
|
2198
|
+
// =============================================================================
|
|
2199
|
+
/**
|
|
2200
|
+
* JS Input:
|
|
2201
|
+
*/
|
|
2202
|
+
export const multipleInputs = (
|
|
2203
|
+
[sum, product]: number[],
|
|
2204
|
+
[a, b]: number[]
|
|
2205
|
+
) => {
|
|
2206
|
+
assert(a + b == sum);
|
|
2207
|
+
assert(a * b == product);
|
|
2208
|
+
};
|
|
2209
|
+
/**
|
|
2210
|
+
* Generated Noir:
|
|
2211
|
+
* \`\`\`noir
|
|
2212
|
+
* fn main(a: Field, b: Field, sum: pub Field, product: pub Field) {
|
|
2213
|
+
* assert(a + b == sum);
|
|
2214
|
+
* assert(a * b == product);
|
|
2215
|
+
* }
|
|
2216
|
+
* \`\`\`
|
|
2217
|
+
*/
|
|
2218
|
+
|
|
2219
|
+
// =============================================================================
|
|
2220
|
+
// Example 3: Variables and Mutability
|
|
2221
|
+
// =============================================================================
|
|
2222
|
+
/**
|
|
2223
|
+
* JS Input:
|
|
2224
|
+
*/
|
|
2225
|
+
export const variablesAndMutability = (
|
|
2226
|
+
[expected]: number[],
|
|
2227
|
+
[a, b]: number[]
|
|
2228
|
+
) => {
|
|
2229
|
+
// Immutable variable
|
|
2230
|
+
let sum = a + b;
|
|
2231
|
+
|
|
2232
|
+
// Mutable variable (mut_ prefix)
|
|
2233
|
+
let mut_result = 0;
|
|
2234
|
+
mut_result = sum * 2;
|
|
2235
|
+
|
|
2236
|
+
assert(mut_result == expected);
|
|
2237
|
+
};
|
|
2238
|
+
/**
|
|
2239
|
+
* Generated Noir:
|
|
2240
|
+
* \`\`\`noir
|
|
2241
|
+
* fn main(a: Field, b: Field, expected: pub Field) {
|
|
2242
|
+
* let sum: Field = a + b;
|
|
2243
|
+
* let mut result: Field = 0;
|
|
2244
|
+
* result = sum * 2;
|
|
2245
|
+
* assert(result == expected);
|
|
2246
|
+
* }
|
|
2247
|
+
* \`\`\`
|
|
2248
|
+
*/
|
|
2249
|
+
|
|
2250
|
+
// =============================================================================
|
|
2251
|
+
// Example 4: Conditional Logic (if/else)
|
|
2252
|
+
// =============================================================================
|
|
2253
|
+
/**
|
|
2254
|
+
* JS Input:
|
|
2255
|
+
*/
|
|
2256
|
+
export const conditionalLogic = (
|
|
2257
|
+
[threshold]: number[],
|
|
2258
|
+
[value]: number[]
|
|
2259
|
+
) => {
|
|
2260
|
+
let mut_result = 0;
|
|
2261
|
+
|
|
2262
|
+
if (value > threshold) {
|
|
2263
|
+
mut_result = 1;
|
|
2264
|
+
} else {
|
|
2265
|
+
mut_result = 0;
|
|
2266
|
+
}
|
|
2267
|
+
|
|
2268
|
+
assert(mut_result == 1);
|
|
2269
|
+
};
|
|
2270
|
+
/**
|
|
2271
|
+
* Generated Noir:
|
|
2272
|
+
* \`\`\`noir
|
|
2273
|
+
* fn main(value: Field, threshold: pub Field) {
|
|
2274
|
+
* let mut result: Field = 0;
|
|
2275
|
+
* if value > threshold {
|
|
2276
|
+
* result = 1;
|
|
2277
|
+
* } else {
|
|
2278
|
+
* result = 0;
|
|
2279
|
+
* }
|
|
2280
|
+
* assert(result == 1);
|
|
2281
|
+
* }
|
|
2282
|
+
* \`\`\`
|
|
2283
|
+
*/
|
|
2284
|
+
|
|
2285
|
+
// =============================================================================
|
|
2286
|
+
// Example 5: Ternary Expression
|
|
2287
|
+
// =============================================================================
|
|
2288
|
+
/**
|
|
2289
|
+
* JS Input:
|
|
2290
|
+
*/
|
|
2291
|
+
export const ternaryExpression = (
|
|
2292
|
+
[threshold]: number[],
|
|
2293
|
+
[value]: number[]
|
|
2294
|
+
) => {
|
|
2295
|
+
// Ternary converts to Noir if-expression
|
|
2296
|
+
let result = value > threshold ? 1 : 0;
|
|
2297
|
+
assert(result == 1);
|
|
2298
|
+
};
|
|
2299
|
+
/**
|
|
2300
|
+
* Generated Noir:
|
|
2301
|
+
* \`\`\`noir
|
|
2302
|
+
* fn main(value: Field, threshold: pub Field) {
|
|
2303
|
+
* let result: Field = if value > threshold { 1 } else { 0 };
|
|
2304
|
+
* assert(result == 1);
|
|
2305
|
+
* }
|
|
2306
|
+
* \`\`\`
|
|
2307
|
+
*/
|
|
2308
|
+
|
|
2309
|
+
// =============================================================================
|
|
2310
|
+
// Example 6: For Loop with Array
|
|
2311
|
+
// =============================================================================
|
|
2312
|
+
/**
|
|
2313
|
+
* JS Input:
|
|
2314
|
+
*/
|
|
2315
|
+
export const forLoopWithArray = (
|
|
2316
|
+
[expected]: number[],
|
|
2317
|
+
[a, b, c, d]: number[]
|
|
2318
|
+
) => {
|
|
2319
|
+
let arr = [a, b, c, d];
|
|
2320
|
+
let mut_sum = 0;
|
|
2321
|
+
|
|
2322
|
+
// Exclusive range: i < 4 \u2192 for i in 0..4
|
|
2323
|
+
for (let i = 0; i < 4; i++) {
|
|
2324
|
+
mut_sum = mut_sum + arr[i];
|
|
2325
|
+
}
|
|
2326
|
+
|
|
2327
|
+
assert(mut_sum == expected);
|
|
2328
|
+
};
|
|
2329
|
+
/**
|
|
2330
|
+
* Generated Noir:
|
|
2331
|
+
* \`\`\`noir
|
|
2332
|
+
* fn main(a: Field, b: Field, c: Field, d: Field, expected: pub Field) {
|
|
2333
|
+
* let arr: [Field; 4] = [a, b, c, d];
|
|
2334
|
+
* let mut sum: Field = 0;
|
|
2335
|
+
* for i in 0..4 {
|
|
2336
|
+
* sum = sum + arr[i];
|
|
2337
|
+
* }
|
|
2338
|
+
* assert(sum == expected);
|
|
2339
|
+
* }
|
|
2340
|
+
* \`\`\`
|
|
2341
|
+
*/
|
|
2342
|
+
|
|
2343
|
+
// =============================================================================
|
|
2344
|
+
// Example 7: Inclusive Range Loop
|
|
2345
|
+
// =============================================================================
|
|
2346
|
+
/**
|
|
2347
|
+
* JS Input:
|
|
2348
|
+
*/
|
|
2349
|
+
export const inclusiveRangeLoop = ([expected]: number[], [n]: number[]) => {
|
|
2350
|
+
let mut_sum = 0;
|
|
2351
|
+
|
|
2352
|
+
// Inclusive range: i <= n \u2192 for i in 1..=n
|
|
2353
|
+
for (let i = 1; i <= n; i++) {
|
|
2354
|
+
mut_sum = mut_sum + i;
|
|
2355
|
+
}
|
|
2356
|
+
|
|
2357
|
+
assert(mut_sum == expected);
|
|
2358
|
+
};
|
|
2359
|
+
/**
|
|
2360
|
+
* Generated Noir:
|
|
2361
|
+
* \`\`\`noir
|
|
2362
|
+
* fn main(n: Field, expected: pub Field) {
|
|
2363
|
+
* let mut sum: Field = 0;
|
|
2364
|
+
* for i in 1..=n {
|
|
2365
|
+
* sum = sum + i;
|
|
2366
|
+
* }
|
|
2367
|
+
* assert(sum == expected);
|
|
2368
|
+
* }
|
|
2369
|
+
* \`\`\`
|
|
2370
|
+
*/
|
|
2371
|
+
|
|
2372
|
+
// =============================================================================
|
|
2373
|
+
// Example 8: Nested Control Flow
|
|
2374
|
+
// =============================================================================
|
|
2375
|
+
/**
|
|
2376
|
+
* JS Input:
|
|
2377
|
+
*/
|
|
2378
|
+
export const nestedControlFlow = ([max]: number[], []: number[]) => {
|
|
2379
|
+
let mut_evenSum = 0;
|
|
2380
|
+
|
|
2381
|
+
for (let i = 1; i <= max; i++) {
|
|
2382
|
+
// Nested if inside loop
|
|
2383
|
+
if (i % 2 == 0) {
|
|
2384
|
+
mut_evenSum = mut_evenSum + i;
|
|
2385
|
+
}
|
|
2386
|
+
}
|
|
2387
|
+
|
|
2388
|
+
assert(mut_evenSum > 0);
|
|
2389
|
+
};
|
|
2390
|
+
/**
|
|
2391
|
+
* Generated Noir:
|
|
2392
|
+
* \`\`\`noir
|
|
2393
|
+
* fn main(max: pub Field) {
|
|
2394
|
+
* let mut evenSum: Field = 0;
|
|
2395
|
+
* for i in 1..=max {
|
|
2396
|
+
* if i % 2 == 0 {
|
|
2397
|
+
* evenSum = evenSum + i;
|
|
2398
|
+
* }
|
|
2399
|
+
* }
|
|
2400
|
+
* assert(evenSum > 0);
|
|
2401
|
+
* }
|
|
2402
|
+
* \`\`\`
|
|
2403
|
+
*/
|
|
2404
|
+
|
|
2405
|
+
// =============================================================================
|
|
2406
|
+
// Example 9: Comparison Operators
|
|
2407
|
+
// =============================================================================
|
|
2408
|
+
/**
|
|
2409
|
+
* JS Input:
|
|
2410
|
+
*/
|
|
2411
|
+
export const comparisonOperators = (
|
|
2412
|
+
[min, max]: number[],
|
|
2413
|
+
[value]: number[]
|
|
2414
|
+
) => {
|
|
2415
|
+
// All comparison operators
|
|
2416
|
+
assert(value >= min);
|
|
2417
|
+
assert(value <= max);
|
|
2418
|
+
assert(value != 0);
|
|
2419
|
+
|
|
2420
|
+
// Combined with arithmetic
|
|
2421
|
+
let doubled = value * 2;
|
|
2422
|
+
assert(doubled < max * 2);
|
|
2423
|
+
};
|
|
2424
|
+
/**
|
|
2425
|
+
* Generated Noir:
|
|
2426
|
+
* \`\`\`noir
|
|
2427
|
+
* fn main(value: Field, min: pub Field, max: pub Field) {
|
|
2428
|
+
* assert(value >= min);
|
|
2429
|
+
* assert(value <= max);
|
|
2430
|
+
* assert(value != 0);
|
|
2431
|
+
* let doubled: Field = value * 2;
|
|
2432
|
+
* assert(doubled < max * 2);
|
|
2433
|
+
* }
|
|
2434
|
+
* \`\`\`
|
|
2435
|
+
*/
|
|
2436
|
+
|
|
2437
|
+
// =============================================================================
|
|
2438
|
+
// Example 10: Array Length Access
|
|
2439
|
+
// =============================================================================
|
|
2440
|
+
/**
|
|
2441
|
+
* JS Input:
|
|
2442
|
+
*/
|
|
2443
|
+
export const arrayLengthAccess = (
|
|
2444
|
+
[expectedLen]: number[],
|
|
2445
|
+
[a, b, c]: number[]
|
|
2446
|
+
) => {
|
|
2447
|
+
let arr = [a, b, c];
|
|
2448
|
+
|
|
2449
|
+
// .length converts to .len() in Noir
|
|
2450
|
+
assert(arr.length == expectedLen);
|
|
2451
|
+
};
|
|
2452
|
+
/**
|
|
2453
|
+
* Generated Noir:
|
|
2454
|
+
* \`\`\`noir
|
|
2455
|
+
* fn main(a: Field, b: Field, c: Field, expectedLen: pub Field) {
|
|
2456
|
+
* let arr: [Field; 3] = [a, b, c];
|
|
2457
|
+
* assert(arr.len() == expectedLen);
|
|
2458
|
+
* }
|
|
2459
|
+
* \`\`\`
|
|
2460
|
+
*/
|
|
2461
|
+
`;
|
|
2462
|
+
var OPERATOR_MAPPING_MD = `# JavaScript to Noir Mapping Reference
|
|
2463
|
+
|
|
2464
|
+
Complete mapping of JavaScript constructs to their Noir equivalents.
|
|
2465
|
+
|
|
2466
|
+
## Binary Operators
|
|
2467
|
+
|
|
2468
|
+
| JavaScript | Noir | Category | Example |
|
|
2469
|
+
|------------|------|----------|---------|
|
|
2470
|
+
| \`==\` | \`==\` | Equality | \`a == b\` \u2192 \`a == b\` |
|
|
2471
|
+
| \`===\` | \`==\` | Strict equality | \`a === b\` \u2192 \`a == b\` |
|
|
2472
|
+
| \`!=\` | \`!=\` | Inequality | \`a != b\` \u2192 \`a != b\` |
|
|
2473
|
+
| \`!==\` | \`!=\` | Strict inequality | \`a !== b\` \u2192 \`a != b\` |
|
|
2474
|
+
| \`+\` | \`+\` | Addition | \`a + b\` \u2192 \`a + b\` |
|
|
2475
|
+
| \`-\` | \`-\` | Subtraction | \`a - b\` \u2192 \`a - b\` |
|
|
2476
|
+
| \`*\` | \`*\` | Multiplication | \`a * b\` \u2192 \`a * b\` |
|
|
2477
|
+
| \`/\` | \`/\` | Division | \`a / b\` \u2192 \`a / b\` |
|
|
2478
|
+
| \`%\` | \`%\` | Modulo | \`a % b\` \u2192 \`a % b\` |
|
|
2479
|
+
| \`<\` | \`<\` | Less than | \`a < b\` \u2192 \`a < b\` |
|
|
2480
|
+
| \`>\` | \`>\` | Greater than | \`a > b\` \u2192 \`a > b\` |
|
|
2481
|
+
| \`<=\` | \`<=\` | Less or equal | \`a <= b\` \u2192 \`a <= b\` |
|
|
2482
|
+
| \`>=\` | \`>=\` | Greater or equal | \`a >= b\` \u2192 \`a >= b\` |
|
|
2483
|
+
| \`&&\` | \`&\` | Logical AND | \`a && b\` \u2192 \`a & b\` |
|
|
2484
|
+
| \`\\|\\|\` | \`\\|\` | Logical OR | \`a \\|\\| b\` \u2192 \`a \\| b\` |
|
|
2485
|
+
|
|
2486
|
+
> **Warning:** Logical operators \`&&\` and \`||\` are converted to bitwise operators \`&\` and \`|\` in Noir.
|
|
2487
|
+
|
|
2488
|
+
## Unary Operators
|
|
2489
|
+
|
|
2490
|
+
| JavaScript | Noir | Category | Example |
|
|
2491
|
+
|------------|------|----------|---------|
|
|
2492
|
+
| \`!\` | \`!\` | Logical NOT | \`!a\` \u2192 \`!a\` |
|
|
2493
|
+
| \`-\` (prefix) | \`-\` | Negation | \`-a\` \u2192 \`-a\` |
|
|
2494
|
+
|
|
2495
|
+
## Type Mapping
|
|
2496
|
+
|
|
2497
|
+
| JavaScript Type | Noir Type | Notes |
|
|
2498
|
+
|-----------------|-----------|-------|
|
|
2499
|
+
| \`number\` | \`Field\` | Default numeric type |
|
|
2500
|
+
| \`bigint\` | \`Field\` | Converted to string |
|
|
2501
|
+
| \`boolean\` | \`bool\` | Only \`true\`/\`false\` literals |
|
|
2502
|
+
| \`string\` | String literal | Quoted in Noir |
|
|
2503
|
+
| \`number[]\` | \`[Field; N]\` | Fixed-size array |
|
|
2504
|
+
|
|
2505
|
+
## Statement Mapping
|
|
2506
|
+
|
|
2507
|
+
| JavaScript | Noir | Notes |
|
|
2508
|
+
|------------|------|-------|
|
|
2509
|
+
| \`assert(cond)\` | \`assert(cond)\` | Direct mapping |
|
|
2510
|
+
| \`assert(cond, "msg")\` | \`assert(cond, "msg")\` | With message |
|
|
2511
|
+
| \`let x = expr\` | \`let x: Type = expr\` | Immutable |
|
|
2512
|
+
| \`let mut_x = expr\` | \`let mut x: Type = expr\` | Mutable (prefix stripped) |
|
|
2513
|
+
| \`const x = expr\` | \`let x: Type = expr\` | Treated as let |
|
|
2514
|
+
| \`mut_x = expr\` | \`x = expr\` | Assignment (mutable only) |
|
|
2515
|
+
| \`if (c) { } else { }\` | \`if c { } else { }\` | No parens in Noir |
|
|
2516
|
+
| \`for (let i=s; i<e; i++)\` | \`for i in s..e { }\` | Exclusive range |
|
|
2517
|
+
| \`for (let i=s; i<=e; i++)\` | \`for i in s..=e { }\` | Inclusive range |
|
|
2518
|
+
|
|
2519
|
+
## Expression Mapping
|
|
2520
|
+
|
|
2521
|
+
| JavaScript | Noir | Notes |
|
|
2522
|
+
|------------|------|-------|
|
|
2523
|
+
| \`x\` | \`x\` | Identifier |
|
|
2524
|
+
| \`5\` | \`5\` | Number literal |
|
|
2525
|
+
| \`100n\` | \`100\` | BigInt to Field |
|
|
2526
|
+
| \`"str"\` | \`"str"\` | String literal |
|
|
2527
|
+
| \`0x1234\` | \`0x1234\` | Hex preserved |
|
|
2528
|
+
| \`true\` / \`false\` | \`true\` / \`false\` | Boolean |
|
|
2529
|
+
| \`[a, b, c]\` | \`[a, b, c]\` | Array literal |
|
|
2530
|
+
| \`arr[i]\` | \`arr[i]\` | Index access |
|
|
2531
|
+
| \`arr.length\` | \`arr.len()\` | Length method |
|
|
2532
|
+
| \`c ? a : b\` | \`if c { a } else { b }\` | Ternary \u2192 if expr |
|
|
2533
|
+
| \`a + b * c\` | \`a + b * c\` | Precedence preserved |
|
|
2534
|
+
|
|
2535
|
+
## Function Parameter Mapping
|
|
2536
|
+
|
|
2537
|
+
\`\`\`typescript
|
|
2538
|
+
// JavaScript
|
|
2539
|
+
([pub1, pub2], [priv1, priv2]) => { ... }
|
|
2540
|
+
|
|
2541
|
+
// Noir (private params first, then public with 'pub')
|
|
2542
|
+
fn main(priv1: Field, priv2: Field, pub1: pub Field, pub2: pub Field) { ... }
|
|
2543
|
+
\`\`\`
|
|
2544
|
+
|
|
2545
|
+
## Variable Naming Convention
|
|
2546
|
+
|
|
2547
|
+
| JavaScript | Noir | Rule |
|
|
2548
|
+
|------------|------|------|
|
|
2549
|
+
| \`x\` | \`x\` | Regular immutable |
|
|
2550
|
+
| \`mut_x\` | \`mut x\` | Mutable (prefix stripped) |
|
|
2551
|
+
| \`mut_counter\` | \`mut counter\` | Mutable (prefix stripped) |
|
|
2552
|
+
|
|
2553
|
+
## For Loop Patterns
|
|
2554
|
+
|
|
2555
|
+
| JavaScript | Noir | Range Type |
|
|
2556
|
+
|------------|------|------------|
|
|
2557
|
+
| \`for (let i = 0; i < 10; i++)\` | \`for i in 0..10\` | Exclusive |
|
|
2558
|
+
| \`for (let i = 1; i <= 5; i++)\` | \`for i in 1..=5\` | Inclusive |
|
|
2559
|
+
| \`for (let i = start; i < end; i++)\` | \`for i in start..end\` | Variable bounds |
|
|
2560
|
+
| \`for (let i = 0; i < n; i = i + 1)\` | \`for i in 0..n\` | Alternative update |
|
|
2561
|
+
|
|
2562
|
+
## Unsupported Operators
|
|
2563
|
+
|
|
2564
|
+
| JavaScript | Status | Alternative |
|
|
2565
|
+
|------------|--------|-------------|
|
|
2566
|
+
| \`**\` (exponent) | Not supported | Use multiplication loop |
|
|
2567
|
+
| \`<<\` (left shift) | Not supported | N/A |
|
|
2568
|
+
| \`>>\` (right shift) | Not supported | N/A |
|
|
2569
|
+
| \`>>>\` (unsigned shift) | Not supported | N/A |
|
|
2570
|
+
| \`&\` (bitwise AND) | Use \`&&\` | Converted to \`&\` |
|
|
2571
|
+
| \`\\|\` (bitwise OR) | Use \`\\|\\|\` | Converted to \`\\|\` |
|
|
2572
|
+
| \`^\` (XOR) | Not supported | N/A |
|
|
2573
|
+
| \`~\` (NOT) | Not supported | N/A |
|
|
2574
|
+
| \`in\` | Not supported | N/A |
|
|
2575
|
+
| \`instanceof\` | Not supported | N/A |
|
|
2576
|
+
`;
|
|
2577
|
+
|
|
1538
2578
|
// src/commands/init.ts
|
|
1539
2579
|
async function initCommand(projectName, options) {
|
|
1540
2580
|
let projectOptions;
|
|
@@ -1544,7 +2584,8 @@ async function initCommand(projectName, options) {
|
|
|
1544
2584
|
template: options.template,
|
|
1545
2585
|
provider: options.provider,
|
|
1546
2586
|
skipInstall: options.skipInstall,
|
|
1547
|
-
skipGit: options.skipGit
|
|
2587
|
+
skipGit: options.skipGit,
|
|
2588
|
+
aiTool: "none"
|
|
1548
2589
|
};
|
|
1549
2590
|
} else {
|
|
1550
2591
|
projectOptions = await promptProjectOptions({
|
|
@@ -1556,14 +2597,14 @@ async function initCommand(projectName, options) {
|
|
|
1556
2597
|
});
|
|
1557
2598
|
}
|
|
1558
2599
|
if (!projectOptions) {
|
|
1559
|
-
console.log(
|
|
2600
|
+
console.log(pc3.yellow("\nOperation cancelled."));
|
|
1560
2601
|
process.exit(0);
|
|
1561
2602
|
}
|
|
1562
2603
|
const projectDir = path2.resolve(process.cwd(), projectOptions.projectName);
|
|
1563
2604
|
if (await directoryExists(projectDir)) {
|
|
1564
2605
|
if (!await isDirectoryEmpty(projectDir)) {
|
|
1565
2606
|
console.log(
|
|
1566
|
-
|
|
2607
|
+
pc3.red(`
|
|
1567
2608
|
Error: Directory "${projectOptions.projectName}" already exists and is not empty.`)
|
|
1568
2609
|
);
|
|
1569
2610
|
process.exit(1);
|
|
@@ -1575,23 +2616,23 @@ Error: Directory "${projectOptions.projectName}" already exists and is not empty
|
|
|
1575
2616
|
await progress.startThinking();
|
|
1576
2617
|
await new Promise((r) => setTimeout(r, 800));
|
|
1577
2618
|
progress.stopThinking();
|
|
1578
|
-
console.log(
|
|
2619
|
+
console.log(pc3.bold("\n Scaffolding your ZK project...\n"));
|
|
1579
2620
|
await createProjectStructure(projectDir, projectOptions, progress);
|
|
1580
2621
|
progress.showSuccess("Project structure created");
|
|
1581
2622
|
} catch (error) {
|
|
1582
2623
|
progress.showError("Failed to create project structure");
|
|
1583
|
-
console.error(
|
|
2624
|
+
console.error(pc3.red("\n"), error);
|
|
1584
2625
|
process.exit(1);
|
|
1585
2626
|
}
|
|
1586
2627
|
if (!projectOptions.skipGit) {
|
|
1587
|
-
const
|
|
1588
|
-
|
|
2628
|
+
const gitProgress = createGitProgress();
|
|
2629
|
+
gitProgress.start();
|
|
1589
2630
|
try {
|
|
1590
2631
|
execSync("git init", { cwd: projectDir, stdio: "ignore" });
|
|
1591
|
-
|
|
2632
|
+
gitProgress.stop(true);
|
|
1592
2633
|
} catch {
|
|
1593
|
-
|
|
1594
|
-
console.log(
|
|
2634
|
+
gitProgress.stop(false);
|
|
2635
|
+
console.log(pc3.yellow(" Warning: Failed to initialize git repository"));
|
|
1595
2636
|
}
|
|
1596
2637
|
}
|
|
1597
2638
|
if (!projectOptions.skipInstall) {
|
|
@@ -1602,7 +2643,7 @@ Error: Directory "${projectOptions.projectName}" already exists and is not empty
|
|
|
1602
2643
|
installProgress.stop(true);
|
|
1603
2644
|
} catch {
|
|
1604
2645
|
installProgress.stop(false);
|
|
1605
|
-
console.log(
|
|
2646
|
+
console.log(pc3.yellow(' Run "npm install" manually.'));
|
|
1606
2647
|
}
|
|
1607
2648
|
}
|
|
1608
2649
|
printSuccessMessage(projectOptions);
|
|
@@ -1648,34 +2689,47 @@ async function createProjectStructure(projectDir, options, progress) {
|
|
|
1648
2689
|
}
|
|
1649
2690
|
files.push(["circuits/index.ts", generateCircuitsIndex(options.template)]);
|
|
1650
2691
|
files.push(["circuits/types.d.ts", generateCircuitTypes()]);
|
|
2692
|
+
const hasSkills = options.aiTool === "claude";
|
|
1651
2693
|
for (let i = 0; i < files.length; i++) {
|
|
1652
2694
|
const [relativePath, content] = files[i];
|
|
1653
2695
|
await writeFile(path2.join(projectDir, relativePath), content);
|
|
1654
|
-
await progress.reportFile(relativePath, i === files.length - 1);
|
|
2696
|
+
await progress.reportFile(relativePath, !hasSkills && i === files.length - 1);
|
|
2697
|
+
}
|
|
2698
|
+
if (hasSkills) {
|
|
2699
|
+
const skillFiles = generateClaudeSkills();
|
|
2700
|
+
const skillEntries = Object.entries(skillFiles);
|
|
2701
|
+
for (let i = 0; i < skillEntries.length; i++) {
|
|
2702
|
+
const [relativePath, content] = skillEntries[i];
|
|
2703
|
+
await writeFile(path2.join(projectDir, relativePath), content);
|
|
2704
|
+
await progress.reportFile(relativePath, i === skillEntries.length - 1);
|
|
2705
|
+
}
|
|
1655
2706
|
}
|
|
1656
2707
|
}
|
|
1657
2708
|
function printSuccessMessage(options) {
|
|
1658
2709
|
console.log();
|
|
1659
|
-
console.log(
|
|
2710
|
+
console.log(pc3.green("\u2713") + " Project created successfully!");
|
|
2711
|
+
if (options.aiTool === "claude") {
|
|
2712
|
+
console.log(pc3.green("\u2713") + " Claude Code skill installed " + pc3.dim("(.claude/skills/izi-noir-circuit-patterns)"));
|
|
2713
|
+
}
|
|
1660
2714
|
console.log();
|
|
1661
2715
|
console.log("Next steps:");
|
|
1662
2716
|
console.log();
|
|
1663
|
-
console.log(
|
|
2717
|
+
console.log(pc3.cyan(` cd ${options.projectName}`));
|
|
1664
2718
|
if (options.skipInstall) {
|
|
1665
|
-
console.log(
|
|
2719
|
+
console.log(pc3.cyan(" npm install"));
|
|
1666
2720
|
}
|
|
1667
|
-
console.log(
|
|
2721
|
+
console.log(pc3.cyan(" npm run dev"));
|
|
1668
2722
|
console.log();
|
|
1669
|
-
console.log("Then open " +
|
|
2723
|
+
console.log("Then open " + pc3.blue("http://localhost:5173") + " in your browser.");
|
|
1670
2724
|
console.log();
|
|
1671
2725
|
console.log("To add circuits:");
|
|
1672
2726
|
console.log();
|
|
1673
|
-
console.log(
|
|
1674
|
-
console.log(
|
|
1675
|
-
console.log(
|
|
2727
|
+
console.log(pc3.dim(" 1. Create a new circuit in circuits/*.ts"));
|
|
2728
|
+
console.log(pc3.dim(" 2. Export it from circuits/index.ts"));
|
|
2729
|
+
console.log(pc3.dim(" 3. Add it to CIRCUITS array in src/App.tsx"));
|
|
1676
2730
|
console.log();
|
|
1677
2731
|
console.log(
|
|
1678
|
-
|
|
2732
|
+
pc3.dim("Learn more: ") + pc3.blue("https://github.com/izi-noir/izi-noir")
|
|
1679
2733
|
);
|
|
1680
2734
|
console.log();
|
|
1681
2735
|
}
|