create-mn-app 0.3.12 → 0.3.14
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/README.md +46 -89
- package/package.json +1 -1
- package/templates/hello-world/src/deploy.ts.template +115 -13
package/README.md
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
# create-mn-app
|
|
2
2
|
|
|
3
|
-
Scaffold Midnight Network applications.
|
|
3
|
+
Scaffold Midnight Network applications on Preprod.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/create-mn-app)
|
|
6
6
|
[](https://www.npmjs.com/package/create-mn-app)
|
|
7
7
|
[](https://opensource.org/licenses/Apache-2.0)
|
|
8
8
|
[](https://nodejs.org/)
|
|
9
9
|
|
|
10
|
-
##
|
|
11
|
-
|
|
12
|
-
**Recommended (no install needed):**
|
|
10
|
+
## Quick Start
|
|
13
11
|
|
|
14
12
|
```bash
|
|
15
13
|
npx create-mn-app my-app
|
|
@@ -17,80 +15,48 @@ cd my-app
|
|
|
17
15
|
npm run setup
|
|
18
16
|
```
|
|
19
17
|
|
|
20
|
-
|
|
18
|
+
The `setup` command:
|
|
21
19
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
```
|
|
20
|
+
1. Starts proof server via Docker
|
|
21
|
+
2. Compiles the Compact contract
|
|
22
|
+
3. Deploys to Preprod (prompts for faucet funding)
|
|
26
23
|
|
|
27
|
-
>
|
|
24
|
+
> Fund your wallet at [faucet.preprod.midnight.network](https://faucet.preprod.midnight.network/)
|
|
28
25
|
|
|
29
26
|
## Why create-mn-app?
|
|
30
27
|
|
|
31
|
-
- **Zero Configuration
|
|
32
|
-
- **
|
|
33
|
-
- **
|
|
34
|
-
- **
|
|
35
|
-
- **
|
|
36
|
-
|
|
37
|
-
## Features
|
|
38
|
-
|
|
39
|
-
- **Interactive project setup** with template selection
|
|
40
|
-
- **Auto-detects package manager** (npm/yarn/pnpm/bun)
|
|
41
|
-
- **Smart dependency management**:
|
|
42
|
-
- Checks Node.js, Docker, and Compact compiler versions
|
|
43
|
-
- **Automatic Compact compiler updates** when version mismatch detected
|
|
44
|
-
- Prompts user before updating with clear explanations
|
|
45
|
-
- **TypeScript** with hot reloading
|
|
46
|
-
- **Pre-configured Compact contracts**
|
|
47
|
-
- **Secure wallet generation**
|
|
48
|
-
- **Environment health checks**
|
|
49
|
-
|
|
50
|
-
## Quick Start
|
|
51
|
-
|
|
52
|
-
### Interactive Mode
|
|
53
|
-
|
|
54
|
-
```bash
|
|
55
|
-
npx create-mn-app
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
### CLI Mode
|
|
59
|
-
|
|
60
|
-
```bash
|
|
61
|
-
npx create-mn-app my-app --template hello-world
|
|
62
|
-
cd my-app
|
|
63
|
-
npm run setup
|
|
64
|
-
```
|
|
28
|
+
- **Zero Configuration** - Start building immediately
|
|
29
|
+
- **Preprod Ready** - Deploys to Midnight Preprod network
|
|
30
|
+
- **SDK 3.0** - Uses latest Midnight wallet and contract SDKs
|
|
31
|
+
- **One Command Setup** - Single `npm run setup` handles everything
|
|
32
|
+
- **Auto-updates** - Built-in notifier for new versions
|
|
65
33
|
|
|
66
34
|
## Templates
|
|
67
35
|
|
|
68
36
|
### Hello World (Default)
|
|
69
37
|
|
|
70
|
-
Basic message storage contract demonstrating state management
|
|
38
|
+
Basic message storage contract demonstrating state management.
|
|
71
39
|
|
|
72
40
|
```bash
|
|
73
41
|
npx create-mn-app my-app
|
|
42
|
+
cd my-app
|
|
43
|
+
npm run setup # starts proof server, compiles, deploys
|
|
44
|
+
npm run cli # interact with deployed contract
|
|
74
45
|
```
|
|
75
46
|
|
|
76
|
-
### Counter
|
|
47
|
+
### Counter
|
|
77
48
|
|
|
78
|
-
|
|
49
|
+
Increment/decrement counter with zkProofs. Cloned from [midnightntwrk/example-counter](https://github.com/midnightntwrk/example-counter).
|
|
79
50
|
|
|
80
51
|
```bash
|
|
81
52
|
npx create-mn-app my-app --template counter
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
Cloned from [Midnight Network's example-counter](https://github.com/midnightntwrk/example-counter). Requires Node.js 22+, Docker, and Compact compiler.
|
|
85
|
-
|
|
86
|
-
After creation:
|
|
87
|
-
|
|
88
|
-
```bash
|
|
89
53
|
cd my-app
|
|
90
54
|
npm install
|
|
91
|
-
#
|
|
55
|
+
# follow displayed instructions
|
|
92
56
|
```
|
|
93
57
|
|
|
58
|
+
Requires Compact compiler - the CLI will check and offer to install it.
|
|
59
|
+
|
|
94
60
|
### Coming Soon
|
|
95
61
|
|
|
96
62
|
- Bulletin Board - Multi-user interactions with privacy patterns
|
|
@@ -99,53 +65,45 @@ npm install
|
|
|
99
65
|
|
|
100
66
|
## Requirements
|
|
101
67
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
- Version compatibility checked before project creation
|
|
108
|
-
- Manual installation: See [Compact releases](https://github.com/midnightntwrk/compact/releases/latest)
|
|
68
|
+
| Requirement | Version | Notes |
|
|
69
|
+
| ---------------- | ------- | -------------------------------------------- |
|
|
70
|
+
| Node.js | 22+ | Required for all templates |
|
|
71
|
+
| Docker | Latest | Runs proof server |
|
|
72
|
+
| Compact Compiler | 0.23.0+ | Counter template only (auto-install offered) |
|
|
109
73
|
|
|
110
74
|
## CLI Options
|
|
111
75
|
|
|
112
76
|
```bash
|
|
113
77
|
npx create-mn-app [project-name] [options]
|
|
114
|
-
|
|
115
|
-
Options:
|
|
116
|
-
-t, --template <name> Template: hello-world, counter
|
|
117
|
-
--use-npm Use npm
|
|
118
|
-
--use-yarn Use Yarn
|
|
119
|
-
--use-pnpm Use pnpm
|
|
120
|
-
--use-bun Use bun
|
|
121
|
-
--skip-install Skip dependency installation
|
|
122
|
-
--skip-git Skip git initialization
|
|
123
|
-
--verbose Show detailed output
|
|
124
|
-
-h, --help Help
|
|
125
|
-
-V, --version Version
|
|
126
78
|
```
|
|
127
79
|
|
|
80
|
+
| Option | Description |
|
|
81
|
+
| ------------------------- | ---------------------------------- |
|
|
82
|
+
| `-t, --template <name>` | Template: `hello-world`, `counter` |
|
|
83
|
+
| `--use-npm/yarn/pnpm/bun` | Force package manager |
|
|
84
|
+
| `--skip-install` | Skip dependency installation |
|
|
85
|
+
| `--skip-git` | Skip git initialization |
|
|
86
|
+
| `--verbose` | Show detailed output |
|
|
87
|
+
| `-h, --help` | Show help |
|
|
88
|
+
| `-V, --version` | Show version |
|
|
89
|
+
|
|
128
90
|
## Project Structure
|
|
129
91
|
|
|
130
92
|
```
|
|
131
|
-
|
|
132
|
-
├──
|
|
133
|
-
│ └──
|
|
93
|
+
my-app/
|
|
94
|
+
├── contracts/
|
|
95
|
+
│ └── hello-world.compact # Compact smart contract
|
|
134
96
|
├── src/
|
|
135
|
-
│ ├── cli.ts
|
|
136
|
-
│ ├──
|
|
137
|
-
│
|
|
138
|
-
|
|
139
|
-
├── templates/
|
|
140
|
-
│ └── hello-world/ # Bundled template
|
|
97
|
+
│ ├── cli.ts # Interact with deployed contract
|
|
98
|
+
│ ├── deploy.ts # Deploy contract to Preprod
|
|
99
|
+
│ └── check-balance.ts # Check wallet balance
|
|
100
|
+
├── docker-compose.yml # Proof server config
|
|
141
101
|
├── package.json
|
|
142
|
-
└──
|
|
102
|
+
└── deployment.json # Generated after deploy (contains wallet seed)
|
|
143
103
|
```
|
|
144
104
|
|
|
145
105
|
## Contributing
|
|
146
106
|
|
|
147
|
-
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
148
|
-
|
|
149
107
|
1. Fork the repository
|
|
150
108
|
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
151
109
|
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
|
@@ -156,10 +114,9 @@ Contributions are welcome! Please feel free to submit a Pull Request.
|
|
|
156
114
|
|
|
157
115
|
- [Midnight Docs](https://docs.midnight.network)
|
|
158
116
|
- [Discord Community](https://discord.com/invite/midnightnetwork)
|
|
159
|
-
- [GitHub](https://github.com/
|
|
117
|
+
- [GitHub](https://github.com/midnightntwrk/create-mn-app)
|
|
118
|
+
- [Preprod Faucet](https://faucet.preprod.midnight.network/)
|
|
160
119
|
|
|
161
120
|
## License
|
|
162
121
|
|
|
163
122
|
Apache-2.0 © 2025 Midnight Foundation
|
|
164
|
-
|
|
165
|
-
See [LICENSE](LICENSE) for more information.
|
package/package.json
CHANGED
|
@@ -167,16 +167,63 @@ async function main() {
|
|
|
167
167
|
const rl = createInterface({ input: stdin, output: stdout });
|
|
168
168
|
|
|
169
169
|
try {
|
|
170
|
+
// Check for existing deployment.json (previous attempt or completed deployment)
|
|
171
|
+
let existingSeed: string | undefined;
|
|
172
|
+
let existingContract: string | undefined;
|
|
173
|
+
|
|
174
|
+
if (fs.existsSync('deployment.json')) {
|
|
175
|
+
try {
|
|
176
|
+
const existing = JSON.parse(fs.readFileSync('deployment.json', 'utf-8'));
|
|
177
|
+
if (existing.seed) existingSeed = existing.seed;
|
|
178
|
+
if (existing.contractAddress) existingContract = existing.contractAddress;
|
|
179
|
+
} catch {
|
|
180
|
+
// Ignore parse errors
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// If already deployed, ask if they want to redeploy
|
|
185
|
+
if (existingContract) {
|
|
186
|
+
console.log('─── Existing Deployment Found ──────────────────────────────────\n');
|
|
187
|
+
console.log(` Contract: ${existingContract}`);
|
|
188
|
+
const redeploy = await rl.question('\n Deploy a new contract? [y/N] ');
|
|
189
|
+
if (redeploy.toLowerCase() !== 'y') {
|
|
190
|
+
console.log('\n Run `npm run cli` to interact with your existing contract.\n');
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
existingSeed = undefined; // Fresh deployment = fresh wallet
|
|
194
|
+
}
|
|
195
|
+
|
|
170
196
|
// 1. Wallet setup
|
|
171
197
|
console.log('─── Step 1: Wallet Setup ───────────────────────────────────────\n');
|
|
172
|
-
const choice = await rl.question(' [1] Create new wallet\n [2] Restore from seed\n > ');
|
|
173
|
-
|
|
174
|
-
const seed = choice.trim() === '2'
|
|
175
|
-
? await rl.question('\n Enter your 64-character seed: ')
|
|
176
|
-
: toHex(Buffer.from(generateRandomSeed()));
|
|
177
198
|
|
|
178
|
-
|
|
179
|
-
|
|
199
|
+
let seed: string;
|
|
200
|
+
|
|
201
|
+
if (existingSeed) {
|
|
202
|
+
// Resume from previous failed deployment
|
|
203
|
+
console.log(' Found saved seed from previous attempt.');
|
|
204
|
+
const useSaved = await rl.question(' Use saved wallet? [Y/n] ');
|
|
205
|
+
if (useSaved.toLowerCase() !== 'n') {
|
|
206
|
+
seed = existingSeed;
|
|
207
|
+
console.log(' Using saved wallet...\n');
|
|
208
|
+
} else {
|
|
209
|
+
const choice = await rl.question(' [1] Create new wallet\n [2] Restore from seed\n > ');
|
|
210
|
+
seed = choice.trim() === '2'
|
|
211
|
+
? await rl.question('\n Enter your 64-character seed: ')
|
|
212
|
+
: toHex(Buffer.from(generateRandomSeed()));
|
|
213
|
+
|
|
214
|
+
if (choice.trim() !== '2') {
|
|
215
|
+
console.log(`\n ⚠️ SAVE THIS SEED (you'll need it later):\n ${seed}\n`);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
} else {
|
|
219
|
+
const choice = await rl.question(' [1] Create new wallet\n [2] Restore from seed\n > ');
|
|
220
|
+
seed = choice.trim() === '2'
|
|
221
|
+
? await rl.question('\n Enter your 64-character seed: ')
|
|
222
|
+
: toHex(Buffer.from(generateRandomSeed()));
|
|
223
|
+
|
|
224
|
+
if (choice.trim() !== '2') {
|
|
225
|
+
console.log(`\n ⚠️ SAVE THIS SEED (you'll need it later):\n ${seed}\n`);
|
|
226
|
+
}
|
|
180
227
|
}
|
|
181
228
|
|
|
182
229
|
console.log(' Creating wallet...');
|
|
@@ -236,12 +283,67 @@ async function main() {
|
|
|
236
283
|
console.log(' Setting up providers...');
|
|
237
284
|
const providers = await createProviders(walletCtx);
|
|
238
285
|
|
|
239
|
-
console.log(' Deploying contract
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
286
|
+
console.log(' Deploying contract...\n');
|
|
287
|
+
|
|
288
|
+
const MAX_RETRIES = 5;
|
|
289
|
+
const RETRY_DELAY_MS = 30000; // 30 seconds between retries
|
|
290
|
+
|
|
291
|
+
let deployed: Awaited<ReturnType<typeof deployContract>> | undefined;
|
|
292
|
+
|
|
293
|
+
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
294
|
+
try {
|
|
295
|
+
deployed = await deployContract(providers, {
|
|
296
|
+
compiledContract,
|
|
297
|
+
privateStateId: 'helloWorldState',
|
|
298
|
+
initialPrivateState: {},
|
|
299
|
+
});
|
|
300
|
+
break; // Success - exit retry loop
|
|
301
|
+
} catch (err: any) {
|
|
302
|
+
const errMsg = err?.message || err?.toString() || '';
|
|
303
|
+
|
|
304
|
+
// Check if it's a DUST-related error
|
|
305
|
+
if (errMsg.includes('Not enough Dust') || errMsg.includes('Wallet.Transacting')) {
|
|
306
|
+
if (attempt < MAX_RETRIES) {
|
|
307
|
+
console.log(` ⏳ Waiting for more DUST to generate (attempt ${attempt}/${MAX_RETRIES})...`);
|
|
308
|
+
console.log(' DUST generates as blocks are produced (~30s per block)');
|
|
309
|
+
console.log(` Retrying in ${RETRY_DELAY_MS / 1000}s...\n`);
|
|
310
|
+
|
|
311
|
+
// Countdown display
|
|
312
|
+
for (let i = RETRY_DELAY_MS / 1000; i > 0; i -= 5) {
|
|
313
|
+
await new Promise((r) => setTimeout(r, 5000));
|
|
314
|
+
if (i > 5) process.stdout.write(`\r ${i - 5}s remaining... `);
|
|
315
|
+
}
|
|
316
|
+
process.stdout.write('\r \r');
|
|
317
|
+
} else {
|
|
318
|
+
// All retries exhausted
|
|
319
|
+
console.log(' ❌ Not enough DUST generated yet.\n');
|
|
320
|
+
console.log(' DUST is generated over time as blocks are produced.');
|
|
321
|
+
console.log(' Your wallet and funds are saved - just retry later:\n');
|
|
322
|
+
console.log(' $ npm run deploy\n');
|
|
323
|
+
|
|
324
|
+
// Save partial deployment info so user can resume
|
|
325
|
+
const partialInfo = {
|
|
326
|
+
seed,
|
|
327
|
+
network: 'preprod',
|
|
328
|
+
status: 'pending_dust',
|
|
329
|
+
lastAttempt: new Date().toISOString(),
|
|
330
|
+
};
|
|
331
|
+
fs.writeFileSync('deployment.json', JSON.stringify(partialInfo, null, 2));
|
|
332
|
+
console.log(' Seed saved to deployment.json for retry.\n');
|
|
333
|
+
|
|
334
|
+
await walletCtx.wallet.stop();
|
|
335
|
+
process.exit(1);
|
|
336
|
+
}
|
|
337
|
+
} else {
|
|
338
|
+
// Not a DUST error - rethrow
|
|
339
|
+
throw err;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (!deployed) {
|
|
345
|
+
throw new Error('Deployment failed after all retries');
|
|
346
|
+
}
|
|
245
347
|
|
|
246
348
|
const contractAddress = deployed.deployTxData.public.contractAddress;
|
|
247
349
|
console.log(' ✅ Contract deployed successfully!\n');
|