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.
- package/LICENSE +21 -0
- package/README.md +43 -0
- package/dist/api.d.ts +34 -0
- package/dist/api.js +12 -0
- package/dist/api.js.map +1 -0
- package/dist/chunk-3YGRLI4R.js +91 -0
- package/dist/chunk-3YGRLI4R.js.map +1 -0
- package/dist/index.js +213 -0
- package/dist/index.js.map +1 -0
- package/package.json +65 -0
- package/templates/erc20-token/CLAUDE.md +45 -0
- package/templates/erc20-token/README.md +35 -0
- package/templates/erc20-token/app/globals.css +95 -0
- package/templates/erc20-token/app/layout.tsx +23 -0
- package/templates/erc20-token/app/page.tsx +5 -0
- package/templates/erc20-token/app/providers.tsx +30 -0
- package/templates/erc20-token/components/demo.tsx +178 -0
- package/templates/erc20-token/contracts/foundry.toml +9 -0
- package/templates/erc20-token/contracts/src/AvaKitToken.sol +53 -0
- package/templates/erc20-token/cursor/rules/avakit.mdc +31 -0
- package/templates/erc20-token/env.example +4 -0
- package/templates/erc20-token/gitignore +15 -0
- package/templates/erc20-token/lib/token-artifact.ts +239 -0
- package/templates/erc20-token/llms.txt +29 -0
- package/templates/erc20-token/manifest.json +6 -0
- package/templates/erc20-token/next.config.ts +7 -0
- package/templates/erc20-token/package.json +32 -0
- package/templates/erc20-token/postcss.config.mjs +7 -0
- package/templates/erc20-token/tsconfig.json +23 -0
- package/templates/minimal/CLAUDE.md +37 -0
- package/templates/minimal/README.md +32 -0
- package/templates/minimal/app/globals.css +95 -0
- package/templates/minimal/app/layout.tsx +23 -0
- package/templates/minimal/app/page.tsx +5 -0
- package/templates/minimal/app/providers.tsx +30 -0
- package/templates/minimal/components/demo.tsx +135 -0
- package/templates/minimal/cursor/rules/avakit.mdc +28 -0
- package/templates/minimal/env.example +4 -0
- package/templates/minimal/gitignore +10 -0
- package/templates/minimal/llms.txt +30 -0
- package/templates/minimal/manifest.json +6 -0
- package/templates/minimal/next.config.ts +7 -0
- package/templates/minimal/package.json +32 -0
- package/templates/minimal/postcss.config.mjs +7 -0
- package/templates/minimal/tsconfig.json +23 -0
- package/templates/nft-mint/CLAUDE.md +45 -0
- package/templates/nft-mint/README.md +36 -0
- package/templates/nft-mint/app/globals.css +95 -0
- package/templates/nft-mint/app/layout.tsx +23 -0
- package/templates/nft-mint/app/page.tsx +5 -0
- package/templates/nft-mint/app/providers.tsx +30 -0
- package/templates/nft-mint/components/demo.tsx +176 -0
- package/templates/nft-mint/contracts/foundry.toml +9 -0
- package/templates/nft-mint/contracts/src/AvaKitNFT.sol +57 -0
- package/templates/nft-mint/cursor/rules/avakit.mdc +32 -0
- package/templates/nft-mint/env.example +4 -0
- package/templates/nft-mint/gitignore +15 -0
- package/templates/nft-mint/lib/nft-artifact.ts +164 -0
- package/templates/nft-mint/llms.txt +31 -0
- package/templates/nft-mint/manifest.json +6 -0
- package/templates/nft-mint/next.config.ts +7 -0
- package/templates/nft-mint/package.json +32 -0
- package/templates/nft-mint/postcss.config.mjs +7 -0
- package/templates/nft-mint/tsconfig.json +23 -0
- package/templates/token-gated-app/CLAUDE.md +45 -0
- package/templates/token-gated-app/README.md +38 -0
- package/templates/token-gated-app/app/globals.css +95 -0
- package/templates/token-gated-app/app/layout.tsx +23 -0
- package/templates/token-gated-app/app/page.tsx +5 -0
- package/templates/token-gated-app/app/providers.tsx +30 -0
- package/templates/token-gated-app/components/demo.tsx +161 -0
- package/templates/token-gated-app/contracts/foundry.toml +9 -0
- package/templates/token-gated-app/contracts/src/AvaKitNFT.sol +57 -0
- package/templates/token-gated-app/cursor/rules/avakit.mdc +36 -0
- package/templates/token-gated-app/env.example +4 -0
- package/templates/token-gated-app/gitignore +15 -0
- package/templates/token-gated-app/lib/nft-artifact.ts +164 -0
- package/templates/token-gated-app/llms.txt +33 -0
- package/templates/token-gated-app/manifest.json +6 -0
- package/templates/token-gated-app/next.config.ts +7 -0
- package/templates/token-gated-app/package.json +32 -0
- package/templates/token-gated-app/postcss.config.mjs +7 -0
- package/templates/token-gated-app/tsconfig.json +23 -0
|
@@ -0,0 +1,176 @@
|
|
|
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 { Moon, Sun } 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 [minting, setMinting] = useState(false);
|
|
44
|
+
const [mintHash, setMintHash] = useState<string | null>(null);
|
|
45
|
+
const [supply, setSupply] = useState<bigint | null>(null);
|
|
46
|
+
const [owned, setOwned] = useState<bigint | null>(null);
|
|
47
|
+
const [error, setError] = useState<string | null>(null);
|
|
48
|
+
|
|
49
|
+
const refreshCounts = useCallback(async () => {
|
|
50
|
+
if (!contractAddress || !address) return;
|
|
51
|
+
try {
|
|
52
|
+
const [total, balance] = await Promise.all([
|
|
53
|
+
contract.read("totalSupply"),
|
|
54
|
+
contract.read("balanceOf", [address]),
|
|
55
|
+
]);
|
|
56
|
+
setSupply(total as bigint);
|
|
57
|
+
setOwned(balance as bigint);
|
|
58
|
+
} catch {
|
|
59
|
+
// contract not ready yet
|
|
60
|
+
}
|
|
61
|
+
}, [contract, contractAddress, address]);
|
|
62
|
+
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
void refreshCounts();
|
|
65
|
+
}, [refreshCounts]);
|
|
66
|
+
|
|
67
|
+
async function handleDeploy() {
|
|
68
|
+
setError(null);
|
|
69
|
+
try {
|
|
70
|
+
const result = await deploy({ abi, bytecode });
|
|
71
|
+
setContractAddress(result.address);
|
|
72
|
+
} catch (e) {
|
|
73
|
+
setError(e instanceof Error ? e.message : String(e));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function handleMint() {
|
|
78
|
+
if (!contractAddress) return;
|
|
79
|
+
setMinting(true);
|
|
80
|
+
setError(null);
|
|
81
|
+
setMintHash(null);
|
|
82
|
+
try {
|
|
83
|
+
const hash = await contract.write("mint", []);
|
|
84
|
+
setMintHash(hash);
|
|
85
|
+
await refreshCounts();
|
|
86
|
+
} catch (e) {
|
|
87
|
+
setError(e instanceof Error ? e.message : String(e));
|
|
88
|
+
} finally {
|
|
89
|
+
setMinting(false);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<div className="mx-auto flex min-h-dvh max-w-xl flex-col gap-8 px-6 py-16">
|
|
95
|
+
<header className="flex items-center justify-between">
|
|
96
|
+
<span className="font-mono text-sm font-semibold">__PROJECT_NAME__</span>
|
|
97
|
+
<div className="flex items-center gap-2">
|
|
98
|
+
<ConnectAvalanche />
|
|
99
|
+
<ThemeToggle />
|
|
100
|
+
</div>
|
|
101
|
+
</header>
|
|
102
|
+
|
|
103
|
+
<div className="flex flex-col gap-2">
|
|
104
|
+
<h1 className="text-3xl font-semibold tracking-tight">Mint an NFT on Avalanche</h1>
|
|
105
|
+
<p className="text-muted-foreground text-sm">
|
|
106
|
+
Deploy the NFT contract from your wallet, then mint — all on {chain.name}. The bytecode is
|
|
107
|
+
bundled, so no Foundry is needed to run this.
|
|
108
|
+
</p>
|
|
109
|
+
</div>
|
|
110
|
+
|
|
111
|
+
{!isConnected || !address ? (
|
|
112
|
+
<div className="text-muted-foreground rounded-xl border border-dashed p-10 text-center text-sm">
|
|
113
|
+
Connect a wallet to begin.
|
|
114
|
+
</div>
|
|
115
|
+
) : !contractAddress ? (
|
|
116
|
+
<div className="flex flex-col gap-3 rounded-xl border p-6">
|
|
117
|
+
<p className="text-sm">Step 1 — deploy your NFT contract.</p>
|
|
118
|
+
<Button onClick={handleDeploy} disabled={deployStatus === "deploying"}>
|
|
119
|
+
{deployStatus === "deploying" ? "Deploying…" : "Deploy NFT contract"}
|
|
120
|
+
</Button>
|
|
121
|
+
{chain.faucetUrl ? (
|
|
122
|
+
<a
|
|
123
|
+
href={chain.faucetUrl}
|
|
124
|
+
target="_blank"
|
|
125
|
+
rel="noreferrer"
|
|
126
|
+
className="text-muted-foreground text-center text-xs underline underline-offset-4"
|
|
127
|
+
>
|
|
128
|
+
Need test AVAX? Open the faucet
|
|
129
|
+
</a>
|
|
130
|
+
) : null}
|
|
131
|
+
</div>
|
|
132
|
+
) : (
|
|
133
|
+
<div className="flex flex-col gap-4 rounded-xl border p-6">
|
|
134
|
+
<Row label="Contract" value={shortenAddress(contractAddress, 6)} mono />
|
|
135
|
+
<Row label="Total minted" value={supply === null ? "…" : supply.toString()} mono />
|
|
136
|
+
<Row label="You own" value={owned === null ? "…" : owned.toString()} mono />
|
|
137
|
+
|
|
138
|
+
<Button onClick={handleMint} disabled={minting}>
|
|
139
|
+
{minting ? "Minting…" : "Mint NFT"}
|
|
140
|
+
</Button>
|
|
141
|
+
|
|
142
|
+
<a
|
|
143
|
+
href={`${chain.explorerUrl}/address/${contractAddress}`}
|
|
144
|
+
target="_blank"
|
|
145
|
+
rel="noreferrer"
|
|
146
|
+
className="text-muted-foreground text-center text-xs underline underline-offset-4"
|
|
147
|
+
>
|
|
148
|
+
View contract on explorer
|
|
149
|
+
</a>
|
|
150
|
+
|
|
151
|
+
{mintHash ? (
|
|
152
|
+
<a
|
|
153
|
+
href={`${chain.explorerUrl}/tx/${mintHash}`}
|
|
154
|
+
target="_blank"
|
|
155
|
+
rel="noreferrer"
|
|
156
|
+
className="text-sm break-all underline underline-offset-4"
|
|
157
|
+
>
|
|
158
|
+
✓ Minted: {mintHash}
|
|
159
|
+
</a>
|
|
160
|
+
) : null}
|
|
161
|
+
</div>
|
|
162
|
+
)}
|
|
163
|
+
|
|
164
|
+
{error ? <p className="text-muted-foreground text-sm">{error}</p> : null}
|
|
165
|
+
</div>
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function Row({ label, value, mono }: { label: string; value: string; mono?: boolean }) {
|
|
170
|
+
return (
|
|
171
|
+
<div className="flex items-center justify-between gap-4">
|
|
172
|
+
<span className="text-muted-foreground text-sm">{label}</span>
|
|
173
|
+
<span className={mono ? "font-mono text-sm" : "text-sm"}>{value}</span>
|
|
174
|
+
</div>
|
|
175
|
+
);
|
|
176
|
+
}
|
|
@@ -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,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: AvaKit + Avalanche NFT conventions for this project
|
|
3
|
+
globs: ["**/*.ts", "**/*.tsx", "**/*.sol", "**/*.css"]
|
|
4
|
+
alwaysApply: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# AvaKit NFT project rules
|
|
8
|
+
|
|
9
|
+
This is an Avalanche NFT dapp built with AvaKit. It deploys an ERC-721 from the browser, then mints.
|
|
10
|
+
|
|
11
|
+
## Flow
|
|
12
|
+
|
|
13
|
+
- Deploy: `useAvaDeploy().deploy({ abi, bytecode })` (artifact in `lib/nft-artifact.ts`).
|
|
14
|
+
- Mint: `useContract({ address, abi }).write("mint", [])`.
|
|
15
|
+
- Read: `.read("totalSupply")`, `.read("balanceOf", [address])`.
|
|
16
|
+
|
|
17
|
+
## Contract
|
|
18
|
+
|
|
19
|
+
- Source: `contracts/src/AvaKitNFT.sol` (minimal, self-contained ERC-721 — no external deps).
|
|
20
|
+
- After editing, recompile: `cd contracts && forge build`, then update `lib/nft-artifact.ts` with the new abi + bytecode.object.
|
|
21
|
+
|
|
22
|
+
## UI
|
|
23
|
+
|
|
24
|
+
- shadcn/ui only; `@avakit/react` components are shadcn-styled.
|
|
25
|
+
- Black & white only for now; dark/light via next-themes; both must work.
|
|
26
|
+
- Animations: Framer Motion or GSAP only.
|
|
27
|
+
|
|
28
|
+
## Safety
|
|
29
|
+
|
|
30
|
+
- Deploying and minting cost gas — fund the wallet on Fuji first.
|
|
31
|
+
- Default chain is Fuji testnet; mainnet requires explicit opt-in.
|
|
32
|
+
- Never hardcode secrets; keys stay in the wallet provider.
|
|
@@ -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,31 @@
|
|
|
1
|
+
# __PROJECT_NAME__
|
|
2
|
+
|
|
3
|
+
> An Avalanche NFT dapp scaffolded with AvaKit. Deploy an ERC-721 from the browser, then mint. Next.js + @avakit/react + shadcn/ui + Foundry.
|
|
4
|
+
|
|
5
|
+
## Project map
|
|
6
|
+
|
|
7
|
+
- [contracts/src/AvaKitNFT.sol](contracts/src/AvaKitNFT.sol): minimal self-contained ERC-721 mint contract.
|
|
8
|
+
- [lib/nft-artifact.ts](lib/nft-artifact.ts): compiled ABI + bytecode (browser deploy, no Foundry at runtime).
|
|
9
|
+
- [components/demo.tsx](components/demo.tsx): deploy + mint flow.
|
|
10
|
+
- [app/providers.tsx](app/providers.tsx): AvaKitProvider (chains + wallet adapters) + theme.
|
|
11
|
+
- [CLAUDE.md](CLAUDE.md): agent guide (AvaKit API, contract workflow, rules).
|
|
12
|
+
|
|
13
|
+
## Key APIs
|
|
14
|
+
|
|
15
|
+
- Deploy: `useAvaDeploy().deploy({ abi, bytecode })`
|
|
16
|
+
- Contract: `useContract({ address, abi }).write("mint", [])` / `.read("totalSupply")`
|
|
17
|
+
- Wallet: `<ConnectAvalanche />`, `useAvaAccount()`, `useAvaChain()`
|
|
18
|
+
- Core: `deployContract`, `getWalletClient`, `ensureChain` from `@avakit/core`
|
|
19
|
+
|
|
20
|
+
## Recompile the contract
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
cd contracts && forge build
|
|
24
|
+
```
|
|
25
|
+
Then copy abi + bytecode.object from `out/AvaKitNFT.sol/AvaKitNFT.json` into `lib/nft-artifact.ts`.
|
|
26
|
+
|
|
27
|
+
## External docs
|
|
28
|
+
|
|
29
|
+
- Avalanche Builder Hub: https://build.avax.network/llms.txt
|
|
30
|
+
- Foundry book: https://book.getfoundry.sh
|
|
31
|
+
- Fuji faucet: https://core.app/tools/testnet-faucet
|
|
@@ -0,0 +1,32 @@
|
|
|
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
|
+
"lucide-react": "1.22.0",
|
|
16
|
+
"next": "16.2.9",
|
|
17
|
+
"next-themes": "0.4.6",
|
|
18
|
+
"react": "19.2.7",
|
|
19
|
+
"react-dom": "19.2.7",
|
|
20
|
+
"viem": "2.54.1"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@tailwindcss/postcss": "4.3.2",
|
|
24
|
+
"@types/node": "26.0.1",
|
|
25
|
+
"@types/react": "19.2.17",
|
|
26
|
+
"@types/react-dom": "19.2.3",
|
|
27
|
+
"postcss": "8.5.16",
|
|
28
|
+
"tailwindcss": "4.3.2",
|
|
29
|
+
"tw-animate-css": "1.4.0",
|
|
30
|
+
"typescript": "6.0.3"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -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,45 @@
|
|
|
1
|
+
# __PROJECT_NAME__ — Token-gated Avalanche dapp (scaffolded with AvaKit)
|
|
2
|
+
|
|
3
|
+
Operational guide for AI agents (Claude Code / Cursor).
|
|
4
|
+
|
|
5
|
+
## Stack
|
|
6
|
+
|
|
7
|
+
Next.js 16 (App Router) · React 19 · `@avakit/react` · `@avakit/core` · viem · shadcn/ui · next-themes · Foundry (access-pass contract)
|
|
8
|
+
|
|
9
|
+
## Idea
|
|
10
|
+
|
|
11
|
+
Content is gated behind ownership of an **access-pass NFT**. If `balanceOf(address) > 0`, the gated section renders; otherwise it stays locked with a mint button.
|
|
12
|
+
|
|
13
|
+
## Architecture
|
|
14
|
+
|
|
15
|
+
- `contracts/src/AvaKitNFT.sol` — the access-pass ERC-721 (minimal, self-contained).
|
|
16
|
+
- `lib/nft-artifact.ts` — compiled ABI + bytecode (browser deploy, no Foundry at runtime).
|
|
17
|
+
- `components/demo.tsx` — deploy pass → mint → gate logic.
|
|
18
|
+
- `app/providers.tsx` — `<AvaKitProvider>` + `ThemeProvider`.
|
|
19
|
+
|
|
20
|
+
## The gate
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
const contract = useContract({ address, abi });
|
|
24
|
+
const balance = (await contract.read("balanceOf", [address])) as bigint;
|
|
25
|
+
const hasAccess = balance > 0n; // render gated content only when true
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
- Deploy: `useAvaDeploy().deploy({ abi, bytecode })`
|
|
29
|
+
- Mint a pass: `contract.write("mint", [])`
|
|
30
|
+
|
|
31
|
+
To gate on a different token (e.g. an existing ERC-20 or NFT), point `useContract` at that address/ABI and adjust the `hasAccess` check (e.g. balance ≥ threshold).
|
|
32
|
+
|
|
33
|
+
## Rules
|
|
34
|
+
|
|
35
|
+
- shadcn/ui only; `@avakit/react` components are shadcn-styled.
|
|
36
|
+
- Black & white only for now; dark/light via next-themes; both must work.
|
|
37
|
+
- Animations: Framer Motion or GSAP only.
|
|
38
|
+
- Deploying/minting costs gas — fund the wallet on Fuji first.
|
|
39
|
+
- Never gate sensitive data purely client-side: the gated content here is illustrative. For real secrets, verify ownership server-side (e.g. sign-in-with-Ethereum + an API check).
|
|
40
|
+
- Social login needs `NEXT_PUBLIC_WEB3AUTH_CLIENT_ID` in `.env.local`.
|
|
41
|
+
|
|
42
|
+
## Commands
|
|
43
|
+
|
|
44
|
+
- `pnpm dev` — dev server
|
|
45
|
+
- `cd contracts && forge build` — recompile the access-pass contract
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# __PROJECT_NAME__
|
|
2
|
+
|
|
3
|
+
A **token-gated** Avalanche dapp scaffolded with [AvaKit](https://github.com/mericcintosun/AvaKit). Content unlocks for holders of an access-pass NFT — no Foundry required to run it.
|
|
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 → **Deploy access-pass contract** → **Mint access pass** → the gated content unlocks. Deploying and minting cost gas, so fund your wallet on Fuji first (in-app faucet link).
|
|
17
|
+
|
|
18
|
+
## How the gate works
|
|
19
|
+
|
|
20
|
+
The gated section renders only when `balanceOf(address) > 0`:
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
const balance = (await useContract({ address, abi }).read("balanceOf", [address])) as bigint;
|
|
24
|
+
const hasAccess = balance > 0n;
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
- `contracts/src/AvaKitNFT.sol` — the access-pass ERC-721 (self-contained).
|
|
28
|
+
- `lib/nft-artifact.ts` — compiled ABI + bytecode, bundled for browser deploy.
|
|
29
|
+
|
|
30
|
+
> **Security:** client-side gating is illustrative. For real protected content, verify ownership server-side (e.g. Sign-In with Ethereum + an API check).
|
|
31
|
+
|
|
32
|
+
## Stack
|
|
33
|
+
|
|
34
|
+
Next.js 16 · `@avakit/react` · `@avakit/core` · viem · shadcn/ui · Foundry
|
|
35
|
+
|
|
36
|
+
## AI-native
|
|
37
|
+
|
|
38
|
+
Ships with `CLAUDE.md`, `llms.txt`, and `.cursor/rules` so Claude Code / Cursor understand the gate pattern and project conventions.
|