create-mn-app 0.3.8 ā 0.3.10
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/create-app.d.ts.map +1 -1
- package/dist/create-app.js +3 -15
- package/dist/create-app.js.map +1 -1
- package/dist/installers/proof-server-setup.d.ts.map +1 -1
- package/dist/installers/proof-server-setup.js +2 -2
- package/dist/installers/proof-server-setup.js.map +1 -1
- package/dist/test.js +2 -4
- package/dist/test.js.map +1 -1
- package/dist/utils/template-manager.d.ts.map +1 -1
- package/dist/utils/template-manager.js +0 -3
- package/dist/utils/template-manager.js.map +1 -1
- package/package.json +1 -1
- package/templates/hello-world/README.md.template +6 -15
- package/templates/hello-world/docker-compose.yml.template +8 -0
- package/templates/hello-world/package.json.template +24 -27
- package/templates/hello-world/src/check-balance.ts.template +121 -84
- package/templates/hello-world/src/cli.ts.template +225 -145
- package/templates/hello-world/src/deploy.ts.template +236 -182
- package/templates/hello-world/_env.template +0 -0
- package/templates/hello-world/nodemon.json.template +0 -7
- package/templates/hello-world/src/health-check.ts.template +0 -127
- package/templates/hello-world/src/providers/midnight-providers.ts.template +0 -50
- package/templates/hello-world/src/utils/environment.ts.template +0 -74
|
@@ -1,214 +1,268 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
} from
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import
|
|
17
|
-
import
|
|
18
|
-
import
|
|
19
|
-
import {
|
|
20
|
-
import
|
|
21
|
-
import
|
|
22
|
-
import {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Deploy {{projectName}} contract to Midnight Preprod network
|
|
3
|
+
*/
|
|
4
|
+
import { createInterface } from 'node:readline/promises';
|
|
5
|
+
import { stdin, stdout } from 'node:process';
|
|
6
|
+
import * as fs from 'node:fs';
|
|
7
|
+
import * as path from 'node:path';
|
|
8
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
9
|
+
import { WebSocket } from 'ws';
|
|
10
|
+
import * as Rx from 'rxjs';
|
|
11
|
+
import { Buffer } from 'buffer';
|
|
12
|
+
|
|
13
|
+
// Midnight SDK imports
|
|
14
|
+
import { deployContract } from '@midnight-ntwrk/midnight-js-contracts';
|
|
15
|
+
import { httpClientProofProvider } from '@midnight-ntwrk/midnight-js-http-client-proof-provider';
|
|
16
|
+
import { indexerPublicDataProvider } from '@midnight-ntwrk/midnight-js-indexer-public-data-provider';
|
|
17
|
+
import { levelPrivateStateProvider } from '@midnight-ntwrk/midnight-js-level-private-state-provider';
|
|
18
|
+
import { NodeZkConfigProvider } from '@midnight-ntwrk/midnight-js-node-zk-config-provider';
|
|
19
|
+
import { setNetworkId, getNetworkId } from '@midnight-ntwrk/midnight-js-network-id';
|
|
20
|
+
import { toHex } from '@midnight-ntwrk/midnight-js-utils';
|
|
21
|
+
import * as ledger from '@midnight-ntwrk/ledger-v7';
|
|
22
|
+
import { unshieldedToken } from '@midnight-ntwrk/ledger-v7';
|
|
23
|
+
import { WalletFacade } from '@midnight-ntwrk/wallet-sdk-facade';
|
|
24
|
+
import { DustWallet } from '@midnight-ntwrk/wallet-sdk-dust-wallet';
|
|
25
|
+
import { HDWallet, Roles, generateRandomSeed } from '@midnight-ntwrk/wallet-sdk-hd';
|
|
26
|
+
import { ShieldedWallet } from '@midnight-ntwrk/wallet-sdk-shielded';
|
|
27
|
+
import { createKeystore, InMemoryTransactionHistoryStorage, PublicKey, UnshieldedWallet } from '@midnight-ntwrk/wallet-sdk-unshielded-wallet';
|
|
28
|
+
import { CompiledContract } from '@midnight-ntwrk/compact-js';
|
|
29
|
+
|
|
30
|
+
// Enable WebSocket for GraphQL subscriptions
|
|
31
|
+
// @ts-expect-error Required for wallet sync
|
|
26
32
|
globalThis.WebSocket = WebSocket;
|
|
27
33
|
|
|
28
|
-
//
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const waitForFunds = (wallet: Wallet) =>
|
|
32
|
-
Rx.firstValueFrom(
|
|
33
|
-
wallet.state().pipe(
|
|
34
|
-
Rx.tap((state) => {
|
|
35
|
-
if (state.syncProgress) {
|
|
36
|
-
console.log(
|
|
37
|
-
`Sync progress: synced=${state.syncProgress.synced}, sourceGap=${state.syncProgress.lag.sourceGap}, applyGap=${state.syncProgress.lag.applyGap}`
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
}),
|
|
41
|
-
Rx.filter((state) => state.syncProgress?.synced === true),
|
|
42
|
-
Rx.map((s) => s.balances[nativeToken()] ?? 0n),
|
|
43
|
-
Rx.filter((balance) => balance > 0n),
|
|
44
|
-
Rx.tap((balance) => console.log(`Wallet funded with balance: ${balance}`))
|
|
45
|
-
)
|
|
46
|
-
);
|
|
34
|
+
// Set network to preprod
|
|
35
|
+
setNetworkId('preprod');
|
|
47
36
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
37
|
+
// Preprod network configuration
|
|
38
|
+
const CONFIG = {
|
|
39
|
+
indexer: 'https://indexer.preprod.midnight.network/api/v3/graphql',
|
|
40
|
+
indexerWS: 'wss://indexer.preprod.midnight.network/api/v3/graphql/ws',
|
|
41
|
+
node: 'https://rpc.preprod.midnight.network',
|
|
42
|
+
proofServer: 'http://127.0.0.1:6300',
|
|
43
|
+
faucetUrl: 'https://faucet.preprod.midnight.network/',
|
|
44
|
+
};
|
|
54
45
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
46
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
47
|
+
const zkConfigPath = path.resolve(__dirname, '..', 'contracts', 'managed', 'hello-world');
|
|
48
|
+
|
|
49
|
+
// Load compiled contract
|
|
50
|
+
const contractPath = path.join(zkConfigPath, 'contract', 'index.js');
|
|
58
51
|
|
|
59
|
-
|
|
52
|
+
// Check if contract is compiled
|
|
53
|
+
if (!fs.existsSync(contractPath)) {
|
|
54
|
+
console.error('\nā Contract not compiled! Run: npm run compile\n');
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const HelloWorld = await import(pathToFileURL(contractPath).href);
|
|
59
|
+
|
|
60
|
+
const compiledContract = CompiledContract.make('hello-world', HelloWorld.Contract).pipe(
|
|
61
|
+
CompiledContract.withVacantWitnesses,
|
|
62
|
+
CompiledContract.withCompiledFileAssets(zkConfigPath),
|
|
63
|
+
);
|
|
60
64
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
+
// āāā Wallet Functions āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
66
|
+
|
|
67
|
+
function deriveKeys(seed: string) {
|
|
68
|
+
const hdWallet = HDWallet.fromSeed(Buffer.from(seed, 'hex'));
|
|
69
|
+
if (hdWallet.type !== 'seedOk') throw new Error('Invalid seed');
|
|
70
|
+
const result = hdWallet.hdWallet.selectAccount(0).selectRoles([Roles.Zswap, Roles.NightExternal, Roles.Dust]).deriveKeysAt(0);
|
|
71
|
+
if (result.type !== 'keysDerived') throw new Error('Key derivation failed');
|
|
72
|
+
hdWallet.hdWallet.clear();
|
|
73
|
+
return result.keys;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function createWallet(seed: string) {
|
|
77
|
+
const keys = deriveKeys(seed);
|
|
78
|
+
const networkId = getNetworkId();
|
|
79
|
+
const shieldedSecretKeys = ledger.ZswapSecretKeys.fromSeed(keys[Roles.Zswap]);
|
|
80
|
+
const dustSecretKey = ledger.DustSecretKey.fromSeed(keys[Roles.Dust]);
|
|
81
|
+
const unshieldedKeystore = createKeystore(keys[Roles.NightExternal], networkId);
|
|
82
|
+
|
|
83
|
+
const walletConfig = {
|
|
84
|
+
networkId,
|
|
85
|
+
indexerClientConnection: { indexerHttpUrl: CONFIG.indexer, indexerWsUrl: CONFIG.indexerWS },
|
|
86
|
+
provingServerUrl: new URL(CONFIG.proofServer),
|
|
87
|
+
relayURL: new URL(CONFIG.node.replace(/^http/, 'ws')),
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const shieldedWallet = ShieldedWallet(walletConfig).startWithSecretKeys(shieldedSecretKeys);
|
|
91
|
+
const unshieldedWallet = UnshieldedWallet({
|
|
92
|
+
networkId,
|
|
93
|
+
indexerClientConnection: walletConfig.indexerClientConnection,
|
|
94
|
+
txHistoryStorage: new InMemoryTransactionHistoryStorage(),
|
|
95
|
+
}).startWithPublicKey(PublicKey.fromKeyStore(unshieldedKeystore));
|
|
96
|
+
const dustWallet = DustWallet({
|
|
97
|
+
...walletConfig,
|
|
98
|
+
costParameters: { additionalFeeOverhead: 300_000_000_000_000n, feeBlocksMargin: 5 },
|
|
99
|
+
}).startWithSecretKey(dustSecretKey, ledger.LedgerParameters.initialParameters().dust);
|
|
100
|
+
|
|
101
|
+
const wallet = new WalletFacade(shieldedWallet, unshieldedWallet, dustWallet);
|
|
102
|
+
await wallet.start(shieldedSecretKeys, dustSecretKey);
|
|
103
|
+
|
|
104
|
+
return { wallet, shieldedSecretKeys, dustSecretKey, unshieldedKeystore };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Workaround for wallet SDK signRecipe bug
|
|
108
|
+
function signTransactionIntents(tx: { intents?: Map<number, any> }, signFn: (payload: Uint8Array) => ledger.Signature, proofMarker: 'proof' | 'pre-proof'): void {
|
|
109
|
+
if (!tx.intents || tx.intents.size === 0) return;
|
|
110
|
+
for (const segment of tx.intents.keys()) {
|
|
111
|
+
const intent = tx.intents.get(segment);
|
|
112
|
+
if (!intent) continue;
|
|
113
|
+
const cloned = ledger.Intent.deserialize<ledger.SignatureEnabled, ledger.Proofish, ledger.PreBinding>('signature', proofMarker, 'pre-binding', intent.serialize());
|
|
114
|
+
const sigData = cloned.signatureData(segment);
|
|
115
|
+
const signature = signFn(sigData);
|
|
116
|
+
if (cloned.fallibleUnshieldedOffer) {
|
|
117
|
+
const sigs = cloned.fallibleUnshieldedOffer.inputs.map((_: any, i: number) => cloned.fallibleUnshieldedOffer!.signatures.at(i) ?? signature);
|
|
118
|
+
cloned.fallibleUnshieldedOffer = cloned.fallibleUnshieldedOffer.addSignatures(sigs);
|
|
65
119
|
}
|
|
120
|
+
if (cloned.guaranteedUnshieldedOffer) {
|
|
121
|
+
const sigs = cloned.guaranteedUnshieldedOffer.inputs.map((_: any, i: number) => cloned.guaranteedUnshieldedOffer!.signatures.at(i) ?? signature);
|
|
122
|
+
cloned.guaranteedUnshieldedOffer = cloned.guaranteedUnshieldedOffer.addSignatures(sigs);
|
|
123
|
+
}
|
|
124
|
+
tx.intents.set(segment, cloned);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async function createProviders(walletCtx: ReturnType<typeof createWallet> extends Promise<infer T> ? T : never) {
|
|
129
|
+
const state = await Rx.firstValueFrom(walletCtx.wallet.state().pipe(Rx.filter((s) => s.isSynced)));
|
|
130
|
+
|
|
131
|
+
const walletProvider = {
|
|
132
|
+
getCoinPublicKey: () => state.shielded.coinPublicKey.toHexString(),
|
|
133
|
+
getEncryptionPublicKey: () => state.shielded.encryptionPublicKey.toHexString(),
|
|
134
|
+
async balanceTx(tx: any, ttl?: Date) {
|
|
135
|
+
const recipe = await walletCtx.wallet.balanceUnboundTransaction(
|
|
136
|
+
tx,
|
|
137
|
+
{ shieldedSecretKeys: walletCtx.shieldedSecretKeys, dustSecretKey: walletCtx.dustSecretKey },
|
|
138
|
+
{ ttl: ttl ?? new Date(Date.now() + 30 * 60 * 1000) },
|
|
139
|
+
);
|
|
140
|
+
const signFn = (payload: Uint8Array) => walletCtx.unshieldedKeystore.signData(payload);
|
|
141
|
+
signTransactionIntents(recipe.baseTransaction, signFn, 'proof');
|
|
142
|
+
if (recipe.balancingTransaction) signTransactionIntents(recipe.balancingTransaction, signFn, 'pre-proof');
|
|
143
|
+
return walletCtx.wallet.finalizeRecipe(recipe);
|
|
144
|
+
},
|
|
145
|
+
submitTx: (tx: any) => walletCtx.wallet.submitTransaction(tx) as any,
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const zkConfigProvider = new NodeZkConfigProvider(zkConfigPath);
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
privateStateProvider: levelPrivateStateProvider({ privateStateStoreName: 'hello-world-state', walletProvider }),
|
|
152
|
+
publicDataProvider: indexerPublicDataProvider(CONFIG.indexer, CONFIG.indexerWS),
|
|
153
|
+
zkConfigProvider,
|
|
154
|
+
proofProvider: httpClientProofProvider(CONFIG.proofServer, zkConfigProvider),
|
|
155
|
+
walletProvider,
|
|
156
|
+
midnightProvider: walletProvider,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
66
159
|
|
|
67
|
-
|
|
160
|
+
// āāā Main Deploy Script āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
68
161
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
networkConfig.indexerWS,
|
|
74
|
-
networkConfig.proofServer,
|
|
75
|
-
networkConfig.node,
|
|
76
|
-
walletSeed,
|
|
77
|
-
getZswapNetworkId(),
|
|
78
|
-
"info"
|
|
79
|
-
);
|
|
162
|
+
async function main() {
|
|
163
|
+
console.log('\nāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā');
|
|
164
|
+
console.log('ā Deploy {{projectName}} to Midnight Preprod ā');
|
|
165
|
+
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n');
|
|
80
166
|
|
|
81
|
-
|
|
82
|
-
const state = await Rx.firstValueFrom(wallet.state());
|
|
167
|
+
const rl = createInterface({ input: stdin, output: stdout });
|
|
83
168
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
console.log();
|
|
169
|
+
try {
|
|
170
|
+
// 1. Wallet setup
|
|
171
|
+
console.log('āāā Step 1: Wallet Setup āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n');
|
|
172
|
+
const choice = await rl.question(' [1] Create new wallet\n [2] Restore from seed\n > ');
|
|
87
173
|
|
|
88
|
-
|
|
174
|
+
const seed = choice.trim() === '2'
|
|
175
|
+
? await rl.question('\n Enter your 64-character seed: ')
|
|
176
|
+
: toHex(Buffer.from(generateRandomSeed()));
|
|
177
|
+
|
|
178
|
+
if (choice.trim() !== '2') {
|
|
179
|
+
console.log(`\n ā ļø SAVE THIS SEED (you'll need it later):\n ${seed}\n`);
|
|
180
|
+
}
|
|
89
181
|
|
|
182
|
+
console.log(' Creating wallet...');
|
|
183
|
+
const walletCtx = await createWallet(seed.trim());
|
|
184
|
+
|
|
185
|
+
console.log(' Syncing with network...');
|
|
186
|
+
const state = await Rx.firstValueFrom(walletCtx.wallet.state().pipe(Rx.throttleTime(5000), Rx.filter((s) => s.isSynced)));
|
|
187
|
+
const address = walletCtx.unshieldedKeystore.getBech32Address();
|
|
188
|
+
const balance = state.unshielded.balances[unshieldedToken().raw] ?? 0n;
|
|
189
|
+
|
|
190
|
+
console.log(`\n Wallet Address: ${address}`);
|
|
191
|
+
console.log(` Balance: ${balance.toLocaleString()} tNight\n`);
|
|
192
|
+
|
|
193
|
+
// 2. Fund wallet if needed
|
|
90
194
|
if (balance === 0n) {
|
|
91
|
-
console.log(
|
|
92
|
-
console.log();
|
|
93
|
-
console.log(
|
|
94
|
-
console.log();
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
console.log(
|
|
105
|
-
console.log(chalk.gray("ā".repeat(60)));
|
|
106
|
-
console.log();
|
|
107
|
-
console.log(chalk.yellow.bold("š” Options while waiting:"));
|
|
108
|
-
console.log(chalk.white(" ⢠") + chalk.cyan("Let this script wait (it will auto-detect when funds arrive)"));
|
|
109
|
-
console.log(chalk.white(" ⢠") + chalk.cyan("OR press ") + chalk.yellow("Ctrl+C") + chalk.cyan(" to stop, then check balance with:"));
|
|
110
|
-
console.log(chalk.yellow.bold(" npm run check-balance"));
|
|
111
|
-
console.log(chalk.white(" ⢠") + chalk.cyan("Once funded, run: ") + chalk.yellow.bold("npm run deploy"));
|
|
112
|
-
console.log();
|
|
113
|
-
console.log(chalk.blue("ā³ Waiting to receive tokens..."));
|
|
114
|
-
balance = await waitForFunds(wallet);
|
|
195
|
+
console.log('āāā Step 2: Fund Your Wallet āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n');
|
|
196
|
+
console.log(` Visit: ${CONFIG.faucetUrl}`);
|
|
197
|
+
console.log(` Address: ${address}\n`);
|
|
198
|
+
console.log(' Waiting for funds...');
|
|
199
|
+
|
|
200
|
+
await Rx.firstValueFrom(
|
|
201
|
+
walletCtx.wallet.state().pipe(
|
|
202
|
+
Rx.throttleTime(10000),
|
|
203
|
+
Rx.filter((s) => s.isSynced),
|
|
204
|
+
Rx.map((s) => s.unshielded.balances[unshieldedToken().raw] ?? 0n),
|
|
205
|
+
Rx.filter((b) => b > 0n),
|
|
206
|
+
),
|
|
207
|
+
);
|
|
208
|
+
console.log(' Funds received!\n');
|
|
115
209
|
}
|
|
116
210
|
|
|
117
|
-
|
|
118
|
-
console.log();
|
|
119
|
-
|
|
120
|
-
// Load compiled contract files
|
|
121
|
-
console.log(chalk.gray("š¦ Loading contract..."));
|
|
122
|
-
const contractPath = path.join(process.cwd(), "contracts");
|
|
123
|
-
const contractModulePath = path.join(
|
|
124
|
-
contractPath,
|
|
125
|
-
"managed",
|
|
126
|
-
contractName,
|
|
127
|
-
"contract",
|
|
128
|
-
"index.cjs"
|
|
129
|
-
);
|
|
130
|
-
|
|
131
|
-
const HelloWorldModule = await import(contractModulePath);
|
|
132
|
-
const contractInstance = new HelloWorldModule.Contract({});
|
|
133
|
-
|
|
134
|
-
// Create wallet provider for transactions
|
|
135
|
-
const walletState = await Rx.firstValueFrom(wallet.state());
|
|
136
|
-
|
|
137
|
-
const walletProvider = {
|
|
138
|
-
coinPublicKey: walletState.coinPublicKey,
|
|
139
|
-
encryptionPublicKey: walletState.encryptionPublicKey,
|
|
140
|
-
balanceTx(tx: any, newCoins: any) {
|
|
141
|
-
return wallet
|
|
142
|
-
.balanceTransaction(
|
|
143
|
-
ZswapTransaction.deserialize(
|
|
144
|
-
tx.serialize(getLedgerNetworkId()),
|
|
145
|
-
getZswapNetworkId()
|
|
146
|
-
),
|
|
147
|
-
newCoins
|
|
148
|
-
)
|
|
149
|
-
.then((tx) => wallet.proveTransaction(tx))
|
|
150
|
-
.then((zswapTx) =>
|
|
151
|
-
Transaction.deserialize(
|
|
152
|
-
zswapTx.serialize(getZswapNetworkId()),
|
|
153
|
-
getLedgerNetworkId()
|
|
154
|
-
)
|
|
155
|
-
)
|
|
156
|
-
.then(createBalancedTx);
|
|
157
|
-
},
|
|
158
|
-
submitTx(tx: any) {
|
|
159
|
-
return wallet.submitTransaction(tx);
|
|
160
|
-
},
|
|
161
|
-
};
|
|
211
|
+
// 3. Register for DUST
|
|
212
|
+
console.log('āāā Step 3: DUST Token Setup āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n');
|
|
213
|
+
const dustState = await Rx.firstValueFrom(walletCtx.wallet.state().pipe(Rx.filter((s) => s.isSynced)));
|
|
162
214
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
215
|
+
if (dustState.dust.walletBalance(new Date()) === 0n) {
|
|
216
|
+
const nightUtxos = dustState.unshielded.availableCoins.filter((c: any) => !c.meta?.registeredForDustGeneration);
|
|
217
|
+
if (nightUtxos.length > 0) {
|
|
218
|
+
console.log(' Registering for DUST generation...');
|
|
219
|
+
const recipe = await walletCtx.wallet.registerNightUtxosForDustGeneration(
|
|
220
|
+
nightUtxos,
|
|
221
|
+
walletCtx.unshieldedKeystore.getPublicKey(),
|
|
222
|
+
(payload) => walletCtx.unshieldedKeystore.signData(payload),
|
|
223
|
+
);
|
|
224
|
+
await walletCtx.wallet.submitTransaction(await walletCtx.wallet.finalizeRecipe(recipe));
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
console.log(' Waiting for DUST tokens...');
|
|
228
|
+
await Rx.firstValueFrom(
|
|
229
|
+
walletCtx.wallet.state().pipe(Rx.throttleTime(5000), Rx.filter((s) => s.isSynced), Rx.filter((s) => s.dust.walletBalance(new Date()) > 0n)),
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
console.log(' DUST tokens ready!\n');
|
|
170
233
|
|
|
171
|
-
// Deploy contract
|
|
172
|
-
console.log(
|
|
173
|
-
console.log();
|
|
234
|
+
// 4. Deploy contract
|
|
235
|
+
console.log('āāā Step 4: Deploy Contract āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n');
|
|
236
|
+
console.log(' Setting up providers...');
|
|
237
|
+
const providers = await createProviders(walletCtx);
|
|
174
238
|
|
|
239
|
+
console.log(' Deploying contract (this may take 30-60 seconds)...\n');
|
|
175
240
|
const deployed = await deployContract(providers, {
|
|
176
|
-
|
|
177
|
-
privateStateId:
|
|
241
|
+
compiledContract,
|
|
242
|
+
privateStateId: 'helloWorldState',
|
|
178
243
|
initialPrivateState: {},
|
|
179
244
|
});
|
|
180
245
|
|
|
181
246
|
const contractAddress = deployed.deployTxData.public.contractAddress;
|
|
247
|
+
console.log(' ā
Contract deployed successfully!\n');
|
|
248
|
+
console.log(` Contract Address: ${contractAddress}\n`);
|
|
182
249
|
|
|
183
|
-
// Save deployment
|
|
184
|
-
|
|
185
|
-
console.log(chalk.green.bold("ā".repeat(60)));
|
|
186
|
-
console.log(chalk.green.bold("š CONTRACT DEPLOYED SUCCESSFULLY!"));
|
|
187
|
-
console.log(chalk.green.bold("ā".repeat(60)));
|
|
188
|
-
console.log();
|
|
189
|
-
console.log(chalk.cyan.bold("š Contract Address:"));
|
|
190
|
-
console.log(chalk.white(` ${contractAddress}`));
|
|
191
|
-
console.log();
|
|
192
|
-
|
|
193
|
-
const info = {
|
|
250
|
+
// 5. Save deployment info
|
|
251
|
+
const deploymentInfo = {
|
|
194
252
|
contractAddress,
|
|
253
|
+
seed,
|
|
254
|
+
network: 'preprod',
|
|
195
255
|
deployedAt: new Date().toISOString(),
|
|
196
|
-
network: networkConfig.name,
|
|
197
|
-
contractName,
|
|
198
256
|
};
|
|
199
257
|
|
|
200
|
-
fs.writeFileSync(
|
|
201
|
-
console.log(
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
console.log(chalk.red.bold("ā Deployment Failed:"));
|
|
209
|
-
console.error(chalk.red(error instanceof Error ? error.message : String(error)));
|
|
210
|
-
console.log();
|
|
211
|
-
process.exit(1);
|
|
258
|
+
fs.writeFileSync('deployment.json', JSON.stringify(deploymentInfo, null, 2));
|
|
259
|
+
console.log(' Saved to deployment.json\n');
|
|
260
|
+
|
|
261
|
+
await walletCtx.wallet.stop();
|
|
262
|
+
console.log('āāā Deployment Complete! āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n');
|
|
263
|
+
console.log(' Next: Run `npm run cli` to interact with your contract.\n');
|
|
264
|
+
} finally {
|
|
265
|
+
rl.close();
|
|
212
266
|
}
|
|
213
267
|
}
|
|
214
268
|
|
|
File without changes
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { execSync } from "child_process";
|
|
3
|
-
import fs from "fs";
|
|
4
|
-
import chalk from "chalk";
|
|
5
|
-
|
|
6
|
-
interface HealthCheck {
|
|
7
|
-
name: string;
|
|
8
|
-
check: () => Promise<boolean>;
|
|
9
|
-
message: string;
|
|
10
|
-
error?: string;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const checks: HealthCheck[] = [
|
|
14
|
-
{
|
|
15
|
-
name: "Node.js Version",
|
|
16
|
-
message: "Checking Node.js version (>= 22.0.0)...",
|
|
17
|
-
check: async () => {
|
|
18
|
-
const version = process.version;
|
|
19
|
-
const major = parseInt(version.slice(1).split(".")[0]);
|
|
20
|
-
return major >= 22;
|
|
21
|
-
},
|
|
22
|
-
error: "Node.js 22 or higher is required. Please upgrade Node.js.",
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
name: "Docker",
|
|
26
|
-
message: "Checking Docker availability...",
|
|
27
|
-
check: async () => {
|
|
28
|
-
try {
|
|
29
|
-
execSync("docker --version", { stdio: "ignore" });
|
|
30
|
-
return true;
|
|
31
|
-
} catch {
|
|
32
|
-
return false;
|
|
33
|
-
}
|
|
34
|
-
},
|
|
35
|
-
error: "Docker is not installed or not running. Install Docker to use the proof server.",
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
name: "Environment File",
|
|
39
|
-
message: "Checking .env configuration...",
|
|
40
|
-
check: async () => {
|
|
41
|
-
return fs.existsSync(".env");
|
|
42
|
-
},
|
|
43
|
-
error: ".env file not found. Run 'npm run setup' first.",
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
name: "Wallet Seed",
|
|
47
|
-
message: "Checking wallet configuration...",
|
|
48
|
-
check: async () => {
|
|
49
|
-
if (!fs.existsSync(".env")) return false;
|
|
50
|
-
const envContent = fs.readFileSync(".env", "utf-8");
|
|
51
|
-
return envContent.includes("WALLET_SEED=");
|
|
52
|
-
},
|
|
53
|
-
error: "WALLET_SEED not found in .env. Run 'npm run setup' to generate a wallet.",
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
name: "Dependencies",
|
|
57
|
-
message: "Checking node_modules...",
|
|
58
|
-
check: async () => {
|
|
59
|
-
return fs.existsSync("node_modules");
|
|
60
|
-
},
|
|
61
|
-
error: "Dependencies not installed. Run 'npm install' first.",
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
name: "Contract Compilation",
|
|
65
|
-
message: "Checking compiled contracts...",
|
|
66
|
-
check: async () => {
|
|
67
|
-
return fs.existsSync("dist") || fs.existsSync("build");
|
|
68
|
-
},
|
|
69
|
-
error: "Project not built. Run 'npm run compile' and 'npm run build'.",
|
|
70
|
-
},
|
|
71
|
-
];
|
|
72
|
-
|
|
73
|
-
async function runHealthCheck() {
|
|
74
|
-
console.log(chalk.blue.bold("\nš„ Midnight App Health Check\n"));
|
|
75
|
-
|
|
76
|
-
let passed = 0;
|
|
77
|
-
let failed = 0;
|
|
78
|
-
|
|
79
|
-
for (const check of checks) {
|
|
80
|
-
process.stdout.write(chalk.gray(`ā³ ${check.message} `));
|
|
81
|
-
|
|
82
|
-
try {
|
|
83
|
-
const result = await check.check();
|
|
84
|
-
|
|
85
|
-
if (result) {
|
|
86
|
-
console.log(chalk.green("ā Passed"));
|
|
87
|
-
passed++;
|
|
88
|
-
} else {
|
|
89
|
-
console.log(chalk.red("ā Failed"));
|
|
90
|
-
if (check.error) {
|
|
91
|
-
console.log(chalk.yellow(` ${check.error}`));
|
|
92
|
-
}
|
|
93
|
-
failed++;
|
|
94
|
-
}
|
|
95
|
-
} catch (error) {
|
|
96
|
-
console.log(chalk.red("ā Error"));
|
|
97
|
-
console.log(chalk.yellow(` ${check.error || "Unknown error"}`));
|
|
98
|
-
failed++;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
console.log();
|
|
103
|
-
console.log(chalk.bold("ā".repeat(60)));
|
|
104
|
-
console.log(
|
|
105
|
-
chalk.bold(
|
|
106
|
-
`Results: ${chalk.green(`${passed} passed`)} | ${chalk.red(`${failed} failed`)}`
|
|
107
|
-
)
|
|
108
|
-
);
|
|
109
|
-
console.log(chalk.bold("ā".repeat(60)));
|
|
110
|
-
|
|
111
|
-
if (failed === 0) {
|
|
112
|
-
console.log(chalk.green.bold("\nā All checks passed! Your environment is ready.\n"));
|
|
113
|
-
process.exit(0);
|
|
114
|
-
} else {
|
|
115
|
-
console.log(
|
|
116
|
-
chalk.yellow.bold(
|
|
117
|
-
"\nā Some checks failed. Please fix the issues above before continuing.\n"
|
|
118
|
-
)
|
|
119
|
-
);
|
|
120
|
-
process.exit(1);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
runHealthCheck().catch((error) => {
|
|
125
|
-
console.error(chalk.red("\nā Health check failed:"), error.message);
|
|
126
|
-
process.exit(1);
|
|
127
|
-
});
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import path from "path";
|
|
2
|
-
import { levelPrivateStateProvider } from "@midnight-ntwrk/midnight-js-level-private-state-provider";
|
|
3
|
-
import { indexerPublicDataProvider } from "@midnight-ntwrk/midnight-js-indexer-public-data-provider";
|
|
4
|
-
import { NodeZkConfigProvider } from "@midnight-ntwrk/midnight-js-node-zk-config-provider";
|
|
5
|
-
import { httpClientProofProvider } from "@midnight-ntwrk/midnight-js-http-client-proof-provider";
|
|
6
|
-
|
|
7
|
-
import { NetworkId } from "@midnight-ntwrk/midnight-js-network-id";
|
|
8
|
-
|
|
9
|
-
export interface NetworkConfig {
|
|
10
|
-
indexer: string;
|
|
11
|
-
indexerWS: string;
|
|
12
|
-
node: string;
|
|
13
|
-
proofServer: string;
|
|
14
|
-
name: string;
|
|
15
|
-
faucetUrl: string;
|
|
16
|
-
networkId: NetworkId;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface ProviderConfig {
|
|
20
|
-
contractName: string;
|
|
21
|
-
walletProvider: any;
|
|
22
|
-
networkConfig: NetworkConfig;
|
|
23
|
-
privateStateStoreName?: string;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export class MidnightProviders {
|
|
27
|
-
static create(config: ProviderConfig) {
|
|
28
|
-
const contractPath = path.join(process.cwd(), "contracts");
|
|
29
|
-
const zkConfigPath = path.join(
|
|
30
|
-
contractPath,
|
|
31
|
-
"managed",
|
|
32
|
-
config.contractName
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
return {
|
|
36
|
-
privateStateProvider: levelPrivateStateProvider({
|
|
37
|
-
privateStateStoreName:
|
|
38
|
-
config.privateStateStoreName || `${config.contractName}-state`,
|
|
39
|
-
}),
|
|
40
|
-
publicDataProvider: indexerPublicDataProvider(
|
|
41
|
-
config.networkConfig.indexer,
|
|
42
|
-
config.networkConfig.indexerWS
|
|
43
|
-
),
|
|
44
|
-
zkConfigProvider: new NodeZkConfigProvider(zkConfigPath),
|
|
45
|
-
proofProvider: httpClientProofProvider(config.networkConfig.proofServer),
|
|
46
|
-
walletProvider: config.walletProvider,
|
|
47
|
-
midnightProvider: config.walletProvider,
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
}
|