create-avalanche-app 0.1.5 → 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.
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # One-command local Interchain Token Transfer (ICTT) bridge.
4
+ #
5
+ # Spins up TWO Avalanche L1s (Subnet-EVM) with Interchain Messaging + a relayer,
6
+ # then deploys a full ICTT bridge across them: a demo ERC-20 + a Token Home on
7
+ # chain1, and a Token Remote on chain2, registered over ICM. Addresses land in
8
+ # bridge.config.json so the app can bridge tokens between the two chains.
9
+ #
10
+ # Requires avalanche-cli. Contract deploys use viem + the bundled ICTT artifacts
11
+ # (no Foundry, no on-machine Solidity compile) run through scripts/deploy-bridge.mjs.
12
+
13
+ set -uo pipefail
14
+
15
+ CHAIN1="chain1"; EVM1=1001; TOK1="TOK1"
16
+ CHAIN2="chain2"; EVM2=1002; TOK2="TOK2"
17
+ ROOT="$(cd "$(dirname "$0")/.." && pwd)"
18
+ CONFIG="$ROOT/bridge.config.json"
19
+
20
+ say() { printf "\033[1;37m▸\033[0m %s\n" "$1"; }
21
+ die() { printf "\033[1;31m✖\033[0m %s\n" "$1" >&2; exit 1; }
22
+
23
+ if ! command -v avalanche >/dev/null 2>&1; then
24
+ cat >&2 <<'EOF'
25
+ ✖ avalanche-cli not found. Install it:
26
+
27
+ curl -sSfL https://raw.githubusercontent.com/ava-labs/avalanche-cli/main/scripts/install.sh | sh -s -- -b /usr/local/bin
28
+
29
+ Then re-run: pnpm bridge
30
+ EOF
31
+ exit 1
32
+ fi
33
+
34
+ # --- 1. Create + deploy the two L1s (ICM + relayer on by default) -----------
35
+ create_l1() {
36
+ local name="$1" cid="$2" tok="$3"
37
+ say "Creating L1 '$name' (evm chainId $cid)…"
38
+ avalanche blockchain create "$name" \
39
+ --evm --latest --evm-chain-id "$cid" --evm-token "$tok" \
40
+ --test-defaults --sovereign=false --icm --force </dev/null >/dev/null 2>&1 \
41
+ || die "Failed to create '$name'. Try: avalanche blockchain delete $name"
42
+ }
43
+ create_l1 "$CHAIN1" "$EVM1" "$TOK1"
44
+ create_l1 "$CHAIN2" "$EVM2" "$TOK2"
45
+
46
+ say "Deploying '$CHAIN1' locally (this also starts the local network)…"
47
+ avalanche blockchain deploy "$CHAIN1" --local </dev/null || die "Deploy of '$CHAIN1' failed."
48
+ say "Deploying '$CHAIN2' locally…"
49
+ avalanche blockchain deploy "$CHAIN2" --local </dev/null || die "Deploy of '$CHAIN2' failed."
50
+
51
+ # --- 2. Discover each chain's RPC URL + hex blockchain ID -------------------
52
+ discover() {
53
+ local name="$1" out
54
+ out="$(avalanche blockchain describe "$name" 2>/dev/null)"
55
+ local rpc bid
56
+ rpc="$(printf '%s' "$out" | grep -oE 'http://127\.0\.0\.1:[0-9]+/ext/bc/[A-Za-z0-9]+/rpc' | head -1)"
57
+ bid="$(printf '%s' "$out" | grep -iE 'BlockchainID \(HEX\)' | grep -oiE '0x[0-9a-f]{64}' | head -1)"
58
+ printf '%s\t%s' "$rpc" "$bid"
59
+ }
60
+ say "Reading chain details…"
61
+ IFS=$'\t' read -r RPC1 BID1 < <(discover "$CHAIN1")
62
+ IFS=$'\t' read -r RPC2 BID2 < <(discover "$CHAIN2")
63
+ [ -n "$RPC1" ] && [ -n "$BID1" ] || die "Could not read '$CHAIN1' RPC/blockchain ID."
64
+ [ -n "$RPC2" ] && [ -n "$BID2" ] || die "Could not read '$CHAIN2' RPC/blockchain ID."
65
+
66
+ # --- 3. Write the partial config the deploy step reads ----------------------
67
+ cat > "$CONFIG" <<EOF
68
+ {
69
+ "configured": false,
70
+ "chain1": { "name": "$CHAIN1", "token": "$TOK1", "evmChainId": $EVM1, "rpcUrl": "$RPC1", "blockchainIdHex": "$BID1" },
71
+ "chain2": { "name": "$CHAIN2", "token": "$TOK2", "evmChainId": $EVM2, "rpcUrl": "$RPC2", "blockchainIdHex": "$BID2" },
72
+ "bridge": null
73
+ }
74
+ EOF
75
+
76
+ # --- 4. Deploy the ICTT bridge (demo token + home + remote + register) ------
77
+ say "Deploying the ICTT bridge (demo token, home, remote, registration)…"
78
+ command -v node >/dev/null 2>&1 || die "node is required to deploy the bridge contracts."
79
+ node "$ROOT/scripts/deploy-bridge.mjs" || die "Bridge deploy failed. Is the network healthy? Try: avalanche network status"
80
+
81
+ cat <<EOF
82
+
83
+ ✔ Local ICTT bridge is up.
84
+
85
+ Next:
86
+ 1. Import the EWOQ dev key into your wallet (Core / MetaMask), pre-funded on both chains:
87
+ 0x56289e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8027
88
+ (public dev key, local only)
89
+ 2. pnpm dev → http://localhost:3000
90
+ 3. Mint the demo token on $CHAIN1, then bridge it to $CHAIN2 and watch it arrive.
91
+
92
+ Reset:
93
+ avalanche network stop # pause (keeps state)
94
+ avalanche network clean # wipe (re-run pnpm bridge)
95
+ EOF
@@ -0,0 +1,107 @@
1
+ // Deploys the ICTT bridge onto the two local L1s that scripts/bridge.sh created.
2
+ //
3
+ // On the home chain: a demo ERC-20, a TeleporterRegistry, and an ERC20TokenHome.
4
+ // On the remote chain: a TeleporterRegistry and an ERC20TokenRemote, then it
5
+ // calls registerWithHome so the remote registers with the home over ICM.
6
+ // All addresses are written back into bridge.config.json for the app to read.
7
+ //
8
+ // Uses viem + the embedded ICTT artifacts (lib/ictt-artifacts.json). Runs with
9
+ // the public EWOQ dev key, which is pre-funded on every local chain.
10
+
11
+ import { readFileSync, writeFileSync } from "node:fs";
12
+ import { fileURLToPath } from "node:url";
13
+ import { createPublicClient, createWalletClient, http } from "viem";
14
+ import { privateKeyToAccount } from "viem/accounts";
15
+
16
+ const ROOT = fileURLToPath(new URL("..", import.meta.url));
17
+ const CONFIG = `${ROOT}/bridge.config.json`;
18
+ const A = JSON.parse(readFileSync(`${ROOT}/lib/ictt-artifacts.json`, "utf8"));
19
+ const cfg = JSON.parse(readFileSync(CONFIG, "utf8"));
20
+
21
+ // The TeleporterMessenger is the same fixed predeploy on every ICM-enabled L1.
22
+ const MESSENGER = "0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf";
23
+ const ZERO = "0x0000000000000000000000000000000000000000";
24
+ // EWOQ: avalanche-cli's public local dev key, pre-funded on every local chain.
25
+ const EWOQ = "0x56289e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8027";
26
+ const account = privateKeyToAccount(EWOQ);
27
+
28
+ function clients(c) {
29
+ const chain = {
30
+ id: c.evmChainId,
31
+ name: c.name,
32
+ nativeCurrency: { name: c.token, symbol: c.token, decimals: 18 },
33
+ rpcUrls: { default: { http: [c.rpcUrl] } },
34
+ };
35
+ return {
36
+ pub: createPublicClient({ chain, transport: http(c.rpcUrl) }),
37
+ wal: createWalletClient({ account, chain, transport: http(c.rpcUrl) }),
38
+ };
39
+ }
40
+
41
+ async function deploy(c, art, args) {
42
+ const hash = await c.wal.deployContract({ abi: art.abi, bytecode: art.bytecode, args, account });
43
+ const r = await c.pub.waitForTransactionReceipt({ hash, timeout: 120000, pollingInterval: 1000 });
44
+ if (r.status !== "success") throw new Error("deploy reverted");
45
+ return r.contractAddress;
46
+ }
47
+ async function tx(c, params, label) {
48
+ const hash = await c.wal.writeContract({ ...params, account });
49
+ const r = await c.pub.waitForTransactionReceipt({ hash, timeout: 120000, pollingInterval: 1000 });
50
+ console.log(` ${label}: ${hash} (${r.status})`);
51
+ if (r.status !== "success") throw new Error(`${label} reverted`);
52
+ }
53
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
54
+
55
+ async function main() {
56
+ const c1 = clients(cfg.chain1);
57
+ const c2 = clients(cfg.chain2);
58
+ const registryArgs = [[{ version: 1n, protocolAddress: MESSENGER }]];
59
+
60
+ console.log("Deploying demo ERC-20 on", cfg.chain1.name);
61
+ const demoToken = await deploy(c1, A.DemoToken, []);
62
+ await tx(c1, { address: demoToken, abi: A.DemoToken.abi, functionName: "mint" }, "mint 100 to deployer");
63
+
64
+ console.log("Deploying home side on", cfg.chain1.name);
65
+ const registry1 = await deploy(c1, A.TeleporterRegistry, registryArgs);
66
+ const home = await deploy(c1, A.ERC20TokenHome, [registry1, account.address, 1n, demoToken, 18]);
67
+
68
+ console.log("Deploying remote side on", cfg.chain2.name);
69
+ const registry2 = await deploy(c2, A.TeleporterRegistry, registryArgs);
70
+ const remote = await deploy(c2, A.ERC20TokenRemote, [
71
+ {
72
+ teleporterRegistryAddress: registry2,
73
+ teleporterManager: account.address,
74
+ minTeleporterVersion: 1n,
75
+ tokenHomeBlockchainID: cfg.chain1.blockchainIdHex,
76
+ tokenHomeAddress: home,
77
+ tokenHomeDecimals: 18,
78
+ },
79
+ `Bridged ${cfg.chain1.token}`,
80
+ `${cfg.chain1.token}.b`,
81
+ 18,
82
+ ]);
83
+
84
+ console.log("Registering remote with home over ICM");
85
+ await tx(
86
+ c2,
87
+ {
88
+ address: remote,
89
+ abi: A.ERC20TokenRemote.abi,
90
+ functionName: "registerWithHome",
91
+ args: [{ feeTokenAddress: ZERO, amount: 0n }],
92
+ },
93
+ "registerWithHome",
94
+ );
95
+ await sleep(8000); // let the relayer deliver the registration
96
+
97
+ cfg.configured = true;
98
+ cfg.bridge = { demoToken, registry1, home, registry2, remote };
99
+ writeFileSync(CONFIG, `${JSON.stringify(cfg, null, 2)}\n`);
100
+ console.log("\nWrote bridge.config.json:");
101
+ console.log(JSON.stringify(cfg.bridge, null, 2));
102
+ }
103
+
104
+ main().catch((e) => {
105
+ console.error(e instanceof Error ? e.message : e);
106
+ process.exit(1);
107
+ });
@@ -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
+ }