create-avalanche-app 0.1.2 → 0.1.3

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 CHANGED
@@ -10,7 +10,8 @@ import { existsSync, readdirSync } from "fs";
10
10
  import path from "path";
11
11
  import * as p from "@clack/prompts";
12
12
  import pc from "picocolors";
13
- var VERSION = "0.1.2";
13
+ var VERSION = "0.1.3";
14
+ var AVAKIT_DEP_VERSION = "0.1.2";
14
15
  function parseArgs(argv) {
15
16
  const opts = { yes: false, local: false, install: true };
16
17
  const rest = argv.slice(2);
@@ -70,7 +71,8 @@ function printHelp() {
70
71
  "Usage: npm create avalanche-app@latest [name] [options]",
71
72
  "",
72
73
  "Options:",
73
- " -t, --template <id> minimal | nft-mint | token-gated-app | erc20-token",
74
+ " -t, --template <id> minimal | nft-mint | token-gated-app | erc20-token |",
75
+ " icm-messenger | eerc-token",
74
76
  " -w, --wallet <id> web3auth | injected (default: web3auth)",
75
77
  " -c, --chain <id> fuji | c-chain (default: fuji)",
76
78
  " --pm <manager> pnpm | npm | yarn | bun",
@@ -170,7 +172,7 @@ Directory "${resolved.projectName}" already exists and is not empty.
170
172
  wallet: resolved.wallet,
171
173
  chain: resolved.chain,
172
174
  local: opts.local,
173
- avakitVersion: VERSION
175
+ avakitVersion: AVAKIT_DEP_VERSION
174
176
  });
175
177
  spin?.stop(`Created ${files.length} files`);
176
178
  if (opts.install) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { spawnSync } from \"node:child_process\";\nimport { existsSync, readdirSync } from \"node:fs\";\nimport path from \"node:path\";\nimport * as p from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport { type ChainId, listTemplates, scaffoldApp, type WalletId } from \"./api.js\";\n\nconst VERSION = \"0.1.2\";\n\ntype PackageManager = \"pnpm\" | \"npm\" | \"yarn\" | \"bun\";\n\ninterface Options {\n projectName?: string;\n template?: string;\n wallet?: WalletId;\n chain?: ChainId;\n pm?: PackageManager;\n yes: boolean;\n local: boolean;\n install: boolean;\n}\n\nfunction parseArgs(argv: string[]): Options {\n const opts: Options = { yes: false, local: false, install: true };\n const rest = argv.slice(2);\n for (let i = 0; i < rest.length; i++) {\n const arg = rest[i];\n const next = () => rest[++i];\n switch (arg) {\n case \"--yes\":\n case \"-y\":\n opts.yes = true;\n break;\n case \"--local\":\n opts.local = true;\n break;\n case \"--no-install\":\n opts.install = false;\n break;\n case \"--template\":\n case \"-t\":\n opts.template = next();\n break;\n case \"--wallet\":\n case \"-w\":\n opts.wallet = next() as WalletId;\n break;\n case \"--chain\":\n case \"-c\":\n opts.chain = next() as ChainId;\n break;\n case \"--pm\":\n opts.pm = next() as PackageManager;\n break;\n case \"--version\":\n case \"-v\":\n process.stdout.write(`${VERSION}\\n`);\n process.exit(0);\n break;\n case \"--help\":\n case \"-h\":\n printHelp();\n process.exit(0);\n break;\n default:\n if (arg && !arg.startsWith(\"-\") && !opts.projectName) {\n opts.projectName = arg;\n }\n }\n }\n return opts;\n}\n\nfunction printHelp(): void {\n process.stdout.write(\n [\n \"create-avalanche-app — scaffold a batteries-included Avalanche dapp\",\n \"\",\n \"Usage: npm create avalanche-app@latest [name] [options]\",\n \"\",\n \"Options:\",\n \" -t, --template <id> minimal | nft-mint | token-gated-app | erc20-token\",\n \" -w, --wallet <id> web3auth | injected (default: web3auth)\",\n \" -c, --chain <id> fuji | c-chain (default: fuji)\",\n \" --pm <manager> pnpm | npm | yarn | bun\",\n \" -y, --yes skip prompts (non-interactive)\",\n \" --no-install do not install dependencies\",\n \" --local link @avakit/* via workspace (repo dev only)\",\n \" -v, --version print version\",\n \" -h, --help print this help\",\n \"\",\n ].join(\"\\n\"),\n );\n}\n\nfunction isValidName(name: string): boolean {\n return /^[a-z0-9][a-z0-9._-]*$/.test(name);\n}\n\nasync function resolveOptions(\n opts: Options,\n): Promise<Required<Omit<Options, \"yes\" | \"local\" | \"install\">>> {\n const templates = listTemplates();\n const templateIds = templates.map((t) => t.id);\n\n if (opts.yes) {\n const projectName = opts.projectName ?? \"my-avax-app\";\n return {\n projectName,\n template: opts.template && templateIds.includes(opts.template) ? opts.template : \"minimal\",\n wallet: opts.wallet ?? \"web3auth\",\n chain: opts.chain ?? \"fuji\",\n pm: opts.pm ?? \"pnpm\",\n };\n }\n\n p.intro(pc.bgCyan(pc.black(\" create-avalanche-app \")));\n\n const projectName =\n opts.projectName ??\n (await p.text({\n message: \"Project name?\",\n placeholder: \"my-avax-app\",\n defaultValue: \"my-avax-app\",\n validate: (v) => (!v || isValidName(v) ? undefined : \"Use lowercase letters, digits, - . _\"),\n }));\n if (p.isCancel(projectName)) cancel();\n\n const template =\n opts.template ??\n (await p.select({\n message: \"Template?\",\n options: templates.map((t) => ({ value: t.id, label: t.title, hint: t.description })),\n initialValue: \"minimal\",\n }));\n if (p.isCancel(template)) cancel();\n\n const wallet =\n opts.wallet ??\n (await p.select({\n message: \"Wallet provider?\",\n options: [\n { value: \"web3auth\", label: \"Social login (Web3Auth)\", hint: \"free, recommended\" },\n { value: \"injected\", label: \"Browser wallet (Core / MetaMask)\" },\n ],\n initialValue: \"web3auth\",\n }));\n if (p.isCancel(wallet)) cancel();\n\n const chain =\n opts.chain ??\n (await p.select({\n message: \"Target chain?\",\n options: [\n { value: \"fuji\", label: \"Avalanche Fuji (testnet)\", hint: \"recommended\" },\n { value: \"c-chain\", label: \"Avalanche C-Chain (mainnet)\" },\n ],\n initialValue: \"fuji\",\n }));\n if (p.isCancel(chain)) cancel();\n\n const pm =\n opts.pm ??\n (await p.select({\n message: \"Package manager?\",\n options: ([\"pnpm\", \"npm\", \"yarn\", \"bun\"] as const).map((m) => ({ value: m, label: m })),\n initialValue: \"pnpm\",\n }));\n if (p.isCancel(pm)) cancel();\n\n return {\n projectName: projectName as string,\n template: template as string,\n wallet: wallet as WalletId,\n chain: chain as ChainId,\n pm: pm as PackageManager,\n };\n}\n\nfunction cancel(): never {\n p.cancel(\"Cancelled.\");\n process.exit(0);\n}\n\nasync function main(): Promise<void> {\n const opts = parseArgs(process.argv);\n const resolved = await resolveOptions(opts);\n\n const targetDir = path.resolve(process.cwd(), resolved.projectName);\n if (existsSync(targetDir) && readdirSync(targetDir).length > 0) {\n process.stderr.write(\n pc.red(`\\nDirectory \"${resolved.projectName}\" already exists and is not empty.\\n`),\n );\n process.exit(1);\n }\n\n const spin = opts.yes ? null : p.spinner();\n spin?.start(\"Scaffolding project\");\n const { files } = await scaffoldApp({\n projectName: resolved.projectName,\n targetDir,\n template: resolved.template,\n wallet: resolved.wallet,\n chain: resolved.chain,\n local: opts.local,\n avakitVersion: VERSION,\n });\n spin?.stop(`Created ${files.length} files`);\n\n if (opts.install) {\n const installSpin = opts.yes ? null : p.spinner();\n installSpin?.start(`Installing dependencies with ${resolved.pm}`);\n const result = spawnSync(resolved.pm, [\"install\"], {\n cwd: targetDir,\n stdio: opts.yes ? \"inherit\" : \"ignore\",\n });\n if (result.status === 0) {\n installSpin?.stop(\"Dependencies installed\");\n } else {\n installSpin?.stop(pc.yellow(\"Install skipped/failed — run it manually\"));\n }\n }\n\n const next = [\n `cd ${resolved.projectName}`,\n ...(opts.install ? [] : [`${resolved.pm} install`]),\n ...(resolved.wallet === \"web3auth\"\n ? [\"cp .env.example .env.local # add your Web3Auth client ID\"]\n : []),\n `${resolved.pm} run dev`,\n ];\n\n if (opts.yes) {\n process.stdout.write(`\\nDone. Next steps:\\n ${next.join(\"\\n \")}\\n`);\n } else {\n p.note(next.join(\"\\n\"), \"Next steps\");\n p.outro(pc.green(\"Your Avalanche dapp is ready.\"));\n }\n}\n\nmain().catch((error: unknown) => {\n process.stderr.write(\n `\\n${pc.red(\"Error:\")} ${error instanceof Error ? error.message : String(error)}\\n`,\n );\n process.exit(1);\n});\n"],"mappings":"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","names":["projectName","cancel"]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { spawnSync } from \"node:child_process\";\nimport { existsSync, readdirSync } from \"node:fs\";\nimport path from \"node:path\";\nimport * as p from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport { type ChainId, listTemplates, scaffoldApp, type WalletId } from \"./api.js\";\n\nconst VERSION = \"0.1.3\";\n\n// The @avakit/* dependency version stamped into scaffolded apps' package.json\n// (as `^AVAKIT_DEP_VERSION`). Kept separate from the CLI's own VERSION: it must\n// resolve every published @avakit package, so it tracks the LOWEST current\n// @avakit version (core 0.1.2 · react 0.1.3 → ^0.1.2 satisfies both). Bump only\n// when the minimum @avakit version a fresh app needs goes up.\nconst AVAKIT_DEP_VERSION = \"0.1.2\";\n\ntype PackageManager = \"pnpm\" | \"npm\" | \"yarn\" | \"bun\";\n\ninterface Options {\n projectName?: string;\n template?: string;\n wallet?: WalletId;\n chain?: ChainId;\n pm?: PackageManager;\n yes: boolean;\n local: boolean;\n install: boolean;\n}\n\nfunction parseArgs(argv: string[]): Options {\n const opts: Options = { yes: false, local: false, install: true };\n const rest = argv.slice(2);\n for (let i = 0; i < rest.length; i++) {\n const arg = rest[i];\n const next = () => rest[++i];\n switch (arg) {\n case \"--yes\":\n case \"-y\":\n opts.yes = true;\n break;\n case \"--local\":\n opts.local = true;\n break;\n case \"--no-install\":\n opts.install = false;\n break;\n case \"--template\":\n case \"-t\":\n opts.template = next();\n break;\n case \"--wallet\":\n case \"-w\":\n opts.wallet = next() as WalletId;\n break;\n case \"--chain\":\n case \"-c\":\n opts.chain = next() as ChainId;\n break;\n case \"--pm\":\n opts.pm = next() as PackageManager;\n break;\n case \"--version\":\n case \"-v\":\n process.stdout.write(`${VERSION}\\n`);\n process.exit(0);\n break;\n case \"--help\":\n case \"-h\":\n printHelp();\n process.exit(0);\n break;\n default:\n if (arg && !arg.startsWith(\"-\") && !opts.projectName) {\n opts.projectName = arg;\n }\n }\n }\n return opts;\n}\n\nfunction printHelp(): void {\n process.stdout.write(\n [\n \"create-avalanche-app — scaffold a batteries-included Avalanche dapp\",\n \"\",\n \"Usage: npm create avalanche-app@latest [name] [options]\",\n \"\",\n \"Options:\",\n \" -t, --template <id> minimal | nft-mint | token-gated-app | erc20-token |\",\n \" icm-messenger | eerc-token\",\n \" -w, --wallet <id> web3auth | injected (default: web3auth)\",\n \" -c, --chain <id> fuji | c-chain (default: fuji)\",\n \" --pm <manager> pnpm | npm | yarn | bun\",\n \" -y, --yes skip prompts (non-interactive)\",\n \" --no-install do not install dependencies\",\n \" --local link @avakit/* via workspace (repo dev only)\",\n \" -v, --version print version\",\n \" -h, --help print this help\",\n \"\",\n ].join(\"\\n\"),\n );\n}\n\nfunction isValidName(name: string): boolean {\n return /^[a-z0-9][a-z0-9._-]*$/.test(name);\n}\n\nasync function resolveOptions(\n opts: Options,\n): Promise<Required<Omit<Options, \"yes\" | \"local\" | \"install\">>> {\n const templates = listTemplates();\n const templateIds = templates.map((t) => t.id);\n\n if (opts.yes) {\n const projectName = opts.projectName ?? \"my-avax-app\";\n return {\n projectName,\n template: opts.template && templateIds.includes(opts.template) ? opts.template : \"minimal\",\n wallet: opts.wallet ?? \"web3auth\",\n chain: opts.chain ?? \"fuji\",\n pm: opts.pm ?? \"pnpm\",\n };\n }\n\n p.intro(pc.bgCyan(pc.black(\" create-avalanche-app \")));\n\n const projectName =\n opts.projectName ??\n (await p.text({\n message: \"Project name?\",\n placeholder: \"my-avax-app\",\n defaultValue: \"my-avax-app\",\n validate: (v) => (!v || isValidName(v) ? undefined : \"Use lowercase letters, digits, - . _\"),\n }));\n if (p.isCancel(projectName)) cancel();\n\n const template =\n opts.template ??\n (await p.select({\n message: \"Template?\",\n options: templates.map((t) => ({ value: t.id, label: t.title, hint: t.description })),\n initialValue: \"minimal\",\n }));\n if (p.isCancel(template)) cancel();\n\n const wallet =\n opts.wallet ??\n (await p.select({\n message: \"Wallet provider?\",\n options: [\n { value: \"web3auth\", label: \"Social login (Web3Auth)\", hint: \"free, recommended\" },\n { value: \"injected\", label: \"Browser wallet (Core / MetaMask)\" },\n ],\n initialValue: \"web3auth\",\n }));\n if (p.isCancel(wallet)) cancel();\n\n const chain =\n opts.chain ??\n (await p.select({\n message: \"Target chain?\",\n options: [\n { value: \"fuji\", label: \"Avalanche Fuji (testnet)\", hint: \"recommended\" },\n { value: \"c-chain\", label: \"Avalanche C-Chain (mainnet)\" },\n ],\n initialValue: \"fuji\",\n }));\n if (p.isCancel(chain)) cancel();\n\n const pm =\n opts.pm ??\n (await p.select({\n message: \"Package manager?\",\n options: ([\"pnpm\", \"npm\", \"yarn\", \"bun\"] as const).map((m) => ({ value: m, label: m })),\n initialValue: \"pnpm\",\n }));\n if (p.isCancel(pm)) cancel();\n\n return {\n projectName: projectName as string,\n template: template as string,\n wallet: wallet as WalletId,\n chain: chain as ChainId,\n pm: pm as PackageManager,\n };\n}\n\nfunction cancel(): never {\n p.cancel(\"Cancelled.\");\n process.exit(0);\n}\n\nasync function main(): Promise<void> {\n const opts = parseArgs(process.argv);\n const resolved = await resolveOptions(opts);\n\n const targetDir = path.resolve(process.cwd(), resolved.projectName);\n if (existsSync(targetDir) && readdirSync(targetDir).length > 0) {\n process.stderr.write(\n pc.red(`\\nDirectory \"${resolved.projectName}\" already exists and is not empty.\\n`),\n );\n process.exit(1);\n }\n\n const spin = opts.yes ? null : p.spinner();\n spin?.start(\"Scaffolding project\");\n const { files } = await scaffoldApp({\n projectName: resolved.projectName,\n targetDir,\n template: resolved.template,\n wallet: resolved.wallet,\n chain: resolved.chain,\n local: opts.local,\n avakitVersion: AVAKIT_DEP_VERSION,\n });\n spin?.stop(`Created ${files.length} files`);\n\n if (opts.install) {\n const installSpin = opts.yes ? null : p.spinner();\n installSpin?.start(`Installing dependencies with ${resolved.pm}`);\n const result = spawnSync(resolved.pm, [\"install\"], {\n cwd: targetDir,\n stdio: opts.yes ? \"inherit\" : \"ignore\",\n });\n if (result.status === 0) {\n installSpin?.stop(\"Dependencies installed\");\n } else {\n installSpin?.stop(pc.yellow(\"Install skipped/failed — run it manually\"));\n }\n }\n\n const next = [\n `cd ${resolved.projectName}`,\n ...(opts.install ? [] : [`${resolved.pm} install`]),\n ...(resolved.wallet === \"web3auth\"\n ? [\"cp .env.example .env.local # add your Web3Auth client ID\"]\n : []),\n `${resolved.pm} run dev`,\n ];\n\n if (opts.yes) {\n process.stdout.write(`\\nDone. Next steps:\\n ${next.join(\"\\n \")}\\n`);\n } else {\n p.note(next.join(\"\\n\"), \"Next steps\");\n p.outro(pc.green(\"Your Avalanche dapp is ready.\"));\n }\n}\n\nmain().catch((error: unknown) => {\n process.stderr.write(\n `\\n${pc.red(\"Error:\")} ${error instanceof Error ? error.message : String(error)}\\n`,\n );\n process.exit(1);\n});\n"],"mappings":"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","names":["projectName","cancel"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-avalanche-app",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Scaffold a batteries-included Avalanche dapp: social-login onboarding, deploy-ready, AI-native.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -0,0 +1,101 @@
1
+ # __PROJECT_NAME__ — Confidential token (eERC) dapp (scaffolded with AvaKit)
2
+
3
+ Operational guide for AI agents (Claude Code / Cursor) working in this project.
4
+
5
+ ## Stack
6
+
7
+ Next.js 16 (App Router) · React 19 · `@avakit/react` · `@avakit/core` · `@avalabs/eerc-sdk` · wagmi · viem · shadcn/ui · next-themes
8
+
9
+ ## What this is
10
+
11
+ A demo of Avalanche's **Encrypted ERC (eERC)** standard: token balances and transfer amounts are
12
+ hidden using zero-knowledge proofs (Groth16) and ElGamal encryption over BabyJubJub, while the
13
+ recipient/sender addresses stay public on-chain. This template points at a **shared, pre-deployed
14
+ standalone eERC instance on Fuji** (`lib/eerc-config.ts`) — there is no `contracts/` directory and
15
+ no browser-deploy step, unlike AvaKit's other templates.
16
+
17
+ ## Why this template deviates from the others
18
+
19
+ - **No bundled contracts.** eERC's contracts depend on real cryptographic primitives (Poseidon
20
+ hashing, a BabyJubJub library, four separate Groth16 verifiers) generated from circuits — not the
21
+ "self-contained, zero-dependency Solidity" pattern other AvaKit templates use. Vendoring the
22
+ circuits/contracts would also pull in Ava Labs' Ecosystem License (not MIT) into an MIT repo, so
23
+ this template consumes the official `@avalabs/eerc-sdk` (ISC-licensed) as an npm dependency
24
+ instead of vendoring source.
25
+ - **Circuits load from a CDN, not the bundle.** Proof generation (register/mint/transfer/withdraw/burn)
26
+ runs client-side via snarkjs against `.wasm`/`.zkey` circuit files (~2–14 MB each). `lib/eerc-config.ts`
27
+ points at `ava-labs/EncryptedERC`'s committed `circom/build/` directory via jsDelivr's GitHub CDN,
28
+ pinned to a commit hash, so nothing is vendored in this package and nobody has to compile circuits.
29
+ - **wagmi is present alongside `@avakit/react`.** `@avalabs/eerc-sdk`'s hooks use `wagmi`'s
30
+ `useReadContract`/`useBlockNumber` internally for balance/state reads. `app/providers.tsx` wraps
31
+ the app in a `WagmiProvider` (config pointed at Fuji) purely to satisfy that — wallet connect and
32
+ signing still go through `AvaKitProvider`'s adapters (Web3Auth / injected); wagmi never owns the
33
+ connected account.
34
+ - **Pinned wagmi 2.x, not the newest major.** `@avalabs/eerc-sdk`'s peer dependency is `wagmi: ^2.0.0`;
35
+ a newer wagmi major would violate that contract, so this template intentionally does not follow
36
+ AvaKit's usual "latest stable of everything" rule for this one package.
37
+
38
+ ## Architecture
39
+
40
+ - `lib/eerc-config.ts` — the deployed `EncryptedERC` contract address (standalone, name "Test",
41
+ symbol "TEST", 2 decimals) and the circuit URLs.
42
+ - `app/providers.tsx` — `<AvaKitProvider>` (wallet) + `<WagmiProvider>` (eERC SDK internals) + `ThemeProvider`.
43
+ - `components/demo.tsx` — register → unlock → mint → confidential transfer → burn.
44
+
45
+ ## The flow
46
+
47
+ 1. Connect a wallet (`<ConnectAvalanche />`).
48
+ 2. Build viem clients: `getPublicClient(chain)` from `@avakit/core`, and a **wallet client with an
49
+ account attached** — `createWalletClient({ chain, transport: custom(provider), account: address })`.
50
+ (`@avakit/core`'s `getWalletClient` does *not* attach an account; the eERC SDK requires
51
+ `wallet.account.address` to be set, so build the client directly with viem instead.)
52
+ 3. `useEERC(publicClient, walletClient, EERC_CONTRACT_ADDRESS, circuitURLs)` from `@avalabs/eerc-sdk`.
53
+ 4. **Register** (one-time, on-chain): `eerc.register()`. Generates a BabyJubJub keypair from a wallet
54
+ signature and calls `Registrar.register()` with a ZK proof. Idempotent — safe to call again.
55
+ 5. **Unlock** (per session, off-chain): `eerc.generateDecryptionKey()`. Re-derives the same
56
+ deterministic decryption key from a signature so the browser can decrypt your balance. No gas.
57
+ 6. `const balance = eerc.useEncryptedBalance()` → `privateMint`, `privateTransfer`, `privateBurn`,
58
+ `decryptedBalance`, `parsedDecryptedBalance`, `refetchBalance`.
59
+ 7. **Mint** — `balance.privateMint(recipient, amountInBaseUnits)`. **Owner-only on-chain**
60
+ (`EncryptedERC.privateMint` is `onlyOwner` by design, for compliance) — only the wallet that
61
+ deployed the contract can mint. Works out of the box only if you deploy your own instance and
62
+ connect with the deployer wallet.
63
+ 8. **Transfer** — `balance.privateTransfer(to, amountInBaseUnits)`. Permissionless for any two
64
+ *registered* accounts; amounts and balances stay encrypted on-chain.
65
+ 9. **Burn** — `balance.privateBurn(amountInBaseUnits)`. Permissionless, standalone-mode only.
66
+
67
+ There is no `withdraw`/`deposit` step in this demo — those convert to/from an underlying public
68
+ ERC-20 in eERC's **converter mode**; this template deploys **standalone mode** (the token only
69
+ exists in encrypted form).
70
+
71
+ ## Deploying your own instance
72
+
73
+ The shared demo contract has no auditor key requirements you control and mint is owner-gated. To
74
+ mint from your own wallet:
75
+
76
+ 1. Clone `ava-labs/EncryptedERC`, `npm install --ignore-scripts` (circuits are already committed
77
+ under `circom/build/`, no need to run `hardhat zkit make`), `npx hardhat compile`.
78
+ 2. Add a `fuji` network to `hardhat.config.ts` (RPC `https://api.avax-test.network/ext/bc/C/rpc`,
79
+ chainId `43113`) with your deployer's private key.
80
+ 3. `npx hardhat run scripts/deploy-standalone.ts --network fuji` — deploys verifiers + Registrar +
81
+ `EncryptedERC` (standalone) and prints the addresses.
82
+ 4. Register your deployer address via the SDK (or this app), then call
83
+ `setAuditorPublicKey(yourAddress)` on the `EncryptedERC` contract as the owner — an auditor must
84
+ be registered *and* set before any mint/transfer/burn will succeed.
85
+ 5. Update `EERC_CONTRACT_ADDRESS` in `lib/eerc-config.ts`.
86
+
87
+ ## Rules
88
+
89
+ - shadcn/ui only; `@avakit/react` components are shadcn-styled; plain Tailwind-styled `<input>` for
90
+ amount/address fields (no separate shadcn Input component is bundled by `@avakit/react`).
91
+ - Black & white only for now; dark/light via next-themes; both must work.
92
+ - Animations: Framer Motion or GSAP only.
93
+ - Amounts are in the token's base units (2 decimals here) — always convert with `parseUnits` /
94
+ `formatUnits`, read `decimals` from `balance.decimals` rather than hardcoding it.
95
+ - Never hardcode private keys; wallet signing always goes through the connected adapter.
96
+ - Default chain is Fuji testnet; mainnet requires explicit opt-in.
97
+
98
+ ## Commands
99
+
100
+ - `pnpm dev` — dev server (http://localhost:3000)
101
+ - `pnpm build` — production build
@@ -0,0 +1,37 @@
1
+ # __PROJECT_NAME__
2
+
3
+ An Avalanche **confidential token** dapp scaffolded with [AvaKit](https://github.com/mericcintosun/AvaKit), built on Avalanche's [Encrypted ERC (eERC)](https://github.com/ava-labs/EncryptedERC) standard. Register, mint, and transfer tokens with hidden balances and amounts — right from your browser.
4
+
5
+ ## Getting started
6
+
7
+ ```bash
8
+ # 1. (social login, optional) add a free Web3Auth client ID
9
+ cp .env.example .env.local
10
+ # → https://dashboard.web3auth.io (Sapphire Devnet, EVM)
11
+
12
+ # 2. run it
13
+ pnpm dev # http://localhost:3000
14
+ ```
15
+
16
+ Then: connect a wallet → **Register** (one-time, on-chain) → **Unlock private balance** (free,
17
+ off-chain) → mint / confidential transfer / burn. These cost gas (except unlock), so fund your
18
+ wallet on Fuji first (in-app faucet link).
19
+
20
+ ## How it works
21
+
22
+ - Points at a shared, pre-deployed standalone eERC instance on Fuji (`lib/eerc-config.ts`) — no
23
+ contract deployment needed to try the demo.
24
+ - Proofs (Groth16, via snarkjs) are generated entirely in the browser. Circuit `.wasm`/`.zkey`
25
+ files load from a CDN (pinned to a commit of `ava-labs/EncryptedERC`), not bundled in this repo.
26
+ - Minting is **owner-only** on the shared instance (eERC design, for compliance) — deploy your own
27
+ instance to mint from your own wallet. See `CLAUDE.md` for the deploy steps. Confidential
28
+ transfer and burn work for any registered wallet.
29
+
30
+ ## Stack
31
+
32
+ Next.js 16 · `@avakit/react` · `@avakit/core` · `@avalabs/eerc-sdk` · wagmi · viem · shadcn/ui
33
+
34
+ ## AI-native
35
+
36
+ Ships with `CLAUDE.md`, `llms.txt`, and `.cursor/rules` so Claude Code / Cursor understand the
37
+ confidential-token flow and the (deliberate) ways this template differs from AvaKit's others.
@@ -0,0 +1,95 @@
1
+ @import "tailwindcss";
2
+ @import "tw-animate-css";
3
+
4
+ /* Scan @avakit/react's shadcn-style classes from node_modules. */
5
+ @source "../node_modules/@avakit/react/dist";
6
+
7
+ @custom-variant dark (&:is(.dark *));
8
+
9
+ /*
10
+ * Black & white only (oklch chroma 0 = pure grayscale). Dark/light wired from
11
+ * day one via next-themes. Add brand colors later by editing these tokens —
12
+ * components never hardcode colors.
13
+ */
14
+ :root {
15
+ --radius: 0.625rem;
16
+ --background: oklch(1 0 0);
17
+ --foreground: oklch(0.145 0 0);
18
+ --card: oklch(1 0 0);
19
+ --card-foreground: oklch(0.145 0 0);
20
+ --popover: oklch(1 0 0);
21
+ --popover-foreground: oklch(0.145 0 0);
22
+ --primary: oklch(0.205 0 0);
23
+ --primary-foreground: oklch(0.985 0 0);
24
+ --secondary: oklch(0.97 0 0);
25
+ --secondary-foreground: oklch(0.205 0 0);
26
+ --muted: oklch(0.97 0 0);
27
+ --muted-foreground: oklch(0.556 0 0);
28
+ --accent: oklch(0.97 0 0);
29
+ --accent-foreground: oklch(0.205 0 0);
30
+ --destructive: oklch(0.3 0 0);
31
+ --destructive-foreground: oklch(0.985 0 0);
32
+ --border: oklch(0.922 0 0);
33
+ --input: oklch(0.922 0 0);
34
+ --ring: oklch(0.708 0 0);
35
+ }
36
+
37
+ .dark {
38
+ --background: oklch(0.145 0 0);
39
+ --foreground: oklch(0.985 0 0);
40
+ --card: oklch(0.205 0 0);
41
+ --card-foreground: oklch(0.985 0 0);
42
+ --popover: oklch(0.205 0 0);
43
+ --popover-foreground: oklch(0.985 0 0);
44
+ --primary: oklch(0.985 0 0);
45
+ --primary-foreground: oklch(0.205 0 0);
46
+ --secondary: oklch(0.269 0 0);
47
+ --secondary-foreground: oklch(0.985 0 0);
48
+ --muted: oklch(0.269 0 0);
49
+ --muted-foreground: oklch(0.708 0 0);
50
+ --accent: oklch(0.269 0 0);
51
+ --accent-foreground: oklch(0.985 0 0);
52
+ --destructive: oklch(0.7 0 0);
53
+ --destructive-foreground: oklch(0.205 0 0);
54
+ --border: oklch(1 0 0 / 10%);
55
+ --input: oklch(1 0 0 / 15%);
56
+ --ring: oklch(0.556 0 0);
57
+ }
58
+
59
+ @theme inline {
60
+ --color-background: var(--background);
61
+ --color-foreground: var(--foreground);
62
+ --color-card: var(--card);
63
+ --color-card-foreground: var(--card-foreground);
64
+ --color-popover: var(--popover);
65
+ --color-popover-foreground: var(--popover-foreground);
66
+ --color-primary: var(--primary);
67
+ --color-primary-foreground: var(--primary-foreground);
68
+ --color-secondary: var(--secondary);
69
+ --color-secondary-foreground: var(--secondary-foreground);
70
+ --color-muted: var(--muted);
71
+ --color-muted-foreground: var(--muted-foreground);
72
+ --color-accent: var(--accent);
73
+ --color-accent-foreground: var(--accent-foreground);
74
+ --color-destructive: var(--destructive);
75
+ --color-destructive-foreground: var(--destructive-foreground);
76
+ --color-border: var(--border);
77
+ --color-input: var(--input);
78
+ --color-ring: var(--ring);
79
+ --radius-sm: calc(var(--radius) - 4px);
80
+ --radius-md: calc(var(--radius) - 2px);
81
+ --radius-lg: var(--radius);
82
+ --radius-xl: calc(var(--radius) + 4px);
83
+ --font-sans: var(--font-geist-sans);
84
+ --font-mono: var(--font-geist-mono);
85
+ }
86
+
87
+ @layer base {
88
+ * {
89
+ border-color: var(--border);
90
+ }
91
+ body {
92
+ background-color: var(--background);
93
+ color: var(--foreground);
94
+ }
95
+ }
@@ -0,0 +1,23 @@
1
+ import type { Metadata } from "next";
2
+ import { Geist, Geist_Mono } from "next/font/google";
3
+ import type { ReactNode } from "react";
4
+ import { Providers } from "./providers";
5
+ import "./globals.css";
6
+
7
+ const geistSans = Geist({ variable: "--font-geist-sans", subsets: ["latin"] });
8
+ const geistMono = Geist_Mono({ variable: "--font-geist-mono", subsets: ["latin"] });
9
+
10
+ export const metadata: Metadata = {
11
+ title: "__PROJECT_NAME__",
12
+ description: "A confidential-token Avalanche dapp scaffolded with AvaKit.",
13
+ };
14
+
15
+ export default function RootLayout({ children }: Readonly<{ children: ReactNode }>) {
16
+ return (
17
+ <html lang="en" suppressHydrationWarning>
18
+ <body className={`${geistSans.variable} ${geistMono.variable} font-sans antialiased`}>
19
+ <Providers>{children}</Providers>
20
+ </body>
21
+ </html>
22
+ );
23
+ }
@@ -0,0 +1,5 @@
1
+ import { Demo } from "@/components/demo";
2
+
3
+ export default function Home() {
4
+ return <Demo />;
5
+ }
@@ -0,0 +1,53 @@
1
+ "use client";
2
+
3
+ import { type WalletAdapter, injectedAdapter, toViemChain } from "@avakit/core";
4
+ import { __CHAIN_CONST__ } from "@avakit/core/chains";
5
+ import { web3authAdapter } from "@avakit/core/web3auth";
6
+ import { AvaKitProvider } from "@avakit/react";
7
+ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
8
+ import { ThemeProvider } from "next-themes";
9
+ import { type ReactNode, useMemo, useState } from "react";
10
+ import { WagmiProvider, http, createConfig } from "wagmi";
11
+
12
+ export function Providers({ children }: { children: ReactNode }) {
13
+ const adapters = useMemo(() => {
14
+ const list: WalletAdapter[] = [];
15
+ // Social login appears only when a Web3Auth client ID is configured.
16
+ const clientId = process.env.NEXT_PUBLIC_WEB3AUTH_CLIENT_ID;
17
+ if (clientId) {
18
+ list.push(web3authAdapter({ clientId }));
19
+ }
20
+ // Injected (Core / MetaMask) is always available.
21
+ list.push(injectedAdapter());
22
+ return list;
23
+ }, []);
24
+
25
+ // The eERC SDK's read hooks (balance/state) are built on wagmi + react-query
26
+ // internally, so this template runs a wagmi config alongside AvaKitProvider
27
+ // purely to satisfy those reads. Wallet connect/sign still goes through
28
+ // AvaKitProvider's adapters (Web3Auth / injected) — wagmi never owns the
29
+ // account here, it only provides an RPC-backed read client. Derive the wagmi
30
+ // chain from the same __CHAIN_CONST__ AvaKit uses, so the two never diverge.
31
+ // (The shared eERC contract in lib/eerc-config.ts only exists on Fuji — deploy
32
+ // your own instance to use another chain; see CLAUDE.md.)
33
+ const [wagmiConfig] = useState(() => {
34
+ const viemChain = toViemChain(__CHAIN_CONST__);
35
+ return createConfig({
36
+ chains: [viemChain],
37
+ transports: { [viemChain.id]: http(__CHAIN_CONST__.rpcUrl) },
38
+ });
39
+ });
40
+ const [queryClient] = useState(() => new QueryClient());
41
+
42
+ return (
43
+ <ThemeProvider attribute="class" defaultTheme="system" enableSystem disableTransitionOnChange>
44
+ <QueryClientProvider client={queryClient}>
45
+ <WagmiProvider config={wagmiConfig}>
46
+ <AvaKitProvider chains={[__CHAIN_CONST__]} adapters={adapters}>
47
+ {children}
48
+ </AvaKitProvider>
49
+ </WagmiProvider>
50
+ </QueryClientProvider>
51
+ </ThemeProvider>
52
+ );
53
+ }
@@ -0,0 +1,250 @@
1
+ "use client";
2
+
3
+ import { getPublicClient, toViemChain } from "@avakit/core";
4
+ import { Button, ConnectAvalanche, shortenAddress, useAvaKit } from "@avakit/react";
5
+ import { useEERC } from "@avalabs/eerc-sdk";
6
+ import { Moon, Sun } from "lucide-react";
7
+ import { useTheme } from "next-themes";
8
+ import { useMemo, useState } from "react";
9
+ import { type Address, createWalletClient, custom, formatUnits, parseUnits } from "viem";
10
+ import { circuitURLs, EERC_CONTRACT_ADDRESS } from "@/lib/eerc-config";
11
+
12
+ function ThemeToggle() {
13
+ const { resolvedTheme, setTheme } = useTheme();
14
+ return (
15
+ <Button
16
+ variant="outline"
17
+ size="icon"
18
+ aria-label="Toggle theme"
19
+ onClick={() => setTheme(resolvedTheme === "dark" ? "light" : "dark")}
20
+ >
21
+ <Sun className="hidden size-4 dark:block" />
22
+ <Moon className="block size-4 dark:hidden" />
23
+ </Button>
24
+ );
25
+ }
26
+
27
+ export function Demo() {
28
+ const { address, provider, chain, status } = useAvaKit();
29
+ const isConnected = status === "connected";
30
+
31
+ return (
32
+ <div className="mx-auto flex min-h-dvh max-w-xl flex-col gap-8 px-6 py-16">
33
+ <header className="flex items-center justify-between">
34
+ <span className="font-mono text-sm font-semibold">__PROJECT_NAME__</span>
35
+ <div className="flex items-center gap-2">
36
+ <ConnectAvalanche />
37
+ <ThemeToggle />
38
+ </div>
39
+ </header>
40
+
41
+ <div className="flex flex-col gap-2">
42
+ <h1 className="text-3xl font-semibold tracking-tight">Confidential token transfers</h1>
43
+ <p className="text-muted-foreground text-sm">
44
+ Register, mint, and transfer tokens with hidden balances and amounts on {chain.name},
45
+ using Avalanche's Encrypted ERC (eERC) standard. Proofs are generated fully client-side.
46
+ </p>
47
+ </div>
48
+
49
+ {!isConnected || !address || !provider ? (
50
+ <div className="text-muted-foreground rounded-xl border border-dashed p-10 text-center text-sm">
51
+ Connect a wallet to begin.
52
+ </div>
53
+ ) : (
54
+ <EercPanel address={address} provider={provider} explorerUrl={chain.explorerUrl} />
55
+ )}
56
+ </div>
57
+ );
58
+ }
59
+
60
+ function EercPanel({
61
+ address,
62
+ provider,
63
+ explorerUrl,
64
+ }: {
65
+ address: Address;
66
+ provider: NonNullable<ReturnType<typeof useAvaKit>["provider"]>;
67
+ explorerUrl: string;
68
+ }) {
69
+ const { chain } = useAvaKit();
70
+
71
+ // eERC needs a wallet client with an account already attached (viem's
72
+ // "account hoisting"), unlike @avakit/core's getWalletClient, which passes
73
+ // account per-call. Build one directly from the connected provider.
74
+ const publicClient = useMemo(() => getPublicClient(chain), [chain]);
75
+ const walletClient = useMemo(
76
+ () => createWalletClient({ chain: toViemChain(chain), transport: custom(provider), account: address }),
77
+ [chain, provider, address],
78
+ );
79
+
80
+ const eerc = useEERC(publicClient, walletClient, EERC_CONTRACT_ADDRESS, circuitURLs);
81
+ const balance = eerc.useEncryptedBalance();
82
+
83
+ const [unlocked, setUnlocked] = useState(false);
84
+ const [mintAmount, setMintAmount] = useState("10");
85
+ const [transferTo, setTransferTo] = useState("");
86
+ const [transferAmount, setTransferAmount] = useState("5");
87
+ const [burnAmount, setBurnAmount] = useState("1");
88
+ const [busy, setBusy] = useState<null | "register" | "unlock" | "mint" | "transfer" | "burn">(null);
89
+ const [error, setError] = useState<string | null>(null);
90
+ const [lastTx, setLastTx] = useState<string | null>(null);
91
+
92
+ async function run(kind: NonNullable<typeof busy>, fn: () => Promise<{ transactionHash?: string } | void>) {
93
+ setBusy(kind);
94
+ setError(null);
95
+ try {
96
+ const result = await fn();
97
+ if (result && "transactionHash" in result && result.transactionHash) {
98
+ setLastTx(result.transactionHash);
99
+ }
100
+ balance.refetchBalance();
101
+ } catch (e) {
102
+ setError(e instanceof Error ? e.message : String(e));
103
+ } finally {
104
+ setBusy(null);
105
+ }
106
+ }
107
+
108
+ if (!eerc.isAllDataFetched) {
109
+ return (
110
+ <div className="text-muted-foreground rounded-xl border border-dashed p-10 text-center text-sm">
111
+ Loading eERC state…
112
+ </div>
113
+ );
114
+ }
115
+
116
+ if (!eerc.isRegistered) {
117
+ return (
118
+ <div className="flex flex-col gap-3 rounded-xl border p-6">
119
+ <p className="text-sm">Step 1 — register a confidential account (signature, no gas cost beyond the tx).</p>
120
+ <Button
121
+ disabled={busy !== null}
122
+ onClick={() => run("register", async () => { await eerc.register(); setUnlocked(true); })}
123
+ >
124
+ {busy === "register" ? "Registering…" : "Register"}
125
+ </Button>
126
+ </div>
127
+ );
128
+ }
129
+
130
+ if (!unlocked) {
131
+ return (
132
+ <div className="flex flex-col gap-3 rounded-xl border p-6">
133
+ <p className="text-sm">
134
+ Step 2 — unlock your private balance for this session (a free signature, derives your
135
+ decryption key locally; nothing is sent on-chain).
136
+ </p>
137
+ <Button
138
+ disabled={busy !== null}
139
+ onClick={() => run("unlock", async () => { await eerc.generateDecryptionKey(); setUnlocked(true); })}
140
+ >
141
+ {busy === "unlock" ? "Unlocking…" : "Unlock private balance"}
142
+ </Button>
143
+ </div>
144
+ );
145
+ }
146
+
147
+ const decimals = Number(balance.decimals ?? 2n);
148
+ const fmt = (v: bigint) => `${formatUnits(v, decimals)} ${eerc.symbol || "TEST"}`;
149
+
150
+ return (
151
+ <div className="flex flex-col gap-4 rounded-xl border p-6">
152
+ <Row label="Token" value={`${eerc.name || "eERC"} (${eerc.symbol || "TEST"})`} mono />
153
+ <Row label="Your private balance" value={fmt(balance.decryptedBalance)} mono />
154
+ <Row label="Your account" value={shortenAddress(address, 6)} mono />
155
+
156
+ <div className="flex flex-col gap-2 border-t pt-4">
157
+ <p className="text-sm">Mint privately to yourself</p>
158
+ <p className="text-muted-foreground text-xs">
159
+ Minting is owner-only on this shared instance — deploy your own eERC contract to mint
160
+ from your own wallet (see CLAUDE.md).
161
+ </p>
162
+ <div className="flex gap-2">
163
+ <AmountInput value={mintAmount} onChange={setMintAmount} />
164
+ <Button
165
+ disabled={busy !== null}
166
+ onClick={() =>
167
+ run("mint", () => balance.privateMint(address, parseUnits(mintAmount || "0", decimals)))
168
+ }
169
+ >
170
+ {busy === "mint" ? "Minting…" : "Mint"}
171
+ </Button>
172
+ </div>
173
+ </div>
174
+
175
+ <div className="flex flex-col gap-2 border-t pt-4">
176
+ <p className="text-sm">Confidential transfer</p>
177
+ <input
178
+ className="border-input bg-transparent rounded-md border px-3 py-2 text-sm"
179
+ placeholder="Recipient address (0x…, must be registered)"
180
+ value={transferTo}
181
+ onChange={(e) => setTransferTo(e.target.value)}
182
+ />
183
+ <div className="flex gap-2">
184
+ <AmountInput value={transferAmount} onChange={setTransferAmount} />
185
+ <Button
186
+ disabled={busy !== null || !transferTo}
187
+ onClick={() =>
188
+ run("transfer", () =>
189
+ balance.privateTransfer(transferTo, parseUnits(transferAmount || "0", decimals)),
190
+ )
191
+ }
192
+ >
193
+ {busy === "transfer" ? "Sending…" : "Send"}
194
+ </Button>
195
+ </div>
196
+ </div>
197
+
198
+ <div className="flex flex-col gap-2 border-t pt-4">
199
+ <p className="text-sm">Burn privately</p>
200
+ <div className="flex gap-2">
201
+ <AmountInput value={burnAmount} onChange={setBurnAmount} />
202
+ <Button
203
+ variant="outline"
204
+ disabled={busy !== null}
205
+ onClick={() => run("burn", () => balance.privateBurn(parseUnits(burnAmount || "0", decimals)))}
206
+ >
207
+ {busy === "burn" ? "Burning…" : "Burn"}
208
+ </Button>
209
+ </div>
210
+ </div>
211
+
212
+ {lastTx ? (
213
+ <a
214
+ href={`${explorerUrl}/tx/${lastTx}`}
215
+ target="_blank"
216
+ rel="noreferrer"
217
+ className="text-muted-foreground text-center text-xs underline underline-offset-4"
218
+ >
219
+ View last transaction on explorer
220
+ </a>
221
+ ) : null}
222
+
223
+ {error ? (
224
+ <p className="border-destructive text-destructive rounded-md border px-3 py-2 text-sm font-medium">
225
+ {error}
226
+ </p>
227
+ ) : null}
228
+ </div>
229
+ );
230
+ }
231
+
232
+ function AmountInput({ value, onChange }: { value: string; onChange: (v: string) => void }) {
233
+ return (
234
+ <input
235
+ className="border-input bg-transparent w-24 rounded-md border px-3 py-2 text-sm"
236
+ inputMode="decimal"
237
+ value={value}
238
+ onChange={(e) => onChange(e.target.value)}
239
+ />
240
+ );
241
+ }
242
+
243
+ function Row({ label, value, mono }: { label: string; value: string; mono?: boolean }) {
244
+ return (
245
+ <div className="flex items-center justify-between gap-4">
246
+ <span className="text-muted-foreground text-sm">{label}</span>
247
+ <span className={mono ? "font-mono text-sm" : "text-sm"}>{value}</span>
248
+ </div>
249
+ );
250
+ }
@@ -0,0 +1,41 @@
1
+ ---
2
+ description: AvaKit eERC (confidential token) conventions for this project
3
+ globs: ["**/*.ts", "**/*.tsx", "**/*.css"]
4
+ alwaysApply: true
5
+ ---
6
+
7
+ # AvaKit eERC project rules
8
+
9
+ Register, mint, and privately transfer tokens with hidden balances using Avalanche's Encrypted ERC standard.
10
+
11
+ ## Flow
12
+
13
+ - Init: `useEERC(publicClient, walletClient, EERC_CONTRACT_ADDRESS, circuitURLs)` (config in `lib/eerc-config.ts`).
14
+ - Register (one-time, on-chain, idempotent): `eerc.register()`.
15
+ - Unlock (per session, off-chain, free): `eerc.generateDecryptionKey()`.
16
+ - Balance/actions: `eerc.useEncryptedBalance()` → `privateMint` (owner-only), `privateTransfer`, `privateBurn`.
17
+
18
+ ## Wallet client
19
+
20
+ `@avakit/core`'s `getWalletClient` does not attach an `account`, but the eERC SDK requires
21
+ `wallet.account.address` to be set. Build the wallet client directly:
22
+ `createWalletClient({ chain: toViemChain(chain), transport: custom(provider), account: address })`.
23
+
24
+ ## Amounts
25
+
26
+ - The token has 2 decimals (read `balance.decimals`, don't hardcode). Always convert with
27
+ `parseUnits` / `formatUnits`.
28
+
29
+ ## Contract
30
+
31
+ - No bundled `contracts/` — this template points at a shared, pre-deployed standalone eERC
32
+ instance on Fuji. To mint from your own wallet you must deploy your own instance (see
33
+ `CLAUDE.md`) since `privateMint` is `onlyOwner`.
34
+
35
+ ## UI & safety
36
+
37
+ - shadcn/ui only; black & white for now; dark/light via next-themes; both must work.
38
+ - Animations: Framer Motion or GSAP only.
39
+ - Registering/minting/transferring/burning cost gas (except unlocking); fund the wallet on Fuji first.
40
+ - Default chain is Fuji testnet; mainnet requires explicit opt-in.
41
+ - wagmi is pinned to 2.x (not the latest major) because `@avalabs/eerc-sdk` peer-depends on `^2.0.0`.
@@ -0,0 +1,4 @@
1
+ # Web3Auth (MetaMask Embedded Wallets) — free client ID for social login.
2
+ # Get one at https://dashboard.web3auth.io (Sapphire Devnet, EVM).
3
+ # Public value, safe to expose. Leave empty to use only the browser wallet.
4
+ NEXT_PUBLIC_WEB3AUTH_CLIENT_ID=
@@ -0,0 +1,10 @@
1
+ node_modules/
2
+ .next/
3
+ out/
4
+ *.tsbuildinfo
5
+ next-env.d.ts
6
+ .env
7
+ .env.*
8
+ !.env.example
9
+ .DS_Store
10
+ *.log
@@ -0,0 +1,41 @@
1
+ import type { Hex } from "viem";
2
+
3
+ /**
4
+ * Standalone eERC deployment on Avalanche Fuji, shared by every app scaffolded
5
+ * from this template. Deployed with `ava-labs/EncryptedERC`'s
6
+ * `scripts/deploy-standalone.ts` (name "Test", symbol "TEST", 2 decimals).
7
+ * Redeploy your own instance if you need a fresh registrar / auditor.
8
+ */
9
+ export const EERC_CONTRACT_ADDRESS: Hex = "0xfB27bcdb845ECF231a36f3d14466e9ce9CF98d58";
10
+
11
+ /**
12
+ * Circuit wasm + zkey files, served from the `ava-labs/EncryptedERC` repo via
13
+ * jsDelivr's GitHub CDN, pinned to a commit so they never change under us.
14
+ * Proof generation (registration/mint/transfer/withdraw/burn) runs fully
15
+ * client-side in the browser via snarkjs — nothing is vendored in this repo.
16
+ */
17
+ const CIRCUIT_COMMIT = "c7eb0e09bc9315e68c35d3c09f5dce4b794d0485";
18
+ const CIRCUIT_BASE = `https://cdn.jsdelivr.net/gh/ava-labs/EncryptedERC@${CIRCUIT_COMMIT}/circom/build`;
19
+
20
+ export const circuitURLs = {
21
+ register: {
22
+ wasm: `${CIRCUIT_BASE}/registration/registration.wasm`,
23
+ zkey: `${CIRCUIT_BASE}/registration/circuit_final.zkey`,
24
+ },
25
+ mint: {
26
+ wasm: `${CIRCUIT_BASE}/mint/mint.wasm`,
27
+ zkey: `${CIRCUIT_BASE}/mint/mint.zkey`,
28
+ },
29
+ transfer: {
30
+ wasm: `${CIRCUIT_BASE}/transfer/transfer.wasm`,
31
+ zkey: `${CIRCUIT_BASE}/transfer/transfer.zkey`,
32
+ },
33
+ withdraw: {
34
+ wasm: `${CIRCUIT_BASE}/withdraw/withdraw.wasm`,
35
+ zkey: `${CIRCUIT_BASE}/withdraw/circuit_final.zkey`,
36
+ },
37
+ burn: {
38
+ wasm: `${CIRCUIT_BASE}/burn/burn.wasm`,
39
+ zkey: `${CIRCUIT_BASE}/burn/burn.zkey`,
40
+ },
41
+ };
@@ -0,0 +1,25 @@
1
+ # __PROJECT_NAME__
2
+
3
+ > An Avalanche confidential-token dapp scaffolded with AvaKit, built on Avalanche's Encrypted ERC (eERC) standard. Register, mint, and privately transfer tokens with hidden balances. Next.js + @avakit/react + @avalabs/eerc-sdk + shadcn/ui.
4
+
5
+ ## Project map
6
+
7
+ - [lib/eerc-config.ts](lib/eerc-config.ts): deployed eERC contract address + CDN circuit URLs.
8
+ - [app/providers.tsx](app/providers.tsx): `<AvaKitProvider>` (wallet) + `<WagmiProvider>` (required by the eERC SDK's internal reads).
9
+ - [components/demo.tsx](components/demo.tsx): register → unlock → mint → confidential transfer → burn.
10
+ - [CLAUDE.md](CLAUDE.md): agent guide — flow, why this template deviates from AvaKit's usual pattern, how to deploy your own instance.
11
+
12
+ ## Key APIs
13
+
14
+ - Init: `useEERC(publicClient, walletClient, EERC_CONTRACT_ADDRESS, circuitURLs)` from `@avalabs/eerc-sdk`.
15
+ - Register: `eerc.register()` (one-time, on-chain, idempotent).
16
+ - Unlock: `eerc.generateDecryptionKey()` (per session, off-chain, no gas).
17
+ - Balance + actions: `eerc.useEncryptedBalance()` → `privateMint(to, amount)` (owner-only), `privateTransfer(to, amount)`, `privateBurn(amount)`, `decryptedBalance`, `parsedDecryptedBalance`.
18
+ - Amounts are base units (token has 2 decimals) — convert with `parseUnits`/`formatUnits`.
19
+
20
+ ## External docs
21
+
22
+ - eERC contracts + deploy scripts: https://github.com/ava-labs/EncryptedERC
23
+ - eERC SDK: https://avacloud.gitbook.io/encrypted-erc/d87pGPcNAEEw9ISVd17M/usage/sdk-overview
24
+ - Avalanche Builder Hub: https://build.avax.network/llms.txt
25
+ - Fuji faucet: https://core.app/tools/testnet-faucet
@@ -0,0 +1,6 @@
1
+ {
2
+ "id": "eerc-token",
3
+ "title": "Confidential token (eERC)",
4
+ "description": "Register, mint, and privately transfer tokens with hidden balances using Avalanche's Encrypted ERC standard",
5
+ "contracts": false
6
+ }
@@ -0,0 +1,7 @@
1
+ import type { NextConfig } from "next";
2
+
3
+ const nextConfig: NextConfig = {
4
+ reactStrictMode: true,
5
+ };
6
+
7
+ export default nextConfig;
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "__PROJECT_NAME__",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "next dev",
8
+ "build": "next build",
9
+ "start": "next start",
10
+ "typecheck": "tsc --noEmit"
11
+ },
12
+ "dependencies": {
13
+ "@avakit/core": "__AVAKIT_DEP__",
14
+ "@avakit/react": "__AVAKIT_DEP__",
15
+ "@avalabs/eerc-sdk": "1.0.2",
16
+ "@tanstack/react-query": "5.101.2",
17
+ "lucide-react": "1.22.0",
18
+ "next": "16.2.9",
19
+ "next-themes": "0.4.6",
20
+ "react": "19.2.7",
21
+ "react-dom": "19.2.7",
22
+ "viem": "2.54.1",
23
+ "wagmi": "2.19.5"
24
+ },
25
+ "devDependencies": {
26
+ "@tailwindcss/postcss": "4.3.2",
27
+ "@types/node": "26.0.1",
28
+ "@types/react": "19.2.17",
29
+ "@types/react-dom": "19.2.3",
30
+ "postcss": "8.5.16",
31
+ "tailwindcss": "4.3.2",
32
+ "tw-animate-css": "1.4.0",
33
+ "typescript": "6.0.3"
34
+ }
35
+ }
@@ -0,0 +1,3 @@
1
+ allowBuilds:
2
+ sharp: true
3
+ blake-hash: true
@@ -0,0 +1,7 @@
1
+ const config = {
2
+ plugins: {
3
+ "@tailwindcss/postcss": {},
4
+ },
5
+ };
6
+
7
+ export default config;
@@ -0,0 +1,23 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "lib": ["dom", "dom.iterable", "esnext"],
5
+ "allowJs": true,
6
+ "skipLibCheck": true,
7
+ "strict": true,
8
+ "noEmit": true,
9
+ "esModuleInterop": true,
10
+ "module": "esnext",
11
+ "moduleResolution": "bundler",
12
+ "resolveJsonModule": true,
13
+ "isolatedModules": true,
14
+ "jsx": "preserve",
15
+ "incremental": true,
16
+ "plugins": [{ "name": "next" }],
17
+ "paths": {
18
+ "@/*": ["./*"]
19
+ }
20
+ },
21
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
22
+ "exclude": ["node_modules"]
23
+ }
@@ -0,0 +1,2 @@
1
+ allowBuilds:
2
+ sharp: true
@@ -0,0 +1,2 @@
1
+ allowBuilds:
2
+ sharp: true
@@ -0,0 +1,2 @@
1
+ allowBuilds:
2
+ sharp: true
@@ -0,0 +1,2 @@
1
+ allowBuilds:
2
+ sharp: true
@@ -0,0 +1,2 @@
1
+ allowBuilds:
2
+ sharp: true