create-avalanche-app 0.1.0 → 0.1.2
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/api.js +1 -1
- package/dist/{chunk-3YGRLI4R.js → chunk-UU7ZIK6L.js} +19 -2
- package/dist/chunk-UU7ZIK6L.js.map +1 -0
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/templates/icm-messenger/CLAUDE.md +68 -0
- package/templates/icm-messenger/README.md +59 -0
- package/templates/icm-messenger/app/globals.css +95 -0
- package/templates/icm-messenger/app/layout.tsx +23 -0
- package/templates/icm-messenger/app/page.tsx +5 -0
- package/templates/icm-messenger/app/providers.tsx +22 -0
- package/templates/icm-messenger/components/demo.tsx +412 -0
- package/templates/icm-messenger/contracts/cache/solidity-files-cache.json +1 -0
- package/templates/icm-messenger/contracts/foundry.toml +6 -0
- package/templates/icm-messenger/contracts/out/AvaKitMessenger.sol/AvaKitMessenger.json +1 -0
- package/templates/icm-messenger/contracts/out/AvaKitMessenger.sol/ITeleporterMessenger.json +1 -0
- package/templates/icm-messenger/contracts/out/AvaKitMessenger.sol/ITeleporterReceiver.json +1 -0
- package/templates/icm-messenger/contracts/out/build-info/790b55b96be74f23.json +1 -0
- package/templates/icm-messenger/contracts/src/AvaKitMessenger.sol +97 -0
- package/templates/icm-messenger/cursor/rules/avakit.mdc +32 -0
- package/templates/icm-messenger/env.example +6 -0
- package/templates/icm-messenger/gitignore +15 -0
- package/templates/icm-messenger/icm.config.json +6 -0
- package/templates/icm-messenger/lib/devnet.ts +58 -0
- package/templates/icm-messenger/lib/messenger-artifact.ts +184 -0
- package/templates/icm-messenger/llms.txt +39 -0
- package/templates/icm-messenger/manifest.json +6 -0
- package/templates/icm-messenger/next.config.ts +7 -0
- package/templates/icm-messenger/package.json +33 -0
- package/templates/icm-messenger/postcss.config.mjs +7 -0
- package/templates/icm-messenger/scripts/devnet.sh +106 -0
- package/templates/icm-messenger/tsconfig.json +23 -0
- package/dist/chunk-3YGRLI4R.js.map +0 -1
package/dist/api.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/api.ts
|
|
4
|
-
import { existsSync, readdirSync, readFileSync } from "fs";
|
|
4
|
+
import { existsSync, readdirSync, readFileSync, writeFileSync } from "fs";
|
|
5
5
|
import path2 from "path";
|
|
6
6
|
import { fileURLToPath } from "url";
|
|
7
7
|
|
|
@@ -50,6 +50,7 @@ async function scaffold({
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
// src/api.ts
|
|
53
|
+
var WEB3AUTH_MODAL_VERSION = "11.2.0";
|
|
53
54
|
var templatesDir = path2.join(
|
|
54
55
|
path2.dirname(fileURLToPath(import.meta.url)),
|
|
55
56
|
"..",
|
|
@@ -80,12 +81,28 @@ async function scaffoldApp(opts) {
|
|
|
80
81
|
__AVAKIT_DEP__: opts.local ? "workspace:*" : `^${opts.avakitVersion ?? "0.1.0"}`
|
|
81
82
|
};
|
|
82
83
|
const files = await scaffold({ templateDir, targetDir: opts.targetDir, replacements });
|
|
84
|
+
if (opts.wallet === "web3auth") {
|
|
85
|
+
addDependency(
|
|
86
|
+
path2.join(opts.targetDir, "package.json"),
|
|
87
|
+
"@web3auth/modal",
|
|
88
|
+
WEB3AUTH_MODAL_VERSION
|
|
89
|
+
);
|
|
90
|
+
}
|
|
83
91
|
return { targetDir: opts.targetDir, files };
|
|
84
92
|
}
|
|
93
|
+
function addDependency(pkgPath, name, version) {
|
|
94
|
+
if (!existsSync(pkgPath)) return;
|
|
95
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
96
|
+
pkg.dependencies = Object.fromEntries(
|
|
97
|
+
Object.entries({ ...pkg.dependencies, [name]: version }).sort(([a], [b]) => a.localeCompare(b))
|
|
98
|
+
);
|
|
99
|
+
writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}
|
|
100
|
+
`);
|
|
101
|
+
}
|
|
85
102
|
|
|
86
103
|
export {
|
|
87
104
|
templatesDir,
|
|
88
105
|
listTemplates,
|
|
89
106
|
scaffoldApp
|
|
90
107
|
};
|
|
91
|
-
//# sourceMappingURL=chunk-
|
|
108
|
+
//# sourceMappingURL=chunk-UU7ZIK6L.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/api.ts","../src/scaffold.ts"],"sourcesContent":["/**\n * Programmatic API for the scaffolder — used by the CLI (src/index.ts) and by\n * @avakit/mcp's `scaffold_app` tool. Keeps template resolution and placeholder\n * replacement in one place.\n */\n\nimport { existsSync, readdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { scaffold } from \"./scaffold.js\";\n\n/** Web3Auth Modal SDK version added to apps that use the social-login wallet. */\nconst WEB3AUTH_MODAL_VERSION = \"11.2.0\";\n\nexport const templatesDir = path.join(\n path.dirname(fileURLToPath(import.meta.url)),\n \"..\",\n \"templates\",\n);\n\nexport interface TemplateInfo {\n id: string;\n title: string;\n description: string;\n contracts: boolean;\n}\n\nexport function listTemplates(): TemplateInfo[] {\n return readdirSync(templatesDir, { withFileTypes: true })\n .filter((e) => e.isDirectory())\n .map((e) => {\n const manifestPath = path.join(templatesDir, e.name, \"manifest.json\");\n const manifest = existsSync(manifestPath)\n ? (JSON.parse(readFileSync(manifestPath, \"utf8\")) as Partial<TemplateInfo>)\n : {};\n return {\n id: e.name,\n title: manifest.title ?? e.name,\n description: manifest.description ?? \"\",\n contracts: manifest.contracts ?? false,\n };\n });\n}\n\nexport type WalletId = \"web3auth\" | \"injected\";\nexport type ChainId = \"fuji\" | \"c-chain\";\n\nexport interface ScaffoldAppOptions {\n projectName: string;\n /** Absolute target directory. */\n targetDir: string;\n template: string;\n wallet?: WalletId;\n chain?: ChainId;\n /** Link @avakit/* via workspace instead of npm versions (repo dev only). */\n local?: boolean;\n /** @avakit version range used when not linking locally. */\n avakitVersion?: string;\n}\n\nexport interface ScaffoldAppResult {\n targetDir: string;\n files: string[];\n}\n\nexport async function scaffoldApp(opts: ScaffoldAppOptions): Promise<ScaffoldAppResult> {\n const templateDir = path.join(templatesDir, opts.template);\n if (!existsSync(templateDir)) {\n throw new Error(\n `Unknown template \"${opts.template}\". Available: ${listTemplates()\n .map((t) => t.id)\n .join(\", \")}`,\n );\n }\n\n const replacements: Record<string, string> = {\n __PROJECT_NAME__: opts.projectName,\n __CHAIN_CONST__: opts.chain === \"c-chain\" ? \"cChain\" : \"fuji\",\n __AVAKIT_DEP__: opts.local ? \"workspace:*\" : `^${opts.avakitVersion ?? \"0.1.0\"}`,\n };\n\n const files = await scaffold({ templateDir, targetDir: opts.targetDir, replacements });\n\n // The social-login wallet needs @web3auth/modal (an optional peer of\n // @avakit/core). Add it to the app so `web3authAdapter` can load at runtime.\n if (opts.wallet === \"web3auth\") {\n addDependency(\n path.join(opts.targetDir, \"package.json\"),\n \"@web3auth/modal\",\n WEB3AUTH_MODAL_VERSION,\n );\n }\n\n return { targetDir: opts.targetDir, files };\n}\n\nfunction addDependency(pkgPath: string, name: string, version: string): void {\n if (!existsSync(pkgPath)) return;\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf8\")) as {\n dependencies?: Record<string, string>;\n };\n pkg.dependencies = Object.fromEntries(\n Object.entries({ ...pkg.dependencies, [name]: version }).sort(([a], [b]) => a.localeCompare(b)),\n );\n writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\\n`);\n}\n","import { mkdir, readdir, readFile, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\n/**\n * Template files are stored with dot-prefixes stripped so they ship cleanly in\n * an npm package (npm refuses to publish a literal `.gitignore`, etc.). On\n * scaffold we restore the leading dot per path segment.\n */\nconst RENAME_SEGMENT: Record<string, string> = {\n gitignore: \".gitignore\",\n \"env.example\": \".env.example\",\n cursor: \".cursor\",\n};\n\n/** Template-only files that must NOT be copied into the generated project. */\nconst TEMPLATE_ONLY = new Set([\"manifest.json\"]);\n\nexport interface ScaffoldOptions {\n templateDir: string;\n targetDir: string;\n /** Literal placeholder → value, applied to every text file's contents. */\n replacements: Record<string, string>;\n}\n\nasync function walk(dir: string): Promise<string[]> {\n const entries = await readdir(dir, { withFileTypes: true });\n const files = await Promise.all(\n entries.map((entry) => {\n const full = path.join(dir, entry.name);\n return entry.isDirectory() ? walk(full) : Promise.resolve([full]);\n }),\n );\n return files.flat();\n}\n\n/** Copy a template into targetDir, renaming dot-files and replacing placeholders. */\nexport async function scaffold({\n templateDir,\n targetDir,\n replacements,\n}: ScaffoldOptions): Promise<string[]> {\n const files = await walk(templateDir);\n const written: string[] = [];\n\n for (const abs of files) {\n const rel = path.relative(templateDir, abs);\n if (TEMPLATE_ONLY.has(rel)) {\n continue;\n }\n const outRel = rel\n .split(path.sep)\n .map((segment) => RENAME_SEGMENT[segment] ?? segment)\n .join(path.sep);\n const outPath = path.join(targetDir, outRel);\n\n await mkdir(path.dirname(outPath), { recursive: true });\n\n let content = await readFile(abs, \"utf8\");\n for (const [token, value] of Object.entries(replacements)) {\n content = content.split(token).join(value);\n }\n await writeFile(outPath, content);\n written.push(outRel);\n }\n\n return written;\n}\n"],"mappings":"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","names":["path","path"]}
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
listTemplates,
|
|
4
4
|
scaffoldApp
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-UU7ZIK6L.js";
|
|
6
6
|
|
|
7
7
|
// src/index.ts
|
|
8
8
|
import { spawnSync } from "child_process";
|
|
@@ -10,7 +10,7 @@ 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.
|
|
13
|
+
var VERSION = "0.1.2";
|
|
14
14
|
function parseArgs(argv) {
|
|
15
15
|
const opts = { yes: false, local: false, install: true };
|
|
16
16
|
const rest = argv.slice(2);
|
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.0\";\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.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"]}
|
package/package.json
CHANGED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# __PROJECT_NAME__ — cross-chain messaging with Avalanche ICM (scaffolded with AvaKit)
|
|
2
|
+
|
|
3
|
+
Operational guide for AI agents (Claude Code / Cursor) working in this project.
|
|
4
|
+
|
|
5
|
+
## What this is
|
|
6
|
+
|
|
7
|
+
A dapp that sends a message from one Avalanche L1 to another using **Interchain
|
|
8
|
+
Messaging (ICM / Teleporter)**. It runs against a **local devnet** of two L1s that
|
|
9
|
+
`scripts/devnet.sh` spins up for you (with the TeleporterMessenger and a relayer
|
|
10
|
+
wired automatically).
|
|
11
|
+
|
|
12
|
+
## Stack
|
|
13
|
+
|
|
14
|
+
Next.js 16 (App Router) · React 19 · `@avakit/react` · `@avakit/core` · viem · shadcn/ui · Foundry (contract) · avalanche-cli (local devnet)
|
|
15
|
+
|
|
16
|
+
## Architecture
|
|
17
|
+
|
|
18
|
+
- `contracts/src/AvaKitMessenger.sol` — one contract that both **sends** (via
|
|
19
|
+
`TeleporterMessenger.sendCrossChainMessage`) and **receives** (implements
|
|
20
|
+
`ITeleporterReceiver.receiveTeleporterMessage`). Deploy the same contract on
|
|
21
|
+
both chains.
|
|
22
|
+
- `lib/messenger-artifact.ts` — the compiled ABI + bytecode (browser deploy, no Foundry at runtime).
|
|
23
|
+
- `icm.config.json` — the two local chains (RPC URL + **hex blockchain ID**). Written by `pnpm devnet`.
|
|
24
|
+
- `lib/devnet.ts` — turns that config into AvaKit chains + helpers.
|
|
25
|
+
- `app/providers.tsx` — `<AvaKitProvider chains={[chain1, chain2]}>` with the injected wallet.
|
|
26
|
+
- `components/demo.tsx` — the **Devnet Studio**: live chain cards (block height + ICM status), deploy on both chains, send, and watch the message land on the other chain. When the devnet isn't up yet it shows a copy-command setup panel (`pnpm devnet`) — it never runs shell itself.
|
|
27
|
+
- `scripts/devnet.sh` — the one-command local ICM devnet.
|
|
28
|
+
|
|
29
|
+
## The flow
|
|
30
|
+
|
|
31
|
+
1. `pnpm devnet` — creates + deploys two L1s locally with ICM + relayer, writes `icm.config.json`.
|
|
32
|
+
2. Import the EWOQ dev key into your wallet (pre-funded on both chains).
|
|
33
|
+
3. `pnpm dev`, then in the app: deploy the messenger on chain1 and chain2.
|
|
34
|
+
4. Send: on the source chain, call `sendMessage(destinationBlockchainID, destinationAddress, text)`.
|
|
35
|
+
5. The relayer delivers it; the destination contract's `receiveTeleporterMessage` stores it. The UI polls `lastMessage()` and shows it arrive.
|
|
36
|
+
|
|
37
|
+
## The one detail that trips everyone up
|
|
38
|
+
|
|
39
|
+
`destinationBlockchainID` is the **bytes32 (Avalanche) blockchain ID in hex** — NOT
|
|
40
|
+
the EVM `chainId` (1001/1002). Get it from `avalanche blockchain describe <name>`
|
|
41
|
+
(the hex form). `lib/devnet.ts` exposes it via `blockchainIdOf(chain)`.
|
|
42
|
+
|
|
43
|
+
## Security
|
|
44
|
+
|
|
45
|
+
- The receiver **must** check `require(msg.sender == TeleporterMessenger)` — otherwise anyone can spoof a delivery. `AvaKitMessenger` does this.
|
|
46
|
+
- The TeleporterMessenger predeploy address (`0x253b…5fcf`) is version-pinned; on a real deployment, read it from `avalanche blockchain describe` rather than assuming.
|
|
47
|
+
|
|
48
|
+
## Editing the contract
|
|
49
|
+
|
|
50
|
+
After changing `contracts/src/AvaKitMessenger.sol`:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
cd contracts && forge build
|
|
54
|
+
# copy out/AvaKitMessenger.sol/AvaKitMessenger.json's abi + bytecode.object into lib/messenger-artifact.ts
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Rules
|
|
58
|
+
|
|
59
|
+
- UI uses **shadcn/ui only** (`@avakit/react` components are shadcn-styled). Black & white; dark/light via `next-themes`.
|
|
60
|
+
- Animations: **Framer Motion** or **GSAP** only.
|
|
61
|
+
- Never hardcode private keys. The EWOQ key is a **public dev key** — local devnet only, never a real network.
|
|
62
|
+
|
|
63
|
+
## Commands
|
|
64
|
+
|
|
65
|
+
- `pnpm devnet` — start the local 2-L1 ICM devnet (writes `icm.config.json`)
|
|
66
|
+
- `pnpm dev` — dev server (http://localhost:3000)
|
|
67
|
+
- `cd contracts && forge build` — recompile the contract
|
|
68
|
+
- `avalanche network stop | clean` — pause | wipe the devnet
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# __PROJECT_NAME__
|
|
2
|
+
|
|
3
|
+
An Avalanche **cross-chain messaging** dapp scaffolded with [AvaKit](https://github.com/mericcintosun/AvaKit). Send a string from one Avalanche L1 to another using **Interchain Messaging (ICM / Teleporter)** — against a local devnet you bring up with one command.
|
|
4
|
+
|
|
5
|
+
## Getting started
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# 1. start two local L1s with ICM + a relayer (writes icm.config.json)
|
|
9
|
+
pnpm devnet
|
|
10
|
+
|
|
11
|
+
# 2. run the app
|
|
12
|
+
pnpm dev # http://localhost:3000
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
`pnpm devnet` needs [avalanche-cli](https://build.avax.network/docs/tooling/avalanche-cli/get-avalanche-cli):
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
curl -sSfL https://raw.githubusercontent.com/ava-labs/avalanche-cli/main/scripts/install.sh | sh -s -- -b /usr/local/bin
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Then in your wallet (Core / MetaMask), **import the EWOQ dev key** (pre-funded on both local chains — public dev key, never use on a real network):
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
0x56289e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8027
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## The flow
|
|
28
|
+
|
|
29
|
+
1. **Deploy the messenger** on chain1 and chain2 (one click each — deploys from your browser).
|
|
30
|
+
2. **Send** a message from one chain to the other.
|
|
31
|
+
3. The relayer delivers it; the destination contract stores it. The app polls and shows it **arrive** on the other L1.
|
|
32
|
+
|
|
33
|
+
## How it works
|
|
34
|
+
|
|
35
|
+
- `contracts/src/AvaKitMessenger.sol` — one self-contained contract that both **sends** (`TeleporterMessenger.sendCrossChainMessage`) and **receives** (`ITeleporterReceiver.receiveTeleporterMessage`). Deployed on both chains.
|
|
36
|
+
- `scripts/devnet.sh` — creates two subnet-EVM L1s, deploys them locally, and lets avalanche-cli wire the TeleporterMessenger + relayer automatically. It then writes each chain's RPC URL and **hex blockchain ID** into `icm.config.json`.
|
|
37
|
+
- `lib/devnet.ts` — turns that config into AvaKit chains and exposes `blockchainIdOf(chain)`.
|
|
38
|
+
|
|
39
|
+
> **Gotcha:** ICM routes by the **bytes32 blockchain ID** (hex), not the EVM `chainId`. This template handles that for you — see `lib/devnet.ts`.
|
|
40
|
+
|
|
41
|
+
To change the contract:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
cd contracts && forge build
|
|
45
|
+
# copy out/AvaKitMessenger.sol/AvaKitMessenger.json (abi + bytecode.object) into lib/messenger-artifact.ts
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Reset the devnet
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
avalanche network stop # pause (keeps state)
|
|
52
|
+
avalanche network clean # wipe (new blockchain IDs — re-run pnpm devnet)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Stack
|
|
56
|
+
|
|
57
|
+
Next.js 16 · React 19 · `@avakit/react` · `@avakit/core` · viem · shadcn/ui · Foundry · avalanche-cli
|
|
58
|
+
|
|
59
|
+
MIT
|
|
@@ -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,22 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { injectedAdapter, type WalletAdapter } from "@avakit/core";
|
|
4
|
+
import { AvaKitProvider } from "@avakit/react";
|
|
5
|
+
import { ThemeProvider } from "next-themes";
|
|
6
|
+
import { type ReactNode, useMemo } from "react";
|
|
7
|
+
import { chain1, chain2 } from "@/lib/devnet";
|
|
8
|
+
|
|
9
|
+
export function Providers({ children }: { children: ReactNode }) {
|
|
10
|
+
// Local devnet: connect a browser wallet (Core / MetaMask) with the imported
|
|
11
|
+
// EWOQ dev key. Both local L1s are registered so the app can switch between
|
|
12
|
+
// them to deploy and send on each.
|
|
13
|
+
const adapters = useMemo<WalletAdapter[]>(() => [injectedAdapter()], []);
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<ThemeProvider attribute="class" defaultTheme="system" enableSystem disableTransitionOnChange>
|
|
17
|
+
<AvaKitProvider chains={[chain1, chain2]} adapters={adapters}>
|
|
18
|
+
{children}
|
|
19
|
+
</AvaKitProvider>
|
|
20
|
+
</ThemeProvider>
|
|
21
|
+
);
|
|
22
|
+
}
|