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,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,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,178 @@
|
|
|
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, formatUnits, parseUnits } from "viem";
|
|
16
|
+
import { abi, bytecode } from "@/lib/token-artifact";
|
|
17
|
+
|
|
18
|
+
const ZERO = "0x0000000000000000000000000000000000000000" as const;
|
|
19
|
+
const BURN = "0x000000000000000000000000000000000000dEaD" as const;
|
|
20
|
+
|
|
21
|
+
function ThemeToggle() {
|
|
22
|
+
const { resolvedTheme, setTheme } = useTheme();
|
|
23
|
+
return (
|
|
24
|
+
<Button
|
|
25
|
+
variant="outline"
|
|
26
|
+
size="icon"
|
|
27
|
+
aria-label="Toggle theme"
|
|
28
|
+
onClick={() => setTheme(resolvedTheme === "dark" ? "light" : "dark")}
|
|
29
|
+
>
|
|
30
|
+
<Sun className="hidden size-4 dark:block" />
|
|
31
|
+
<Moon className="block size-4 dark:hidden" />
|
|
32
|
+
</Button>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function Demo() {
|
|
37
|
+
const { address, isConnected } = useAvaAccount();
|
|
38
|
+
const { chain } = useAvaChain();
|
|
39
|
+
const { deploy, status: deployStatus } = useAvaDeploy();
|
|
40
|
+
|
|
41
|
+
const [contractAddress, setContractAddress] = useState<Address | null>(null);
|
|
42
|
+
const token = useContract({ address: contractAddress ?? ZERO, abi });
|
|
43
|
+
|
|
44
|
+
const [balance, setBalance] = useState<bigint | null>(null);
|
|
45
|
+
const [supply, setSupply] = useState<bigint | null>(null);
|
|
46
|
+
const [busy, setBusy] = useState<null | "mint" | "transfer">(null);
|
|
47
|
+
const [error, setError] = useState<string | null>(null);
|
|
48
|
+
|
|
49
|
+
const refresh = useCallback(async () => {
|
|
50
|
+
if (!contractAddress || !address) return;
|
|
51
|
+
try {
|
|
52
|
+
const [bal, total] = await Promise.all([
|
|
53
|
+
token.read("balanceOf", [address]),
|
|
54
|
+
token.read("totalSupply"),
|
|
55
|
+
]);
|
|
56
|
+
setBalance(bal as bigint);
|
|
57
|
+
setSupply(total as bigint);
|
|
58
|
+
} catch {
|
|
59
|
+
// not ready
|
|
60
|
+
}
|
|
61
|
+
}, [token, contractAddress, address]);
|
|
62
|
+
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
void refresh();
|
|
65
|
+
}, [refresh]);
|
|
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 run(kind: "mint" | "transfer", fn: () => Promise<unknown>) {
|
|
78
|
+
setBusy(kind);
|
|
79
|
+
setError(null);
|
|
80
|
+
try {
|
|
81
|
+
await fn();
|
|
82
|
+
await refresh();
|
|
83
|
+
} catch (e) {
|
|
84
|
+
setError(e instanceof Error ? e.message : String(e));
|
|
85
|
+
} finally {
|
|
86
|
+
setBusy(null);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const fmt = (v: bigint | null) => (v === null ? "…" : `${formatUnits(v, 18)} AKT`);
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<div className="mx-auto flex min-h-dvh max-w-xl flex-col gap-8 px-6 py-16">
|
|
94
|
+
<header className="flex items-center justify-between">
|
|
95
|
+
<span className="font-mono text-sm font-semibold">__PROJECT_NAME__</span>
|
|
96
|
+
<div className="flex items-center gap-2">
|
|
97
|
+
<ConnectAvalanche />
|
|
98
|
+
<ThemeToggle />
|
|
99
|
+
</div>
|
|
100
|
+
</header>
|
|
101
|
+
|
|
102
|
+
<div className="flex flex-col gap-2">
|
|
103
|
+
<h1 className="text-3xl font-semibold tracking-tight">Launch an ERC-20 token</h1>
|
|
104
|
+
<p className="text-muted-foreground text-sm">
|
|
105
|
+
Deploy your token contract from the browser, mint some supply, and transfer — all on{" "}
|
|
106
|
+
{chain.name}. Bytecode is bundled, so no Foundry is needed to run this.
|
|
107
|
+
</p>
|
|
108
|
+
</div>
|
|
109
|
+
|
|
110
|
+
{!isConnected || !address ? (
|
|
111
|
+
<div className="text-muted-foreground rounded-xl border border-dashed p-10 text-center text-sm">
|
|
112
|
+
Connect a wallet to begin.
|
|
113
|
+
</div>
|
|
114
|
+
) : !contractAddress ? (
|
|
115
|
+
<div className="flex flex-col gap-3 rounded-xl border p-6">
|
|
116
|
+
<p className="text-sm">Step 1 — deploy your ERC-20 contract.</p>
|
|
117
|
+
<Button onClick={handleDeploy} disabled={deployStatus === "deploying"}>
|
|
118
|
+
{deployStatus === "deploying" ? "Deploying…" : "Deploy token contract"}
|
|
119
|
+
</Button>
|
|
120
|
+
{chain.faucetUrl ? (
|
|
121
|
+
<a
|
|
122
|
+
href={chain.faucetUrl}
|
|
123
|
+
target="_blank"
|
|
124
|
+
rel="noreferrer"
|
|
125
|
+
className="text-muted-foreground text-center text-xs underline underline-offset-4"
|
|
126
|
+
>
|
|
127
|
+
Need test AVAX? Open the faucet
|
|
128
|
+
</a>
|
|
129
|
+
) : null}
|
|
130
|
+
</div>
|
|
131
|
+
) : (
|
|
132
|
+
<div className="flex flex-col gap-4 rounded-xl border p-6">
|
|
133
|
+
<Row label="Token" value={`AvaKit Token (AKT) · ${shortenAddress(contractAddress, 6)}`} mono />
|
|
134
|
+
<Row label="Total supply" value={fmt(supply)} mono />
|
|
135
|
+
<Row label="Your balance" value={fmt(balance)} mono />
|
|
136
|
+
|
|
137
|
+
<div className="flex flex-col gap-2 pt-2 sm:flex-row">
|
|
138
|
+
<Button
|
|
139
|
+
className="flex-1"
|
|
140
|
+
disabled={busy !== null}
|
|
141
|
+
onClick={() => run("mint", () => token.write("mint", []))}
|
|
142
|
+
>
|
|
143
|
+
{busy === "mint" ? "Minting…" : "Mint 100 AKT"}
|
|
144
|
+
</Button>
|
|
145
|
+
<Button
|
|
146
|
+
className="flex-1"
|
|
147
|
+
variant="outline"
|
|
148
|
+
disabled={busy !== null || (balance ?? 0n) < parseUnits("10", 18)}
|
|
149
|
+
onClick={() => run("transfer", () => token.write("transfer", [BURN, parseUnits("10", 18)]))}
|
|
150
|
+
>
|
|
151
|
+
{busy === "transfer" ? "Sending…" : "Burn 10 AKT"}
|
|
152
|
+
</Button>
|
|
153
|
+
</div>
|
|
154
|
+
|
|
155
|
+
<a
|
|
156
|
+
href={`${chain.explorerUrl}/address/${contractAddress}`}
|
|
157
|
+
target="_blank"
|
|
158
|
+
rel="noreferrer"
|
|
159
|
+
className="text-muted-foreground text-center text-xs underline underline-offset-4"
|
|
160
|
+
>
|
|
161
|
+
View token on explorer
|
|
162
|
+
</a>
|
|
163
|
+
</div>
|
|
164
|
+
)}
|
|
165
|
+
|
|
166
|
+
{error ? <p className="text-muted-foreground text-sm">{error}</p> : null}
|
|
167
|
+
</div>
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function Row({ label, value, mono }: { label: string; value: string; mono?: boolean }) {
|
|
172
|
+
return (
|
|
173
|
+
<div className="flex items-center justify-between gap-4">
|
|
174
|
+
<span className="text-muted-foreground text-sm">{label}</span>
|
|
175
|
+
<span className={mono ? "font-mono text-sm" : "text-sm"}>{value}</span>
|
|
176
|
+
</div>
|
|
177
|
+
);
|
|
178
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.20;
|
|
3
|
+
|
|
4
|
+
/// @title AvaKitToken
|
|
5
|
+
/// @notice A minimal, self-contained ERC-20 with a public demo faucet (`mint`).
|
|
6
|
+
/// No external deps, so it compiles out of the box with `forge build`.
|
|
7
|
+
contract AvaKitToken {
|
|
8
|
+
string public constant name = "AvaKit Token";
|
|
9
|
+
string public constant symbol = "AKT";
|
|
10
|
+
uint8 public constant decimals = 18;
|
|
11
|
+
|
|
12
|
+
uint256 public totalSupply;
|
|
13
|
+
mapping(address => uint256) public balanceOf;
|
|
14
|
+
mapping(address => mapping(address => uint256)) public allowance;
|
|
15
|
+
|
|
16
|
+
event Transfer(address indexed from, address indexed to, uint256 value);
|
|
17
|
+
event Approval(address indexed owner, address indexed spender, uint256 value);
|
|
18
|
+
|
|
19
|
+
/// @notice Mint 100 AKT to the caller (demo faucet).
|
|
20
|
+
function mint() external {
|
|
21
|
+
uint256 amount = 100 * 10 ** decimals;
|
|
22
|
+
totalSupply += amount;
|
|
23
|
+
unchecked {
|
|
24
|
+
balanceOf[msg.sender] += amount;
|
|
25
|
+
}
|
|
26
|
+
emit Transfer(address(0), msg.sender, amount);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function transfer(address to, uint256 value) external returns (bool) {
|
|
30
|
+
balanceOf[msg.sender] -= value;
|
|
31
|
+
unchecked {
|
|
32
|
+
balanceOf[to] += value;
|
|
33
|
+
}
|
|
34
|
+
emit Transfer(msg.sender, to, value);
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function approve(address spender, uint256 value) external returns (bool) {
|
|
39
|
+
allowance[msg.sender][spender] = value;
|
|
40
|
+
emit Approval(msg.sender, spender, value);
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function transferFrom(address from, address to, uint256 value) external returns (bool) {
|
|
45
|
+
allowance[from][msg.sender] -= value;
|
|
46
|
+
balanceOf[from] -= value;
|
|
47
|
+
unchecked {
|
|
48
|
+
balanceOf[to] += value;
|
|
49
|
+
}
|
|
50
|
+
emit Transfer(from, to, value);
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: AvaKit ERC-20 conventions for this project
|
|
3
|
+
globs: ["**/*.ts", "**/*.tsx", "**/*.sol", "**/*.css"]
|
|
4
|
+
alwaysApply: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# AvaKit ERC-20 project rules
|
|
8
|
+
|
|
9
|
+
Deploy a token from the browser, mint, and transfer.
|
|
10
|
+
|
|
11
|
+
## Flow
|
|
12
|
+
|
|
13
|
+
- Deploy: `useAvaDeploy().deploy({ abi, bytecode })` (artifact in `lib/token-artifact.ts`).
|
|
14
|
+
- Mint: `useContract({ address, abi }).write("mint", [])` (mints 100 AKT).
|
|
15
|
+
- Transfer: `.write("transfer", [to, parseUnits("10", 18)])`.
|
|
16
|
+
- Read: `.read("balanceOf", [address])`, `.read("totalSupply")`; format with `formatUnits(v, 18)`.
|
|
17
|
+
|
|
18
|
+
## Amounts
|
|
19
|
+
|
|
20
|
+
- The token has 18 decimals. Always convert with `parseUnits` / `formatUnits` — never pass raw human numbers as wei.
|
|
21
|
+
|
|
22
|
+
## Contract
|
|
23
|
+
|
|
24
|
+
- `contracts/src/AvaKitToken.sol` (minimal, self-contained). Recompile: `cd contracts && forge build`, then update `lib/token-artifact.ts`.
|
|
25
|
+
|
|
26
|
+
## UI & safety
|
|
27
|
+
|
|
28
|
+
- shadcn/ui only; black & white for now; dark/light via next-themes; both must work.
|
|
29
|
+
- Animations: Framer Motion or GSAP only.
|
|
30
|
+
- Deploying/minting/transferring cost gas; fund the wallet on Fuji first.
|
|
31
|
+
- Default chain is Fuji testnet; mainnet requires explicit opt-in.
|