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
|
@@ -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,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,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,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
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
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 } from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { scaffold } from \"./scaffold.js\";\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 return { targetDir: opts.targetDir, files };\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"]}
|