create-avalanche-app 0.1.0 → 0.1.1

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.
Files changed (30) hide show
  1. package/dist/index.js +1 -1
  2. package/dist/index.js.map +1 -1
  3. package/package.json +1 -1
  4. package/templates/icm-messenger/CLAUDE.md +68 -0
  5. package/templates/icm-messenger/README.md +59 -0
  6. package/templates/icm-messenger/app/globals.css +95 -0
  7. package/templates/icm-messenger/app/layout.tsx +23 -0
  8. package/templates/icm-messenger/app/page.tsx +5 -0
  9. package/templates/icm-messenger/app/providers.tsx +22 -0
  10. package/templates/icm-messenger/components/demo.tsx +412 -0
  11. package/templates/icm-messenger/contracts/cache/solidity-files-cache.json +1 -0
  12. package/templates/icm-messenger/contracts/foundry.toml +6 -0
  13. package/templates/icm-messenger/contracts/out/AvaKitMessenger.sol/AvaKitMessenger.json +1 -0
  14. package/templates/icm-messenger/contracts/out/AvaKitMessenger.sol/ITeleporterMessenger.json +1 -0
  15. package/templates/icm-messenger/contracts/out/AvaKitMessenger.sol/ITeleporterReceiver.json +1 -0
  16. package/templates/icm-messenger/contracts/out/build-info/790b55b96be74f23.json +1 -0
  17. package/templates/icm-messenger/contracts/src/AvaKitMessenger.sol +97 -0
  18. package/templates/icm-messenger/cursor/rules/avakit.mdc +32 -0
  19. package/templates/icm-messenger/env.example +6 -0
  20. package/templates/icm-messenger/gitignore +15 -0
  21. package/templates/icm-messenger/icm.config.json +6 -0
  22. package/templates/icm-messenger/lib/devnet.ts +58 -0
  23. package/templates/icm-messenger/lib/messenger-artifact.ts +184 -0
  24. package/templates/icm-messenger/llms.txt +39 -0
  25. package/templates/icm-messenger/manifest.json +6 -0
  26. package/templates/icm-messenger/next.config.ts +7 -0
  27. package/templates/icm-messenger/package.json +33 -0
  28. package/templates/icm-messenger/postcss.config.mjs +7 -0
  29. package/templates/icm-messenger/scripts/devnet.sh +106 -0
  30. package/templates/icm-messenger/tsconfig.json +23 -0
@@ -0,0 +1,6 @@
1
+ # The ICM messenger template runs against a LOCAL devnet and uses a browser
2
+ # wallet (Core / MetaMask). No environment variables are required.
3
+ #
4
+ # In your wallet, import the local EWOQ dev key (pre-funded on every local chain):
5
+ # 0x56289e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8027
6
+ # Never use this key on a real network — it is publicly known.
@@ -0,0 +1,15 @@
1
+ node_modules/
2
+ .next/
3
+ out/
4
+ *.tsbuildinfo
5
+ next-env.d.ts
6
+ .env
7
+ .env.*
8
+ !.env.example
9
+ .DS_Store
10
+ *.log
11
+
12
+ # Foundry
13
+ contracts/out/
14
+ contracts/cache/
15
+ contracts/broadcast/
@@ -0,0 +1,6 @@
1
+ {
2
+ "configured": false,
3
+ "teleporterMessenger": "0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf",
4
+ "chain1": { "name": "chain1", "token": "TOK1", "evmChainId": 1001, "rpcUrl": "", "blockchainIdHex": "" },
5
+ "chain2": { "name": "chain2", "token": "TOK2", "evmChainId": 1002, "rpcUrl": "", "blockchainIdHex": "" }
6
+ }
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Local ICM devnet configuration.
3
+ *
4
+ * `scripts/devnet.sh` spins up two Avalanche L1s with Interchain Messaging and a
5
+ * relayer, then writes the discovered RPC URLs and blockchain IDs into
6
+ * `icm.config.json`. This module turns that into AvaKit chains the app can use.
7
+ */
8
+
9
+ import { type AvaChain, defineChain } from "@avakit/core/chains";
10
+ import config from "../icm.config.json";
11
+
12
+ export interface IcmChainConfig {
13
+ name: string;
14
+ token: string;
15
+ /** EVM chain id (for the wallet / RPC). */
16
+ evmChainId: number;
17
+ rpcUrl: string;
18
+ /** The bytes32 (Avalanche) blockchain ID in hex — Teleporter's routing key. */
19
+ blockchainIdHex: string;
20
+ }
21
+
22
+ export interface IcmConfig {
23
+ configured: boolean;
24
+ /** TeleporterMessenger predeploy, the same address on every ICM-enabled chain. */
25
+ teleporterMessenger: string;
26
+ chain1: IcmChainConfig;
27
+ chain2: IcmChainConfig;
28
+ }
29
+
30
+ export const icm = config as IcmConfig;
31
+
32
+ /** True once `pnpm devnet` has filled in the two chains' RPC URLs. */
33
+ export const isConfigured = Boolean(
34
+ icm.configured && icm.chain1.rpcUrl && icm.chain2.rpcUrl,
35
+ );
36
+
37
+ function toAvaChain(c: IcmChainConfig): AvaChain {
38
+ return defineChain({
39
+ id: c.evmChainId,
40
+ name: c.name,
41
+ rpcUrl: c.rpcUrl || "http://127.0.0.1:9650",
42
+ explorerUrl: "",
43
+ nativeCurrency: { name: c.token, symbol: c.token, decimals: 18 },
44
+ testnet: true,
45
+ });
46
+ }
47
+
48
+ export const chain1 = toAvaChain(icm.chain1);
49
+ export const chain2 = toAvaChain(icm.chain2);
50
+
51
+ /** The two local chains, indexed the way the UI pairs them. */
52
+ export const localChains: [AvaChain, AvaChain] = [chain1, chain2];
53
+
54
+ /** Blockchain ID (bytes32 hex) for a given local chain — needed to send to it. */
55
+ export function blockchainIdOf(chain: AvaChain): `0x${string}` {
56
+ const hex = chain.id === icm.chain1.evmChainId ? icm.chain1.blockchainIdHex : icm.chain2.blockchainIdHex;
57
+ return hex as `0x${string}`;
58
+ }
@@ -0,0 +1,184 @@
1
+ // Auto-generated from contracts/src/AvaKitMessenger.sol via `forge build`.
2
+ // Re-generate after editing the contract: cd contracts && forge build, then
3
+ // copy abi + bytecode.object here. Lets the app deploy from the browser with
4
+ // no Foundry required at runtime.
5
+
6
+ import type { Abi, Hex } from "viem";
7
+
8
+ export const abi = [
9
+ {
10
+ "type": "function",
11
+ "name": "TELEPORTER",
12
+ "inputs": [],
13
+ "outputs": [
14
+ {
15
+ "name": "",
16
+ "type": "address",
17
+ "internalType": "contract ITeleporterMessenger"
18
+ }
19
+ ],
20
+ "stateMutability": "view"
21
+ },
22
+ {
23
+ "type": "function",
24
+ "name": "lastMessage",
25
+ "inputs": [],
26
+ "outputs": [
27
+ {
28
+ "name": "",
29
+ "type": "string",
30
+ "internalType": "string"
31
+ }
32
+ ],
33
+ "stateMutability": "view"
34
+ },
35
+ {
36
+ "type": "function",
37
+ "name": "lastOriginSender",
38
+ "inputs": [],
39
+ "outputs": [
40
+ {
41
+ "name": "",
42
+ "type": "address",
43
+ "internalType": "address"
44
+ }
45
+ ],
46
+ "stateMutability": "view"
47
+ },
48
+ {
49
+ "type": "function",
50
+ "name": "lastSourceBlockchainID",
51
+ "inputs": [],
52
+ "outputs": [
53
+ {
54
+ "name": "",
55
+ "type": "bytes32",
56
+ "internalType": "bytes32"
57
+ }
58
+ ],
59
+ "stateMutability": "view"
60
+ },
61
+ {
62
+ "type": "function",
63
+ "name": "messagesReceived",
64
+ "inputs": [],
65
+ "outputs": [
66
+ {
67
+ "name": "",
68
+ "type": "uint256",
69
+ "internalType": "uint256"
70
+ }
71
+ ],
72
+ "stateMutability": "view"
73
+ },
74
+ {
75
+ "type": "function",
76
+ "name": "receiveTeleporterMessage",
77
+ "inputs": [
78
+ {
79
+ "name": "sourceBlockchainID",
80
+ "type": "bytes32",
81
+ "internalType": "bytes32"
82
+ },
83
+ {
84
+ "name": "originSenderAddress",
85
+ "type": "address",
86
+ "internalType": "address"
87
+ },
88
+ {
89
+ "name": "message",
90
+ "type": "bytes",
91
+ "internalType": "bytes"
92
+ }
93
+ ],
94
+ "outputs": [],
95
+ "stateMutability": "nonpayable"
96
+ },
97
+ {
98
+ "type": "function",
99
+ "name": "sendMessage",
100
+ "inputs": [
101
+ {
102
+ "name": "destinationBlockchainID",
103
+ "type": "bytes32",
104
+ "internalType": "bytes32"
105
+ },
106
+ {
107
+ "name": "destinationAddress",
108
+ "type": "address",
109
+ "internalType": "address"
110
+ },
111
+ {
112
+ "name": "message",
113
+ "type": "string",
114
+ "internalType": "string"
115
+ }
116
+ ],
117
+ "outputs": [
118
+ {
119
+ "name": "messageID",
120
+ "type": "bytes32",
121
+ "internalType": "bytes32"
122
+ }
123
+ ],
124
+ "stateMutability": "nonpayable"
125
+ },
126
+ {
127
+ "type": "event",
128
+ "name": "MessageReceived",
129
+ "inputs": [
130
+ {
131
+ "name": "sourceBlockchainID",
132
+ "type": "bytes32",
133
+ "indexed": true,
134
+ "internalType": "bytes32"
135
+ },
136
+ {
137
+ "name": "originSender",
138
+ "type": "address",
139
+ "indexed": true,
140
+ "internalType": "address"
141
+ },
142
+ {
143
+ "name": "message",
144
+ "type": "string",
145
+ "indexed": false,
146
+ "internalType": "string"
147
+ }
148
+ ],
149
+ "anonymous": false
150
+ },
151
+ {
152
+ "type": "event",
153
+ "name": "MessageSent",
154
+ "inputs": [
155
+ {
156
+ "name": "messageID",
157
+ "type": "bytes32",
158
+ "indexed": true,
159
+ "internalType": "bytes32"
160
+ },
161
+ {
162
+ "name": "destinationBlockchainID",
163
+ "type": "bytes32",
164
+ "indexed": true,
165
+ "internalType": "bytes32"
166
+ },
167
+ {
168
+ "name": "destinationAddress",
169
+ "type": "address",
170
+ "indexed": false,
171
+ "internalType": "address"
172
+ },
173
+ {
174
+ "name": "message",
175
+ "type": "string",
176
+ "indexed": false,
177
+ "internalType": "string"
178
+ }
179
+ ],
180
+ "anonymous": false
181
+ }
182
+ ] as const satisfies Abi;
183
+
184
+ export const bytecode = "0x6080604052348015600e575f5ffd5b506108f08061001c5f395ff3fe608060405234801561000f575f5ffd5b506004361061007a575f3560e01c80635be63c71116100585780635be63c71146100e4578063813275bc146100ed578063c0207bb814610108578063c868efaa14610111575f5ffd5b8063103070051461007e5780632ed2e8e0146100ae57806332970710146100cf575b5f5ffd5b600254610091906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b6100c16100bc3660046104aa565b610126565b6040519081526020016100a5565b6100d76102a6565b6040516100a5919061052e565b6100c160035481565b61009173253b2784c75e510dd0ff1da844684a1ac0aa5fcf81565b6100c160015481565b61012461011f3660046104aa565b610331565b005b5f73253b2784c75e510dd0ff1da844684a1ac0aa5fcf6001600160a01b031663624488506040518060c00160405280888152602001876001600160a01b0316815260200160405180604001604052805f6001600160a01b031681526020015f81525081526020016203d09081526020015f67ffffffffffffffff8111156101af576101af610547565b6040519080825280602002602001820160405280156101d8578160200160208202803683370190505b50815260200186866040516020016101f1929190610583565b6040516020818303038152906040528152506040518263ffffffff1660e01b815260040161021f91906105e1565b6020604051808303815f875af115801561023b573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061025f9190610668565b905084817f4c6ebdf8689746d81e856c78d4049f7c9ae732786dd632398e24775ade8f62748686866040516102969392919061067f565b60405180910390a3949350505050565b5f80546102b2906106a3565b80601f01602080910402602001604051908101604052809291908181526020018280546102de906106a3565b80156103295780601f1061030057610100808354040283529160200191610329565b820191905f5260205f20905b81548152906001019060200180831161030c57829003601f168201915b505050505081565b3373253b2784c75e510dd0ff1da844684a1ac0aa5fcf146103ae5760405162461bcd60e51b815260206004820152602d60248201527f4176614b69744d657373656e6765723a2063616c6c6572206973206e6f74207460448201526c3432902a32b632b837b93a32b960991b606482015260840160405180910390fd5b5f6103bb828401846106db565b90505f6103c882826107da565b506001858155600280546001600160a01b0319166001600160a01b038716179055600380545f906103fa908490610895565b92505081905550836001600160a01b0316857fb668d57046ecd82ea44ba2a8fbfe10de34bec187561b8386394fed442e0de5468360405161043b919061052e565b60405180910390a35050505050565b80356001600160a01b0381168114610460575f5ffd5b919050565b5f5f83601f840112610475575f5ffd5b50813567ffffffffffffffff81111561048c575f5ffd5b6020830191508360208285010111156104a3575f5ffd5b9250929050565b5f5f5f5f606085870312156104bd575f5ffd5b843593506104cd6020860161044a565b9250604085013567ffffffffffffffff8111156104e8575f5ffd5b6104f487828801610465565b95989497509550505050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f6105406020830184610500565b9392505050565b634e487b7160e01b5f52604160045260245ffd5b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b602081525f61059660208301848661055b565b949350505050565b5f8151808452602084019350602083015f5b828110156105d75781516001600160a01b03168652602095860195909101906001016105b0565b5093949350505050565b602081528151602082015260018060a01b0360208301511660408201525f604083015160018060a01b0381511660608401526020810151608084015250606083015160a0830152608083015160e060c084015261064261010084018261059e565b905060a0840151601f198483030160e085015261065f8282610500565b95945050505050565b5f60208284031215610678575f5ffd5b5051919050565b6001600160a01b03841681526040602082018190525f9061065f908301848661055b565b600181811c908216806106b757607f821691505b6020821081036106d557634e487b7160e01b5f52602260045260245ffd5b50919050565b5f602082840312156106eb575f5ffd5b813567ffffffffffffffff811115610701575f5ffd5b8201601f81018413610711575f5ffd5b803567ffffffffffffffff81111561072b5761072b610547565b604051601f8201601f19908116603f0116810167ffffffffffffffff8111828210171561075a5761075a610547565b604052818152828201602001861015610771575f5ffd5b816020840160208301375f91810160200191909152949350505050565b601f8211156107d557805f5260205f20601f840160051c810160208510156107b35750805b601f840160051c820191505b818110156107d2575f81556001016107bf565b50505b505050565b815167ffffffffffffffff8111156107f4576107f4610547565b6108088161080284546106a3565b8461078e565b6020601f82116001811461083a575f83156108235750848201515b5f19600385901b1c1916600184901b1784556107d2565b5f84815260208120601f198516915b828110156108695787850151825560209485019460019092019101610849565b508482101561088657868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b808201808211156108b457634e487b7160e01b5f52601160045260245ffd5b9291505056fea2646970667358221220edd28d4270dcbf530c3f8f54e0b5659e1f054d971e767fac9a41dbc42de8ada264736f6c634300081c0033" as Hex;
@@ -0,0 +1,39 @@
1
+ # __PROJECT_NAME__
2
+
3
+ > An Avalanche cross-chain dapp scaffolded with AvaKit. Send a message between two Avalanche L1s using Interchain Messaging (ICM / Teleporter), against a one-command local devnet. Next.js + @avakit/react + shadcn/ui + Foundry + avalanche-cli.
4
+
5
+ ## Project map
6
+
7
+ - [contracts/src/AvaKitMessenger.sol](contracts/src/AvaKitMessenger.sol): one contract that both sends and receives ICM messages (implements ITeleporterReceiver).
8
+ - [lib/messenger-artifact.ts](lib/messenger-artifact.ts): compiled ABI + bytecode (browser deploy, no Foundry at runtime).
9
+ - [scripts/devnet.sh](scripts/devnet.sh): one command — two local L1s with ICM + relayer.
10
+ - [icm.config.json](icm.config.json): the two chains (RPC URL + hex blockchain ID); written by `pnpm devnet`.
11
+ - [lib/devnet.ts](lib/devnet.ts): config → AvaKit chains + `blockchainIdOf()`.
12
+ - [components/demo.tsx](components/demo.tsx): the Devnet Studio — live chain cards (block + ICM status), deploy on both chains, send, watch it arrive. Setup is a copy-command panel (never runs shell).
13
+ - [CLAUDE.md](CLAUDE.md): agent guide (ICM API, the blockchainID-vs-chainId gotcha, rules).
14
+
15
+ ## Key facts
16
+
17
+ - `destinationBlockchainID` is a **bytes32 hex blockchain ID**, NOT the EVM chainId. Use `blockchainIdOf(chain)`.
18
+ - TeleporterMessenger predeploy: `0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf` (same on every ICM chain).
19
+ - The receiver must `require(msg.sender == TeleporterMessenger)`.
20
+
21
+ ## Key APIs
22
+
23
+ - Send: `AvaKitMessenger.sendMessage(destinationBlockchainID, destinationAddress, text)`
24
+ - Receive: `receiveTeleporterMessage(sourceBlockchainID, originSender, message)` (stores `lastMessage`)
25
+ - Deploy/read/write: `deployContract`, `getWalletClient`, `readContract`, `ensureChain` from `@avakit/core`
26
+ - Wallet: `<ConnectAvalanche />`, `useAvaChain().setChain` to switch between the two L1s
27
+
28
+ ## Run it
29
+
30
+ ```
31
+ pnpm devnet # start 2 local L1s + ICM + relayer
32
+ pnpm dev # http://localhost:3000
33
+ ```
34
+
35
+ ## External docs
36
+
37
+ - Avalanche ICM: https://build.avax.network/docs/cross-chain/teleporter/overview
38
+ - ICM on a local network: https://build.avax.network/docs/cross-chain/icm-contracts/icm-contracts-on-local-network
39
+ - Avalanche Builder Hub: https://build.avax.network/llms.txt
@@ -0,0 +1,6 @@
1
+ {
2
+ "id": "icm-messenger",
3
+ "title": "ICM cross-chain messenger",
4
+ "description": "Send a message between two Avalanche L1s with Interchain Messaging (local devnet)",
5
+ "contracts": true
6
+ }
@@ -0,0 +1,7 @@
1
+ import type { NextConfig } from "next";
2
+
3
+ const nextConfig: NextConfig = {
4
+ reactStrictMode: true,
5
+ };
6
+
7
+ export default nextConfig;
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "__PROJECT_NAME__",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "next dev",
8
+ "build": "next build",
9
+ "start": "next start",
10
+ "typecheck": "tsc --noEmit",
11
+ "devnet": "bash scripts/devnet.sh"
12
+ },
13
+ "dependencies": {
14
+ "@avakit/core": "__AVAKIT_DEP__",
15
+ "@avakit/react": "__AVAKIT_DEP__",
16
+ "lucide-react": "1.22.0",
17
+ "next": "16.2.9",
18
+ "next-themes": "0.4.6",
19
+ "react": "19.2.7",
20
+ "react-dom": "19.2.7",
21
+ "viem": "2.54.1"
22
+ },
23
+ "devDependencies": {
24
+ "@tailwindcss/postcss": "4.3.2",
25
+ "@types/node": "26.0.1",
26
+ "@types/react": "19.2.17",
27
+ "@types/react-dom": "19.2.3",
28
+ "postcss": "8.5.16",
29
+ "tailwindcss": "4.3.2",
30
+ "tw-animate-css": "1.4.0",
31
+ "typescript": "6.0.3"
32
+ }
33
+ }
@@ -0,0 +1,7 @@
1
+ const config = {
2
+ plugins: {
3
+ "@tailwindcss/postcss": {},
4
+ },
5
+ };
6
+
7
+ export default config;
@@ -0,0 +1,106 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # One-command local ICM devnet.
4
+ #
5
+ # Spins up TWO Avalanche L1s (subnet-EVM) with Interchain Messaging (Teleporter)
6
+ # and a relayer, all on a local network, then writes the discovered RPC URLs and
7
+ # blockchain IDs into icm.config.json so the app can talk to both chains.
8
+ #
9
+ # Requires avalanche-cli. It downloads avalanchego + subnet-EVM on first run.
10
+
11
+ set -uo pipefail
12
+
13
+ CHAIN1="chain1"
14
+ CHAIN2="chain2"
15
+ EVM_CHAIN_ID_1=1001
16
+ EVM_CHAIN_ID_2=1002
17
+ TOKEN_1="TOK1"
18
+ TOKEN_2="TOK2"
19
+ ROOT="$(cd "$(dirname "$0")/.." && pwd)"
20
+ CONFIG="$ROOT/icm.config.json"
21
+
22
+ say() { printf "\033[1;37m▸\033[0m %s\n" "$1"; }
23
+ die() { printf "\033[1;31m✖\033[0m %s\n" "$1" >&2; exit 1; }
24
+
25
+ # --- 0. avalanche-cli present? ---------------------------------------------
26
+ if ! command -v avalanche >/dev/null 2>&1; then
27
+ cat >&2 <<'EOF'
28
+ ✖ avalanche-cli not found. Install it (it bundles nothing else you need):
29
+
30
+ curl -sSfL https://raw.githubusercontent.com/ava-labs/avalanche-cli/main/scripts/install.sh | sh -s -- -b /usr/local/bin
31
+
32
+ # or: brew install ava-labs/tap/avalanche-cli
33
+
34
+ Then re-run: pnpm devnet
35
+ EOF
36
+ exit 1
37
+ fi
38
+
39
+ # --- 1. Create the two L1s (ICM + relayer are on by default) ----------------
40
+ create_l1() {
41
+ local name="$1" cid="$2" tok="$3"
42
+ say "Creating L1 '$name' (evm chainId $cid)…"
43
+ avalanche blockchain create "$name" \
44
+ --evm --latest \
45
+ --evm-chain-id "$cid" \
46
+ --evm-token "$tok" \
47
+ --test-defaults \
48
+ --sovereign=false \
49
+ --icm \
50
+ --force </dev/null >/dev/null 2>&1 || die "Failed to create '$name'. Try: avalanche blockchain delete $name"
51
+ }
52
+ create_l1 "$CHAIN1" "$EVM_CHAIN_ID_1" "$TOKEN_1"
53
+ create_l1 "$CHAIN2" "$EVM_CHAIN_ID_2" "$TOKEN_2"
54
+
55
+ # --- 2. Deploy them to the local network ------------------------------------
56
+ # The first deploy boots the local network (C-Chain included) and auto-deploys
57
+ # the TeleporterMessenger + configures the relayer for every chain pair.
58
+ say "Deploying '$CHAIN1' locally (this also starts the local network)…"
59
+ avalanche blockchain deploy "$CHAIN1" --local </dev/null || die "Deploy of '$CHAIN1' failed."
60
+ say "Deploying '$CHAIN2' locally…"
61
+ avalanche blockchain deploy "$CHAIN2" --local </dev/null || die "Deploy of '$CHAIN2' failed."
62
+
63
+ # --- 3. Discover RPC URL + hex blockchain ID for each chain -----------------
64
+ # `describe` prints the RPC URL and the blockchain ID in hex. We grep by shape
65
+ # (robust across CLI versions): the localhost RPC path, and a 0x + 64-hex id.
66
+ discover() {
67
+ local name="$1" out rpc bid
68
+ out="$(avalanche blockchain describe "$name" 2>/dev/null)"
69
+ rpc="$(printf '%s' "$out" | grep -oE 'http://127\.0\.0\.1:[0-9]+/ext/bc/[A-Za-z0-9]+/rpc' | head -1)"
70
+ # The bytes32 blockchain ID lives on the "BlockchainID (HEX)" row.
71
+ bid="$(printf '%s' "$out" | grep -iE 'BlockchainID \(HEX\)' | grep -oiE '0x[0-9a-f]{64}' | head -1)"
72
+ printf '%s\t%s' "$rpc" "$bid"
73
+ }
74
+ say "Reading chain details…"
75
+ IFS=$'\t' read -r RPC1 BID1 < <(discover "$CHAIN1")
76
+ IFS=$'\t' read -r RPC2 BID2 < <(discover "$CHAIN2")
77
+
78
+ [ -n "$RPC1" ] && [ -n "$BID1" ] || die "Could not read '$CHAIN1' RPC/blockchain ID. Run: avalanche blockchain describe $CHAIN1"
79
+ [ -n "$RPC2" ] && [ -n "$BID2" ] || die "Could not read '$CHAIN2' RPC/blockchain ID. Run: avalanche blockchain describe $CHAIN2"
80
+
81
+ # --- 4. Write icm.config.json -----------------------------------------------
82
+ cat > "$CONFIG" <<EOF
83
+ {
84
+ "configured": true,
85
+ "teleporterMessenger": "0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf",
86
+ "chain1": { "name": "$CHAIN1", "token": "$TOKEN_1", "evmChainId": $EVM_CHAIN_ID_1, "rpcUrl": "$RPC1", "blockchainIdHex": "$BID1" },
87
+ "chain2": { "name": "$CHAIN2", "token": "$TOKEN_2", "evmChainId": $EVM_CHAIN_ID_2, "rpcUrl": "$RPC2", "blockchainIdHex": "$BID2" }
88
+ }
89
+ EOF
90
+
91
+ say "Wrote icm.config.json:"
92
+ printf " %s → %s\n %s → %s\n" "$CHAIN1" "$RPC1" "$CHAIN2" "$RPC2"
93
+ cat <<EOF
94
+
95
+ ✔ Local ICM devnet is up.
96
+
97
+ Next:
98
+ 1. In your wallet (Core / MetaMask), import the EWOQ dev key (pre-funded on both chains):
99
+ 0x56289e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8027
100
+ 2. pnpm dev → http://localhost:3000
101
+ 3. Deploy the messenger on both chains, then send a message across.
102
+
103
+ Stop / reset the devnet:
104
+ avalanche network stop # pause (keeps state)
105
+ avalanche network clean # wipe (new blockchain IDs — re-run pnpm devnet)
106
+ EOF
@@ -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
+ }