create-avalanche-app 0.1.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.
Files changed (83) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +43 -0
  3. package/dist/api.d.ts +34 -0
  4. package/dist/api.js +12 -0
  5. package/dist/api.js.map +1 -0
  6. package/dist/chunk-3YGRLI4R.js +91 -0
  7. package/dist/chunk-3YGRLI4R.js.map +1 -0
  8. package/dist/index.js +213 -0
  9. package/dist/index.js.map +1 -0
  10. package/package.json +65 -0
  11. package/templates/erc20-token/CLAUDE.md +45 -0
  12. package/templates/erc20-token/README.md +35 -0
  13. package/templates/erc20-token/app/globals.css +95 -0
  14. package/templates/erc20-token/app/layout.tsx +23 -0
  15. package/templates/erc20-token/app/page.tsx +5 -0
  16. package/templates/erc20-token/app/providers.tsx +30 -0
  17. package/templates/erc20-token/components/demo.tsx +178 -0
  18. package/templates/erc20-token/contracts/foundry.toml +9 -0
  19. package/templates/erc20-token/contracts/src/AvaKitToken.sol +53 -0
  20. package/templates/erc20-token/cursor/rules/avakit.mdc +31 -0
  21. package/templates/erc20-token/env.example +4 -0
  22. package/templates/erc20-token/gitignore +15 -0
  23. package/templates/erc20-token/lib/token-artifact.ts +239 -0
  24. package/templates/erc20-token/llms.txt +29 -0
  25. package/templates/erc20-token/manifest.json +6 -0
  26. package/templates/erc20-token/next.config.ts +7 -0
  27. package/templates/erc20-token/package.json +32 -0
  28. package/templates/erc20-token/postcss.config.mjs +7 -0
  29. package/templates/erc20-token/tsconfig.json +23 -0
  30. package/templates/minimal/CLAUDE.md +37 -0
  31. package/templates/minimal/README.md +32 -0
  32. package/templates/minimal/app/globals.css +95 -0
  33. package/templates/minimal/app/layout.tsx +23 -0
  34. package/templates/minimal/app/page.tsx +5 -0
  35. package/templates/minimal/app/providers.tsx +30 -0
  36. package/templates/minimal/components/demo.tsx +135 -0
  37. package/templates/minimal/cursor/rules/avakit.mdc +28 -0
  38. package/templates/minimal/env.example +4 -0
  39. package/templates/minimal/gitignore +10 -0
  40. package/templates/minimal/llms.txt +30 -0
  41. package/templates/minimal/manifest.json +6 -0
  42. package/templates/minimal/next.config.ts +7 -0
  43. package/templates/minimal/package.json +32 -0
  44. package/templates/minimal/postcss.config.mjs +7 -0
  45. package/templates/minimal/tsconfig.json +23 -0
  46. package/templates/nft-mint/CLAUDE.md +45 -0
  47. package/templates/nft-mint/README.md +36 -0
  48. package/templates/nft-mint/app/globals.css +95 -0
  49. package/templates/nft-mint/app/layout.tsx +23 -0
  50. package/templates/nft-mint/app/page.tsx +5 -0
  51. package/templates/nft-mint/app/providers.tsx +30 -0
  52. package/templates/nft-mint/components/demo.tsx +176 -0
  53. package/templates/nft-mint/contracts/foundry.toml +9 -0
  54. package/templates/nft-mint/contracts/src/AvaKitNFT.sol +57 -0
  55. package/templates/nft-mint/cursor/rules/avakit.mdc +32 -0
  56. package/templates/nft-mint/env.example +4 -0
  57. package/templates/nft-mint/gitignore +15 -0
  58. package/templates/nft-mint/lib/nft-artifact.ts +164 -0
  59. package/templates/nft-mint/llms.txt +31 -0
  60. package/templates/nft-mint/manifest.json +6 -0
  61. package/templates/nft-mint/next.config.ts +7 -0
  62. package/templates/nft-mint/package.json +32 -0
  63. package/templates/nft-mint/postcss.config.mjs +7 -0
  64. package/templates/nft-mint/tsconfig.json +23 -0
  65. package/templates/token-gated-app/CLAUDE.md +45 -0
  66. package/templates/token-gated-app/README.md +38 -0
  67. package/templates/token-gated-app/app/globals.css +95 -0
  68. package/templates/token-gated-app/app/layout.tsx +23 -0
  69. package/templates/token-gated-app/app/page.tsx +5 -0
  70. package/templates/token-gated-app/app/providers.tsx +30 -0
  71. package/templates/token-gated-app/components/demo.tsx +161 -0
  72. package/templates/token-gated-app/contracts/foundry.toml +9 -0
  73. package/templates/token-gated-app/contracts/src/AvaKitNFT.sol +57 -0
  74. package/templates/token-gated-app/cursor/rules/avakit.mdc +36 -0
  75. package/templates/token-gated-app/env.example +4 -0
  76. package/templates/token-gated-app/gitignore +15 -0
  77. package/templates/token-gated-app/lib/nft-artifact.ts +164 -0
  78. package/templates/token-gated-app/llms.txt +33 -0
  79. package/templates/token-gated-app/manifest.json +6 -0
  80. package/templates/token-gated-app/next.config.ts +7 -0
  81. package/templates/token-gated-app/package.json +32 -0
  82. package/templates/token-gated-app/postcss.config.mjs +7 -0
  83. package/templates/token-gated-app/tsconfig.json +23 -0
@@ -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: "An 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,30 @@
1
+ "use client";
2
+
3
+ import { type WalletAdapter, injectedAdapter } 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 { ThemeProvider } from "next-themes";
8
+ import { type ReactNode, useMemo } from "react";
9
+
10
+ export function Providers({ children }: { children: ReactNode }) {
11
+ const adapters = useMemo(() => {
12
+ const list: WalletAdapter[] = [];
13
+ // Social login appears only when a Web3Auth client ID is configured.
14
+ const clientId = process.env.NEXT_PUBLIC_WEB3AUTH_CLIENT_ID;
15
+ if (clientId) {
16
+ list.push(web3authAdapter({ clientId }));
17
+ }
18
+ // Injected (Core / MetaMask) is always available.
19
+ list.push(injectedAdapter());
20
+ return list;
21
+ }, []);
22
+
23
+ return (
24
+ <ThemeProvider attribute="class" defaultTheme="system" enableSystem disableTransitionOnChange>
25
+ <AvaKitProvider chains={[__CHAIN_CONST__]} adapters={adapters}>
26
+ {children}
27
+ </AvaKitProvider>
28
+ </ThemeProvider>
29
+ );
30
+ }
@@ -0,0 +1,161 @@
1
+ "use client";
2
+
3
+ import {
4
+ Button,
5
+ ConnectAvalanche,
6
+ shortenAddress,
7
+ useAvaAccount,
8
+ useAvaChain,
9
+ useAvaDeploy,
10
+ useContract,
11
+ } from "@avakit/react";
12
+ import { Lock, Moon, Sun, Unlock } from "lucide-react";
13
+ import { useTheme } from "next-themes";
14
+ import { useCallback, useEffect, useState } from "react";
15
+ import type { Address } from "viem";
16
+ import { abi, bytecode } from "@/lib/nft-artifact";
17
+
18
+ const ZERO = "0x0000000000000000000000000000000000000000" as const;
19
+
20
+ function ThemeToggle() {
21
+ const { resolvedTheme, setTheme } = useTheme();
22
+ return (
23
+ <Button
24
+ variant="outline"
25
+ size="icon"
26
+ aria-label="Toggle theme"
27
+ onClick={() => setTheme(resolvedTheme === "dark" ? "light" : "dark")}
28
+ >
29
+ <Sun className="hidden size-4 dark:block" />
30
+ <Moon className="block size-4 dark:hidden" />
31
+ </Button>
32
+ );
33
+ }
34
+
35
+ export function Demo() {
36
+ const { address, isConnected } = useAvaAccount();
37
+ const { chain } = useAvaChain();
38
+ const { deploy, status: deployStatus } = useAvaDeploy();
39
+
40
+ const [contractAddress, setContractAddress] = useState<Address | null>(null);
41
+ const contract = useContract({ address: contractAddress ?? ZERO, abi });
42
+
43
+ const [owned, setOwned] = useState<bigint | null>(null);
44
+ const [minting, setMinting] = useState(false);
45
+ const [error, setError] = useState<string | null>(null);
46
+
47
+ const refreshOwned = useCallback(async () => {
48
+ if (!contractAddress || !address) return;
49
+ try {
50
+ const balance = await contract.read("balanceOf", [address]);
51
+ setOwned(balance as bigint);
52
+ } catch {
53
+ // contract not ready
54
+ }
55
+ }, [contract, contractAddress, address]);
56
+
57
+ useEffect(() => {
58
+ void refreshOwned();
59
+ }, [refreshOwned]);
60
+
61
+ async function handleDeploy() {
62
+ setError(null);
63
+ try {
64
+ const result = await deploy({ abi, bytecode });
65
+ setContractAddress(result.address);
66
+ } catch (e) {
67
+ setError(e instanceof Error ? e.message : String(e));
68
+ }
69
+ }
70
+
71
+ async function handleMint() {
72
+ if (!contractAddress) return;
73
+ setMinting(true);
74
+ setError(null);
75
+ try {
76
+ await contract.write("mint", []);
77
+ await refreshOwned();
78
+ } catch (e) {
79
+ setError(e instanceof Error ? e.message : String(e));
80
+ } finally {
81
+ setMinting(false);
82
+ }
83
+ }
84
+
85
+ const hasAccess = owned !== null && owned > 0n;
86
+
87
+ return (
88
+ <div className="mx-auto flex min-h-dvh max-w-xl flex-col gap-8 px-6 py-16">
89
+ <header className="flex items-center justify-between">
90
+ <span className="font-mono text-sm font-semibold">__PROJECT_NAME__</span>
91
+ <div className="flex items-center gap-2">
92
+ <ConnectAvalanche />
93
+ <ThemeToggle />
94
+ </div>
95
+ </header>
96
+
97
+ <div className="flex flex-col gap-2">
98
+ <h1 className="text-3xl font-semibold tracking-tight">Token-gated content</h1>
99
+ <p className="text-muted-foreground text-sm">
100
+ Hold the access-pass NFT to unlock the content below. Deploy the pass contract, mint one,
101
+ and the gate opens — all on {chain.name}.
102
+ </p>
103
+ </div>
104
+
105
+ {!isConnected || !address ? (
106
+ <div className="text-muted-foreground rounded-xl border border-dashed p-10 text-center text-sm">
107
+ Connect a wallet to begin.
108
+ </div>
109
+ ) : !contractAddress ? (
110
+ <div className="flex flex-col gap-3 rounded-xl border p-6">
111
+ <p className="text-sm">Step 1 — deploy the access-pass contract.</p>
112
+ <Button onClick={handleDeploy} disabled={deployStatus === "deploying"}>
113
+ {deployStatus === "deploying" ? "Deploying…" : "Deploy access-pass contract"}
114
+ </Button>
115
+ {chain.faucetUrl ? (
116
+ <a
117
+ href={chain.faucetUrl}
118
+ target="_blank"
119
+ rel="noreferrer"
120
+ className="text-muted-foreground text-center text-xs underline underline-offset-4"
121
+ >
122
+ Need test AVAX? Open the faucet
123
+ </a>
124
+ ) : null}
125
+ </div>
126
+ ) : hasAccess ? (
127
+ <div className="flex flex-col gap-3 rounded-xl border p-6">
128
+ <div className="text-foreground flex items-center gap-2 text-sm font-medium">
129
+ <Unlock className="size-4" />
130
+ Access granted
131
+ </div>
132
+ <p className="text-muted-foreground text-sm">
133
+ 🎉 You hold {owned?.toString()} access pass{owned === 1n ? "" : "es"}. Here's the gated
134
+ content: this section only renders for holders. Put your premium UI, downloads, or
135
+ members area here.
136
+ </p>
137
+ <a
138
+ href={`${chain.explorerUrl}/address/${contractAddress}`}
139
+ target="_blank"
140
+ rel="noreferrer"
141
+ className="text-muted-foreground text-xs underline underline-offset-4"
142
+ >
143
+ Pass contract: {shortenAddress(contractAddress, 6)}
144
+ </a>
145
+ </div>
146
+ ) : (
147
+ <div className="flex flex-col gap-3 rounded-xl border border-dashed p-6">
148
+ <div className="text-muted-foreground flex items-center gap-2 text-sm font-medium">
149
+ <Lock className="size-4" />
150
+ Locked — you don't hold an access pass
151
+ </div>
152
+ <Button onClick={handleMint} disabled={minting}>
153
+ {minting ? "Minting…" : "Mint access pass"}
154
+ </Button>
155
+ </div>
156
+ )}
157
+
158
+ {error ? <p className="text-muted-foreground text-sm">{error}</p> : null}
159
+ </div>
160
+ );
161
+ }
@@ -0,0 +1,9 @@
1
+ [profile.default]
2
+ src = "src"
3
+ out = "out"
4
+ libs = ["lib"]
5
+ optimizer = true
6
+ optimizer_runs = 200
7
+
8
+ [rpc_endpoints]
9
+ fuji = "https://api.avax-test.network/ext/bc/C/rpc"
@@ -0,0 +1,57 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.20;
3
+
4
+ /// @title AvaKitNFT
5
+ /// @notice A minimal, self-contained ERC-721 mint demo (no external deps so it
6
+ /// compiles out of the box with `forge build`). Public mint, on-chain
7
+ /// metadata. Not a full ERC-721 (no transfer/approve) — it's a starting
8
+ /// point you can extend.
9
+ contract AvaKitNFT {
10
+ string public constant name = "AvaKit NFT";
11
+ string public constant symbol = "AVAKIT";
12
+
13
+ uint256 public totalSupply;
14
+ mapping(uint256 => address) public ownerOf;
15
+ mapping(address => uint256) public balanceOf;
16
+
17
+ event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
18
+
19
+ /// @notice Mint the next token to the caller.
20
+ function mint() external returns (uint256 tokenId) {
21
+ tokenId = ++totalSupply;
22
+ ownerOf[tokenId] = msg.sender;
23
+ unchecked {
24
+ balanceOf[msg.sender] += 1;
25
+ }
26
+ emit Transfer(address(0), msg.sender, tokenId);
27
+ }
28
+
29
+ /// @notice Minimal on-chain metadata.
30
+ function tokenURI(uint256 tokenId) external pure returns (string memory) {
31
+ return string.concat(
32
+ 'data:application/json;utf8,{"name":"AvaKit #', _toString(tokenId), '"}'
33
+ );
34
+ }
35
+
36
+ /// @notice ERC-165: advertises ERC-721 + ERC-165 interface ids.
37
+ function supportsInterface(bytes4 interfaceId) external pure returns (bool) {
38
+ return interfaceId == 0x80ac58cd || interfaceId == 0x01ffc9a7;
39
+ }
40
+
41
+ function _toString(uint256 value) internal pure returns (string memory) {
42
+ if (value == 0) return "0";
43
+ uint256 temp = value;
44
+ uint256 digits;
45
+ while (temp != 0) {
46
+ digits++;
47
+ temp /= 10;
48
+ }
49
+ bytes memory buffer = new bytes(digits);
50
+ while (value != 0) {
51
+ digits -= 1;
52
+ buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
53
+ value /= 10;
54
+ }
55
+ return string(buffer);
56
+ }
57
+ }
@@ -0,0 +1,36 @@
1
+ ---
2
+ description: AvaKit token-gating conventions for this project
3
+ globs: ["**/*.ts", "**/*.tsx", "**/*.sol", "**/*.css"]
4
+ alwaysApply: true
5
+ ---
6
+
7
+ # AvaKit token-gated project rules
8
+
9
+ Content is gated behind ownership of an access-pass NFT (`balanceOf(address) > 0`).
10
+
11
+ ## Gate pattern
12
+
13
+ ```ts
14
+ const balance = (await useContract({ address, abi }).read("balanceOf", [address])) as bigint;
15
+ const hasAccess = balance > 0n; // only render gated content when true
16
+ ```
17
+
18
+ - Deploy the pass: `useAvaDeploy().deploy({ abi, bytecode })` (artifact in `lib/nft-artifact.ts`).
19
+ - Mint a pass: `useContract({ address, abi }).write("mint", [])`.
20
+ - To gate on a different token, point `useContract` at its address/ABI and adjust the threshold.
21
+
22
+ ## Contract
23
+
24
+ - `contracts/src/AvaKitNFT.sol` (minimal, self-contained). Recompile: `cd contracts && forge build`, then update `lib/nft-artifact.ts`.
25
+
26
+ ## UI
27
+
28
+ - shadcn/ui only; `@avakit/react` components are shadcn-styled.
29
+ - Black & white only for now; dark/light via next-themes; both must work.
30
+ - Animations: Framer Motion or GSAP only.
31
+
32
+ ## Safety
33
+
34
+ - Client-side gating is illustrative — verify ownership server-side for real secrets (SIWE + API).
35
+ - Deploying/minting costs gas; fund the wallet on Fuji first.
36
+ - Default chain is Fuji testnet; mainnet requires explicit opt-in.
@@ -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,15 @@
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
11
+
12
+ # Foundry
13
+ contracts/out/
14
+ contracts/cache/
15
+ contracts/broadcast/
@@ -0,0 +1,164 @@
1
+ // Auto-generated from contracts/src/AvaKitNFT.sol via `forge build`.
2
+ // Re-generate after editing the contract: cd contracts && forge build, then
3
+ // copy abi + bytecode.object here. Lets the app deploy from the browser with
4
+ // no Foundry required at runtime.
5
+
6
+ import type { Abi, Hex } from "viem";
7
+
8
+ export const abi = [
9
+ {
10
+ "type": "function",
11
+ "name": "balanceOf",
12
+ "inputs": [
13
+ {
14
+ "name": "",
15
+ "type": "address",
16
+ "internalType": "address"
17
+ }
18
+ ],
19
+ "outputs": [
20
+ {
21
+ "name": "",
22
+ "type": "uint256",
23
+ "internalType": "uint256"
24
+ }
25
+ ],
26
+ "stateMutability": "view"
27
+ },
28
+ {
29
+ "type": "function",
30
+ "name": "mint",
31
+ "inputs": [],
32
+ "outputs": [
33
+ {
34
+ "name": "tokenId",
35
+ "type": "uint256",
36
+ "internalType": "uint256"
37
+ }
38
+ ],
39
+ "stateMutability": "nonpayable"
40
+ },
41
+ {
42
+ "type": "function",
43
+ "name": "name",
44
+ "inputs": [],
45
+ "outputs": [
46
+ {
47
+ "name": "",
48
+ "type": "string",
49
+ "internalType": "string"
50
+ }
51
+ ],
52
+ "stateMutability": "view"
53
+ },
54
+ {
55
+ "type": "function",
56
+ "name": "ownerOf",
57
+ "inputs": [
58
+ {
59
+ "name": "",
60
+ "type": "uint256",
61
+ "internalType": "uint256"
62
+ }
63
+ ],
64
+ "outputs": [
65
+ {
66
+ "name": "",
67
+ "type": "address",
68
+ "internalType": "address"
69
+ }
70
+ ],
71
+ "stateMutability": "view"
72
+ },
73
+ {
74
+ "type": "function",
75
+ "name": "supportsInterface",
76
+ "inputs": [
77
+ {
78
+ "name": "interfaceId",
79
+ "type": "bytes4",
80
+ "internalType": "bytes4"
81
+ }
82
+ ],
83
+ "outputs": [
84
+ {
85
+ "name": "",
86
+ "type": "bool",
87
+ "internalType": "bool"
88
+ }
89
+ ],
90
+ "stateMutability": "pure"
91
+ },
92
+ {
93
+ "type": "function",
94
+ "name": "symbol",
95
+ "inputs": [],
96
+ "outputs": [
97
+ {
98
+ "name": "",
99
+ "type": "string",
100
+ "internalType": "string"
101
+ }
102
+ ],
103
+ "stateMutability": "view"
104
+ },
105
+ {
106
+ "type": "function",
107
+ "name": "tokenURI",
108
+ "inputs": [
109
+ {
110
+ "name": "tokenId",
111
+ "type": "uint256",
112
+ "internalType": "uint256"
113
+ }
114
+ ],
115
+ "outputs": [
116
+ {
117
+ "name": "",
118
+ "type": "string",
119
+ "internalType": "string"
120
+ }
121
+ ],
122
+ "stateMutability": "pure"
123
+ },
124
+ {
125
+ "type": "function",
126
+ "name": "totalSupply",
127
+ "inputs": [],
128
+ "outputs": [
129
+ {
130
+ "name": "",
131
+ "type": "uint256",
132
+ "internalType": "uint256"
133
+ }
134
+ ],
135
+ "stateMutability": "view"
136
+ },
137
+ {
138
+ "type": "event",
139
+ "name": "Transfer",
140
+ "inputs": [
141
+ {
142
+ "name": "from",
143
+ "type": "address",
144
+ "indexed": true,
145
+ "internalType": "address"
146
+ },
147
+ {
148
+ "name": "to",
149
+ "type": "address",
150
+ "indexed": true,
151
+ "internalType": "address"
152
+ },
153
+ {
154
+ "name": "tokenId",
155
+ "type": "uint256",
156
+ "indexed": true,
157
+ "internalType": "uint256"
158
+ }
159
+ ],
160
+ "anonymous": false
161
+ }
162
+ ] as const satisfies Abi;
163
+
164
+ export const bytecode = "0x6080604052348015600e575f5ffd5b5061056b8061001c5f395ff3fe608060405234801561000f575f5ffd5b5060043610610085575f3560e01c80636352211e116100585780636352211e1461010557806370a082311461014557806395d89b4114610164578063c87b56dd14610189575f5ffd5b806301ffc9a71461008957806306fdde03146100b15780631249c58b146100e757806318160ddd146100fd575b5f5ffd5b61009c610097366004610383565b61019c565b60405190151581526020015b60405180910390f35b6100da6040518060400160405280600a815260200169105d9852da5d0813919560b21b81525081565b6040516100a891906103b1565b6100ef6101d2565b6040519081526020016100a8565b6100ef5f5481565b61012d6101133660046103e6565b60016020525f90815260409020546001600160a01b031681565b6040516001600160a01b0390911681526020016100a8565b6100ef6101533660046103fd565b60026020525f908152604090205481565b6100da6040518060400160405280600681526020016510559052d25560d21b81525081565b6100da6101973660046103e6565b61024d565b5f6380ac58cd60e01b6001600160e01b0319831614806101cc57506301ffc9a760e01b6001600160e01b03198316145b92915050565b5f5f5f81546101e090610437565b91829055505f81815260016020818152604080842080546001600160a01b0319163390811790915580855260029092528084208054909301909255905192935083929091907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a490565b60606102588261027e565b604051602001610268919061044f565b6040516020818303038152906040529050919050565b6060815f036102a45750506040805180820190915260018152600360fc1b602082015290565b815f5b81156102cd57806102b781610437565b91506102c69050600a836104c1565b91506102a7565b5f8167ffffffffffffffff8111156102e7576102e76104d4565b6040519080825280601f01601f191660200182016040528015610311576020820181803683370190505b5090505b841561037b576103266001836104e8565b9150610333600a866104fb565b61033e90603061050e565b60f81b81838151811061035357610353610521565b60200101906001600160f81b03191690815f1a905350610374600a866104c1565b9450610315565b949350505050565b5f60208284031215610393575f5ffd5b81356001600160e01b0319811681146103aa575f5ffd5b9392505050565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b5f602082840312156103f6575f5ffd5b5035919050565b5f6020828403121561040d575f5ffd5b81356001600160a01b03811681146103aa575f5ffd5b634e487b7160e01b5f52601160045260245ffd5b5f6001820161044857610448610423565b5060010190565b7f646174613a6170706c69636174696f6e2f6a736f6e3b757466382c7b226e616d81526b65223a224176614b6974202360a01b60208201525f82518060208501602c85015e61227d60f01b602c939091019283015250602e01919050565b634e487b7160e01b5f52601260045260245ffd5b5f826104cf576104cf6104ad565b500490565b634e487b7160e01b5f52604160045260245ffd5b818103818111156101cc576101cc610423565b5f82610509576105096104ad565b500690565b808201808211156101cc576101cc610423565b634e487b7160e01b5f52603260045260245ffdfea2646970667358221220636aeafda61f36794e1b5b3ea1a8dfe71613339168fec5fb9f9f3cb22f2abc9464736f6c634300081c0033" as Hex;
@@ -0,0 +1,33 @@
1
+ # __PROJECT_NAME__
2
+
3
+ > A token-gated Avalanche dapp scaffolded with AvaKit. Content unlocks for holders of an access-pass NFT. Next.js + @avakit/react + shadcn/ui + Foundry.
4
+
5
+ ## Project map
6
+
7
+ - [contracts/src/AvaKitNFT.sol](contracts/src/AvaKitNFT.sol): the access-pass ERC-721.
8
+ - [lib/nft-artifact.ts](lib/nft-artifact.ts): compiled ABI + bytecode (browser deploy).
9
+ - [components/demo.tsx](components/demo.tsx): deploy → mint → gate logic.
10
+ - [app/providers.tsx](app/providers.tsx): AvaKitProvider + theme.
11
+ - [CLAUDE.md](CLAUDE.md): agent guide (gate pattern, security caveats, rules).
12
+
13
+ ## The gate
14
+
15
+ ```ts
16
+ const balance = (await useContract({ address, abi }).read("balanceOf", [address])) as bigint;
17
+ const hasAccess = balance > 0n;
18
+ ```
19
+
20
+ ## Key APIs
21
+
22
+ - Deploy: `useAvaDeploy().deploy({ abi, bytecode })`
23
+ - Read/write: `useContract({ address, abi }).read(...)` / `.write("mint", [])`
24
+ - Wallet: `<ConnectAvalanche />`, `useAvaAccount()`, `useAvaChain()`
25
+
26
+ ## Security note
27
+
28
+ Client-side gating is illustrative. For real protected content, verify ownership server-side (e.g. SIWE + an API check) — never trust the browser alone.
29
+
30
+ ## External docs
31
+
32
+ - Avalanche Builder Hub: https://build.avax.network/llms.txt
33
+ - Fuji faucet: https://core.app/tools/testnet-faucet
@@ -0,0 +1,6 @@
1
+ {
2
+ "id": "token-gated-app",
3
+ "title": "Token-gated app",
4
+ "description": "Unlock content for holders of an access-pass NFT",
5
+ "contracts": true
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;