create-mn-app 0.3.9 → 0.3.11

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.
@@ -1,98 +1,135 @@
1
- import "dotenv/config";
2
- import { WalletBuilder } from "@midnight-ntwrk/wallet";
3
- import { getZswapNetworkId } from "@midnight-ntwrk/midnight-js-network-id";
4
- import { nativeToken } from "@midnight-ntwrk/ledger";
5
- import { WebSocket } from "ws";
6
- import * as Rx from "rxjs";
7
- import chalk from "chalk";
8
- import { EnvironmentManager } from "./utils/environment.js";
9
-
10
- // Fix WebSocket for Node.js environment
11
- // @ts-ignore
1
+ /**
2
+ * Check wallet balance on Midnight Preprod network
3
+ */
4
+ import * as fs from 'node:fs';
5
+ import { WebSocket } from 'ws';
6
+ import * as Rx from 'rxjs';
7
+ import { Buffer } from 'buffer';
8
+
9
+ // Midnight SDK imports
10
+ import { setNetworkId, getNetworkId } from '@midnight-ntwrk/midnight-js-network-id';
11
+ import * as ledger from '@midnight-ntwrk/ledger-v7';
12
+ import { unshieldedToken } from '@midnight-ntwrk/ledger-v7';
13
+ import { WalletFacade } from '@midnight-ntwrk/wallet-sdk-facade';
14
+ import { DustWallet } from '@midnight-ntwrk/wallet-sdk-dust-wallet';
15
+ import { HDWallet, Roles } from '@midnight-ntwrk/wallet-sdk-hd';
16
+ import { ShieldedWallet } from '@midnight-ntwrk/wallet-sdk-shielded';
17
+ import { createKeystore, InMemoryTransactionHistoryStorage, PublicKey, UnshieldedWallet } from '@midnight-ntwrk/wallet-sdk-unshielded-wallet';
18
+
19
+ // Enable WebSocket for GraphQL subscriptions
20
+ // @ts-expect-error Required for wallet sync
12
21
  globalThis.WebSocket = WebSocket;
13
22
 
14
- // Initialize network from environment config
15
- const networkConfig = EnvironmentManager.initializeNetwork();
23
+ // Set network to preprod
24
+ setNetworkId('preprod');
25
+
26
+ // Preprod network configuration
27
+ const CONFIG = {
28
+ indexer: 'https://indexer.preprod.midnight.network/api/v3/graphql',
29
+ indexerWS: 'wss://indexer.preprod.midnight.network/api/v3/graphql/ws',
30
+ node: 'https://rpc.preprod.midnight.network',
31
+ proofServer: 'http://127.0.0.1:6300',
32
+ faucetUrl: 'https://faucet.preprod.midnight.network/',
33
+ };
34
+
35
+ // ─── Wallet Functions ──────────────────────────────────────────────────────────
36
+
37
+ function deriveKeys(seed: string) {
38
+ const hdWallet = HDWallet.fromSeed(Buffer.from(seed, 'hex'));
39
+ if (hdWallet.type !== 'seedOk') throw new Error('Invalid seed');
40
+ const result = hdWallet.hdWallet.selectAccount(0).selectRoles([Roles.Zswap, Roles.NightExternal, Roles.Dust]).deriveKeysAt(0);
41
+ if (result.type !== 'keysDerived') throw new Error('Key derivation failed');
42
+ hdWallet.hdWallet.clear();
43
+ return result.keys;
44
+ }
45
+
46
+ async function createWallet(seed: string) {
47
+ const keys = deriveKeys(seed);
48
+ const networkId = getNetworkId();
49
+ const shieldedSecretKeys = ledger.ZswapSecretKeys.fromSeed(keys[Roles.Zswap]);
50
+ const dustSecretKey = ledger.DustSecretKey.fromSeed(keys[Roles.Dust]);
51
+ const unshieldedKeystore = createKeystore(keys[Roles.NightExternal], networkId);
52
+
53
+ const walletConfig = {
54
+ networkId,
55
+ indexerClientConnection: { indexerHttpUrl: CONFIG.indexer, indexerWsUrl: CONFIG.indexerWS },
56
+ provingServerUrl: new URL(CONFIG.proofServer),
57
+ relayURL: new URL(CONFIG.node.replace(/^http/, 'ws')),
58
+ };
59
+
60
+ const shieldedWallet = ShieldedWallet(walletConfig).startWithSecretKeys(shieldedSecretKeys);
61
+ const unshieldedWallet = UnshieldedWallet({
62
+ networkId,
63
+ indexerClientConnection: walletConfig.indexerClientConnection,
64
+ txHistoryStorage: new InMemoryTransactionHistoryStorage(),
65
+ }).startWithPublicKey(PublicKey.fromKeyStore(unshieldedKeystore));
66
+ const dustWallet = DustWallet({
67
+ ...walletConfig,
68
+ costParameters: { additionalFeeOverhead: 300_000_000_000_000n, feeBlocksMargin: 5 },
69
+ }).startWithSecretKey(dustSecretKey, ledger.LedgerParameters.initialParameters().dust);
70
+
71
+ const wallet = new WalletFacade(shieldedWallet, unshieldedWallet, dustWallet);
72
+ await wallet.start(shieldedSecretKeys, dustSecretKey);
73
+
74
+ return { wallet, unshieldedKeystore };
75
+ }
76
+
77
+ // ─── Main ──────────────────────────────────────────────────────────────────────
78
+
79
+ async function main() {
80
+ console.log('\n╔══════════════════════════════════════════════════════════════╗');
81
+ console.log('║ Wallet Balance Checker ║');
82
+ console.log('╚══════════════════════════════════════════════════════════════╝\n');
83
+
84
+ // Check for deployment.json to get seed
85
+ if (!fs.existsSync('deployment.json')) {
86
+ console.error('❌ No deployment.json found! Run: npm run deploy\n');
87
+ process.exit(1);
88
+ }
89
+
90
+ const deployment = JSON.parse(fs.readFileSync('deployment.json', 'utf-8'));
91
+
92
+ if (!deployment.seed) {
93
+ console.error('❌ No wallet seed in deployment.json\n');
94
+ process.exit(1);
95
+ }
16
96
 
17
- async function checkBalance() {
18
97
  try {
19
- console.log();
20
- console.log(chalk.blue.bold("━".repeat(60)));
21
- console.log(chalk.blue.bold("🌙 Wallet Balance Checker"));
22
- console.log(chalk.blue.bold("━".repeat(60)));
23
- console.log();
24
-
25
- // Validate environment (checks WALLET_SEED)
26
- EnvironmentManager.validateEnvironment();
27
- const seed = process.env.WALLET_SEED!;
28
-
29
- console.log(chalk.gray("Building wallet..."));
30
- console.log();
31
-
32
- // Build wallet from seed
33
- const wallet = await WalletBuilder.buildFromSeed(
34
- networkConfig.indexer,
35
- networkConfig.indexerWS,
36
- networkConfig.proofServer,
37
- networkConfig.node,
38
- seed,
39
- getZswapNetworkId(),
40
- "info"
98
+ console.log(' Building wallet...');
99
+ const { wallet, unshieldedKeystore } = await createWallet(deployment.seed);
100
+
101
+ console.log(' Syncing with network...');
102
+ const state = await Rx.firstValueFrom(
103
+ wallet.state().pipe(Rx.throttleTime(5000), Rx.filter((s) => s.isSynced)),
41
104
  );
42
105
 
43
- wallet.start();
44
-
45
- const state = await Rx.firstValueFrom(wallet.state());
46
-
47
- console.log(chalk.cyan.bold("📍 Wallet Address:"));
48
- console.log(chalk.white(` ${state.address}`));
49
- console.log();
50
-
51
- const balance = state.balances[nativeToken()] || 0n;
52
-
53
- if (balance === 0n) {
54
- console.log(chalk.yellow.bold("💰 Balance: ") + chalk.red.bold("0 DUST"));
55
- console.log();
56
- console.log(chalk.red("❌ No funds detected."));
57
- console.log();
58
- console.log(chalk.magenta.bold("━".repeat(60)));
59
- console.log(chalk.magenta.bold("📝 How to Get Test Tokens:"));
60
- console.log(chalk.magenta.bold("━".repeat(60)));
61
- console.log();
62
- console.log(chalk.white(" 1. ") + chalk.cyan("Visit: ") + chalk.underline(networkConfig.faucetUrl));
63
- console.log(chalk.white(" 2. ") + chalk.cyan("Paste your wallet address (shown above)"));
64
- console.log(chalk.white(" 3. ") + chalk.cyan("Request tokens from the faucet"));
65
- console.log(chalk.white(" 4. ") + chalk.cyan("Wait 2-5 minutes for processing"));
66
- console.log(chalk.white(" 5. ") + chalk.cyan("Run ") + chalk.yellow.bold("'npm run check-balance'") + chalk.cyan(" again"));
67
- console.log();
68
- console.log(chalk.gray("━".repeat(60)));
69
- console.log(chalk.gray("💡 Tip: Faucet transactions typically take 2-5 minutes to process."));
70
- console.log(chalk.gray("━".repeat(60)));
106
+ const address = unshieldedKeystore.getBech32Address();
107
+ const tNightBalance = state.unshielded.balances[unshieldedToken().raw] ?? 0n;
108
+ const dustBalance = state.dust.walletBalance(new Date());
109
+
110
+ console.log('\n─── Wallet Details ─────────────────────────────────────────────\n');
111
+ console.log(` Address: ${address}`);
112
+ console.log(` Network: preprod\n`);
113
+
114
+ console.log('─── Balances ───────────────────────────────────────────────────\n');
115
+ console.log(` tNight: ${tNightBalance.toLocaleString()}`);
116
+ console.log(` DUST: ${dustBalance.toLocaleString()}\n`);
117
+
118
+ if (tNightBalance === 0n) {
119
+ console.log('─── Need Funds? ────────────────────────────────────────────────\n');
120
+ console.log(` 1. Visit: ${CONFIG.faucetUrl}`);
121
+ console.log(` 2. Paste your address: ${address}`);
122
+ console.log(` 3. Request tokens and wait ~2-5 minutes`);
123
+ console.log(` 4. Run this command again to check balance\n`);
71
124
  } else {
72
- console.log(chalk.yellow.bold("💰 Balance: ") + chalk.green.bold(`${balance} DUST`));
73
- console.log();
74
- console.log(chalk.green.bold("✅ Wallet is funded and ready!"));
75
- console.log();
76
- console.log(chalk.magenta.bold("━".repeat(60)));
77
- console.log(chalk.magenta.bold("🚀 Next Step:"));
78
- console.log(chalk.magenta.bold("━".repeat(60)));
79
- console.log();
80
- console.log(chalk.cyan(" Deploy your contract with:"));
81
- console.log(chalk.yellow.bold(" npm run deploy"));
82
- console.log();
83
- console.log(chalk.gray("━".repeat(60)));
125
+ console.log(' ✅ Wallet is funded and ready!\n');
84
126
  }
85
127
 
86
- console.log();
87
- wallet.close();
88
- process.exit(0);
128
+ await wallet.stop();
89
129
  } catch (error) {
90
- console.log();
91
- console.log(chalk.red.bold("❌ Error checking balance:"));
92
- console.error(chalk.red(error instanceof Error ? error.message : String(error)));
93
- console.log();
130
+ console.error('\n❌ Error:', error instanceof Error ? error.message : error);
94
131
  process.exit(1);
95
132
  }
96
133
  }
97
134
 
98
- checkBalance();
135
+ main();
@@ -1,188 +1,268 @@
1
- import "dotenv/config";
2
- import * as readline from "readline/promises";
3
- import { WalletBuilder } from "@midnight-ntwrk/wallet";
4
- import { findDeployedContract } from "@midnight-ntwrk/midnight-js-contracts";
5
- import {
6
- getZswapNetworkId,
7
- getLedgerNetworkId,
8
- } from "@midnight-ntwrk/midnight-js-network-id";
9
- import { createBalancedTx } from "@midnight-ntwrk/midnight-js-types";
10
- import { Transaction } from "@midnight-ntwrk/ledger";
11
- import { Transaction as ZswapTransaction } from "@midnight-ntwrk/zswap";
12
- import { WebSocket } from "ws";
13
- import * as path from "path";
14
- import * as fs from "fs";
15
- import * as Rx from "rxjs";
16
- import { MidnightProviders } from "./providers/midnight-providers.js";
17
- import { EnvironmentManager } from "./utils/environment.js";
18
-
19
- // Fix WebSocket for Node.js environment
20
- // @ts-ignore
1
+ /**
2
+ * CLI for interacting with {{projectName}} contract
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 { findDeployedContract } 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 * as ledger from '@midnight-ntwrk/ledger-v7';
21
+ import { unshieldedToken } from '@midnight-ntwrk/ledger-v7';
22
+ import { WalletFacade } from '@midnight-ntwrk/wallet-sdk-facade';
23
+ import { DustWallet } from '@midnight-ntwrk/wallet-sdk-dust-wallet';
24
+ import { HDWallet, Roles } from '@midnight-ntwrk/wallet-sdk-hd';
25
+ import { ShieldedWallet } from '@midnight-ntwrk/wallet-sdk-shielded';
26
+ import { createKeystore, InMemoryTransactionHistoryStorage, PublicKey, UnshieldedWallet } from '@midnight-ntwrk/wallet-sdk-unshielded-wallet';
27
+ import { CompiledContract } from '@midnight-ntwrk/compact-js';
28
+
29
+ // Enable WebSocket for GraphQL subscriptions
30
+ // @ts-expect-error Required for wallet sync
21
31
  globalThis.WebSocket = WebSocket;
22
32
 
23
- // Initialize network from environment config
24
- const networkConfig = EnvironmentManager.initializeNetwork();
33
+ // Set network to preprod
34
+ setNetworkId('preprod');
35
+
36
+ // Preprod network configuration
37
+ const CONFIG = {
38
+ indexer: 'https://indexer.preprod.midnight.network/api/v3/graphql',
39
+ indexerWS: 'wss://indexer.preprod.midnight.network/api/v3/graphql/ws',
40
+ node: 'https://rpc.preprod.midnight.network',
41
+ proofServer: 'http://127.0.0.1:6300',
42
+ };
43
+
44
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
45
+ const zkConfigPath = path.resolve(__dirname, '..', 'contracts', 'managed', 'hello-world');
46
+
47
+ // Load compiled contract
48
+ const contractPath = path.join(zkConfigPath, 'contract', 'index.js');
49
+
50
+ // Check if contract is compiled
51
+ if (!fs.existsSync(contractPath)) {
52
+ console.error('\n❌ Contract not compiled! Run: npm run compile\n');
53
+ process.exit(1);
54
+ }
55
+
56
+ const HelloWorld = await import(pathToFileURL(contractPath).href);
57
+
58
+ const compiledContract = CompiledContract.make('hello-world', HelloWorld.Contract).pipe(
59
+ CompiledContract.withVacantWitnesses,
60
+ CompiledContract.withCompiledFileAssets(zkConfigPath),
61
+ );
62
+
63
+ // ─── Wallet Functions ──────────────────────────────────────────────────────────
64
+
65
+ function deriveKeys(seed: string) {
66
+ const hdWallet = HDWallet.fromSeed(Buffer.from(seed, 'hex'));
67
+ if (hdWallet.type !== 'seedOk') throw new Error('Invalid seed');
68
+ const result = hdWallet.hdWallet.selectAccount(0).selectRoles([Roles.Zswap, Roles.NightExternal, Roles.Dust]).deriveKeysAt(0);
69
+ if (result.type !== 'keysDerived') throw new Error('Key derivation failed');
70
+ hdWallet.hdWallet.clear();
71
+ return result.keys;
72
+ }
73
+
74
+ async function createWallet(seed: string) {
75
+ const keys = deriveKeys(seed);
76
+ const networkId = getNetworkId();
77
+ const shieldedSecretKeys = ledger.ZswapSecretKeys.fromSeed(keys[Roles.Zswap]);
78
+ const dustSecretKey = ledger.DustSecretKey.fromSeed(keys[Roles.Dust]);
79
+ const unshieldedKeystore = createKeystore(keys[Roles.NightExternal], networkId);
80
+
81
+ const walletConfig = {
82
+ networkId,
83
+ indexerClientConnection: { indexerHttpUrl: CONFIG.indexer, indexerWsUrl: CONFIG.indexerWS },
84
+ provingServerUrl: new URL(CONFIG.proofServer),
85
+ relayURL: new URL(CONFIG.node.replace(/^http/, 'ws')),
86
+ };
87
+
88
+ const shieldedWallet = ShieldedWallet(walletConfig).startWithSecretKeys(shieldedSecretKeys);
89
+ const unshieldedWallet = UnshieldedWallet({
90
+ networkId,
91
+ indexerClientConnection: walletConfig.indexerClientConnection,
92
+ txHistoryStorage: new InMemoryTransactionHistoryStorage(),
93
+ }).startWithPublicKey(PublicKey.fromKeyStore(unshieldedKeystore));
94
+ const dustWallet = DustWallet({
95
+ ...walletConfig,
96
+ costParameters: { additionalFeeOverhead: 300_000_000_000_000n, feeBlocksMargin: 5 },
97
+ }).startWithSecretKey(dustSecretKey, ledger.LedgerParameters.initialParameters().dust);
98
+
99
+ const wallet = new WalletFacade(shieldedWallet, unshieldedWallet, dustWallet);
100
+ await wallet.start(shieldedSecretKeys, dustSecretKey);
101
+
102
+ return { wallet, shieldedSecretKeys, dustSecretKey, unshieldedKeystore };
103
+ }
104
+
105
+ // Workaround for wallet SDK signRecipe bug
106
+ function signTransactionIntents(tx: { intents?: Map<number, any> }, signFn: (payload: Uint8Array) => ledger.Signature, proofMarker: 'proof' | 'pre-proof'): void {
107
+ if (!tx.intents || tx.intents.size === 0) return;
108
+ for (const segment of tx.intents.keys()) {
109
+ const intent = tx.intents.get(segment);
110
+ if (!intent) continue;
111
+ const cloned = ledger.Intent.deserialize<ledger.SignatureEnabled, ledger.Proofish, ledger.PreBinding>('signature', proofMarker, 'pre-binding', intent.serialize());
112
+ const sigData = cloned.signatureData(segment);
113
+ const signature = signFn(sigData);
114
+ if (cloned.fallibleUnshieldedOffer) {
115
+ const sigs = cloned.fallibleUnshieldedOffer.inputs.map((_: any, i: number) => cloned.fallibleUnshieldedOffer!.signatures.at(i) ?? signature);
116
+ cloned.fallibleUnshieldedOffer = cloned.fallibleUnshieldedOffer.addSignatures(sigs);
117
+ }
118
+ if (cloned.guaranteedUnshieldedOffer) {
119
+ const sigs = cloned.guaranteedUnshieldedOffer.inputs.map((_: any, i: number) => cloned.guaranteedUnshieldedOffer!.signatures.at(i) ?? signature);
120
+ cloned.guaranteedUnshieldedOffer = cloned.guaranteedUnshieldedOffer.addSignatures(sigs);
121
+ }
122
+ tx.intents.set(segment, cloned);
123
+ }
124
+ }
125
+
126
+ async function createProviders(walletCtx: ReturnType<typeof createWallet> extends Promise<infer T> ? T : never) {
127
+ const state = await Rx.firstValueFrom(walletCtx.wallet.state().pipe(Rx.filter((s) => s.isSynced)));
128
+
129
+ const walletProvider = {
130
+ getCoinPublicKey: () => state.shielded.coinPublicKey.toHexString(),
131
+ getEncryptionPublicKey: () => state.shielded.encryptionPublicKey.toHexString(),
132
+ async balanceTx(tx: any, ttl?: Date) {
133
+ const recipe = await walletCtx.wallet.balanceUnboundTransaction(
134
+ tx,
135
+ { shieldedSecretKeys: walletCtx.shieldedSecretKeys, dustSecretKey: walletCtx.dustSecretKey },
136
+ { ttl: ttl ?? new Date(Date.now() + 30 * 60 * 1000) },
137
+ );
138
+ const signFn = (payload: Uint8Array) => walletCtx.unshieldedKeystore.signData(payload);
139
+ signTransactionIntents(recipe.baseTransaction, signFn, 'proof');
140
+ if (recipe.balancingTransaction) signTransactionIntents(recipe.balancingTransaction, signFn, 'pre-proof');
141
+ return walletCtx.wallet.finalizeRecipe(recipe);
142
+ },
143
+ submitTx: (tx: any) => walletCtx.wallet.submitTransaction(tx) as any,
144
+ };
145
+
146
+ const zkConfigProvider = new NodeZkConfigProvider(zkConfigPath);
147
+
148
+ return {
149
+ privateStateProvider: levelPrivateStateProvider({ privateStateStoreName: 'hello-world-state', walletProvider }),
150
+ publicDataProvider: indexerPublicDataProvider(CONFIG.indexer, CONFIG.indexerWS),
151
+ zkConfigProvider,
152
+ proofProvider: httpClientProofProvider(CONFIG.proofServer, zkConfigProvider),
153
+ walletProvider,
154
+ midnightProvider: walletProvider,
155
+ };
156
+ }
157
+
158
+ // ─── Main CLI ──────────────────────────────────────────────────────────────────
25
159
 
26
160
  async function main() {
27
- const rl = readline.createInterface({
28
- input: process.stdin,
29
- output: process.stdout,
30
- });
161
+ console.log('\n╔══════════════════════════════════════════════════════════════╗');
162
+ console.log('║ {{projectName}} CLI ║');
163
+ console.log('╚══════════════════════════════════════════════════════════════╝\n');
164
+
165
+ const rl = createInterface({ input: stdin, output: stdout });
166
+
167
+ // Check for deployment
168
+ if (!fs.existsSync('deployment.json')) {
169
+ console.error('❌ No deployment.json found! Run: npm run deploy\n');
170
+ process.exit(1);
171
+ }
31
172
 
32
- console.log("🌙 {{projectName}} CLI\n");
173
+ const deployment = JSON.parse(fs.readFileSync('deployment.json', 'utf-8'));
174
+ console.log(` Contract: ${deployment.contractAddress}`);
175
+ console.log(` Network: ${deployment.network || 'preprod'}\n`);
33
176
 
34
177
  try {
35
- // Validate environment
36
- EnvironmentManager.validateEnvironment();
178
+ // Create wallet from saved seed
179
+ console.log(' Connecting to wallet...');
180
+ const walletCtx = await createWallet(deployment.seed);
37
181
 
38
- // Check for deployment file
39
- if (!fs.existsSync("deployment.json")) {
40
- console.error("❌ No deployment.json found! Run npm run deploy first.");
41
- process.exit(1);
42
- }
182
+ console.log(' Syncing with network...');
183
+ const state = await Rx.firstValueFrom(walletCtx.wallet.state().pipe(Rx.throttleTime(5000), Rx.filter((s) => s.isSynced)));
184
+ const balance = state.unshielded.balances[unshieldedToken().raw] ?? 0n;
185
+ console.log(` Balance: ${balance.toLocaleString()} tNight\n`);
43
186
 
44
- const deployment = JSON.parse(fs.readFileSync("deployment.json", "utf-8"));
45
- console.log(`Contract: ${deployment.contractAddress}\n`);
46
-
47
- const contractName =
48
- deployment.contractName || process.env.CONTRACT_NAME || "hello-world";
49
- const walletSeed = process.env.WALLET_SEED!;
50
-
51
- console.log("Connecting to Midnight network...");
52
-
53
- // Build wallet
54
- const wallet = await WalletBuilder.buildFromSeed(
55
- networkConfig.indexer,
56
- networkConfig.indexerWS,
57
- networkConfig.proofServer,
58
- networkConfig.node,
59
- walletSeed,
60
- getZswapNetworkId(),
61
- "info"
62
- );
63
-
64
- wallet.start();
65
-
66
- // Wait for sync
67
- await Rx.firstValueFrom(
68
- wallet.state().pipe(Rx.filter((s) => s.syncProgress?.synced === true))
69
- );
70
-
71
- // Load contract
72
- const contractPath = path.join(process.cwd(), "contracts");
73
- const contractModulePath = path.join(
74
- contractPath,
75
- "managed",
76
- contractName,
77
- "contract",
78
- "index.cjs"
79
- );
80
- const HelloWorldModule = await import(contractModulePath);
81
- const contractInstance = new HelloWorldModule.Contract({});
82
-
83
- // Create wallet provider
84
- const walletState = await Rx.firstValueFrom(wallet.state());
85
-
86
- const walletProvider = {
87
- coinPublicKey: walletState.coinPublicKey,
88
- encryptionPublicKey: walletState.encryptionPublicKey,
89
- balanceTx(tx: any, newCoins: any) {
90
- return wallet
91
- .balanceTransaction(
92
- ZswapTransaction.deserialize(
93
- tx.serialize(getLedgerNetworkId()),
94
- getZswapNetworkId()
95
- ),
96
- newCoins
97
- )
98
- .then((tx) => wallet.proveTransaction(tx))
99
- .then((zswapTx) =>
100
- Transaction.deserialize(
101
- zswapTx.serialize(getZswapNetworkId()),
102
- getLedgerNetworkId()
103
- )
104
- )
105
- .then(createBalancedTx);
106
- },
107
- submitTx(tx: any) {
108
- return wallet.submitTransaction(tx);
109
- },
110
- };
111
-
112
- // Configure providers
113
- const providers = MidnightProviders.create({
114
- contractName,
115
- walletProvider,
116
- networkConfig,
117
- });
187
+ // Setup providers and connect to contract
188
+ console.log(' Connecting to contract...');
189
+ const providers = await createProviders(walletCtx);
118
190
 
119
- // Connect to contract
120
191
  const deployed: any = await findDeployedContract(providers, {
192
+ compiledContract,
121
193
  contractAddress: deployment.contractAddress,
122
- contract: contractInstance,
123
- privateStateId: "helloWorldState",
194
+ privateStateId: 'helloWorldState',
124
195
  initialPrivateState: {},
125
196
  });
126
197
 
127
- console.log("✅ Connected to contract\n");
198
+ console.log(' ✅ Connected!\n');
128
199
 
129
- // Main menu loop
200
+ // Interactive CLI loop
130
201
  let running = true;
131
202
  while (running) {
132
- console.log("--- Menu ---");
133
- console.log("1. Store message");
134
- console.log("2. Read current message");
135
- console.log("3. Exit");
203
+ console.log('─── Menu ───────────────────────────────────────────────────────');
204
+ console.log(' 1. Store a message');
205
+ console.log(' 2. Read current message');
206
+ console.log(' 3. Check wallet balance');
207
+ console.log(' 4. Exit\n');
136
208
 
137
- const choice = await rl.question("\nYour choice: ");
209
+ const choice = await rl.question(' Your choice: ');
138
210
 
139
- switch (choice) {
140
- case "1":
141
- console.log("\nStoring custom message...");
142
- const customMessage = await rl.question("Enter your message: ");
211
+ switch (choice.trim()) {
212
+ case '1': {
213
+ const message = await rl.question(' Enter your message: ');
214
+ console.log('\n Submitting transaction (this may take 30-60 seconds)...');
143
215
  try {
144
- const tx = await deployed.callTx.storeMessage(customMessage);
145
- console.log("Success!");
146
- console.log(`Message: "${customMessage}"`);
147
- console.log(`Transaction ID: ${tx.public.txId}`);
148
- console.log(`Block height: ${tx.public.blockHeight}\n`);
216
+ const tx = await deployed.callTx.storeMessage(message);
217
+ console.log(`\n Message stored: "${message}"`);
218
+ console.log(` Transaction ID: ${tx.public.txId}`);
219
+ console.log(` Block height: ${tx.public.blockHeight}\n`);
149
220
  } catch (error) {
150
- console.error("❌ Failed to store message:", error);
221
+ console.error('\n ❌ Failed:', error instanceof Error ? error.message : error);
151
222
  }
152
223
  break;
224
+ }
153
225
 
154
- case "2":
155
- console.log("\nReading message from blockchain...");
226
+ case '2': {
227
+ console.log('\n Reading message from blockchain...');
156
228
  try {
157
- const state = await providers.publicDataProvider.queryContractState(
158
- deployment.contractAddress
159
- );
160
- if (state) {
161
- const ledger = HelloWorldModule.ledger(state.data);
162
- const message = Buffer.from(ledger.message).toString();
163
- console.log(`📋 Current message: "${message}"\n`);
229
+ const contractState = await providers.publicDataProvider.queryContractState(deployment.contractAddress);
230
+ if (contractState) {
231
+ const ledgerState = HelloWorld.ledger(contractState.data);
232
+ const message = Buffer.from(ledgerState.message).toString();
233
+ console.log(`\n 📋 Current message: "${message}"\n`);
164
234
  } else {
165
- console.log("📋 No message found\n");
235
+ console.log('\n 📋 No message found (contract state empty)\n');
166
236
  }
167
237
  } catch (error) {
168
- console.error("❌ Failed to read message:", error);
238
+ console.error('\n ❌ Failed:', error instanceof Error ? error.message : error);
169
239
  }
170
240
  break;
241
+ }
242
+
243
+ case '3': {
244
+ console.log('\n Checking balance...');
245
+ const currentState = await Rx.firstValueFrom(walletCtx.wallet.state().pipe(Rx.filter((s) => s.isSynced)));
246
+ const currentBalance = currentState.unshielded.balances[unshieldedToken().raw] ?? 0n;
247
+ const dustBalance = currentState.dust.walletBalance(new Date());
248
+ console.log(`\n tNight: ${currentBalance.toLocaleString()}`);
249
+ console.log(` DUST: ${dustBalance.toLocaleString()}\n`);
250
+ break;
251
+ }
171
252
 
172
- case "3":
253
+ case '4':
173
254
  running = false;
174
- console.log("\n👋 Goodbye!");
255
+ console.log('\n 👋 Goodbye!\n');
175
256
  break;
176
257
 
177
258
  default:
178
- console.log("❌ Invalid choice. Please enter 1, 2, or 3.\n");
259
+ console.log('\n ❌ Invalid choice. Please enter 1-4.\n');
179
260
  }
180
261
  }
181
262
 
182
- // Clean up
183
- await wallet.close();
263
+ await walletCtx.wallet.stop();
184
264
  } catch (error) {
185
- console.error("\n❌ Error:", error);
265
+ console.error('\n❌ Error:', error instanceof Error ? error.message : error);
186
266
  } finally {
187
267
  rl.close();
188
268
  }