create-veil-app 0.3.0

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.
@@ -0,0 +1,2052 @@
1
+ // src/cli.ts
2
+ import inquirer from "inquirer";
3
+ import chalk from "chalk";
4
+ import ora from "ora";
5
+
6
+ // src/scaffold.ts
7
+ import fs from "fs-extra";
8
+ import path from "path";
9
+ async function scaffoldProject(config) {
10
+ const projectPath = path.resolve(process.cwd(), config.projectName);
11
+ await fs.ensureDir(projectPath);
12
+ const srcDir = config.framework === "nextjs" ? "app" : "src";
13
+ const folders = [
14
+ srcDir,
15
+ `${srcDir}/components`,
16
+ `${srcDir}/components/privacy`,
17
+ `${srcDir}/components/ui`,
18
+ "lib",
19
+ "lib/privacy",
20
+ "lib/veil",
21
+ "hooks",
22
+ "contexts",
23
+ "types"
24
+ ];
25
+ if (config.features.shadowpay) folders.push("lib/shadowpay");
26
+ if (config.features.voting) folders.push("lib/voting");
27
+ if (config.features.staking) folders.push("lib/staking");
28
+ if (config.features.multisig) folders.push("lib/multisig");
29
+ for (const folder of folders) {
30
+ await fs.ensureDir(path.join(projectPath, folder));
31
+ }
32
+ const files = [
33
+ // Root config files
34
+ { path: "veil.config.ts", content: generateVeilConfig(config) },
35
+ { path: ".env.example", content: generateEnvExample(config) },
36
+ { path: "README.md", content: generateReadme(config) },
37
+ { path: "package.json", content: generatePackageJson(config) },
38
+ { path: "tsconfig.json", content: generateTsConfig(config) },
39
+ { path: "tailwind.config.js", content: generateTailwindConfig(config) },
40
+ { path: "postcss.config.js", content: generatePostcssConfig() },
41
+ // Privacy/Veil lib modules
42
+ { path: "lib/privacy/login.ts", content: generateLoginTs() },
43
+ { path: "lib/privacy/recovery.ts", content: generateRecoveryTs() },
44
+ { path: "lib/privacy/access.ts", content: generateAccessTs() },
45
+ { path: "lib/privacy/guarantees.ts", content: generateGuaranteesTs() },
46
+ { path: "lib/privacy/index.ts", content: `export * from "./login.js";
47
+ export * from "./recovery.js";
48
+ export * from "./access.js";
49
+ export * from "./guarantees.js";
50
+ ` },
51
+ // Veil SDK integration
52
+ { path: "lib/veil/rpc.ts", content: generateRpcTs(config) },
53
+ { path: "lib/veil/helius.ts", content: generateHeliusTs(config) },
54
+ { path: "lib/veil/index.ts", content: `export * from "./rpc.js";
55
+ export * from "./helius.js";
56
+ ` },
57
+ // Hooks
58
+ { path: "hooks/useVeil.ts", content: generateVeilHooks(config) },
59
+ { path: "hooks/useSdk.ts", content: generateSdkHooks() },
60
+ { path: "hooks/index.ts", content: `export * from "./useVeil.js";
61
+ export * from "./useSdk.js";
62
+ ` },
63
+ // Contexts
64
+ { path: "contexts/VeilProvider.tsx", content: generateVeilProvider(config) },
65
+ { path: "contexts/VeilSDKProvider.tsx", content: generateSdkProvider(config) },
66
+ { path: "contexts/index.ts", content: `export * from "./VeilProvider.js";
67
+ export * from "./VeilSDKProvider.js";
68
+ ` },
69
+ // Components
70
+ { path: `${srcDir}/components/WalletButton.tsx`, content: generateWalletButton() },
71
+ { path: `${srcDir}/components/privacy/PrivacyStatus.tsx`, content: generatePrivacyStatus() },
72
+ // App files
73
+ { path: `${srcDir}/page.tsx`, content: generateAppEntry(config) },
74
+ { path: `${srcDir}/layout.tsx`, content: generateLayoutTsx(config) },
75
+ { path: `${srcDir}/globals.css`, content: generateGlobalsCss() },
76
+ { path: `${srcDir}/providers.tsx`, content: generateProvidersTsx(config) }
77
+ ];
78
+ if (config.framework === "nextjs") {
79
+ files.push({ path: "next.config.js", content: generateNextConfig() });
80
+ }
81
+ if (config.features.shadowpay) {
82
+ files.push({ path: "lib/shadowpay/index.ts", content: generateShadowPayModule(config) });
83
+ }
84
+ for (const file of files) {
85
+ await fs.writeFile(
86
+ path.join(projectPath, file.path),
87
+ file.content,
88
+ "utf-8"
89
+ );
90
+ }
91
+ const gitignore = `node_modules/
92
+ .env
93
+ .env.local
94
+ dist/
95
+ .next/
96
+ `;
97
+ await fs.writeFile(path.join(projectPath, ".gitignore"), gitignore, "utf-8");
98
+ }
99
+
100
+ // src/cli.ts
101
+ var TEMPLATE_INFO = {
102
+ // DeFi
103
+ dex: { name: "DEX Interface", description: "Private swaps via ShadowWire", category: "DeFi" },
104
+ lending: { name: "Lending Protocol", description: "Private lending positions", category: "DeFi" },
105
+ yield: { name: "Yield Farming", description: "Private stake amounts", category: "DeFi" },
106
+ pool: { name: "Liquidity Pool", description: "Private LP deposits", category: "DeFi" },
107
+ // DApp
108
+ gaming: { name: "Gaming App", description: "Private game assets & scores", category: "DApp" },
109
+ nft: { name: "NFT Marketplace", description: "Private bids & offers", category: "DApp" },
110
+ social: { name: "Social Platform", description: "Private messaging & identity", category: "DApp" },
111
+ governance: { name: "DAO Governance", description: "Private voting & proposals", category: "DApp" },
112
+ // Exchange
113
+ cex: { name: "Exchange Interface", description: "CEX-style with privacy", category: "Exchange" },
114
+ aggregator: { name: "DEX Aggregator", description: "Private swap routing", category: "Exchange" },
115
+ trading: { name: "Trading Dashboard", description: "Private order books", category: "Exchange" },
116
+ // Wallet
117
+ wallet: { name: "Multi-sig Wallet", description: "Stealth signers & treasury", category: "Wallet" },
118
+ portfolio: { name: "Portfolio Tracker", description: "Private holdings view", category: "Wallet" },
119
+ payments: { name: "Payment App", description: "Full ShadowPay integration", category: "Wallet" },
120
+ // Basic
121
+ basic: { name: "Basic Starter", description: "Minimal privacy-first app", category: "Starter" }
122
+ };
123
+ async function runInit(options) {
124
+ const answers = await promptUser(options);
125
+ const spinner = ora({
126
+ text: "Creating your project with Veil privacy...",
127
+ color: "cyan"
128
+ }).start();
129
+ try {
130
+ await scaffoldProject(answers);
131
+ spinner.succeed(chalk.green("Veil initialized successfully."));
132
+ printNextSteps(answers.projectName);
133
+ } catch (error) {
134
+ spinner.fail(chalk.red("Failed to initialize project."));
135
+ console.error(error);
136
+ process.exit(1);
137
+ }
138
+ }
139
+ async function promptUser(options) {
140
+ const hasAllOptions = options.name && options.template && options.framework && options.network;
141
+ if (hasAllOptions) {
142
+ const shadowPayMode2 = options.shadowPay ? options.shadowPayMode || "app" : false;
143
+ return {
144
+ projectName: options.name,
145
+ template: options.template,
146
+ framework: options.framework,
147
+ helius: options.helius ?? true,
148
+ shadowPay: shadowPayMode2,
149
+ network: options.network,
150
+ features: {
151
+ identity: true,
152
+ recovery: true,
153
+ voting: true,
154
+ staking: true,
155
+ multisig: true,
156
+ shadowpay: !!options.shadowPay
157
+ }
158
+ };
159
+ }
160
+ const questions = [];
161
+ if (!options.name) {
162
+ questions.push({
163
+ type: "input",
164
+ name: "projectName",
165
+ message: "Project name:",
166
+ default: "my-veil-app",
167
+ validate: (input) => {
168
+ if (/^[a-z0-9-]+$/.test(input)) return true;
169
+ return "Project name must be lowercase with hyphens only";
170
+ }
171
+ });
172
+ }
173
+ const initialAnswers = await inquirer.prompt(questions);
174
+ let selectedTemplate = options.template || "basic";
175
+ if (!options.template) {
176
+ const categoryAnswer = await inquirer.prompt([{
177
+ type: "list",
178
+ name: "category",
179
+ message: "What are you building?",
180
+ choices: [
181
+ { name: chalk.cyan("\u{1F3E6} DeFi") + " \u2014 DEX, Lending, Yield, Pools", value: "defi" },
182
+ { name: chalk.magenta("\u{1F3AE} DApp") + " \u2014 Gaming, NFT, Social, Governance", value: "dapp" },
183
+ { name: chalk.yellow("\u{1F4CA} Exchange") + " \u2014 CEX, Aggregator, Trading", value: "exchange" },
184
+ { name: chalk.green("\u{1F45B} Wallet") + " \u2014 Multisig, Portfolio, Payments", value: "wallet" },
185
+ { name: chalk.dim("\u{1F4E6} Basic") + " \u2014 Minimal starter template", value: "basic" }
186
+ ]
187
+ }]);
188
+ if (categoryAnswer.category === "basic") {
189
+ selectedTemplate = "basic";
190
+ } else {
191
+ const templateChoices = Object.entries(TEMPLATE_INFO).filter(([_, info]) => info.category.toLowerCase() === categoryAnswer.category).map(([key, info]) => ({
192
+ name: `${info.name} \u2014 ${info.description}`,
193
+ value: key
194
+ }));
195
+ const templateAnswer = await inquirer.prompt([{
196
+ type: "list",
197
+ name: "template",
198
+ message: "Select template:",
199
+ choices: templateChoices
200
+ }]);
201
+ selectedTemplate = templateAnswer.template;
202
+ }
203
+ }
204
+ let framework = options.framework || "nextjs";
205
+ if (!options.framework) {
206
+ const frameworkAnswer = await inquirer.prompt([{
207
+ type: "list",
208
+ name: "framework",
209
+ message: "Frontend framework:",
210
+ choices: [
211
+ { name: "Next.js (recommended)", value: "nextjs" },
212
+ { name: "Vite + React", value: "vite" }
213
+ ]
214
+ }]);
215
+ framework = frameworkAnswer.framework;
216
+ }
217
+ let helius = options.helius ?? true;
218
+ if (options.helius === void 0) {
219
+ const heliusAnswer = await inquirer.prompt([{
220
+ type: "confirm",
221
+ name: "helius",
222
+ message: "Enable Helius RPC? (recommended for production)",
223
+ default: true
224
+ }]);
225
+ helius = heliusAnswer.helius;
226
+ }
227
+ let shadowPayMode = false;
228
+ const wantsShadowPay = options.shadowPay ?? true;
229
+ if (wantsShadowPay) {
230
+ console.log();
231
+ console.log(chalk.yellow("\u26A1 ShadowPay uses mainnet for real private payments"));
232
+ console.log(chalk.dim(" Veil features (voting, staking, multisig) work on devnet"));
233
+ console.log();
234
+ const shadowPayAnswer = await inquirer.prompt([{
235
+ type: "list",
236
+ name: "mode",
237
+ message: "ShadowPay mode:",
238
+ choices: [
239
+ { name: "App \u2014 Receive private payments from users", value: "app" },
240
+ { name: "Wallet \u2014 Send private payments to apps/users", value: "wallet" },
241
+ { name: "Skip \u2014 Don't include ShadowPay", value: "skip" }
242
+ ]
243
+ }]);
244
+ if (shadowPayAnswer.mode !== "skip") {
245
+ shadowPayMode = shadowPayAnswer.mode;
246
+ }
247
+ }
248
+ let network = options.network || "devnet";
249
+ if (!options.network) {
250
+ const networkAnswer = await inquirer.prompt([{
251
+ type: "list",
252
+ name: "network",
253
+ message: "Veil features network:",
254
+ choices: [
255
+ { name: "Devnet (recommended)", value: "devnet" },
256
+ { name: "Localnet", value: "localnet" }
257
+ ]
258
+ }]);
259
+ network = networkAnswer.network;
260
+ }
261
+ return {
262
+ projectName: options.name || initialAnswers.projectName,
263
+ template: selectedTemplate,
264
+ framework,
265
+ helius,
266
+ shadowPay: shadowPayMode,
267
+ network,
268
+ features: {
269
+ identity: true,
270
+ recovery: true,
271
+ voting: true,
272
+ staking: true,
273
+ multisig: true,
274
+ shadowpay: shadowPayMode !== false
275
+ }
276
+ };
277
+ }
278
+ function printNextSteps(projectName) {
279
+ console.log();
280
+ console.log(chalk.bold("Next steps:"));
281
+ console.log(chalk.dim("1."), `cd ${projectName}`);
282
+ console.log(chalk.dim("2."), "cp .env.example .env");
283
+ console.log(chalk.dim("3."), "pnpm install");
284
+ console.log(chalk.dim("4."), "pnpm dev");
285
+ console.log();
286
+ console.log(
287
+ chalk.dim("Privacy guarantees are documented in"),
288
+ chalk.cyan("/privacy/guarantees.ts")
289
+ );
290
+ console.log();
291
+ }
292
+ async function runAdd(options) {
293
+ const fsExtra = await import("fs-extra");
294
+ const fs2 = fsExtra.default;
295
+ const path2 = await import("path");
296
+ const cwd = process.cwd();
297
+ const packageJsonPath = path2.join(cwd, "package.json");
298
+ if (!await fs2.pathExists(packageJsonPath)) {
299
+ console.log(chalk.red("Error: No package.json found."));
300
+ console.log(chalk.dim("Run this command from the root of your project."));
301
+ process.exit(1);
302
+ }
303
+ const packageJson = await fs2.readJson(packageJsonPath);
304
+ const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
305
+ const isNext = "next" in deps;
306
+ const isVite = "vite" in deps;
307
+ const hasReact = "react" in deps;
308
+ if (!hasReact) {
309
+ console.log(chalk.red("Error: Veil requires a React project."));
310
+ console.log(chalk.dim("Detected packages:"), Object.keys(deps).slice(0, 5).join(", "));
311
+ process.exit(1);
312
+ }
313
+ console.log(chalk.dim("Detected:"), isNext ? "Next.js" : isVite ? "Vite" : "React");
314
+ const answers = await inquirer.prompt([
315
+ {
316
+ type: "confirm",
317
+ name: "helius",
318
+ message: "Use Helius RPC for better reliability?",
319
+ default: options.helius !== false,
320
+ when: () => options.helius === void 0
321
+ },
322
+ {
323
+ type: "confirm",
324
+ name: "shadowPay",
325
+ message: "Add ShadowPay for privacy-preserving payments?",
326
+ default: false,
327
+ when: () => options.shadowPay === void 0
328
+ },
329
+ {
330
+ type: "list",
331
+ name: "shadowPayMode",
332
+ message: "What are you building?",
333
+ choices: [
334
+ { name: "App \u2014 receive payments from users", value: "app" },
335
+ { name: "Wallet \u2014 send payments to apps/users", value: "wallet" }
336
+ ],
337
+ when: (ans) => ans.shadowPay || options.shadowPay
338
+ }
339
+ ]);
340
+ const config = {
341
+ projectName: packageJson.name || "my-app",
342
+ template: isNext ? "nextjs" : "vite",
343
+ helius: answers.helius ?? options.helius ?? true,
344
+ shadowPay: answers.shadowPay || options.shadowPay ? answers.shadowPayMode || "app" : false,
345
+ network: "devnet"
346
+ };
347
+ const spinner = ora({
348
+ text: "Adding Veil privacy layer...",
349
+ color: "cyan"
350
+ }).start();
351
+ try {
352
+ await addVeilToProject(cwd, config);
353
+ spinner.succeed(chalk.green("Veil added successfully."));
354
+ printAddNextSteps();
355
+ } catch (error) {
356
+ spinner.fail(chalk.red("Failed to add Veil."));
357
+ console.error(error);
358
+ process.exit(1);
359
+ }
360
+ }
361
+ async function addVeilToProject(projectPath, config) {
362
+ const fsExtra = await import("fs-extra");
363
+ const fs2 = fsExtra.default;
364
+ const path2 = await import("path");
365
+ const {
366
+ generateVeilConfig: generateVeilConfig2,
367
+ generateEnvExample: generateEnvExample2,
368
+ generateLoginTs: generateLoginTs2,
369
+ generateRecoveryTs: generateRecoveryTs2,
370
+ generateAccessTs: generateAccessTs2,
371
+ generateGuaranteesTs: generateGuaranteesTs2,
372
+ generateRpcTs: generateRpcTs2,
373
+ generateHeliusTs: generateHeliusTs2,
374
+ generateVeilProvider: generateVeilProvider2,
375
+ generateVeilHooks: generateVeilHooks2,
376
+ generateShadowPayModule: generateShadowPayModule2
377
+ } = await import("./templates-TJ2JYQDS.js");
378
+ const dirs = ["privacy", "infra", "contexts", "hooks"];
379
+ for (const dir of dirs) {
380
+ await fs2.ensureDir(path2.join(projectPath, dir));
381
+ }
382
+ const files = [
383
+ { path: "veil.config.ts", content: generateVeilConfig2(config) },
384
+ { path: ".env.example", content: generateEnvExample2(config), append: true },
385
+ // Privacy module
386
+ { path: "privacy/login.ts", content: generateLoginTs2() },
387
+ { path: "privacy/recovery.ts", content: generateRecoveryTs2() },
388
+ { path: "privacy/access.ts", content: generateAccessTs2() },
389
+ { path: "privacy/guarantees.ts", content: generateGuaranteesTs2() },
390
+ { path: "privacy/index.ts", content: `export * from "./login.js";
391
+ export * from "./recovery.js";
392
+ export * from "./access.js";
393
+ export * from "./guarantees.js";
394
+ ` },
395
+ // Infra module
396
+ { path: "infra/rpc.ts", content: generateRpcTs2() },
397
+ { path: "infra/helius.ts", content: generateHeliusTs2() },
398
+ { path: "infra/index.ts", content: `export * from "./rpc.js";
399
+ export * from "./helius.js";
400
+ ` },
401
+ // Contexts
402
+ { path: "contexts/VeilProvider.tsx", content: generateVeilProvider2(config) },
403
+ { path: "contexts/index.ts", content: `export * from "./VeilProvider.js";
404
+ ` },
405
+ // Hooks
406
+ { path: "hooks/useVeil.ts", content: generateVeilHooks2() },
407
+ { path: "hooks/index.ts", content: `export * from "./useVeil.js";
408
+ ` }
409
+ ];
410
+ if (config.shadowPay) {
411
+ files.push({ path: "shadowpay/index.ts", content: generateShadowPayModule2(config) });
412
+ }
413
+ for (const file of files) {
414
+ const filePath = path2.join(projectPath, file.path);
415
+ if (file.append && await fs2.pathExists(filePath)) {
416
+ const existing = await fs2.readFile(filePath, "utf-8");
417
+ if (!existing.includes("HELIUS")) {
418
+ await fs2.appendFile(filePath, "\n" + file.content);
419
+ }
420
+ } else if (!await fs2.pathExists(filePath)) {
421
+ await fs2.ensureDir(path2.dirname(filePath));
422
+ await fs2.writeFile(filePath, file.content, "utf-8");
423
+ }
424
+ }
425
+ }
426
+ function printAddNextSteps() {
427
+ console.log();
428
+ console.log(chalk.bold("What was added:"));
429
+ console.log(chalk.dim("\u2022"), chalk.cyan("privacy/") + " \u2014 login, recovery, access guarantees");
430
+ console.log(chalk.dim("\u2022"), chalk.cyan("contexts/") + " \u2014 VeilProvider for auth state");
431
+ console.log(chalk.dim("\u2022"), chalk.cyan("hooks/") + " \u2014 useVeil hook");
432
+ console.log(chalk.dim("\u2022"), chalk.cyan("infra/") + " \u2014 RPC and Helius helpers");
433
+ console.log();
434
+ console.log(chalk.bold("Next steps:"));
435
+ console.log(chalk.dim("1."), "Wrap your app with <VeilProvider>");
436
+ console.log(chalk.dim("2."), "Use the useVeil() hook for authentication");
437
+ console.log(chalk.dim("3."), "Review privacy/guarantees.ts");
438
+ console.log();
439
+ }
440
+
441
+ // src/templates/privacy.ts
442
+ function generateLoginTs() {
443
+ return `/**
444
+ * Privacy-Preserving Login
445
+ *
446
+ * This module handles authentication without exposing identity on-chain.
447
+ *
448
+ * PRIVACY GUARANTEE:
449
+ * - Email/passkey \u2192 generates unlinkable Solana wallet
450
+ * - No identity stored on-chain
451
+ * - No correlation between login method and wallet address
452
+ */
453
+
454
+ import { Keypair } from "@solana/web3.js";
455
+
456
+ export interface LoginSession {
457
+ /** Ephemeral session ID (never stored on-chain) */
458
+ sessionId: string;
459
+ /** Derived wallet (unlinkable to identity) */
460
+ wallet: Keypair;
461
+ /** Session expiry timestamp */
462
+ expiresAt: number;
463
+ }
464
+
465
+ /**
466
+ * Derive a wallet from identity proof without exposing the identity.
467
+ *
468
+ * In production, this would use ZK proofs to:
469
+ * 1. Prove you own an email/passkey
470
+ * 2. Derive a deterministic but unlinkable wallet
471
+ * 3. Never reveal the email/passkey on-chain
472
+ */
473
+ export async function derivePrivateWallet(
474
+ identityProof: Uint8Array
475
+ ): Promise<Keypair> {
476
+ // Conceptual: In production, use proper ZK derivation
477
+ // This demonstrates the privacy-first architecture
478
+ const seed = await crypto.subtle.digest("SHA-256", identityProof.buffer as ArrayBuffer);
479
+ return Keypair.fromSeed(new Uint8Array(seed));
480
+ }
481
+
482
+ /**
483
+ * Create a new login session.
484
+ * Session data is ephemeral and never touches the blockchain.
485
+ */
486
+ export async function createLoginSession(
487
+ identityProof: Uint8Array,
488
+ durationMs: number = 24 * 60 * 60 * 1000
489
+ ): Promise<LoginSession> {
490
+ const wallet = await derivePrivateWallet(identityProof);
491
+
492
+ return {
493
+ sessionId: crypto.randomUUID(),
494
+ wallet,
495
+ expiresAt: Date.now() + durationMs,
496
+ };
497
+ }
498
+
499
+ /**
500
+ * Validate session without on-chain lookup.
501
+ */
502
+ export function isSessionValid(session: LoginSession): boolean {
503
+ return Date.now() < session.expiresAt;
504
+ }
505
+ `;
506
+ }
507
+ function generateRecoveryTs() {
508
+ return `/**
509
+ * Privacy-Preserving Recovery
510
+ *
511
+ * This module handles wallet recovery without exposing guardians.
512
+ *
513
+ * PRIVACY GUARANTEE:
514
+ * - Guardian identities are never revealed
515
+ * - Recovery process doesn't leak social graph
516
+ * - Time-lock or Shamir methods available
517
+ */
518
+
519
+ export type RecoveryMethod = "timelock" | "shamir";
520
+
521
+ export interface RecoveryConfig {
522
+ method: RecoveryMethod;
523
+ /** For timelock: delay in seconds before recovery completes */
524
+ timelockDelay?: number;
525
+ /** For shamir: threshold of guardians needed */
526
+ shamirThreshold?: number;
527
+ /** For shamir: total number of guardian shares */
528
+ shamirTotal?: number;
529
+ }
530
+
531
+ /**
532
+ * Commitment hash for guardian (hides actual identity).
533
+ * Guardian is identified by hash, not by address or email.
534
+ */
535
+ export function createGuardianCommitment(
536
+ guardianSecret: Uint8Array
537
+ ): Promise<Uint8Array> {
538
+ return crypto.subtle.digest("SHA-256", guardianSecret.buffer as ArrayBuffer).then(
539
+ (hash) => new Uint8Array(hash)
540
+ );
541
+ }
542
+
543
+ /**
544
+ * Initiate recovery with timelock.
545
+ * The recovery is public, but WHO initiated it is not.
546
+ */
547
+ export async function initiateTimelockRecovery(
548
+ commitmentHash: Uint8Array,
549
+ delaySeconds: number
550
+ ): Promise<{ recoveryId: string; unlocksAt: number }> {
551
+ return {
552
+ recoveryId: crypto.randomUUID(),
553
+ unlocksAt: Date.now() + delaySeconds * 1000,
554
+ };
555
+ }
556
+
557
+ /**
558
+ * Initiate Shamir recovery.
559
+ * Guardians provide shares without revealing who they are.
560
+ */
561
+ export async function initiateShamirRecovery(
562
+ shares: Uint8Array[],
563
+ threshold: number
564
+ ): Promise<{ recoveryId: string; sharesCollected: number }> {
565
+ if (shares.length < threshold) {
566
+ throw new Error(\`Need \${threshold} shares, got \${shares.length}\`);
567
+ }
568
+
569
+ return {
570
+ recoveryId: crypto.randomUUID(),
571
+ sharesCollected: shares.length,
572
+ };
573
+ }
574
+ `;
575
+ }
576
+ function generateAccessTs() {
577
+ return `/**
578
+ * Privacy-Preserving Access Control
579
+ *
580
+ * This module handles proof-based access without exposing addresses.
581
+ *
582
+ * PRIVACY GUARANTEE:
583
+ * - Access is verified via proof, not address lookup
584
+ * - Wallet address is not revealed during verification
585
+ * - Proof can be verified without knowing who created it
586
+ */
587
+
588
+ export interface AccessProof {
589
+ /** Proof data (ZK proof in production) */
590
+ proof: Uint8Array;
591
+ /** Public inputs for verification */
592
+ publicInputs: Uint8Array;
593
+ /** Timestamp of proof creation */
594
+ createdAt: number;
595
+ }
596
+
597
+ /**
598
+ * Generate access proof without revealing wallet address.
599
+ *
600
+ * In production, this creates a ZK proof that:
601
+ * 1. Proves ownership of a valid wallet
602
+ * 2. Does NOT reveal which wallet
603
+ * 3. Can be verified by anyone
604
+ */
605
+ export async function generateAccessProof(
606
+ wallet: { publicKey: { toBytes(): Uint8Array }; },
607
+ challenge: Uint8Array
608
+ ): Promise<AccessProof> {
609
+ // Conceptual: Create proof of wallet ownership
610
+ const message = new Uint8Array([...wallet.publicKey.toBytes(), ...challenge]);
611
+ const proof = await crypto.subtle.digest("SHA-256", message.buffer as ArrayBuffer);
612
+
613
+ return {
614
+ proof: new Uint8Array(proof),
615
+ publicInputs: challenge,
616
+ createdAt: Date.now(),
617
+ };
618
+ }
619
+
620
+ /**
621
+ * Verify access proof without knowing the wallet.
622
+ */
623
+ export async function verifyAccessProof(
624
+ proof: AccessProof,
625
+ expectedChallenge: Uint8Array
626
+ ): Promise<boolean> {
627
+ // Verify the challenge matches
628
+ if (proof.publicInputs.length !== expectedChallenge.length) {
629
+ return false;
630
+ }
631
+
632
+ for (let i = 0; i < expectedChallenge.length; i++) {
633
+ if (proof.publicInputs[i] !== expectedChallenge[i]) {
634
+ return false;
635
+ }
636
+ }
637
+
638
+ // In production: verify ZK proof
639
+ return proof.proof.length === 32;
640
+ }
641
+ `;
642
+ }
643
+ function generateGuaranteesTs() {
644
+ return `/**
645
+ * VEIL PRIVACY GUARANTEES
646
+ *
647
+ * This file documents what Veil protects and what it does NOT protect.
648
+ * Read this carefully before building your application.
649
+ *
650
+ * \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
651
+ *
652
+ * \u2705 WHAT VEIL PROTECTS (Private)
653
+ *
654
+ * 1. IDENTITY
655
+ * - Your email/passkey is never stored on-chain
656
+ * - No correlation between login method and wallet
657
+ * - Identity proof generates unlinkable wallet
658
+ *
659
+ * 2. ACCESS PATTERNS
660
+ * - Proof-based verification (not address lookup)
661
+ * - Session data is ephemeral
662
+ * - No on-chain access logs
663
+ *
664
+ * 3. RECOVERY SOCIAL GRAPH
665
+ * - Guardian identities are hidden (commitment hashes)
666
+ * - Recovery doesn't reveal who helped you
667
+ * - Timelock prevents instant unauthorized recovery
668
+ *
669
+ * \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
670
+ *
671
+ * \u274C WHAT VEIL DOES NOT PROTECT (Public on Solana)
672
+ *
673
+ * 1. TRANSACTION AMOUNTS
674
+ * - All SOL/token amounts are visible
675
+ * - This is a Solana limitation, not Veil
676
+ *
677
+ * 2. TRANSACTION RECIPIENTS
678
+ * - Destination addresses are public
679
+ * - Anyone can see who you transact with
680
+ *
681
+ * 3. WALLET BALANCES
682
+ * - All balances are publicly queryable
683
+ * - Historical balance changes are visible
684
+ *
685
+ * 4. TRANSACTION HISTORY
686
+ * - All transactions are permanently recorded
687
+ * - Transaction graph analysis is possible
688
+ *
689
+ * \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
690
+ *
691
+ * \u{1F3AF} VEIL'S MISSION
692
+ *
693
+ * "Hide the user, not the transactions."
694
+ *
695
+ * Veil ensures that even if someone sees your transactions,
696
+ * they cannot link them back to your real-world identity.
697
+ *
698
+ * \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
699
+ */
700
+
701
+ export const PRIVACY_GUARANTEES = {
702
+ identity: {
703
+ protected: true,
704
+ description: "Your real identity is never stored or revealed on-chain",
705
+ },
706
+ accessPatterns: {
707
+ protected: true,
708
+ description: "How you access your wallet is hidden from observers",
709
+ },
710
+ recoveryGuardians: {
711
+ protected: true,
712
+ description: "Guardian identities are hidden behind commitment hashes",
713
+ },
714
+ transactionAmounts: {
715
+ protected: false,
716
+ description: "Transaction amounts are visible on Solana",
717
+ },
718
+ transactionRecipients: {
719
+ protected: false,
720
+ description: "Transaction recipients are visible on Solana",
721
+ },
722
+ walletBalances: {
723
+ protected: false,
724
+ description: "Wallet balances are publicly queryable",
725
+ },
726
+ } as const;
727
+
728
+ export type PrivacyGuarantee = keyof typeof PRIVACY_GUARANTEES;
729
+ `;
730
+ }
731
+
732
+ // src/templates/infra.ts
733
+ function generateRpcTs(config) {
734
+ const rpcUrl = config.helius ? "process.env.HELIUS_RPC_URL" : config.network === "devnet" ? '"https://api.devnet.solana.com"' : '"http://localhost:8899"';
735
+ return `/**
736
+ * RPC Configuration
737
+ *
738
+ * Privacy-aware RPC layer that abstracts connection details.
739
+ *
740
+ * PRIVACY NOTE:
741
+ * - RPC providers can see your requests
742
+ * - Use Helius for better rate limits and webhooks
743
+ * - Never expose API keys in client-side code
744
+ */
745
+
746
+ import { Connection, clusterApiUrl } from "@solana/web3.js";
747
+
748
+ const NETWORK = "${config.network}";
749
+
750
+ /**
751
+ * Get the RPC URL based on configuration.
752
+ * Server-side only for privacy.
753
+ */
754
+ function getRpcUrl(): string {
755
+ if (typeof window !== "undefined") {
756
+ // Client-side: use public endpoint (limited)
757
+ return clusterApiUrl("${config.network}");
758
+ }
759
+
760
+ // Server-side: use configured RPC
761
+ return ${rpcUrl} || clusterApiUrl("${config.network}");
762
+ }
763
+
764
+ /**
765
+ * Create a connection to the Solana network.
766
+ */
767
+ export function createConnection(): Connection {
768
+ return new Connection(getRpcUrl(), {
769
+ commitment: "confirmed",
770
+ confirmTransactionInitialTimeout: 60000,
771
+ });
772
+ }
773
+
774
+ /**
775
+ * Get network name for display.
776
+ */
777
+ export function getNetworkName(): string {
778
+ return NETWORK.charAt(0).toUpperCase() + NETWORK.slice(1);
779
+ }
780
+
781
+ export { NETWORK };
782
+ `;
783
+ }
784
+ function generateHeliusTs(config) {
785
+ if (!config.helius) {
786
+ return `/**
787
+ * Helius Integration (Disabled)
788
+ *
789
+ * Helius provides enhanced RPC and webhooks for Solana.
790
+ * Enable it during project setup to use these features.
791
+ */
792
+
793
+ export const HELIUS_ENABLED = false;
794
+
795
+ export function getHeliusRpcUrl(): string {
796
+ throw new Error("Helius is not enabled. Run veil init with Helius enabled.");
797
+ }
798
+ `;
799
+ }
800
+ return `/**
801
+ * Helius Integration
802
+ *
803
+ * Enhanced RPC and webhook support for Solana.
804
+ *
805
+ * PRIVACY NOTE:
806
+ * - Helius sees your RPC requests (standard for any RPC provider)
807
+ * - Webhooks can notify you of on-chain events
808
+ * - Never expose API keys client-side
809
+ */
810
+
811
+ export const HELIUS_ENABLED = true;
812
+
813
+ /**
814
+ * Get Helius RPC URL (server-side only).
815
+ */
816
+ export function getHeliusRpcUrl(): string {
817
+ const apiKey = process.env.HELIUS_API_KEY;
818
+ if (!apiKey) {
819
+ throw new Error("HELIUS_API_KEY not configured");
820
+ }
821
+ return \`https://${config.network}.helius-rpc.com/?api-key=\${apiKey}\`;
822
+ }
823
+
824
+ /**
825
+ * Verify Helius webhook signature.
826
+ */
827
+ export async function verifyWebhookSignature(
828
+ payload: string,
829
+ signature: string
830
+ ): Promise<boolean> {
831
+ const secret = process.env.HELIUS_WEBHOOK_SECRET;
832
+ if (!secret) {
833
+ console.warn("HELIUS_WEBHOOK_SECRET not configured");
834
+ return false;
835
+ }
836
+
837
+ const encoder = new TextEncoder();
838
+ const secretBytes = encoder.encode(secret);
839
+ const key = await crypto.subtle.importKey(
840
+ "raw",
841
+ secretBytes.buffer as ArrayBuffer,
842
+ { name: "HMAC", hash: "SHA-256" },
843
+ false,
844
+ ["verify"]
845
+ );
846
+
847
+ const signatureBytes = Uint8Array.from(
848
+ atob(signature),
849
+ (c) => c.charCodeAt(0)
850
+ );
851
+
852
+ const payloadBytes = encoder.encode(payload);
853
+ return crypto.subtle.verify(
854
+ "HMAC",
855
+ key,
856
+ signatureBytes.buffer as ArrayBuffer,
857
+ payloadBytes.buffer as ArrayBuffer
858
+ );
859
+ }
860
+
861
+ /**
862
+ * Webhook handler placeholder.
863
+ * Implement your own logic based on event type.
864
+ */
865
+ export interface HeliusWebhookEvent {
866
+ type: string;
867
+ data: unknown;
868
+ timestamp: number;
869
+ }
870
+
871
+ export async function handleWebhook(event: HeliusWebhookEvent): Promise<void> {
872
+ console.log("Received webhook event:", event.type);
873
+ // Implement your webhook handling logic here
874
+ }
875
+ `;
876
+ }
877
+
878
+ // src/templates/project.ts
879
+ function generatePackageJson(config) {
880
+ const isNext = config.framework === "nextjs";
881
+ const deps = {
882
+ "@solana/web3.js": "^1.95.0",
883
+ "@solana/wallet-adapter-base": "^0.9.23",
884
+ "@solana/wallet-adapter-react": "^0.15.35",
885
+ "@solana/wallet-adapter-react-ui": "^0.9.35",
886
+ "@solana/wallet-adapter-phantom": "^0.9.24",
887
+ "@solana/wallet-adapter-solflare": "^0.6.28"
888
+ };
889
+ if (config.features.shadowpay) {
890
+ deps["@radr/shadowwire"] = "^0.1.0";
891
+ }
892
+ const devDeps = {
893
+ "typescript": "^5.3.3",
894
+ "@types/node": "^20.11.0",
895
+ "tailwindcss": "^3.4.1",
896
+ "postcss": "^8.4.33",
897
+ "autoprefixer": "^10.4.17"
898
+ };
899
+ if (isNext) {
900
+ deps["next"] = "^14.1.0";
901
+ deps["react"] = "^18.2.0";
902
+ deps["react-dom"] = "^18.2.0";
903
+ devDeps["@types/react"] = "^18.2.48";
904
+ devDeps["@types/react-dom"] = "^18.2.18";
905
+ } else {
906
+ deps["react"] = "^18.2.0";
907
+ deps["react-dom"] = "^18.2.0";
908
+ devDeps["vite"] = "^5.0.12";
909
+ devDeps["@vitejs/plugin-react"] = "^4.2.1";
910
+ devDeps["@types/react"] = "^18.2.48";
911
+ devDeps["@types/react-dom"] = "^18.2.18";
912
+ }
913
+ const scripts = isNext ? {
914
+ dev: "next dev",
915
+ build: "next build",
916
+ start: "next start",
917
+ lint: "next lint"
918
+ } : {
919
+ dev: "vite",
920
+ build: "vite build",
921
+ preview: "vite preview"
922
+ };
923
+ const templateInfo = TEMPLATE_INFO[config.template];
924
+ const pkg = {
925
+ name: config.projectName,
926
+ version: "0.1.0",
927
+ description: `${templateInfo.name} - ${templateInfo.description}. Built with Veil privacy stack.`,
928
+ private: true,
929
+ scripts,
930
+ dependencies: deps,
931
+ devDependencies: devDeps
932
+ };
933
+ return JSON.stringify(pkg, null, 2);
934
+ }
935
+ function generateTsConfig(config) {
936
+ const isNext = config.framework === "nextjs";
937
+ if (isNext) {
938
+ return JSON.stringify({
939
+ compilerOptions: {
940
+ target: "ES2017",
941
+ lib: ["dom", "dom.iterable", "esnext"],
942
+ allowJs: true,
943
+ skipLibCheck: true,
944
+ strict: true,
945
+ noEmit: true,
946
+ esModuleInterop: true,
947
+ module: "esnext",
948
+ moduleResolution: "bundler",
949
+ resolveJsonModule: true,
950
+ isolatedModules: true,
951
+ jsx: "preserve",
952
+ incremental: true,
953
+ plugins: [{ name: "next" }],
954
+ paths: { "@/*": ["./*"] }
955
+ },
956
+ include: ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
957
+ exclude: ["node_modules"]
958
+ }, null, 2);
959
+ }
960
+ return JSON.stringify({
961
+ compilerOptions: {
962
+ target: "ES2020",
963
+ useDefineForClassFields: true,
964
+ lib: ["ES2020", "DOM", "DOM.Iterable"],
965
+ module: "ESNext",
966
+ skipLibCheck: true,
967
+ moduleResolution: "bundler",
968
+ allowImportingTsExtensions: true,
969
+ resolveJsonModule: true,
970
+ isolatedModules: true,
971
+ noEmit: true,
972
+ jsx: "react-jsx",
973
+ strict: true,
974
+ noUnusedLocals: true,
975
+ noUnusedParameters: true,
976
+ noFallthroughCasesInSwitch: true
977
+ },
978
+ include: ["src"],
979
+ references: [{ path: "./tsconfig.node.json" }]
980
+ }, null, 2);
981
+ }
982
+ function generateAppEntry(config) {
983
+ const isNext = config.template === "nextjs";
984
+ if (isNext) {
985
+ return `"use client";
986
+
987
+ import { useWallet } from "@solana/wallet-adapter-react";
988
+ import { WalletButton } from "./components/WalletButton";
989
+ import { PrivacyStatus } from "./components/PrivacyStatus";
990
+ import { useVeil } from "../hooks/useVeil";
991
+
992
+ export default function Home() {
993
+ const { connected } = useWallet();
994
+ const { isAuthenticated, login, logout } = useVeil();
995
+
996
+ return (
997
+ <main className="min-h-screen p-8">
998
+ {/* Header */}
999
+ <header className="flex justify-between items-center mb-12">
1000
+ <div className="flex items-center gap-4">
1001
+ <h1 className="text-2xl font-bold bg-gradient-to-r from-purple-400 to-blue-400 bg-clip-text text-transparent">
1002
+ ${config.projectName}
1003
+ </h1>
1004
+ <PrivacyStatus />
1005
+ </div>
1006
+ <WalletButton />
1007
+ </header>
1008
+
1009
+ {/* Hero Section */}
1010
+ <section className="max-w-2xl mx-auto text-center mb-16">
1011
+ <h2 className="text-5xl font-bold mb-6">
1012
+ Privacy-First
1013
+ <span className="block bg-gradient-to-r from-purple-400 to-blue-400 bg-clip-text text-transparent">
1014
+ Solana App
1015
+ </span>
1016
+ </h2>
1017
+ <p className="text-gray-400 text-lg mb-8">
1018
+ Built with Veil \u2014 Your identity is never on-chain.
1019
+ </p>
1020
+
1021
+ {connected && !isAuthenticated && (
1022
+ <button
1023
+ onClick={login}
1024
+ className="px-8 py-3 rounded-xl font-semibold
1025
+ bg-gradient-to-r from-purple-600 to-blue-600
1026
+ hover:from-purple-700 hover:to-blue-700
1027
+ transition-all duration-200 shadow-lg"
1028
+ >
1029
+ Create Private Session
1030
+ </button>
1031
+ )}
1032
+
1033
+ {isAuthenticated && (
1034
+ <div className="glass rounded-xl p-6">
1035
+ <p className="text-green-400 mb-4">\u2705 You are privately authenticated</p>
1036
+ <button
1037
+ onClick={logout}
1038
+ className="text-gray-400 hover:text-white transition-colors"
1039
+ >
1040
+ End Session
1041
+ </button>
1042
+ </div>
1043
+ )}
1044
+ </section>
1045
+
1046
+ {/* Privacy Guarantees */}
1047
+ <section className="max-w-4xl mx-auto">
1048
+ <h3 className="text-2xl font-bold mb-6 text-center">Privacy Guarantees</h3>
1049
+ <div className="grid md:grid-cols-2 gap-4">
1050
+ <div className="glass rounded-xl p-6">
1051
+ <h4 className="text-green-400 font-semibold mb-2">\u2705 What's Private</h4>
1052
+ <ul className="text-gray-300 space-y-2 text-sm">
1053
+ <li>\u2022 Your identity (never on-chain)</li>
1054
+ <li>\u2022 How you access your wallet</li>
1055
+ <li>\u2022 Who your recovery guardians are</li>
1056
+ </ul>
1057
+ </div>
1058
+ <div className="glass rounded-xl p-6">
1059
+ <h4 className="text-red-400 font-semibold mb-2">\u274C What's Public (Solana)</h4>
1060
+ <ul className="text-gray-300 space-y-2 text-sm">
1061
+ <li>\u2022 Transaction amounts</li>
1062
+ <li>\u2022 Transaction recipients</li>
1063
+ <li>\u2022 Wallet balances</li>
1064
+ </ul>
1065
+ </div>
1066
+ </div>
1067
+ </section>
1068
+ </main>
1069
+ );
1070
+ }
1071
+ `;
1072
+ }
1073
+ return `import { WalletAdapterNetwork } from "@solana/wallet-adapter-base";
1074
+ import { ConnectionProvider, WalletProvider } from "@solana/wallet-adapter-react";
1075
+ import { WalletModalProvider } from "@solana/wallet-adapter-react-ui";
1076
+ import { PhantomWalletAdapter } from "@solana/wallet-adapter-wallets";
1077
+ import { clusterApiUrl } from "@solana/web3.js";
1078
+ import { useMemo } from "react";
1079
+
1080
+ import "@solana/wallet-adapter-react-ui/styles.css";
1081
+
1082
+ export default function App() {
1083
+ const network = WalletAdapterNetwork.Devnet;
1084
+ const endpoint = useMemo(() => clusterApiUrl(network), [network]);
1085
+ const wallets = useMemo(() => [new PhantomWalletAdapter()], []);
1086
+
1087
+ return (
1088
+ <ConnectionProvider endpoint={endpoint}>
1089
+ <WalletProvider wallets={wallets} autoConnect>
1090
+ <WalletModalProvider>
1091
+ <main style={{ padding: "2rem" }}>
1092
+ <h1>${config.projectName}</h1>
1093
+ <p>Privacy-first Solana app built with Veil</p>
1094
+ </main>
1095
+ </WalletModalProvider>
1096
+ </WalletProvider>
1097
+ </ConnectionProvider>
1098
+ );
1099
+ }
1100
+ `;
1101
+ }
1102
+
1103
+ // src/templates/components.ts
1104
+ function generateWalletButton() {
1105
+ return `"use client";
1106
+
1107
+ import { useWallet } from "@solana/wallet-adapter-react";
1108
+ import { useWalletModal } from "@solana/wallet-adapter-react-ui";
1109
+
1110
+ export function WalletButton() {
1111
+ const { wallet, connect, disconnect, connected, publicKey } = useWallet();
1112
+ const { setVisible } = useWalletModal();
1113
+
1114
+ const handleClick = () => {
1115
+ if (connected) {
1116
+ disconnect();
1117
+ } else if (wallet) {
1118
+ connect();
1119
+ } else {
1120
+ setVisible(true);
1121
+ }
1122
+ };
1123
+
1124
+ const truncateAddress = (address: string) => {
1125
+ return \`\${address.slice(0, 4)}...\${address.slice(-4)}\`;
1126
+ };
1127
+
1128
+ return (
1129
+ <button
1130
+ onClick={handleClick}
1131
+ className="px-4 py-2 rounded-lg font-medium transition-all duration-200
1132
+ bg-gradient-to-r from-purple-600 to-blue-600
1133
+ hover:from-purple-700 hover:to-blue-700
1134
+ text-white shadow-lg hover:shadow-xl"
1135
+ >
1136
+ {connected && publicKey
1137
+ ? truncateAddress(publicKey.toBase58())
1138
+ : "Connect Wallet"}
1139
+ </button>
1140
+ );
1141
+ }
1142
+ `;
1143
+ }
1144
+ function generatePrivacyStatus() {
1145
+ return `"use client";
1146
+
1147
+ import { useVeil } from "../../hooks/useVeil";
1148
+
1149
+ export function PrivacyStatus() {
1150
+ const { isAuthenticated, privacyLevel } = useVeil();
1151
+
1152
+ const getStatusColor = () => {
1153
+ if (!isAuthenticated) return "bg-gray-500";
1154
+ switch (privacyLevel) {
1155
+ case "high": return "bg-green-500";
1156
+ case "medium": return "bg-yellow-500";
1157
+ case "low": return "bg-red-500";
1158
+ default: return "bg-gray-500";
1159
+ }
1160
+ };
1161
+
1162
+ const getStatusText = () => {
1163
+ if (!isAuthenticated) return "Not Connected";
1164
+ switch (privacyLevel) {
1165
+ case "high": return "Privacy: High";
1166
+ case "medium": return "Privacy: Medium";
1167
+ case "low": return "Privacy: Low";
1168
+ default: return "Unknown";
1169
+ }
1170
+ };
1171
+
1172
+ return (
1173
+ <div className="flex items-center gap-2 px-3 py-1.5 rounded-full bg-gray-800/50 border border-gray-700">
1174
+ <div className={\`w-2 h-2 rounded-full \${getStatusColor()}\`} />
1175
+ <span className="text-sm text-gray-300">{getStatusText()}</span>
1176
+ </div>
1177
+ );
1178
+ }
1179
+ `;
1180
+ }
1181
+ function generateVeilProvider(config) {
1182
+ return `"use client";
1183
+
1184
+ import { createContext, useContext, useState, useEffect, ReactNode } from "react";
1185
+ import { useWallet } from "@solana/wallet-adapter-react";
1186
+ import { createLoginSession, isSessionValid, LoginSession } from "../privacy/login";
1187
+
1188
+ interface VeilContextType {
1189
+ session: LoginSession | null;
1190
+ isAuthenticated: boolean;
1191
+ privacyLevel: "high" | "medium" | "low" | null;
1192
+ login: () => Promise<void>;
1193
+ logout: () => void;
1194
+ }
1195
+
1196
+ const VeilContext = createContext<VeilContextType | null>(null);
1197
+
1198
+ export function VeilProvider({ children }: { children: ReactNode }) {
1199
+ const { connected, publicKey, signMessage } = useWallet();
1200
+ const [session, setSession] = useState<LoginSession | null>(null);
1201
+
1202
+ const isAuthenticated = connected && session !== null && isSessionValid(session);
1203
+ const privacyLevel = isAuthenticated ? "high" : null;
1204
+
1205
+ const login = async () => {
1206
+ if (!connected || !publicKey || !signMessage) {
1207
+ throw new Error("Wallet not connected");
1208
+ }
1209
+
1210
+ // Create identity proof from wallet signature
1211
+ const message = new TextEncoder().encode(
1212
+ \`Veil Login: \${Date.now()}\`
1213
+ );
1214
+ const signature = await signMessage(message);
1215
+
1216
+ const newSession = await createLoginSession(signature);
1217
+ setSession(newSession);
1218
+ };
1219
+
1220
+ const logout = () => {
1221
+ setSession(null);
1222
+ };
1223
+
1224
+ // Auto-logout when wallet disconnects
1225
+ useEffect(() => {
1226
+ if (!connected) {
1227
+ setSession(null);
1228
+ }
1229
+ }, [connected]);
1230
+
1231
+ return (
1232
+ <VeilContext.Provider value={{ session, isAuthenticated, privacyLevel, login, logout }}>
1233
+ {children}
1234
+ </VeilContext.Provider>
1235
+ );
1236
+ }
1237
+
1238
+ export function useVeilContext() {
1239
+ const context = useContext(VeilContext);
1240
+ if (!context) {
1241
+ throw new Error("useVeilContext must be used within VeilProvider");
1242
+ }
1243
+ return context;
1244
+ }
1245
+ `;
1246
+ }
1247
+ function generateVeilHooks(config) {
1248
+ return `"use client";
1249
+
1250
+ import { useVeilContext } from "../contexts/VeilProvider";
1251
+ import { PRIVACY_GUARANTEES } from "../privacy/guarantees";
1252
+
1253
+ export function useVeil() {
1254
+ const context = useVeilContext();
1255
+ return context;
1256
+ }
1257
+
1258
+ export function usePrivacyGuarantees() {
1259
+ return PRIVACY_GUARANTEES;
1260
+ }
1261
+
1262
+ export function usePrivacyCheck() {
1263
+ const { isAuthenticated, privacyLevel } = useVeil();
1264
+
1265
+ return {
1266
+ isPrivate: privacyLevel === "high",
1267
+ warnings: privacyLevel === "low"
1268
+ ? ["Your session may be linkable to your identity"]
1269
+ : [],
1270
+ };
1271
+ }
1272
+ `;
1273
+ }
1274
+
1275
+ // src/templates/config.ts
1276
+ function generateLayoutTsx(config) {
1277
+ return `import type { Metadata } from "next";
1278
+ import { Inter } from "next/font/google";
1279
+ import "./globals.css";
1280
+ import { Providers } from "./providers";
1281
+
1282
+ const inter = Inter({ subsets: ["latin"] });
1283
+
1284
+ export const metadata: Metadata = {
1285
+ title: "${config.projectName}",
1286
+ description: "Privacy-first Solana app built with Veil",
1287
+ };
1288
+
1289
+ export default function RootLayout({
1290
+ children,
1291
+ }: {
1292
+ children: React.ReactNode;
1293
+ }) {
1294
+ return (
1295
+ <html lang="en">
1296
+ <body className={inter.className}>
1297
+ <Providers>
1298
+ {children}
1299
+ </Providers>
1300
+ </body>
1301
+ </html>
1302
+ );
1303
+ }
1304
+ `;
1305
+ }
1306
+ function generateGlobalsCss() {
1307
+ return `@tailwind base;
1308
+ @tailwind components;
1309
+ @tailwind utilities;
1310
+
1311
+ :root {
1312
+ --foreground-rgb: 255, 255, 255;
1313
+ --background-start-rgb: 10, 10, 20;
1314
+ --background-end-rgb: 20, 20, 40;
1315
+ }
1316
+
1317
+ body {
1318
+ color: rgb(var(--foreground-rgb));
1319
+ background: linear-gradient(
1320
+ to bottom,
1321
+ rgb(var(--background-start-rgb)),
1322
+ rgb(var(--background-end-rgb))
1323
+ );
1324
+ min-height: 100vh;
1325
+ }
1326
+
1327
+ /* Veil custom utilities */
1328
+ .glass {
1329
+ @apply bg-white/5 backdrop-blur-xl border border-white/10;
1330
+ }
1331
+
1332
+ .glass-hover {
1333
+ @apply hover:bg-white/10 hover:border-white/20 transition-all duration-300;
1334
+ }
1335
+ `;
1336
+ }
1337
+ function generateTailwindConfig(config) {
1338
+ const contentPath = config.template === "nextjs" ? `"./app/**/*.{js,ts,jsx,tsx,mdx}",` : `"./src/**/*.{js,ts,jsx,tsx,mdx}",`;
1339
+ return `/** @type {import('tailwindcss').Config} */
1340
+ module.exports = {
1341
+ content: [
1342
+ ${contentPath}
1343
+ "./components/**/*.{js,ts,jsx,tsx,mdx}",
1344
+ "./contexts/**/*.{js,ts,jsx,tsx,mdx}",
1345
+ ],
1346
+ theme: {
1347
+ extend: {
1348
+ colors: {
1349
+ veil: {
1350
+ purple: "#7C3AED",
1351
+ blue: "#3B82F6",
1352
+ dark: "#0A0A14",
1353
+ },
1354
+ },
1355
+ },
1356
+ },
1357
+ plugins: [],
1358
+ };
1359
+ `;
1360
+ }
1361
+ function generatePostcssConfig() {
1362
+ return `module.exports = {
1363
+ plugins: {
1364
+ tailwindcss: {},
1365
+ autoprefixer: {},
1366
+ },
1367
+ };
1368
+ `;
1369
+ }
1370
+ function generateNextConfig() {
1371
+ return `/** @type {import('next').NextConfig} */
1372
+ const nextConfig = {
1373
+ webpack: (config) => {
1374
+ config.externals.push("pino-pretty", "lokijs", "encoding");
1375
+ return config;
1376
+ },
1377
+ };
1378
+
1379
+ module.exports = nextConfig;
1380
+ `;
1381
+ }
1382
+ function generateProvidersTsx(config) {
1383
+ return `"use client";
1384
+
1385
+ import { WalletAdapterNetwork } from "@solana/wallet-adapter-base";
1386
+ import {
1387
+ ConnectionProvider,
1388
+ WalletProvider,
1389
+ } from "@solana/wallet-adapter-react";
1390
+ import { WalletModalProvider } from "@solana/wallet-adapter-react-ui";
1391
+ import { PhantomWalletAdapter } from "@solana/wallet-adapter-phantom";
1392
+ import { SolflareWalletAdapter } from "@solana/wallet-adapter-solflare";
1393
+ import { clusterApiUrl } from "@solana/web3.js";
1394
+ import { useMemo, ReactNode } from "react";
1395
+ import { VeilProvider } from "../contexts/VeilProvider";
1396
+
1397
+ import "@solana/wallet-adapter-react-ui/styles.css";
1398
+
1399
+ export function Providers({ children }: { children: ReactNode }) {
1400
+ const network = WalletAdapterNetwork.${config.network === "devnet" ? "Devnet" : "Devnet"};
1401
+ const endpoint = useMemo(() => {
1402
+ // Use Helius if configured, otherwise default
1403
+ if (typeof window === "undefined" && process.env.HELIUS_RPC_URL) {
1404
+ return process.env.HELIUS_RPC_URL;
1405
+ }
1406
+ return clusterApiUrl(network);
1407
+ }, [network]);
1408
+
1409
+ const wallets = useMemo(
1410
+ () => [new PhantomWalletAdapter(), new SolflareWalletAdapter()],
1411
+ []
1412
+ );
1413
+
1414
+ return (
1415
+ <ConnectionProvider endpoint={endpoint}>
1416
+ <WalletProvider wallets={wallets} autoConnect>
1417
+ <WalletModalProvider>
1418
+ <VeilProvider>{children}</VeilProvider>
1419
+ </WalletModalProvider>
1420
+ </WalletProvider>
1421
+ </ConnectionProvider>
1422
+ );
1423
+ }
1424
+ `;
1425
+ }
1426
+
1427
+ // src/templates/shadowpay.ts
1428
+ function generateShadowPayModule(config) {
1429
+ if (config.shadowPay === "app") {
1430
+ return generateShadowPayApp();
1431
+ } else if (config.shadowPay === "wallet") {
1432
+ return generateShadowPayWallet();
1433
+ }
1434
+ return "";
1435
+ }
1436
+ function generateShadowPayApp() {
1437
+ return `/**
1438
+ * ShadowPay - App Mode (MAINNET)
1439
+ *
1440
+ * \u26A0\uFE0F IMPORTANT: ShadowPay uses MAINNET for real private transfers
1441
+ *
1442
+ * Your app RECEIVES private payments from users.
1443
+ * Users can pay you without revealing their identity to you.
1444
+ *
1445
+ * PRIVACY GUARANTEE:
1446
+ * - You receive payments but don't know who sent them
1447
+ * - Payment amounts are still visible on-chain
1448
+ * - User identity is protected via commitment schemes
1449
+ *
1450
+ * NETWORK: mainnet-beta (real SOL/tokens)
1451
+ * This is separate from Veil features which run on devnet
1452
+ */
1453
+
1454
+ import { PublicKey, Transaction, Connection, clusterApiUrl } from "@solana/web3.js";
1455
+
1456
+ // ShadowPay always uses mainnet for real private transfers
1457
+ export const SHADOWPAY_NETWORK = "mainnet-beta";
1458
+ export const SHADOWPAY_RPC = process.env.NEXT_PUBLIC_SHADOWPAY_RPC || clusterApiUrl("mainnet-beta");
1459
+
1460
+ export function getShadowPayConnection(): Connection {
1461
+ return new Connection(SHADOWPAY_RPC, "confirmed");
1462
+ }
1463
+
1464
+ export interface PrivatePaymentRequest {
1465
+ /** Amount in lamports */
1466
+ amount: number;
1467
+ /** Your receiving address */
1468
+ recipient: PublicKey;
1469
+ /** Optional memo (visible on-chain) */
1470
+ memo?: string;
1471
+ /** Payment expiry timestamp */
1472
+ expiresAt?: number;
1473
+ }
1474
+
1475
+ export interface PrivatePayment {
1476
+ /** Unique payment ID */
1477
+ id: string;
1478
+ /** Amount received */
1479
+ amount: number;
1480
+ /** Transaction signature */
1481
+ signature: string;
1482
+ /** Commitment hash (hides sender) */
1483
+ senderCommitment: Uint8Array;
1484
+ /** Timestamp */
1485
+ timestamp: number;
1486
+ }
1487
+
1488
+ /**
1489
+ * Create a payment request that users can fulfill privately.
1490
+ */
1491
+ export function createPaymentRequest(
1492
+ amount: number,
1493
+ recipient: PublicKey,
1494
+ options?: { memo?: string; expiresInSeconds?: number }
1495
+ ): PrivatePaymentRequest {
1496
+ return {
1497
+ amount,
1498
+ recipient,
1499
+ memo: options?.memo,
1500
+ expiresAt: options?.expiresInSeconds
1501
+ ? Date.now() + options.expiresInSeconds * 1000
1502
+ : undefined,
1503
+ };
1504
+ }
1505
+
1506
+ /**
1507
+ * Generate a payment link for users.
1508
+ * They can scan/click this to pay privately.
1509
+ */
1510
+ export function generatePaymentLink(request: PrivatePaymentRequest): string {
1511
+ const params = new URLSearchParams({
1512
+ amount: request.amount.toString(),
1513
+ recipient: request.recipient.toBase58(),
1514
+ ...(request.memo && { memo: request.memo }),
1515
+ ...(request.expiresAt && { expires: request.expiresAt.toString() }),
1516
+ });
1517
+
1518
+ return \`shadowpay://pay?\${params.toString()}\`;
1519
+ }
1520
+
1521
+ /**
1522
+ * Verify a received payment without knowing the sender.
1523
+ */
1524
+ export async function verifyPayment(
1525
+ connection: Connection,
1526
+ signature: string,
1527
+ expectedAmount: number,
1528
+ recipient: PublicKey
1529
+ ): Promise<boolean> {
1530
+ const tx = await connection.getTransaction(signature, {
1531
+ commitment: "confirmed",
1532
+ });
1533
+
1534
+ if (!tx || !tx.meta) return false;
1535
+
1536
+ // Verify the payment was received
1537
+ const recipientIndex = tx.transaction.message.accountKeys.findIndex(
1538
+ (key) => key.equals(recipient)
1539
+ );
1540
+
1541
+ if (recipientIndex === -1) return false;
1542
+
1543
+ const postBalance = tx.meta.postBalances[recipientIndex];
1544
+ const preBalance = tx.meta.preBalances[recipientIndex];
1545
+ const received = postBalance - preBalance;
1546
+
1547
+ return received >= expectedAmount;
1548
+ }
1549
+
1550
+ /**
1551
+ * Listen for incoming private payments.
1552
+ */
1553
+ export function onPaymentReceived(
1554
+ callback: (payment: PrivatePayment) => void
1555
+ ): () => void {
1556
+ // In production: Set up WebSocket or polling
1557
+ console.log("Payment listener started");
1558
+
1559
+ return () => {
1560
+ console.log("Payment listener stopped");
1561
+ };
1562
+ }
1563
+ `;
1564
+ }
1565
+ function generateShadowPayWallet() {
1566
+ return `/**
1567
+ * ShadowPay - Wallet Mode (MAINNET)
1568
+ *
1569
+ * \u26A0\uFE0F IMPORTANT: ShadowPay uses MAINNET for real private transfers
1570
+ *
1571
+ * Your wallet SENDS private payments to apps/users.
1572
+ * You can pay without revealing your identity to recipients.
1573
+ *
1574
+ * PRIVACY GUARANTEE:
1575
+ * - Recipients don't know your identity
1576
+ * - Payment is verified via commitment proofs
1577
+ * - Your wallet address is not directly linked
1578
+ *
1579
+ * NETWORK: mainnet-beta (real SOL/tokens)
1580
+ * This is separate from Veil features which run on devnet
1581
+ */
1582
+
1583
+ import { PublicKey, Transaction, Connection, Keypair, clusterApiUrl } from "@solana/web3.js";
1584
+
1585
+ // ShadowPay always uses mainnet for real private transfers
1586
+ export const SHADOWPAY_NETWORK = "mainnet-beta";
1587
+ export const SHADOWPAY_RPC = process.env.NEXT_PUBLIC_SHADOWPAY_RPC || clusterApiUrl("mainnet-beta");
1588
+
1589
+ export function getShadowPayConnection(): Connection {
1590
+ return new Connection(SHADOWPAY_RPC, "confirmed");
1591
+ }
1592
+
1593
+ export interface PrivatePaymentIntent {
1594
+ /** Amount in lamports */
1595
+ amount: number;
1596
+ /** Recipient address */
1597
+ recipient: PublicKey;
1598
+ /** Your commitment (hides your identity) */
1599
+ commitment: Uint8Array;
1600
+ }
1601
+
1602
+ /**
1603
+ * Create a commitment that hides your identity.
1604
+ */
1605
+ export async function createSenderCommitment(
1606
+ senderSecret: Uint8Array
1607
+ ): Promise<Uint8Array> {
1608
+ const hash = await crypto.subtle.digest("SHA-256", senderSecret.buffer as ArrayBuffer);
1609
+ return new Uint8Array(hash);
1610
+ }
1611
+
1612
+ /**
1613
+ * Prepare a private payment.
1614
+ * The recipient will receive funds but won't know your identity.
1615
+ */
1616
+ export async function preparePrivatePayment(
1617
+ amount: number,
1618
+ recipient: PublicKey,
1619
+ senderSecret: Uint8Array
1620
+ ): Promise<PrivatePaymentIntent> {
1621
+ const commitment = await createSenderCommitment(senderSecret);
1622
+
1623
+ return {
1624
+ amount,
1625
+ recipient,
1626
+ commitment,
1627
+ };
1628
+ }
1629
+ `;
1630
+ }
1631
+
1632
+ // src/templates/sdk-integration.ts
1633
+ function generateSdkProvider(config) {
1634
+ return `"use client";
1635
+
1636
+ /**
1637
+ * VeilSDKProvider - Full SDK integration for privacy features
1638
+ *
1639
+ * This provider wraps your app with the Veil SDK, enabling:
1640
+ * - ZK-based identity (no on-chain identity)
1641
+ * - Shielded balances (hide your holdings)
1642
+ * - Private transfers (hidden amounts)
1643
+ * - Private token operations
1644
+ * - Private DEX swaps
1645
+ */
1646
+
1647
+ import { createContext, useContext, useState, useEffect, ReactNode, useCallback } from "react";
1648
+ import { useWallet, useConnection } from "@solana/wallet-adapter-react";
1649
+ import { VeilClient, ShieldedClient, TransferClient, TokenClient, DexClient } from "@veil-protocol/sdk";
1650
+
1651
+ interface VeilSDKContextType {
1652
+ // Core client
1653
+ veil: VeilClient | null;
1654
+
1655
+ // Sub-clients
1656
+ shielded: ShieldedClient | null;
1657
+ transfer: TransferClient | null;
1658
+ tokens: TokenClient | null;
1659
+ dex: DexClient | null;
1660
+
1661
+ // State
1662
+ isConnected: boolean;
1663
+ isLoading: boolean;
1664
+ error: string | null;
1665
+
1666
+ // Balances
1667
+ publicBalance: number;
1668
+ shieldedBalance: number;
1669
+
1670
+ // Actions
1671
+ refreshBalances: () => Promise<void>;
1672
+ shield: (amount: number) => Promise<string>;
1673
+ unshield: (amount: number) => Promise<string>;
1674
+ privateTransfer: (recipient: string, amount: number) => Promise<string>;
1675
+ }
1676
+
1677
+ const VeilSDKContext = createContext<VeilSDKContextType | null>(null);
1678
+
1679
+ export function VeilSDKProvider({ children }: { children: ReactNode }) {
1680
+ const { connection } = useConnection();
1681
+ const { publicKey, signTransaction, connected } = useWallet();
1682
+
1683
+ const [veil, setVeil] = useState<VeilClient | null>(null);
1684
+ const [shielded, setShielded] = useState<ShieldedClient | null>(null);
1685
+ const [transfer, setTransfer] = useState<TransferClient | null>(null);
1686
+ const [tokens, setTokens] = useState<TokenClient | null>(null);
1687
+ const [dex, setDex] = useState<DexClient | null>(null);
1688
+
1689
+ const [isLoading, setIsLoading] = useState(false);
1690
+ const [error, setError] = useState<string | null>(null);
1691
+ const [publicBalance, setPublicBalance] = useState(0);
1692
+ const [shieldedBalance, setShieldedBalance] = useState(0);
1693
+
1694
+ // Initialize SDK when wallet connects
1695
+ useEffect(() => {
1696
+ if (connected && publicKey) {
1697
+ const client = new VeilClient({ connection });
1698
+ setVeil(client);
1699
+ setShielded(client.shielded);
1700
+ setTransfer(client.transfer);
1701
+ setTokens(client.tokens);
1702
+ setDex(client.dex);
1703
+ } else {
1704
+ setVeil(null);
1705
+ setShielded(null);
1706
+ setTransfer(null);
1707
+ setTokens(null);
1708
+ setDex(null);
1709
+ }
1710
+ }, [connected, publicKey, connection]);
1711
+
1712
+ const refreshBalances = useCallback(async () => {
1713
+ if (!veil || !publicKey) return;
1714
+
1715
+ setIsLoading(true);
1716
+ try {
1717
+ const balances = await veil.getBalances(publicKey);
1718
+ setPublicBalance(balances.public);
1719
+ setShieldedBalance(balances.shielded);
1720
+ } catch (err) {
1721
+ setError(err instanceof Error ? err.message : "Failed to fetch balances");
1722
+ } finally {
1723
+ setIsLoading(false);
1724
+ }
1725
+ }, [veil, publicKey]);
1726
+
1727
+ const shield = useCallback(async (amount: number): Promise<string> => {
1728
+ if (!shielded || !publicKey || !signTransaction) {
1729
+ throw new Error("Wallet not connected");
1730
+ }
1731
+ return shielded.deposit(publicKey, amount, signTransaction);
1732
+ }, [shielded, publicKey, signTransaction]);
1733
+
1734
+ const unshield = useCallback(async (amount: number): Promise<string> => {
1735
+ if (!shielded || !publicKey || !signTransaction) {
1736
+ throw new Error("Wallet not connected");
1737
+ }
1738
+ return shielded.withdraw(publicKey, amount, signTransaction);
1739
+ }, [shielded, publicKey, signTransaction]);
1740
+
1741
+ const privateTransfer = useCallback(async (recipient: string, amount: number): Promise<string> => {
1742
+ if (!transfer || !publicKey || !signTransaction) {
1743
+ throw new Error("Wallet not connected");
1744
+ }
1745
+ return transfer.privateTransfer(publicKey, recipient, amount, signTransaction);
1746
+ }, [transfer, publicKey, signTransaction]);
1747
+
1748
+ return (
1749
+ <VeilSDKContext.Provider value={{
1750
+ veil,
1751
+ shielded,
1752
+ transfer,
1753
+ tokens,
1754
+ dex,
1755
+ isConnected: connected && veil !== null,
1756
+ isLoading,
1757
+ error,
1758
+ publicBalance,
1759
+ shieldedBalance,
1760
+ refreshBalances,
1761
+ shield,
1762
+ unshield,
1763
+ privateTransfer,
1764
+ }}>
1765
+ {children}
1766
+ </VeilSDKContext.Provider>
1767
+ );
1768
+ }
1769
+
1770
+ export function useVeilSDK() {
1771
+ const context = useContext(VeilSDKContext);
1772
+ if (!context) {
1773
+ throw new Error("useVeilSDK must be used within VeilSDKProvider");
1774
+ }
1775
+ return context;
1776
+ }
1777
+ `;
1778
+ }
1779
+ function generateSdkHooks() {
1780
+ return `"use client";
1781
+
1782
+ /**
1783
+ * Veil SDK Hooks
1784
+ *
1785
+ * Convenient hooks for common SDK operations
1786
+ */
1787
+
1788
+ import { useVeilSDK } from "../contexts/VeilSDKProvider";
1789
+ import { useState, useCallback } from "react";
1790
+
1791
+ export function useShieldedBalance() {
1792
+ const { publicBalance, shieldedBalance, refreshBalances, isLoading } = useVeilSDK();
1793
+ return { publicBalance, shieldedBalance, refresh: refreshBalances, isLoading };
1794
+ }
1795
+
1796
+ export function usePrivateTransfer() {
1797
+ const { privateTransfer } = useVeilSDK();
1798
+ const [isPending, setIsPending] = useState(false);
1799
+ const [txHash, setTxHash] = useState<string | null>(null);
1800
+ const [error, setError] = useState<string | null>(null);
1801
+
1802
+ const send = useCallback(async (recipient: string, amount: number) => {
1803
+ setIsPending(true);
1804
+ setError(null);
1805
+ try {
1806
+ const hash = await privateTransfer(recipient, amount);
1807
+ setTxHash(hash);
1808
+ return hash;
1809
+ } catch (err) {
1810
+ setError(err instanceof Error ? err.message : "Transfer failed");
1811
+ throw err;
1812
+ } finally {
1813
+ setIsPending(false);
1814
+ }
1815
+ }, [privateTransfer]);
1816
+
1817
+ return { send, isPending, txHash, error };
1818
+ }
1819
+ `;
1820
+ }
1821
+
1822
+ // src/templates/index.ts
1823
+ function generateVeilConfig(config) {
1824
+ return `/**
1825
+ * Veil Configuration
1826
+ *
1827
+ * This file documents the privacy guarantees of your application.
1828
+ * It is used by the Veil CLI and serves as documentation.
1829
+ */
1830
+
1831
+ export interface VeilConfig {
1832
+ project: {
1833
+ name: string;
1834
+ network: "devnet" | "mainnet-beta";
1835
+ };
1836
+ privacy: {
1837
+ identity: {
1838
+ enabled: boolean;
1839
+ method: "zk-login" | "passkey";
1840
+ storeOnChain: boolean;
1841
+ };
1842
+ access: {
1843
+ proofRequired: boolean;
1844
+ revealAddress: boolean;
1845
+ };
1846
+ recovery: {
1847
+ enabled: boolean;
1848
+ method: "timelock" | "shamir";
1849
+ publicGuardians: boolean;
1850
+ revealParticipants: boolean;
1851
+ };
1852
+ };
1853
+ infrastructure: {
1854
+ rpc: {
1855
+ provider: "helius" | "default";
1856
+ publicPolling: boolean;
1857
+ };
1858
+ observability: {
1859
+ webhooks: boolean;
1860
+ exposeMetadata: boolean;
1861
+ };
1862
+ };
1863
+ integrations: {
1864
+ shadowPay: {
1865
+ enabled: boolean;
1866
+ };
1867
+ };
1868
+ }
1869
+
1870
+ export function defineVeilConfig(config: VeilConfig): VeilConfig {
1871
+ return config;
1872
+ }
1873
+
1874
+ export default defineVeilConfig({
1875
+ project: {
1876
+ name: "${config.projectName}",
1877
+ network: "${config.network}",
1878
+ },
1879
+
1880
+ privacy: {
1881
+ identity: {
1882
+ enabled: true,
1883
+ method: "zk-login", // conceptual, hackathon-safe
1884
+ storeOnChain: false,
1885
+ },
1886
+
1887
+ access: {
1888
+ proofRequired: true,
1889
+ revealAddress: false,
1890
+ },
1891
+
1892
+ recovery: {
1893
+ enabled: true,
1894
+ method: "timelock", // or "shamir"
1895
+ publicGuardians: false,
1896
+ revealParticipants: false,
1897
+ },
1898
+ },
1899
+
1900
+ infrastructure: {
1901
+ rpc: {
1902
+ provider: "${config.helius ? "helius" : "default"}",
1903
+ publicPolling: false,
1904
+ },
1905
+
1906
+ observability: {
1907
+ webhooks: ${config.helius},
1908
+ exposeMetadata: false,
1909
+ },
1910
+ },
1911
+
1912
+ integrations: {
1913
+ shadowPay: {
1914
+ enabled: ${config.shadowPay ? "true" : "false"},
1915
+ },
1916
+ },
1917
+ });
1918
+ `;
1919
+ }
1920
+ function generateEnvExample(config) {
1921
+ let env = `# ============================================
1922
+ # Veil Configuration
1923
+ # ============================================
1924
+
1925
+ # Veil Features Network (voting, staking, multisig)
1926
+ NEXT_PUBLIC_NETWORK=${config.network}
1927
+ `;
1928
+ if (config.helius) {
1929
+ env += `
1930
+ # Helius RPC for Veil features (${config.network})
1931
+ # Get your key at https://helius.dev
1932
+ HELIUS_API_KEY=your_helius_api_key
1933
+ HELIUS_RPC_URL=https://${config.network}.helius-rpc.com/?api-key=YOUR_KEY
1934
+ HELIUS_WEBHOOK_SECRET=your_webhook_secret
1935
+ `;
1936
+ }
1937
+ if (config.shadowPay) {
1938
+ env += `
1939
+ # ============================================
1940
+ # ShadowPay Configuration (MAINNET)
1941
+ # ============================================
1942
+ # \u26A0\uFE0F ShadowPay uses MAINNET for real private transfers
1943
+ # This is separate from Veil features which use ${config.network}
1944
+
1945
+ # Mainnet RPC for ShadowPay (Helius recommended for production)
1946
+ NEXT_PUBLIC_SHADOWPAY_RPC=https://mainnet.helius-rpc.com/?api-key=YOUR_KEY
1947
+
1948
+ # Optional: ShadowPay API for enhanced features
1949
+ SHADOWPAY_API_KEY=your_shadowpay_key
1950
+ `;
1951
+ }
1952
+ return env;
1953
+ }
1954
+ function generateReadme(config) {
1955
+ const srcDir = config.framework === "nextjs" ? "app" : "src";
1956
+ const templateInfo = TEMPLATE_INFO[config.template];
1957
+ return `# ${config.projectName}
1958
+
1959
+ **${templateInfo.name}** \u2014 ${templateInfo.description}
1960
+
1961
+ A privacy-first Solana application built with Veil + ShadowWire.
1962
+
1963
+ ## Privacy Architecture
1964
+
1965
+ This app includes the **complete Veil privacy stack**:
1966
+
1967
+ | Feature | Description | Network |
1968
+ |---------|-------------|---------|
1969
+ | \u{1F510} **Identity** | ZK authentication, no PII on-chain | ${config.network} |
1970
+ | \u{1F504} **Recovery** | Shamir secret sharing, hidden guardians | ${config.network} |
1971
+ | \u{1F5F3}\uFE0F **Voting** | Commit-reveal privacy | ${config.network} |
1972
+ | \u{1F4CA} **Staking** | Hidden amounts via Pedersen | ${config.network} |
1973
+ | \u{1F465} **Multisig** | Stealth signers | ${config.network} |
1974
+ ${config.features.shadowpay ? `| \u26A1 **ShadowPay** | Private transfers via @radr/shadowwire | mainnet |` : ""}
1975
+
1976
+ ## What's Private vs Public
1977
+
1978
+ | Aspect | Private | Public |
1979
+ |--------|---------|--------|
1980
+ | Your identity | \u2705 Never on-chain | |
1981
+ | Wallet access method | \u2705 Hidden | |
1982
+ | Recovery guardians | \u2705 Hidden | |
1983
+ | Vote choices | \u2705 Hidden until reveal | |
1984
+ | Stake amounts | \u2705 Committed privately | |
1985
+ | Transaction amounts | | \u274C Visible (unless via ShadowPay) |
1986
+
1987
+ ## Project Structure
1988
+
1989
+ \`\`\`
1990
+ /${srcDir} \u2192 Frontend application
1991
+ /${srcDir}/components \u2192 UI & privacy components
1992
+ /lib/privacy \u2192 Privacy modules (login, recovery, access)
1993
+ /lib/veil \u2192 Veil SDK integration (RPC, Helius)
1994
+ ${config.features.shadowpay ? `/lib/shadowpay \u2192 ShadowWire private transfers (mainnet)` : ""}
1995
+ /hooks \u2192 React hooks for Veil SDK
1996
+ /contexts \u2192 Provider contexts
1997
+ \`\`\`
1998
+
1999
+ ## Getting Started
2000
+
2001
+ \`\`\`bash
2002
+ # Install dependencies
2003
+ pnpm install
2004
+
2005
+ # Copy environment variables
2006
+ cp .env.example .env
2007
+
2008
+ # Start development server
2009
+ pnpm dev
2010
+ \`\`\`
2011
+
2012
+ ## Network Configuration
2013
+
2014
+ - **Veil Features**: Run on \`${config.network}\` for development
2015
+ - **ShadowPay**: Runs on \`mainnet\` for real private transfers
2016
+
2017
+ ---
2018
+
2019
+ Built with [Veil](https://github.com/veil-protocol) + [ShadowWire](https://shadowwire.io)
2020
+ `;
2021
+ }
2022
+
2023
+ export {
2024
+ generateLoginTs,
2025
+ generateRecoveryTs,
2026
+ generateAccessTs,
2027
+ generateGuaranteesTs,
2028
+ generateRpcTs,
2029
+ generateHeliusTs,
2030
+ generatePackageJson,
2031
+ generateTsConfig,
2032
+ generateAppEntry,
2033
+ generateWalletButton,
2034
+ generatePrivacyStatus,
2035
+ generateVeilProvider,
2036
+ generateVeilHooks,
2037
+ generateLayoutTsx,
2038
+ generateGlobalsCss,
2039
+ generateTailwindConfig,
2040
+ generatePostcssConfig,
2041
+ generateNextConfig,
2042
+ generateProvidersTsx,
2043
+ generateShadowPayModule,
2044
+ generateSdkProvider,
2045
+ generateSdkHooks,
2046
+ generateVeilConfig,
2047
+ generateEnvExample,
2048
+ generateReadme,
2049
+ TEMPLATE_INFO,
2050
+ runInit,
2051
+ runAdd
2052
+ };