create-avalanche-app 0.1.6 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js
CHANGED
|
@@ -10,7 +10,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.7";
|
|
14
14
|
var AVAKIT_DEP_VERSION = "0.1.2";
|
|
15
15
|
function parseArgs(argv) {
|
|
16
16
|
const opts = { yes: false, local: false, install: true };
|
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.6\";\n\n// The @avakit/* dependency version stamped into scaffolded apps' package.json\n// (as `^AVAKIT_DEP_VERSION`). Kept separate from the CLI's own VERSION: it must\n// resolve every published @avakit package, so it tracks the LOWEST current\n// @avakit version (core 0.1.2 · react 0.1.3 → ^0.1.2 satisfies both). Bump only\n// when the minimum @avakit version a fresh app needs goes up.\nconst AVAKIT_DEP_VERSION = \"0.1.2\";\n\ntype PackageManager = \"pnpm\" | \"npm\" | \"yarn\" | \"bun\";\n\ninterface Options {\n projectName?: string;\n template?: string;\n wallet?: WalletId;\n chain?: ChainId;\n pm?: PackageManager;\n yes: boolean;\n local: boolean;\n install: boolean;\n}\n\nfunction parseArgs(argv: string[]): Options {\n const opts: Options = { yes: false, local: false, install: true };\n const rest = argv.slice(2);\n for (let i = 0; i < rest.length; i++) {\n const arg = rest[i];\n const next = () => rest[++i];\n switch (arg) {\n case \"--yes\":\n case \"-y\":\n opts.yes = true;\n break;\n case \"--local\":\n opts.local = true;\n break;\n case \"--no-install\":\n opts.install = false;\n break;\n case \"--template\":\n case \"-t\":\n opts.template = next();\n break;\n case \"--wallet\":\n case \"-w\":\n opts.wallet = next() as WalletId;\n break;\n case \"--chain\":\n case \"-c\":\n opts.chain = next() as ChainId;\n break;\n case \"--pm\":\n opts.pm = next() as PackageManager;\n break;\n case \"--version\":\n case \"-v\":\n process.stdout.write(`${VERSION}\\n`);\n process.exit(0);\n break;\n case \"--help\":\n case \"-h\":\n printHelp();\n process.exit(0);\n break;\n default:\n if (arg && !arg.startsWith(\"-\") && !opts.projectName) {\n opts.projectName = arg;\n }\n }\n }\n return opts;\n}\n\nfunction printHelp(): void {\n process.stdout.write(\n [\n \"create-avalanche-app — scaffold a batteries-included Avalanche dapp\",\n \"\",\n \"Usage: npm create avalanche-app@latest [name] [options]\",\n \"\",\n \"Options:\",\n \" -t, --template <id> minimal | nft-mint | token-gated-app | erc20-token |\",\n \" icm-messenger | eerc-token | l1-launch | token-bridge\",\n \" -w, --wallet <id> web3auth | injected (default: web3auth)\",\n \" -c, --chain <id> fuji | c-chain (default: fuji)\",\n \" --pm <manager> pnpm | npm | yarn | bun\",\n \" -y, --yes skip prompts (non-interactive)\",\n \" --no-install do not install dependencies\",\n \" --local link @avakit/* via workspace (repo dev only)\",\n \" -v, --version print version\",\n \" -h, --help print this help\",\n \"\",\n ].join(\"\\n\"),\n );\n}\n\nfunction isValidName(name: string): boolean {\n return /^[a-z0-9][a-z0-9._-]*$/.test(name);\n}\n\nasync function resolveOptions(\n opts: Options,\n): Promise<Required<Omit<Options, \"yes\" | \"local\" | \"install\">>> {\n const templates = listTemplates();\n const templateIds = templates.map((t) => t.id);\n\n if (opts.yes) {\n const projectName = opts.projectName ?? \"my-avax-app\";\n return {\n projectName,\n template: opts.template && templateIds.includes(opts.template) ? opts.template : \"minimal\",\n wallet: opts.wallet ?? \"web3auth\",\n chain: opts.chain ?? \"fuji\",\n pm: opts.pm ?? \"pnpm\",\n };\n }\n\n p.intro(pc.bgCyan(pc.black(\" create-avalanche-app \")));\n\n const projectName =\n opts.projectName ??\n (await p.text({\n message: \"Project name?\",\n placeholder: \"my-avax-app\",\n defaultValue: \"my-avax-app\",\n validate: (v) => (!v || isValidName(v) ? undefined : \"Use lowercase letters, digits, - . _\"),\n }));\n if (p.isCancel(projectName)) cancel();\n\n const template =\n opts.template ??\n (await p.select({\n message: \"Template?\",\n options: templates.map((t) => ({ value: t.id, label: t.title, hint: t.description })),\n initialValue: \"minimal\",\n }));\n if (p.isCancel(template)) cancel();\n\n const wallet =\n opts.wallet ??\n (await p.select({\n message: \"Wallet provider?\",\n options: [\n { value: \"web3auth\", label: \"Social login (Web3Auth)\", hint: \"free, recommended\" },\n { value: \"injected\", label: \"Browser wallet (Core / MetaMask)\" },\n ],\n initialValue: \"web3auth\",\n }));\n if (p.isCancel(wallet)) cancel();\n\n const chain =\n opts.chain ??\n (await p.select({\n message: \"Target chain?\",\n options: [\n { value: \"fuji\", label: \"Avalanche Fuji (testnet)\", hint: \"recommended\" },\n { value: \"c-chain\", label: \"Avalanche C-Chain (mainnet)\" },\n ],\n initialValue: \"fuji\",\n }));\n if (p.isCancel(chain)) cancel();\n\n const pm =\n opts.pm ??\n (await p.select({\n message: \"Package manager?\",\n options: ([\"pnpm\", \"npm\", \"yarn\", \"bun\"] as const).map((m) => ({ value: m, label: m })),\n initialValue: \"pnpm\",\n }));\n if (p.isCancel(pm)) cancel();\n\n return {\n projectName: projectName as string,\n template: template as string,\n wallet: wallet as WalletId,\n chain: chain as ChainId,\n pm: pm as PackageManager,\n };\n}\n\nfunction cancel(): never {\n p.cancel(\"Cancelled.\");\n process.exit(0);\n}\n\nasync function main(): Promise<void> {\n const opts = parseArgs(process.argv);\n const resolved = await resolveOptions(opts);\n\n const targetDir = path.resolve(process.cwd(), resolved.projectName);\n if (existsSync(targetDir) && readdirSync(targetDir).length > 0) {\n process.stderr.write(\n pc.red(`\\nDirectory \"${resolved.projectName}\" already exists and is not empty.\\n`),\n );\n process.exit(1);\n }\n\n const spin = opts.yes ? null : p.spinner();\n spin?.start(\"Scaffolding project\");\n const { files } = await scaffoldApp({\n projectName: resolved.projectName,\n targetDir,\n template: resolved.template,\n wallet: resolved.wallet,\n chain: resolved.chain,\n local: opts.local,\n avakitVersion: AVAKIT_DEP_VERSION,\n });\n spin?.stop(`Created ${files.length} files`);\n\n if (opts.install) {\n const installSpin = opts.yes ? null : p.spinner();\n installSpin?.start(`Installing dependencies with ${resolved.pm}`);\n const result = spawnSync(resolved.pm, [\"install\"], {\n cwd: targetDir,\n stdio: opts.yes ? \"inherit\" : \"ignore\",\n });\n if (result.status === 0) {\n installSpin?.stop(\"Dependencies installed\");\n } else {\n installSpin?.stop(pc.yellow(\"Install skipped/failed — run it manually\"));\n }\n }\n\n const next = [\n `cd ${resolved.projectName}`,\n ...(opts.install ? [] : [`${resolved.pm} install`]),\n ...(resolved.wallet === \"web3auth\"\n ? [\"cp .env.example .env.local # add your Web3Auth client ID\"]\n : []),\n `${resolved.pm} run dev`,\n ];\n\n if (opts.yes) {\n process.stdout.write(`\\nDone. Next steps:\\n ${next.join(\"\\n \")}\\n`);\n } else {\n p.note(next.join(\"\\n\"), \"Next steps\");\n p.outro(pc.green(\"Your Avalanche dapp is ready.\"));\n }\n}\n\nmain().catch((error: unknown) => {\n process.stderr.write(\n `\\n${pc.red(\"Error:\")} ${error instanceof Error ? error.message : String(error)}\\n`,\n );\n process.exit(1);\n});\n"],"mappings":"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","names":["projectName","cancel"]}
|
|
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.7\";\n\n// The @avakit/* dependency version stamped into scaffolded apps' package.json\n// (as `^AVAKIT_DEP_VERSION`). Kept separate from the CLI's own VERSION: it must\n// resolve every published @avakit package, so it tracks the LOWEST current\n// @avakit version (core 0.1.2 · react 0.1.3 → ^0.1.2 satisfies both). Bump only\n// when the minimum @avakit version a fresh app needs goes up.\nconst AVAKIT_DEP_VERSION = \"0.1.2\";\n\ntype PackageManager = \"pnpm\" | \"npm\" | \"yarn\" | \"bun\";\n\ninterface Options {\n projectName?: string;\n template?: string;\n wallet?: WalletId;\n chain?: ChainId;\n pm?: PackageManager;\n yes: boolean;\n local: boolean;\n install: boolean;\n}\n\nfunction parseArgs(argv: string[]): Options {\n const opts: Options = { yes: false, local: false, install: true };\n const rest = argv.slice(2);\n for (let i = 0; i < rest.length; i++) {\n const arg = rest[i];\n const next = () => rest[++i];\n switch (arg) {\n case \"--yes\":\n case \"-y\":\n opts.yes = true;\n break;\n case \"--local\":\n opts.local = true;\n break;\n case \"--no-install\":\n opts.install = false;\n break;\n case \"--template\":\n case \"-t\":\n opts.template = next();\n break;\n case \"--wallet\":\n case \"-w\":\n opts.wallet = next() as WalletId;\n break;\n case \"--chain\":\n case \"-c\":\n opts.chain = next() as ChainId;\n break;\n case \"--pm\":\n opts.pm = next() as PackageManager;\n break;\n case \"--version\":\n case \"-v\":\n process.stdout.write(`${VERSION}\\n`);\n process.exit(0);\n break;\n case \"--help\":\n case \"-h\":\n printHelp();\n process.exit(0);\n break;\n default:\n if (arg && !arg.startsWith(\"-\") && !opts.projectName) {\n opts.projectName = arg;\n }\n }\n }\n return opts;\n}\n\nfunction printHelp(): void {\n process.stdout.write(\n [\n \"create-avalanche-app — scaffold a batteries-included Avalanche dapp\",\n \"\",\n \"Usage: npm create avalanche-app@latest [name] [options]\",\n \"\",\n \"Options:\",\n \" -t, --template <id> minimal | nft-mint | token-gated-app | erc20-token |\",\n \" icm-messenger | eerc-token | l1-launch | token-bridge\",\n \" -w, --wallet <id> web3auth | injected (default: web3auth)\",\n \" -c, --chain <id> fuji | c-chain (default: fuji)\",\n \" --pm <manager> pnpm | npm | yarn | bun\",\n \" -y, --yes skip prompts (non-interactive)\",\n \" --no-install do not install dependencies\",\n \" --local link @avakit/* via workspace (repo dev only)\",\n \" -v, --version print version\",\n \" -h, --help print this help\",\n \"\",\n ].join(\"\\n\"),\n );\n}\n\nfunction isValidName(name: string): boolean {\n return /^[a-z0-9][a-z0-9._-]*$/.test(name);\n}\n\nasync function resolveOptions(\n opts: Options,\n): Promise<Required<Omit<Options, \"yes\" | \"local\" | \"install\">>> {\n const templates = listTemplates();\n const templateIds = templates.map((t) => t.id);\n\n if (opts.yes) {\n const projectName = opts.projectName ?? \"my-avax-app\";\n return {\n projectName,\n template: opts.template && templateIds.includes(opts.template) ? opts.template : \"minimal\",\n wallet: opts.wallet ?? \"web3auth\",\n chain: opts.chain ?? \"fuji\",\n pm: opts.pm ?? \"pnpm\",\n };\n }\n\n p.intro(pc.bgCyan(pc.black(\" create-avalanche-app \")));\n\n const projectName =\n opts.projectName ??\n (await p.text({\n message: \"Project name?\",\n placeholder: \"my-avax-app\",\n defaultValue: \"my-avax-app\",\n validate: (v) => (!v || isValidName(v) ? undefined : \"Use lowercase letters, digits, - . _\"),\n }));\n if (p.isCancel(projectName)) cancel();\n\n const template =\n opts.template ??\n (await p.select({\n message: \"Template?\",\n options: templates.map((t) => ({ value: t.id, label: t.title, hint: t.description })),\n initialValue: \"minimal\",\n }));\n if (p.isCancel(template)) cancel();\n\n const wallet =\n opts.wallet ??\n (await p.select({\n message: \"Wallet provider?\",\n options: [\n { value: \"web3auth\", label: \"Social login (Web3Auth)\", hint: \"free, recommended\" },\n { value: \"injected\", label: \"Browser wallet (Core / MetaMask)\" },\n ],\n initialValue: \"web3auth\",\n }));\n if (p.isCancel(wallet)) cancel();\n\n const chain =\n opts.chain ??\n (await p.select({\n message: \"Target chain?\",\n options: [\n { value: \"fuji\", label: \"Avalanche Fuji (testnet)\", hint: \"recommended\" },\n { value: \"c-chain\", label: \"Avalanche C-Chain (mainnet)\" },\n ],\n initialValue: \"fuji\",\n }));\n if (p.isCancel(chain)) cancel();\n\n const pm =\n opts.pm ??\n (await p.select({\n message: \"Package manager?\",\n options: ([\"pnpm\", \"npm\", \"yarn\", \"bun\"] as const).map((m) => ({ value: m, label: m })),\n initialValue: \"pnpm\",\n }));\n if (p.isCancel(pm)) cancel();\n\n return {\n projectName: projectName as string,\n template: template as string,\n wallet: wallet as WalletId,\n chain: chain as ChainId,\n pm: pm as PackageManager,\n };\n}\n\nfunction cancel(): never {\n p.cancel(\"Cancelled.\");\n process.exit(0);\n}\n\nasync function main(): Promise<void> {\n const opts = parseArgs(process.argv);\n const resolved = await resolveOptions(opts);\n\n const targetDir = path.resolve(process.cwd(), resolved.projectName);\n if (existsSync(targetDir) && readdirSync(targetDir).length > 0) {\n process.stderr.write(\n pc.red(`\\nDirectory \"${resolved.projectName}\" already exists and is not empty.\\n`),\n );\n process.exit(1);\n }\n\n const spin = opts.yes ? null : p.spinner();\n spin?.start(\"Scaffolding project\");\n const { files } = await scaffoldApp({\n projectName: resolved.projectName,\n targetDir,\n template: resolved.template,\n wallet: resolved.wallet,\n chain: resolved.chain,\n local: opts.local,\n avakitVersion: AVAKIT_DEP_VERSION,\n });\n spin?.stop(`Created ${files.length} files`);\n\n if (opts.install) {\n const installSpin = opts.yes ? null : p.spinner();\n installSpin?.start(`Installing dependencies with ${resolved.pm}`);\n const result = spawnSync(resolved.pm, [\"install\"], {\n cwd: targetDir,\n stdio: opts.yes ? \"inherit\" : \"ignore\",\n });\n if (result.status === 0) {\n installSpin?.stop(\"Dependencies installed\");\n } else {\n installSpin?.stop(pc.yellow(\"Install skipped/failed — run it manually\"));\n }\n }\n\n const next = [\n `cd ${resolved.projectName}`,\n ...(opts.install ? [] : [`${resolved.pm} install`]),\n ...(resolved.wallet === \"web3auth\"\n ? [\"cp .env.example .env.local # add your Web3Auth client ID\"]\n : []),\n `${resolved.pm} run dev`,\n ];\n\n if (opts.yes) {\n process.stdout.write(`\\nDone. Next steps:\\n ${next.join(\"\\n \")}\\n`);\n } else {\n p.note(next.join(\"\\n\"), \"Next steps\");\n p.outro(pc.green(\"Your Avalanche dapp is ready.\"));\n }\n}\n\nmain().catch((error: unknown) => {\n process.stderr.write(\n `\\n${pc.red(\"Error:\")} ${error instanceof Error ? error.message : String(error)}\\n`,\n );\n process.exit(1);\n});\n"],"mappings":"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","names":["projectName","cancel"]}
|
package/package.json
CHANGED
|
@@ -70,6 +70,44 @@ cross-chain transfer to the P-Chain), (2) a **bootstrap validator** — your mac
|
|
|
70
70
|
It is a multi-step, multi-minute process, not one command. There is no automatic hosted explorer for
|
|
71
71
|
a custom Fuji L1 — this app's built-in explorer keeps working by pointing at your Fuji L1's RPC.
|
|
72
72
|
|
|
73
|
+
## AI guide: state-aware Fuji walkthrough
|
|
74
|
+
|
|
75
|
+
When the user asks for help getting their L1 onto Fuji, first figure out WHERE they are, then give
|
|
76
|
+
the ONE next command. Detect state like this:
|
|
77
|
+
|
|
78
|
+
1. **Do they have a funded key?** Run `avalanche key list --keys <name>` (or create one:
|
|
79
|
+
`avalanche key create <name>`). Check the key's **C-Chain** balance — if it's 0, the next step is
|
|
80
|
+
the faucet: send them to `https://build.avax.network/console/primary-network/faucet` for their key's
|
|
81
|
+
C-Chain address, and wait until the balance is non-zero. Budget ~1–2 test AVAX (validators pay a
|
|
82
|
+
continuous P-Chain fee, ~1 AVAX ≈ 1 month).
|
|
83
|
+
2. **Is the balance on the P-Chain?** The L1 deploy spends from the **P-Chain**, but the faucet funds
|
|
84
|
+
the **C-Chain**. If P-Chain balance is 0, next command:
|
|
85
|
+
`avalanche key transfer --key <name> --fuji --c-chain-sender --p-chain-receiver --amount <x>`
|
|
86
|
+
(the C/P selectors are the boolean flags `--c-chain-sender` / `--p-chain-receiver`, NOT
|
|
87
|
+
`--sender-blockchain c`).
|
|
88
|
+
3. **Is the blockchain config created?** If `~/.avalanche-cli/subnets/<name>` doesn't exist, create it:
|
|
89
|
+
`avalanche blockchain create <name> --evm --latest --evm-chain-id <id> --evm-token <TOK>
|
|
90
|
+
--proof-of-authority --validator-manager-owner <yourAddr> --proxy-contract-owner <yourAddr>
|
|
91
|
+
--production-defaults --force`.
|
|
92
|
+
4. **Deploy to Fuji.** `avalanche blockchain deploy <name> --fuji --key <name> --use-local-machine
|
|
93
|
+
--num-bootstrap-validators 1 --balance 0.1 --vmc-L1`. Notes for driving this non-interactively:
|
|
94
|
+
`--vmc-L1` pre-answers the "deploy Validator Manager into an external blockchain?" prompt; the final
|
|
95
|
+
"fund the relayer?" prompt is optional and can be skipped (the L1 still deploys — the relayer only
|
|
96
|
+
matters for cross-chain messaging). If it complains a **local deploy already exists**, the machine
|
|
97
|
+
has stale state — `avalanche network clean` (or pick a fresh `<name>`) and retry. The node
|
|
98
|
+
bootstraps to Fuji first (downloads a multi-hundred-MB archive, a few minutes) — that wait is
|
|
99
|
+
normal, not a hang.
|
|
100
|
+
5. **Verify it's live.** The deploy prints an `RPC Endpoint`. Point this app at it (set
|
|
101
|
+
`l1.config.json` → `network: "fuji"`, `rpcUrl`, `blockchainIdHex`). Confirm: `cast chain-id
|
|
102
|
+
--rpc-url <rpc>` returns your chain id, `cast block-number` advances, and the blockchain is listed
|
|
103
|
+
on Fuji's P-Chain via `platform.getBlockchains` on `https://api.avax-test.network/ext/bc/P`.
|
|
104
|
+
6. **Remind them of the ongoing cost:** the validator node must stay running, and its balance drains —
|
|
105
|
+
top it up (`avalanche blockchain addValidator` / increase-balance) or the L1 goes inactive. For an
|
|
106
|
+
always-on chain, run the node on a server, not a laptop.
|
|
107
|
+
|
|
108
|
+
Also available with the visual version of this flow: **AvaKit Studio** (`npx @avakit/studio`) has a
|
|
109
|
+
"Launch on Fuji" wizard that runs these exact steps with a balance poller and a live progress log.
|
|
110
|
+
|
|
73
111
|
## Editing the demo contract
|
|
74
112
|
|
|
75
113
|
After changing `contracts/src/AvaKitToken.sol`: `cd contracts && forge build`, then copy
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
useAvaAccount,
|
|
9
9
|
useAvaKit,
|
|
10
10
|
} from "@avakit/react";
|
|
11
|
-
import { Blocks, Check, Copy, Moon, Rocket, Sun } from "lucide-react";
|
|
11
|
+
import { Blocks, Check, Copy, ExternalLink, Moon, Rocket, Sun, Wallet } from "lucide-react";
|
|
12
12
|
import { useTheme } from "next-themes";
|
|
13
13
|
import { useCallback, useEffect, useState } from "react";
|
|
14
14
|
import { type Address, type Hash, formatEther, formatUnits } from "viem";
|
|
@@ -274,11 +274,109 @@ function Dashboard() {
|
|
|
274
274
|
</div>
|
|
275
275
|
</section>
|
|
276
276
|
|
|
277
|
+
<NextSteps hasToken={Boolean(token)} />
|
|
278
|
+
|
|
277
279
|
{error ? <p className="text-muted-foreground text-sm break-all">{error}</p> : null}
|
|
278
280
|
</>
|
|
279
281
|
);
|
|
280
282
|
}
|
|
281
283
|
|
|
284
|
+
// Guides the user through what to do once their L1 is live: add it to a wallet,
|
|
285
|
+
// get gas, deploy something, and (on Fuji) the realities of keeping it running.
|
|
286
|
+
function NextSteps({ hasToken }: { hasToken: boolean }) {
|
|
287
|
+
const { provider } = useAvaKit();
|
|
288
|
+
const [added, setAdded] = useState(false);
|
|
289
|
+
const isFuji = l1.network === "fuji";
|
|
290
|
+
|
|
291
|
+
const addToWallet = useCallback(async () => {
|
|
292
|
+
if (!provider) return;
|
|
293
|
+
try {
|
|
294
|
+
await provider.request({
|
|
295
|
+
method: "wallet_addEthereumChain",
|
|
296
|
+
params: [
|
|
297
|
+
{
|
|
298
|
+
chainId: `0x${chain.id.toString(16)}`,
|
|
299
|
+
chainName: chain.name,
|
|
300
|
+
rpcUrls: [chain.rpcUrl],
|
|
301
|
+
nativeCurrency: { name: l1.token, symbol: l1.token, decimals: 18 },
|
|
302
|
+
},
|
|
303
|
+
],
|
|
304
|
+
});
|
|
305
|
+
setAdded(true);
|
|
306
|
+
setTimeout(() => setAdded(false), 2000);
|
|
307
|
+
} catch {
|
|
308
|
+
// user rejected or wallet unavailable
|
|
309
|
+
}
|
|
310
|
+
}, [provider]);
|
|
311
|
+
|
|
312
|
+
return (
|
|
313
|
+
<section className="flex flex-col gap-3 rounded-xl border p-5">
|
|
314
|
+
<p className="text-muted-foreground text-xs uppercase tracking-wide">Next steps</p>
|
|
315
|
+
<div className="flex flex-col gap-2">
|
|
316
|
+
<StepRow n={1} done={added} label="Add this L1 to your wallet">
|
|
317
|
+
<Button size="sm" variant="outline" onClick={addToWallet}>
|
|
318
|
+
<Wallet className="size-4" />
|
|
319
|
+
{added ? "Added" : "Add network"}
|
|
320
|
+
</Button>
|
|
321
|
+
</StepRow>
|
|
322
|
+
|
|
323
|
+
<StepRow n={2} label={isFuji ? "Get gas — fund your wallet with test AVAX" : "Get gas — import the EWOQ dev key"}>
|
|
324
|
+
{isFuji ? (
|
|
325
|
+
<a
|
|
326
|
+
href="https://build.avax.network/console/primary-network/faucet"
|
|
327
|
+
target="_blank"
|
|
328
|
+
rel="noreferrer"
|
|
329
|
+
className="text-muted-foreground inline-flex items-center gap-1 text-xs underline underline-offset-4"
|
|
330
|
+
>
|
|
331
|
+
Fuji faucet <ExternalLink className="size-3" />
|
|
332
|
+
</a>
|
|
333
|
+
) : (
|
|
334
|
+
<span className="text-muted-foreground text-xs">
|
|
335
|
+
EWOQ key is pre-funded (printed by <span className="font-mono">pnpm l1</span>).
|
|
336
|
+
</span>
|
|
337
|
+
)}
|
|
338
|
+
</StepRow>
|
|
339
|
+
|
|
340
|
+
<StepRow n={3} done={hasToken} label="Deploy your first contract (the demo token above)" />
|
|
341
|
+
</div>
|
|
342
|
+
|
|
343
|
+
{isFuji ? (
|
|
344
|
+
<p className="text-muted-foreground border-t pt-3 text-xs">
|
|
345
|
+
⚠︎ Your L1 only produces blocks while its validator node stays running, and the
|
|
346
|
+
validator's balance drains over time (~1 AVAX ≈ 1 month) — top it up, or the chain goes
|
|
347
|
+
inactive. For an always-on L1, run the validator on a server rather than your laptop.
|
|
348
|
+
</p>
|
|
349
|
+
) : null}
|
|
350
|
+
</section>
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function StepRow({
|
|
355
|
+
n,
|
|
356
|
+
label,
|
|
357
|
+
done,
|
|
358
|
+
children,
|
|
359
|
+
}: {
|
|
360
|
+
n: number;
|
|
361
|
+
label: string;
|
|
362
|
+
done?: boolean;
|
|
363
|
+
children?: React.ReactNode;
|
|
364
|
+
}) {
|
|
365
|
+
return (
|
|
366
|
+
<div className="flex items-center justify-between gap-3">
|
|
367
|
+
<span className="flex items-center gap-2 text-sm">
|
|
368
|
+
<span
|
|
369
|
+
className={`flex size-5 items-center justify-center rounded-full border text-xs ${done ? "bg-foreground text-background" : "text-muted-foreground"}`}
|
|
370
|
+
>
|
|
371
|
+
{done ? <Check className="size-3" /> : n}
|
|
372
|
+
</span>
|
|
373
|
+
{label}
|
|
374
|
+
</span>
|
|
375
|
+
{children}
|
|
376
|
+
</div>
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
|
|
282
380
|
function ago(timestamp: bigint): string {
|
|
283
381
|
const secs = Math.max(0, Math.floor(Date.now() / 1000) - Number(timestamp));
|
|
284
382
|
if (secs < 60) return `${secs}s ago`;
|