create-mn-app 0.3.27 → 0.4.0
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 +75 -89
- package/dist/utils/templates.js +4 -4
- package/package.json +1 -1
- package/templates/hello-world/README.md.template +145 -60
- package/templates/hello-world/_gitignore +3 -1
- package/templates/hello-world/contracts/hello-world.compact.template +1 -1
- package/templates/hello-world/docker-compose.yml.template +66 -5
- package/templates/hello-world/package.json.template +17 -15
- package/templates/hello-world/scripts/e2e-check.ts.template +158 -0
- package/templates/hello-world/src/check-balance.ts.template +32 -36
- package/templates/hello-world/src/cli.ts.template +43 -32
- package/templates/hello-world/src/deploy.ts.template +241 -301
- package/templates/hello-world/src/network.ts +320 -0
- package/templates/hello-world/src/setup.ts.template +37 -0
- package/templates/hello-world/tsconfig.json.template +1 -1
- package/dist/installers/wallet-generator.d.ts +0 -4
- package/dist/installers/wallet-generator.d.ts.map +0 -1
- package/dist/installers/wallet-generator.js +0 -78
- package/dist/installers/wallet-generator.js.map +0 -1
- package/dist/test.d.ts +0 -2
- package/dist/test.d.ts.map +0 -1
- package/dist/test.js +0 -65
- package/dist/test.js.map +0 -1
|
@@ -8,40 +8,42 @@
|
|
|
8
8
|
"scripts": {
|
|
9
9
|
"compile": "compact compile contracts/hello-world.compact contracts/managed/hello-world",
|
|
10
10
|
"build": "npx tsc --noEmit || true",
|
|
11
|
-
"setup": "
|
|
11
|
+
"setup": "npx tsx src/setup.ts",
|
|
12
12
|
"deploy": "npx tsx src/deploy.ts",
|
|
13
13
|
"cli": "npx tsx src/cli.ts",
|
|
14
14
|
"check-balance": "npx tsx src/check-balance.ts",
|
|
15
|
+
"network": "npx tsx src/network.ts",
|
|
15
16
|
"proof-server:start": "docker compose up -d",
|
|
16
17
|
"proof-server:stop": "docker compose down",
|
|
17
|
-
"clean": "rm -rf contracts/managed
|
|
18
|
-
"test": "echo \"Error: no test specified\" && exit 1"
|
|
18
|
+
"clean": "rm -rf contracts/managed .midnight-state.json",
|
|
19
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
20
|
+
"test:e2e": "tsx scripts/e2e-check.ts"
|
|
19
21
|
},
|
|
20
22
|
"dependencies": {
|
|
21
23
|
"@midnight-ntwrk/compact-js": "2.5.0",
|
|
22
|
-
"rxjs": "^7.8.
|
|
23
|
-
"@midnight-ntwrk/compact-runtime": "0.
|
|
24
|
+
"rxjs": "^7.8.2",
|
|
25
|
+
"@midnight-ntwrk/compact-runtime": "0.16.0",
|
|
24
26
|
"@midnight-ntwrk/ledger-v8": "8.0.3",
|
|
25
|
-
"@midnight-ntwrk/midnight-js-contracts": "4.0.
|
|
26
|
-
"@midnight-ntwrk/midnight-js-http-client-proof-provider": "4.0.
|
|
27
|
-
"@midnight-ntwrk/midnight-js-indexer-public-data-provider": "4.0.
|
|
28
|
-
"@midnight-ntwrk/midnight-js-level-private-state-provider": "4.0.
|
|
29
|
-
"@midnight-ntwrk/midnight-js-network-id": "4.0.
|
|
30
|
-
"@midnight-ntwrk/midnight-js-node-zk-config-provider": "4.0.
|
|
31
|
-
"@midnight-ntwrk/midnight-js-types": "4.0.
|
|
32
|
-
"@midnight-ntwrk/midnight-js-utils": "4.0.
|
|
27
|
+
"@midnight-ntwrk/midnight-js-contracts": "4.0.4",
|
|
28
|
+
"@midnight-ntwrk/midnight-js-http-client-proof-provider": "4.0.4",
|
|
29
|
+
"@midnight-ntwrk/midnight-js-indexer-public-data-provider": "4.0.4",
|
|
30
|
+
"@midnight-ntwrk/midnight-js-level-private-state-provider": "4.0.4",
|
|
31
|
+
"@midnight-ntwrk/midnight-js-network-id": "4.0.4",
|
|
32
|
+
"@midnight-ntwrk/midnight-js-node-zk-config-provider": "4.0.4",
|
|
33
|
+
"@midnight-ntwrk/midnight-js-types": "4.0.4",
|
|
34
|
+
"@midnight-ntwrk/midnight-js-utils": "4.0.4",
|
|
33
35
|
"@midnight-ntwrk/wallet-sdk-dust-wallet": "3.0.0",
|
|
34
36
|
"@midnight-ntwrk/wallet-sdk-facade": "3.0.0",
|
|
35
37
|
"@midnight-ntwrk/wallet-sdk-hd": "3.0.1",
|
|
36
38
|
"@midnight-ntwrk/wallet-sdk-shielded": "2.1.0",
|
|
37
39
|
"@midnight-ntwrk/wallet-sdk-unshielded-wallet": "2.1.0",
|
|
38
|
-
"ws": "^8.
|
|
40
|
+
"ws": "^8.20.0"
|
|
39
41
|
},
|
|
40
42
|
"devDependencies": {
|
|
41
43
|
"@types/node": "^22.0.0",
|
|
42
44
|
"@types/ws": "^8.18.1",
|
|
43
45
|
"tsx": "^4.21.0",
|
|
44
|
-
"typescript": "^
|
|
46
|
+
"typescript": "^6.0.3"
|
|
45
47
|
},
|
|
46
48
|
"engines": {
|
|
47
49
|
"node": ">=22.0.0"
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* End-to-end smoke check for {{projectName}}.
|
|
3
|
+
*
|
|
4
|
+
* Reconnects to the deployed contract, reads its ledger state, and exits 0
|
|
5
|
+
* on success. Used by `npm run test:e2e` and by the project's CI workflows.
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from 'node:fs';
|
|
8
|
+
import * as path from 'node:path';
|
|
9
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
10
|
+
import { WebSocket } from 'ws';
|
|
11
|
+
import { Buffer } from 'buffer';
|
|
12
|
+
|
|
13
|
+
import { findDeployedContract } from '@midnight-ntwrk/midnight-js-contracts';
|
|
14
|
+
import { httpClientProofProvider } from '@midnight-ntwrk/midnight-js-http-client-proof-provider';
|
|
15
|
+
import { indexerPublicDataProvider } from '@midnight-ntwrk/midnight-js-indexer-public-data-provider';
|
|
16
|
+
import { levelPrivateStateProvider } from '@midnight-ntwrk/midnight-js-level-private-state-provider';
|
|
17
|
+
import { NodeZkConfigProvider } from '@midnight-ntwrk/midnight-js-node-zk-config-provider';
|
|
18
|
+
import { setNetworkId, getNetworkId } from '@midnight-ntwrk/midnight-js-network-id';
|
|
19
|
+
import { resolveNetwork, getOrCreateSeed, getDeployment } from '../src/network';
|
|
20
|
+
import * as ledger from '@midnight-ntwrk/ledger-v8';
|
|
21
|
+
import { WalletFacade } from '@midnight-ntwrk/wallet-sdk-facade';
|
|
22
|
+
import { DustWallet } from '@midnight-ntwrk/wallet-sdk-dust-wallet';
|
|
23
|
+
import { HDWallet, Roles } from '@midnight-ntwrk/wallet-sdk-hd';
|
|
24
|
+
import { ShieldedWallet } from '@midnight-ntwrk/wallet-sdk-shielded';
|
|
25
|
+
import { createKeystore, NoOpTransactionHistoryStorage, PublicKey, UnshieldedWallet } from '@midnight-ntwrk/wallet-sdk-unshielded-wallet';
|
|
26
|
+
import { CompiledContract } from '@midnight-ntwrk/compact-js';
|
|
27
|
+
|
|
28
|
+
// @ts-expect-error wallet sync requires WebSocket
|
|
29
|
+
globalThis.WebSocket = WebSocket;
|
|
30
|
+
|
|
31
|
+
// ─── Network configuration ─────────────────────────────────────────────────────
|
|
32
|
+
|
|
33
|
+
const { network, config: networkConfig } = resolveNetwork();
|
|
34
|
+
setNetworkId(networkConfig.networkId);
|
|
35
|
+
const SEED = getOrCreateSeed(network);
|
|
36
|
+
|
|
37
|
+
function fail(msg: string): never {
|
|
38
|
+
console.error(`❌ e2e-check failed: ${msg}`);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function isHexAddress(s: unknown): s is string {
|
|
43
|
+
return typeof s === 'string' && /^[0-9a-fA-F]+$/.test(s) && s.length >= 32;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function main() {
|
|
47
|
+
// 1. Deployment sanity
|
|
48
|
+
const deployment = getDeployment(network);
|
|
49
|
+
if (!deployment) {
|
|
50
|
+
console.error(`No deploy on file for network ${network}.`);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
if (!isHexAddress(deployment.address)) {
|
|
54
|
+
fail(`Deployment address missing or invalid: ${JSON.stringify(deployment, null, 2)}`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// 2. Build wallet (genesis seed) and providers
|
|
58
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
59
|
+
const zkConfigPath = path.resolve(__dirname, '..', 'contracts', 'managed', 'hello-world');
|
|
60
|
+
const contractPath = path.join(zkConfigPath, 'contract', 'index.js');
|
|
61
|
+
if (!fs.existsSync(contractPath)) fail('Compiled contract missing — run `npm run compile`.');
|
|
62
|
+
const HelloWorld = await import(pathToFileURL(contractPath).href);
|
|
63
|
+
const compiledContract = CompiledContract.make('hello-world', HelloWorld.Contract).pipe(
|
|
64
|
+
CompiledContract.withVacantWitnesses,
|
|
65
|
+
CompiledContract.withCompiledFileAssets(zkConfigPath),
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
const hd = HDWallet.fromSeed(Buffer.from(SEED, 'hex'));
|
|
69
|
+
if (hd.type !== 'seedOk') fail('Bad seed.');
|
|
70
|
+
const derived = hd.hdWallet
|
|
71
|
+
.selectAccount(0)
|
|
72
|
+
.selectRoles([Roles.Zswap, Roles.NightExternal, Roles.Dust])
|
|
73
|
+
.deriveKeysAt(0);
|
|
74
|
+
if (derived.type !== 'keysDerived') fail('Key derivation failed.');
|
|
75
|
+
hd.hdWallet.clear();
|
|
76
|
+
|
|
77
|
+
const networkId = getNetworkId();
|
|
78
|
+
const shieldedSecretKeys = ledger.ZswapSecretKeys.fromSeed(derived.keys[Roles.Zswap]);
|
|
79
|
+
const dustSecretKey = ledger.DustSecretKey.fromSeed(derived.keys[Roles.Dust]);
|
|
80
|
+
const unshieldedKeystore = createKeystore(derived.keys[Roles.NightExternal], networkId);
|
|
81
|
+
|
|
82
|
+
const wallet = await WalletFacade.init({
|
|
83
|
+
configuration: {
|
|
84
|
+
networkId,
|
|
85
|
+
indexerClientConnection: { indexerHttpUrl: networkConfig.indexer, indexerWsUrl: networkConfig.indexerWS },
|
|
86
|
+
provingServerUrl: new URL(networkConfig.proofServer),
|
|
87
|
+
relayURL: new URL(networkConfig.node.replace(/^http/, 'ws')),
|
|
88
|
+
txHistoryStorage: new NoOpTransactionHistoryStorage(),
|
|
89
|
+
costParameters: { additionalFeeOverhead: 300_000_000_000_000n, feeBlocksMargin: 5 },
|
|
90
|
+
},
|
|
91
|
+
shielded: async (c) => ShieldedWallet(c).startWithSecretKeys(shieldedSecretKeys),
|
|
92
|
+
unshielded: async (c) =>
|
|
93
|
+
UnshieldedWallet(c).startWithPublicKey(PublicKey.fromKeyStore(unshieldedKeystore)),
|
|
94
|
+
dust: async (c) =>
|
|
95
|
+
DustWallet(c).startWithSecretKey(dustSecretKey, ledger.LedgerParameters.initialParameters().dust),
|
|
96
|
+
});
|
|
97
|
+
await wallet.start(shieldedSecretKeys, dustSecretKey);
|
|
98
|
+
const state = await wallet.waitForSyncedState();
|
|
99
|
+
|
|
100
|
+
const zkConfigProvider = new NodeZkConfigProvider(zkConfigPath);
|
|
101
|
+
const walletProvider = {
|
|
102
|
+
getCoinPublicKey: () => state.shielded.coinPublicKey.toHexString(),
|
|
103
|
+
getEncryptionPublicKey: () => state.shielded.encryptionPublicKey.toHexString(),
|
|
104
|
+
async balanceTx() {
|
|
105
|
+
throw new Error('e2e-check is read-only and should not balance transactions');
|
|
106
|
+
},
|
|
107
|
+
submitTx() {
|
|
108
|
+
throw new Error('e2e-check is read-only and should not submit transactions');
|
|
109
|
+
},
|
|
110
|
+
} as any;
|
|
111
|
+
|
|
112
|
+
const providers = {
|
|
113
|
+
privateStateProvider: levelPrivateStateProvider({
|
|
114
|
+
privateStateStoreName: 'hello-world-state',
|
|
115
|
+
accountId: unshieldedKeystore.getBech32Address().toString(),
|
|
116
|
+
// SDK requires ≥16 chars. e2e-check is read-only so we don't expose
|
|
117
|
+
// the env-var override here — match the deploy script's local-devnet default.
|
|
118
|
+
privateStoragePasswordProvider: () => 'Local-Devnet-Development-Placeholder-1',
|
|
119
|
+
}),
|
|
120
|
+
publicDataProvider: indexerPublicDataProvider(networkConfig.indexer, networkConfig.indexerWS),
|
|
121
|
+
zkConfigProvider,
|
|
122
|
+
proofProvider: httpClientProofProvider(networkConfig.proofServer, zkConfigProvider),
|
|
123
|
+
walletProvider,
|
|
124
|
+
midnightProvider: walletProvider,
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
// 3. Reconnect to the deployed contract — proves callTx interface is wired
|
|
128
|
+
try {
|
|
129
|
+
await findDeployedContract(providers, {
|
|
130
|
+
contractAddress: deployment.address,
|
|
131
|
+
compiledContract: compiledContract as any,
|
|
132
|
+
});
|
|
133
|
+
} catch (err: any) {
|
|
134
|
+
await wallet.stop();
|
|
135
|
+
fail(`findDeployedContract threw: ${err?.message ?? err}`);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// 4. Read the on-chain contract state via the public data provider — proves
|
|
139
|
+
// the contract is indexed and queryable on the chain itself, not just that
|
|
140
|
+
// we know how to construct the local handle.
|
|
141
|
+
const onChainState = await providers.publicDataProvider.queryContractState(deployment.address);
|
|
142
|
+
if (!onChainState) {
|
|
143
|
+
await wallet.stop();
|
|
144
|
+
fail(`queryContractState returned null for ${deployment.address}`);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
console.log(`✅ e2e-check passed`);
|
|
148
|
+
console.log(` contractAddress: ${deployment.address}`);
|
|
149
|
+
console.log(` network: ${network}`);
|
|
150
|
+
|
|
151
|
+
await wallet.stop();
|
|
152
|
+
process.exit(0);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
main().catch(async (err) => {
|
|
156
|
+
console.error(err);
|
|
157
|
+
process.exit(1);
|
|
158
|
+
});
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Check wallet balance on Midnight
|
|
2
|
+
* Check wallet balance on the local Midnight devnet
|
|
3
3
|
*/
|
|
4
|
-
import * as fs from 'node:fs';
|
|
5
4
|
import { WebSocket } from 'ws';
|
|
6
5
|
import * as Rx from 'rxjs';
|
|
7
6
|
import { Buffer } from 'buffer';
|
|
@@ -14,23 +13,18 @@ import { WalletFacade } from '@midnight-ntwrk/wallet-sdk-facade';
|
|
|
14
13
|
import { DustWallet } from '@midnight-ntwrk/wallet-sdk-dust-wallet';
|
|
15
14
|
import { HDWallet, Roles } from '@midnight-ntwrk/wallet-sdk-hd';
|
|
16
15
|
import { ShieldedWallet } from '@midnight-ntwrk/wallet-sdk-shielded';
|
|
17
|
-
import { createKeystore,
|
|
16
|
+
import { createKeystore, NoOpTransactionHistoryStorage, PublicKey, UnshieldedWallet } from '@midnight-ntwrk/wallet-sdk-unshielded-wallet';
|
|
17
|
+
import { resolveNetwork, getOrCreateSeed } from './network';
|
|
18
18
|
|
|
19
19
|
// Enable WebSocket for GraphQL subscriptions
|
|
20
20
|
// @ts-expect-error Required for wallet sync
|
|
21
21
|
globalThis.WebSocket = WebSocket;
|
|
22
22
|
|
|
23
|
-
//
|
|
24
|
-
setNetworkId('preprod');
|
|
23
|
+
// ─── Network configuration ─────────────────────────────────────────────────────
|
|
25
24
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
};
|
|
25
|
+
const { network, config: networkConfig } = resolveNetwork();
|
|
26
|
+
setNetworkId(networkConfig.networkId);
|
|
27
|
+
const SEED = getOrCreateSeed(network);
|
|
34
28
|
|
|
35
29
|
// ─── Wallet Functions ──────────────────────────────────────────────────────────
|
|
36
30
|
|
|
@@ -52,10 +46,10 @@ async function createWallet(seed: string) {
|
|
|
52
46
|
|
|
53
47
|
const walletConfig = {
|
|
54
48
|
networkId,
|
|
55
|
-
indexerClientConnection: { indexerHttpUrl:
|
|
56
|
-
provingServerUrl: new URL(
|
|
57
|
-
relayURL: new URL(
|
|
58
|
-
txHistoryStorage: new
|
|
49
|
+
indexerClientConnection: { indexerHttpUrl: networkConfig.indexer, indexerWsUrl: networkConfig.indexerWS },
|
|
50
|
+
provingServerUrl: new URL(networkConfig.proofServer),
|
|
51
|
+
relayURL: new URL(networkConfig.node.replace(/^http/, 'ws')),
|
|
52
|
+
txHistoryStorage: new NoOpTransactionHistoryStorage(),
|
|
59
53
|
costParameters: { additionalFeeOverhead: 300_000_000_000_000n, feeBlocksMargin: 5 },
|
|
60
54
|
};
|
|
61
55
|
|
|
@@ -78,26 +72,23 @@ async function main() {
|
|
|
78
72
|
console.log('║ Wallet Balance Checker ║');
|
|
79
73
|
console.log('╚══════════════════════════════════════════════════════════════╝\n');
|
|
80
74
|
|
|
81
|
-
|
|
82
|
-
if (!fs.existsSync('deployment.json')) {
|
|
83
|
-
console.error('❌ No deployment.json found! Run: npm run deploy\n');
|
|
84
|
-
process.exit(1);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const deployment = JSON.parse(fs.readFileSync('deployment.json', 'utf-8'));
|
|
88
|
-
|
|
89
|
-
if (!fs.existsSync('.midnight-seed')) {
|
|
90
|
-
console.error('❌ No .midnight-seed file found! Run: npm run deploy\n');
|
|
91
|
-
process.exit(1);
|
|
92
|
-
}
|
|
93
|
-
const seed = fs.readFileSync('.midnight-seed', 'utf-8').trim();
|
|
75
|
+
const seed = SEED;
|
|
94
76
|
|
|
95
77
|
try {
|
|
96
78
|
console.log(' Building wallet...');
|
|
97
79
|
const { wallet, unshieldedKeystore } = await createWallet(seed);
|
|
98
80
|
|
|
99
81
|
console.log(' Syncing with network...');
|
|
82
|
+
console.log(' ℹ This may take several minutes depending on network size.');
|
|
83
|
+
console.log(' RPC disconnection messages during sync are normal and can be safely ignored.\n');
|
|
84
|
+
const syncStart = Date.now();
|
|
85
|
+
const syncInterval = setInterval(() => {
|
|
86
|
+
const elapsed = Math.round((Date.now() - syncStart) / 1000);
|
|
87
|
+
process.stdout.write(`\r ⏳ Still syncing... (${elapsed}s elapsed) `);
|
|
88
|
+
}, 5000);
|
|
100
89
|
const state = await wallet.waitForSyncedState();
|
|
90
|
+
clearInterval(syncInterval);
|
|
91
|
+
process.stdout.write('\r ✓ Synced with network. \n');
|
|
101
92
|
|
|
102
93
|
const address = unshieldedKeystore.getBech32Address();
|
|
103
94
|
const tNightBalance = state.unshielded.balances[unshieldedToken().raw] ?? 0n;
|
|
@@ -105,18 +96,23 @@ async function main() {
|
|
|
105
96
|
|
|
106
97
|
console.log('\n─── Wallet Details ─────────────────────────────────────────────\n');
|
|
107
98
|
console.log(` Address: ${address}`);
|
|
108
|
-
console.log(` Network:
|
|
99
|
+
console.log(` Network: ${networkConfig.networkId}\n`);
|
|
109
100
|
|
|
110
101
|
console.log('─── Balances ───────────────────────────────────────────────────\n');
|
|
111
102
|
console.log(` tNight: ${tNightBalance.toLocaleString()}`);
|
|
112
103
|
console.log(` DUST: ${dustBalance.toLocaleString()}\n`);
|
|
113
104
|
|
|
114
105
|
if (tNightBalance === 0n) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
106
|
+
if (network === 'undeployed') {
|
|
107
|
+
console.log(' ⚠ Wallet has no tNight. Make sure the local devnet is running');
|
|
108
|
+
console.log(' (npm run setup) — the genesis seed is pre-funded by the dev preset.\n');
|
|
109
|
+
} else if (networkConfig.faucet) {
|
|
110
|
+
console.log(` ⚠ Wallet has no tNight. Fund it from the faucet:`);
|
|
111
|
+
console.log(` ${networkConfig.faucet}`);
|
|
112
|
+
console.log(` Wallet address: ${address}\n`);
|
|
113
|
+
} else {
|
|
114
|
+
console.log(' ⚠ Wallet has no tNight.\n');
|
|
115
|
+
}
|
|
120
116
|
} else {
|
|
121
117
|
console.log(' ✅ Wallet is funded and ready!\n');
|
|
122
118
|
}
|
|
@@ -17,29 +17,23 @@ import { indexerPublicDataProvider } from '@midnight-ntwrk/midnight-js-indexer-p
|
|
|
17
17
|
import { levelPrivateStateProvider } from '@midnight-ntwrk/midnight-js-level-private-state-provider';
|
|
18
18
|
import { NodeZkConfigProvider } from '@midnight-ntwrk/midnight-js-node-zk-config-provider';
|
|
19
19
|
import { setNetworkId, getNetworkId } from '@midnight-ntwrk/midnight-js-network-id';
|
|
20
|
+
import { resolveNetwork, getOrCreateSeed, getDeployment } from './network';
|
|
20
21
|
import * as ledger from '@midnight-ntwrk/ledger-v8';
|
|
21
22
|
import { unshieldedToken } from '@midnight-ntwrk/ledger-v8';
|
|
22
23
|
import { WalletFacade } from '@midnight-ntwrk/wallet-sdk-facade';
|
|
23
24
|
import { DustWallet } from '@midnight-ntwrk/wallet-sdk-dust-wallet';
|
|
24
25
|
import { HDWallet, Roles } from '@midnight-ntwrk/wallet-sdk-hd';
|
|
25
26
|
import { ShieldedWallet } from '@midnight-ntwrk/wallet-sdk-shielded';
|
|
26
|
-
import { createKeystore,
|
|
27
|
+
import { createKeystore, NoOpTransactionHistoryStorage, PublicKey, UnshieldedWallet } from '@midnight-ntwrk/wallet-sdk-unshielded-wallet';
|
|
27
28
|
import { CompiledContract } from '@midnight-ntwrk/compact-js';
|
|
28
29
|
|
|
29
30
|
// Enable WebSocket for GraphQL subscriptions
|
|
30
31
|
// @ts-expect-error Required for wallet sync
|
|
31
32
|
globalThis.WebSocket = WebSocket;
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
setNetworkId(
|
|
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
|
-
};
|
|
34
|
+
const { network, config: networkConfig } = resolveNetwork();
|
|
35
|
+
setNetworkId(networkConfig.networkId);
|
|
36
|
+
const SEED = getOrCreateSeed(network);
|
|
43
37
|
|
|
44
38
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
45
39
|
const zkConfigPath = path.resolve(__dirname, '..', 'contracts', 'managed', 'hello-world');
|
|
@@ -80,10 +74,10 @@ async function createWallet(seed: string) {
|
|
|
80
74
|
|
|
81
75
|
const walletConfig = {
|
|
82
76
|
networkId,
|
|
83
|
-
indexerClientConnection: { indexerHttpUrl:
|
|
84
|
-
provingServerUrl: new URL(
|
|
85
|
-
relayURL: new URL(
|
|
86
|
-
txHistoryStorage: new
|
|
77
|
+
indexerClientConnection: { indexerHttpUrl: networkConfig.indexer, indexerWsUrl: networkConfig.indexerWS },
|
|
78
|
+
provingServerUrl: new URL(networkConfig.proofServer),
|
|
79
|
+
relayURL: new URL(networkConfig.node.replace(/^http/, 'ws')),
|
|
80
|
+
txHistoryStorage: new NoOpTransactionHistoryStorage(),
|
|
87
81
|
costParameters: { additionalFeeOverhead: 300_000_000_000_000n, feeBlocksMargin: 5 },
|
|
88
82
|
};
|
|
89
83
|
|
|
@@ -100,7 +94,10 @@ async function createWallet(seed: string) {
|
|
|
100
94
|
}
|
|
101
95
|
|
|
102
96
|
async function createProviders(walletCtx: ReturnType<typeof createWallet> extends Promise<infer T> ? T : never) {
|
|
103
|
-
|
|
97
|
+
// The SDK requires the private-state password to be at least 16 characters.
|
|
98
|
+
// The default below is a placeholder for local devnet only — set a strong
|
|
99
|
+
// password via PRIVATE_STATE_PASSWORD when you move to a non-local target.
|
|
100
|
+
const privateStatePassword = process.env.PRIVATE_STATE_PASSWORD?.trim() || 'Local-Devnet-Development-Placeholder-1';
|
|
104
101
|
|
|
105
102
|
const state = await walletCtx.wallet.waitForSyncedState();
|
|
106
103
|
|
|
@@ -130,9 +127,9 @@ async function createProviders(walletCtx: ReturnType<typeof createWallet> extend
|
|
|
130
127
|
accountId,
|
|
131
128
|
privateStoragePasswordProvider: () => privateStatePassword,
|
|
132
129
|
}),
|
|
133
|
-
publicDataProvider: indexerPublicDataProvider(
|
|
130
|
+
publicDataProvider: indexerPublicDataProvider(networkConfig.indexer, networkConfig.indexerWS),
|
|
134
131
|
zkConfigProvider,
|
|
135
|
-
proofProvider: httpClientProofProvider(
|
|
132
|
+
proofProvider: httpClientProofProvider(networkConfig.proofServer, zkConfigProvider),
|
|
136
133
|
walletProvider,
|
|
137
134
|
midnightProvider: walletProvider,
|
|
138
135
|
};
|
|
@@ -148,38 +145,52 @@ async function main() {
|
|
|
148
145
|
const rl = createInterface({ input: stdin, output: stdout });
|
|
149
146
|
|
|
150
147
|
// Check for deployment
|
|
151
|
-
|
|
152
|
-
|
|
148
|
+
const deployment = getDeployment(network);
|
|
149
|
+
if (!deployment) {
|
|
150
|
+
console.error(`No deploy on file for network ${network}. Run \`npm run setup -- --network ${network}\` first.`);
|
|
153
151
|
process.exit(1);
|
|
154
152
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
console.log(` Contract: ${deployment.contractAddress}`);
|
|
158
|
-
console.log(` Network: ${deployment.network || 'preprod'}\n`);
|
|
153
|
+
console.log(` Contract: ${deployment.address}`);
|
|
154
|
+
console.log(` Network: ${network}\n`);
|
|
159
155
|
|
|
160
156
|
try {
|
|
161
|
-
|
|
162
|
-
if (!fs.existsSync('.midnight-seed')) {
|
|
163
|
-
console.error('❌ No .midnight-seed file found! Run: npm run deploy\n');
|
|
164
|
-
process.exit(1);
|
|
165
|
-
}
|
|
166
|
-
const seed = fs.readFileSync('.midnight-seed', 'utf-8').trim();
|
|
157
|
+
const seed = SEED;
|
|
167
158
|
|
|
168
159
|
console.log(' Connecting to wallet...');
|
|
169
160
|
const walletCtx = await createWallet(seed);
|
|
170
161
|
|
|
171
162
|
console.log(' Syncing with network...');
|
|
163
|
+
console.log(' ℹ This may take several minutes depending on network size.');
|
|
164
|
+
console.log(' RPC disconnection messages during sync are normal and can be safely ignored.\n');
|
|
165
|
+
const syncStart = Date.now();
|
|
166
|
+
const syncInterval = setInterval(() => {
|
|
167
|
+
const elapsed = Math.round((Date.now() - syncStart) / 1000);
|
|
168
|
+
process.stdout.write(`\r ⏳ Still syncing... (${elapsed}s elapsed) `);
|
|
169
|
+
}, 5000);
|
|
172
170
|
const state = await walletCtx.wallet.waitForSyncedState();
|
|
171
|
+
clearInterval(syncInterval);
|
|
172
|
+
process.stdout.write('\r ✓ Synced with network. \n');
|
|
173
173
|
const balance = state.unshielded.balances[unshieldedToken().raw] ?? 0n;
|
|
174
174
|
console.log(` Balance: ${balance.toLocaleString()} tNight\n`);
|
|
175
175
|
|
|
176
|
+
// Surface a faucet hint when a public-network wallet has 0 tNIGHT.
|
|
177
|
+
// Reads (option 2) work without funds, but writes (option 1) need DUST
|
|
178
|
+
// generated from registered NIGHT — without this hint the next failure
|
|
179
|
+
// mode is a confusing "Insufficient Funds" deep inside the tx builder.
|
|
180
|
+
if (balance === 0n && network !== 'undeployed' && networkConfig.faucet) {
|
|
181
|
+
const address = walletCtx.unshieldedKeystore.getBech32Address();
|
|
182
|
+
console.log(' ⚠ Wallet has no tNight. Fund it from the faucet to send transactions:');
|
|
183
|
+
console.log(` ${networkConfig.faucet}`);
|
|
184
|
+
console.log(` Wallet address: ${address}\n`);
|
|
185
|
+
}
|
|
186
|
+
|
|
176
187
|
// Setup providers and connect to contract
|
|
177
188
|
console.log(' Connecting to contract...');
|
|
178
189
|
const providers = await createProviders(walletCtx);
|
|
179
190
|
|
|
180
191
|
const deployed: any = await findDeployedContract(providers, {
|
|
181
192
|
compiledContract: compiledContract as any,
|
|
182
|
-
contractAddress: deployment.
|
|
193
|
+
contractAddress: deployment.address,
|
|
183
194
|
});
|
|
184
195
|
|
|
185
196
|
console.log(' ✅ Connected!\n');
|
|
@@ -213,7 +224,7 @@ async function main() {
|
|
|
213
224
|
case '2': {
|
|
214
225
|
console.log('\n Reading message from blockchain...');
|
|
215
226
|
try {
|
|
216
|
-
const contractState = await providers.publicDataProvider.queryContractState(deployment.
|
|
227
|
+
const contractState = await providers.publicDataProvider.queryContractState(deployment.address);
|
|
217
228
|
if (contractState) {
|
|
218
229
|
const ledgerState = HelloWorld.ledger(contractState.data);
|
|
219
230
|
const message = Buffer.from(ledgerState.message).toString();
|