movehat 0.1.3 → 0.1.4

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.
Files changed (58) hide show
  1. package/README.md +159 -80
  2. package/dist/commands/run.d.ts.map +1 -1
  3. package/dist/commands/run.js +40 -3
  4. package/dist/commands/run.js.map +1 -1
  5. package/dist/core/AccountManager.d.ts +186 -0
  6. package/dist/core/AccountManager.d.ts.map +1 -0
  7. package/dist/core/AccountManager.js +363 -0
  8. package/dist/core/AccountManager.js.map +1 -0
  9. package/dist/fork/manager.d.ts +33 -0
  10. package/dist/fork/manager.d.ts.map +1 -1
  11. package/dist/fork/manager.js +61 -0
  12. package/dist/fork/manager.js.map +1 -1
  13. package/dist/fork/storage.d.ts +10 -0
  14. package/dist/fork/storage.d.ts.map +1 -1
  15. package/dist/fork/storage.js +28 -1
  16. package/dist/fork/storage.js.map +1 -1
  17. package/dist/helpers/index.d.ts +8 -1
  18. package/dist/helpers/index.d.ts.map +1 -1
  19. package/dist/helpers/index.js +4 -0
  20. package/dist/helpers/index.js.map +1 -1
  21. package/dist/helpers/setup.d.ts.map +1 -1
  22. package/dist/helpers/setup.js +5 -4
  23. package/dist/helpers/setup.js.map +1 -1
  24. package/dist/helpers/setupLocalTesting.d.ts +60 -0
  25. package/dist/helpers/setupLocalTesting.d.ts.map +1 -0
  26. package/dist/helpers/setupLocalTesting.js +277 -0
  27. package/dist/helpers/setupLocalTesting.js.map +1 -0
  28. package/dist/helpers/testFixtures.d.ts +115 -0
  29. package/dist/helpers/testFixtures.d.ts.map +1 -0
  30. package/dist/helpers/testFixtures.js +163 -0
  31. package/dist/helpers/testFixtures.js.map +1 -0
  32. package/dist/node/LocalNodeManager.d.ts +68 -0
  33. package/dist/node/LocalNodeManager.d.ts.map +1 -0
  34. package/dist/node/LocalNodeManager.js +237 -0
  35. package/dist/node/LocalNodeManager.js.map +1 -0
  36. package/dist/runtime.d.ts.map +1 -1
  37. package/dist/runtime.js +91 -46
  38. package/dist/runtime.js.map +1 -1
  39. package/dist/templates/README.md +17 -4
  40. package/dist/templates/move/sources/Counter.move +12 -4
  41. package/dist/templates/tests/Counter.test.ts +98 -57
  42. package/dist/types/config.d.ts +24 -0
  43. package/dist/types/config.d.ts.map +1 -1
  44. package/package.json +1 -1
  45. package/src/commands/run.ts +43 -3
  46. package/src/core/AccountManager.ts +439 -0
  47. package/src/fork/manager.ts +74 -0
  48. package/src/fork/storage.ts +33 -1
  49. package/src/helpers/index.ts +18 -1
  50. package/src/helpers/setup.ts +4 -3
  51. package/src/helpers/setupLocalTesting.ts +335 -0
  52. package/src/helpers/testFixtures.ts +222 -0
  53. package/src/node/LocalNodeManager.ts +297 -0
  54. package/src/runtime.ts +108 -47
  55. package/src/templates/README.md +17 -4
  56. package/src/templates/move/sources/Counter.move +12 -4
  57. package/src/templates/tests/Counter.test.ts +98 -57
  58. package/src/types/config.ts +32 -0
@@ -0,0 +1,335 @@
1
+ import { join } from "path";
2
+ import { existsSync } from "fs";
3
+ import { initRuntime } from "../runtime.js";
4
+ import type { MovehatRuntime } from "../types/runtime.js";
5
+ import { ForkManager } from "../fork/manager.js";
6
+ import { ForkServer } from "../fork/server.js";
7
+ import { LocalNodeManager } from "../node/LocalNodeManager.js";
8
+ import { AccountManager } from "../core/AccountManager.js";
9
+ import type { LocalTestOptions } from "../types/config.js";
10
+
11
+ let currentForkServer: ForkServer | null = null;
12
+ let currentForkManager: ForkManager | null = null;
13
+ let currentLocalNode: LocalNodeManager | null = null;
14
+
15
+ /**
16
+ * Setup a local testing environment with either a local node or fork server
17
+ *
18
+ * This function provides a complete local testing setup similar to Hardhat:
19
+ *
20
+ * **Local Node Mode** (default, recommended):
21
+ * 1. Starts a full Movement node locally
22
+ * 2. Generates and funds test accounts from local faucet
23
+ * 3. Auto-deploys modules (works because node can process transactions)
24
+ * 4. Returns runtime ready to use
25
+ *
26
+ * **Fork Mode** (faster, read-only):
27
+ * 1. Creates/loads a fork of testnet
28
+ * 2. Starts a fork server
29
+ * 3. Generates and funds accounts (in fork state only)
30
+ * 4. Cannot auto-deploy (fork is read-only)
31
+ * 5. Returns runtime for reading data
32
+ *
33
+ * @param options Configuration options for local testing
34
+ * @returns MovehatRuntime configured for local testing
35
+ *
36
+ * @example
37
+ * ```typescript
38
+ * // Local node mode (default) - Full blockchain, can deploy
39
+ * const mh = await setupLocalTesting({
40
+ * mode: 'local-node',
41
+ * accountLabels: ['alice', 'bob'],
42
+ * autoDeploy: ['counter'], // ✅ Works!
43
+ * });
44
+ *
45
+ * // Fork mode - Fast, read-only
46
+ * const mh = await setupLocalTesting({
47
+ * mode: 'fork',
48
+ * accountLabels: ['alice', 'bob'],
49
+ * autoDeploy: ['counter'], // ❌ Won't work (fork can't deploy)
50
+ * });
51
+ * ```
52
+ */
53
+ export async function setupLocalTesting(
54
+ options: LocalTestOptions = {}
55
+ ): Promise<MovehatRuntime> {
56
+ // Default options
57
+ const mode = options.mode || 'local-node'; // Default to local node
58
+ const autoFund = options.autoFund !== false; // default true
59
+ const defaultBalance = options.defaultBalance || 100_000_000; // 100 APT
60
+ const accountLabels = options.accountLabels || ["deployer", "alice", "bob"];
61
+
62
+ console.log(`\n🔧 Setting up local testing environment...`);
63
+ console.log(` Mode: ${mode}`);
64
+ console.log(` Accounts: ${accountLabels.join(", ")}\n`);
65
+
66
+ if (mode === 'local-node') {
67
+ return await setupWithLocalNode(options, accountLabels, autoFund, defaultBalance);
68
+ } else {
69
+ return await setupWithFork(options, accountLabels, autoFund, defaultBalance);
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Setup using local Movement node (full blockchain)
75
+ */
76
+ async function setupWithLocalNode(
77
+ options: LocalTestOptions,
78
+ accountLabels: readonly string[],
79
+ autoFund: boolean,
80
+ defaultBalance: number
81
+ ): Promise<MovehatRuntime> {
82
+ const nodeTestDir = options.nodeTestDir || join(process.cwd(), ".movehat", "local-node");
83
+ const nodeForceRestart = options.nodeForceRestart !== false; // default true
84
+ const nodeFaucetPort = options.nodeFaucetPort || 8081;
85
+ const nodeApiPort = options.nodeApiPort || 8080;
86
+ const nodeReadyPort = options.nodeReadyPort || 8070;
87
+ const nodeSilent = options.nodeSilent ?? false;
88
+
89
+ // 1. Start local node
90
+ const localNode = new LocalNodeManager({
91
+ testDir: nodeTestDir,
92
+ forceRestart: nodeForceRestart,
93
+ faucetPort: nodeFaucetPort,
94
+ apiPort: nodeApiPort,
95
+ readyPort: nodeReadyPort,
96
+ silent: nodeSilent,
97
+ });
98
+
99
+ currentLocalNode = localNode;
100
+
101
+ const nodeInfo = await localNode.start();
102
+
103
+ // 2. Generate accounts with AccountManager
104
+ console.log(`👥 Generating ${accountLabels.length} test accounts...`);
105
+ const accounts = AccountManager.createBatch(accountLabels);
106
+
107
+ for (const [label, account] of Object.entries(accounts)) {
108
+ console.log(` ${label}: ${account.accountAddress.toString()}`);
109
+ }
110
+ console.log();
111
+
112
+ // 3. Fund accounts from local faucet
113
+ if (autoFund) {
114
+ const accountsList = Object.values(accounts);
115
+ await localNode.fundAccounts(accountsList, defaultBalance);
116
+ }
117
+
118
+ // 4. Initialize runtime pointing to local node
119
+ console.log(`⚙️ Initializing runtime for local network...`);
120
+
121
+ const deployerPrivateKey = AccountManager.exportPrivateKeys(["deployer"]).deployer;
122
+
123
+ if (!deployerPrivateKey) {
124
+ throw new Error("Failed to get deployer private key");
125
+ }
126
+
127
+ const runtime = await initRuntime({
128
+ network: "local",
129
+ configOverride: {
130
+ networks: {
131
+ local: {
132
+ url: `${nodeInfo.rpcUrl}/v1`,
133
+ chainId: "local",
134
+ },
135
+ },
136
+ accounts: [deployerPrivateKey],
137
+ },
138
+ });
139
+
140
+ console.log(`✓ Runtime initialized\n`);
141
+
142
+ // 5. Auto-deploy modules if specified
143
+ if (options.autoDeploy && options.autoDeploy.length > 0) {
144
+ console.log(`📦 Auto-deploying ${options.autoDeploy.length} module(s)...`);
145
+
146
+ // Force redeploy in local-node mode (for testing)
147
+ const previousRedeploy = process.env.MH_CLI_REDEPLOY;
148
+ process.env.MH_CLI_REDEPLOY = 'true';
149
+
150
+ try {
151
+ for (const moduleName of options.autoDeploy) {
152
+ try {
153
+ console.log(` Deploying ${moduleName}...`);
154
+ await runtime.deployContract(moduleName);
155
+ console.log(` ✓ ${moduleName} deployed`);
156
+ } catch (error: any) {
157
+ console.error(` ✗ Failed to deploy ${moduleName}: ${error.message}`);
158
+ throw error;
159
+ }
160
+ }
161
+ } finally {
162
+ // Restore previous value
163
+ if (previousRedeploy === undefined) {
164
+ delete process.env.MH_CLI_REDEPLOY;
165
+ } else {
166
+ process.env.MH_CLI_REDEPLOY = previousRedeploy;
167
+ }
168
+ }
169
+
170
+ console.log();
171
+ }
172
+
173
+ console.log(`✅ Local testing environment ready!\n`);
174
+ console.log(` Mode: local-node`);
175
+ console.log(` RPC: ${nodeInfo.rpcUrl}/v1`);
176
+ console.log(` Faucet: ${nodeInfo.faucetUrl}`);
177
+ console.log(` Accounts: ${Array.from(accountLabels).join(", ")}`);
178
+ console.log(` Balance per account: ${defaultBalance / 100_000_000} APT\n`);
179
+
180
+ return runtime;
181
+ }
182
+
183
+ /**
184
+ * Setup using fork server (read-only)
185
+ */
186
+ async function setupWithFork(
187
+ options: LocalTestOptions,
188
+ accountLabels: readonly string[],
189
+ autoFund: boolean,
190
+ defaultBalance: number
191
+ ): Promise<MovehatRuntime> {
192
+ const forkNetwork = options.forkNetwork || "testnet";
193
+ const forkName = options.forkName || "test-local";
194
+ const forkPort = options.forkPort || 8080;
195
+ const forkResetState = options.forkResetState !== false; // default true
196
+
197
+ console.log(` Fork network: ${forkNetwork}`);
198
+ console.log(` Fork name: ${forkName}`);
199
+ console.log(` Server port: ${forkPort}\n`);
200
+
201
+ // Warn about auto-deploy in fork mode
202
+ if (options.autoDeploy && options.autoDeploy.length > 0) {
203
+ console.warn(`⚠️ WARNING: Auto-deploy doesn't work in fork mode (read-only).`);
204
+ console.warn(` Switch to 'local-node' mode for deployment support.\n`);
205
+ }
206
+
207
+ // 1. Setup fork
208
+ const forkPath = join(process.cwd(), ".movehat", "forks", forkName);
209
+ const forkManager = new ForkManager(forkPath);
210
+ currentForkManager = forkManager;
211
+
212
+ const forkExists = existsSync(join(forkPath, "metadata.json"));
213
+
214
+ if (!forkExists) {
215
+ console.log(`📸 Creating fork from ${forkNetwork}...`);
216
+ const testnetRpc = "https://testnet.movementnetwork.xyz/v1";
217
+ await forkManager.initialize(testnetRpc, forkNetwork);
218
+ console.log(`✓ Fork created at ${forkPath}\n`);
219
+ } else {
220
+ console.log(`✓ Loading existing fork from ${forkPath}`);
221
+ forkManager.load();
222
+
223
+ if (forkResetState) {
224
+ console.log(`🔄 Resetting fork state...`);
225
+ await forkManager.resetState();
226
+ }
227
+
228
+ console.log();
229
+ }
230
+
231
+ // 2. Start fork server
232
+ console.log(`🚀 Starting fork server on port ${forkPort}...`);
233
+ const forkServer = new ForkServer(forkPath, forkPort);
234
+ currentForkServer = forkServer;
235
+
236
+ await forkServer.start();
237
+ console.log(`✓ Fork server running at http://localhost:${forkPort}\n`);
238
+
239
+ await new Promise((resolve) => setTimeout(resolve, 500));
240
+
241
+ // 3. Generate accounts
242
+ console.log(`👥 Generating ${accountLabels.length} test accounts...`);
243
+ const accounts = AccountManager.createBatch(accountLabels);
244
+
245
+ for (const [label, account] of Object.entries(accounts)) {
246
+ console.log(` ${label}: ${account.accountAddress.toString()}`);
247
+ }
248
+ console.log();
249
+
250
+ // 4. Fund accounts in fork
251
+ if (autoFund) {
252
+ const addresses = Object.values(accounts).map((acc) =>
253
+ acc.accountAddress.toString()
254
+ );
255
+ await forkManager.fundMultipleAccounts(addresses, defaultBalance);
256
+ }
257
+
258
+ // 5. Initialize runtime pointing to fork
259
+ console.log(`⚙️ Initializing runtime for local network...`);
260
+
261
+ const deployerPrivateKey = AccountManager.exportPrivateKeys(["deployer"]).deployer;
262
+
263
+ if (!deployerPrivateKey) {
264
+ throw new Error("Failed to get deployer private key");
265
+ }
266
+
267
+ const runtime = await initRuntime({
268
+ network: "local",
269
+ configOverride: {
270
+ networks: {
271
+ local: {
272
+ url: `http://localhost:${forkPort}/v1`,
273
+ chainId: "local",
274
+ },
275
+ },
276
+ accounts: [deployerPrivateKey],
277
+ },
278
+ });
279
+
280
+ console.log(`✓ Runtime initialized\n`);
281
+ console.log(`✅ Local testing environment ready!\n`);
282
+ console.log(` Mode: fork (read-only)`);
283
+ console.log(` RPC: http://localhost:${forkPort}/v1`);
284
+ console.log(` Accounts: ${Array.from(accountLabels).join(", ")}`);
285
+ console.log(` Balance per account: ${defaultBalance / 100_000_000} APT\n`);
286
+
287
+ return runtime;
288
+ }
289
+
290
+ /**
291
+ * Stop the local testing environment (cleanup)
292
+ */
293
+ export async function stopLocalTesting(): Promise<void> {
294
+ console.log(`\n🛑 Stopping local testing environment...`);
295
+
296
+ // Stop local node if running
297
+ if (currentLocalNode) {
298
+ await currentLocalNode.stop();
299
+ currentLocalNode = null;
300
+ }
301
+
302
+ // Stop fork server if running
303
+ if (currentForkServer) {
304
+ await currentForkServer.stop();
305
+ currentForkServer = null;
306
+ currentForkManager = null;
307
+ }
308
+
309
+ console.log(`✓ Environment stopped\n`);
310
+ }
311
+
312
+ /**
313
+ * Get the current fork manager (if fork mode is active)
314
+ */
315
+ export function getCurrentForkManager(): ForkManager | null {
316
+ return currentForkManager;
317
+ }
318
+
319
+ /**
320
+ * Get the current local node (if local node mode is active)
321
+ */
322
+ export function getCurrentLocalNode(): LocalNodeManager | null {
323
+ return currentLocalNode;
324
+ }
325
+
326
+ /**
327
+ * Reset fork state to initial snapshot (fork mode only)
328
+ */
329
+ export async function resetForkState(): Promise<void> {
330
+ if (currentForkManager) {
331
+ await currentForkManager.resetState();
332
+ } else {
333
+ console.warn("Warning: No active fork manager to reset");
334
+ }
335
+ }
@@ -0,0 +1,222 @@
1
+ import type { Account } from "@aptos-labs/ts-sdk";
2
+ import type { MovehatRuntime } from "../types/runtime.js";
3
+ import type { MoveContract } from "../core/contract.js";
4
+ import { AccountManager } from "../core/AccountManager.js";
5
+ import { setupLocalTesting, stopLocalTesting } from "./setupLocalTesting.js";
6
+ import type { LocalTestOptions } from "../types/config.js";
7
+
8
+ /**
9
+ * Test fixture with pre-configured accounts and contracts
10
+ *
11
+ * @template TModules - Union type of module names for type-safe contract access
12
+ */
13
+ export interface TestFixture<TModules extends string = string> {
14
+ /** Movehat runtime instance */
15
+ mh: MovehatRuntime;
16
+
17
+ /** Named accounts (deployer, alice, bob, etc.) */
18
+ accounts: {
19
+ deployer: Account;
20
+ alice: Account;
21
+ bob: Account;
22
+ [key: string]: Account;
23
+ };
24
+
25
+ /** Deployed contracts by module name - type-safe based on modules parameter */
26
+ contracts: Record<TModules, MoveContract>;
27
+ }
28
+
29
+ /**
30
+ * Setup a complete test fixture with local fork, accounts, and deployed contracts
31
+ *
32
+ * This is the recommended way to setup tests in movehat. It provides:
33
+ * - Local fork server (no testnet required)
34
+ * - Pre-funded labeled accounts
35
+ * - Auto-deployment of specified modules
36
+ * - Contract instances ready to use
37
+ *
38
+ * @param modules Array of module names to auto-deploy
39
+ * @param accountLabels Optional array of account labels (defaults to ['alice', 'bob'])
40
+ * @param options Optional LocalTestOptions for advanced configuration
41
+ * @returns TestFixture with runtime, accounts, and contracts
42
+ *
43
+ * @example
44
+ * ```typescript
45
+ * import { setupTestFixture } from "movehat/helpers";
46
+ *
47
+ * describe("Counter Contract", () => {
48
+ * let fixture: TestFixture;
49
+ *
50
+ * before(async function () {
51
+ * this.timeout(60000); // Allow time for fork + deployment
52
+ *
53
+ * fixture = await setupTestFixture(['counter'], ['alice', 'bob']);
54
+ * });
55
+ *
56
+ * it("should initialize with value 0", async () => {
57
+ * const counter = fixture.contracts.counter;
58
+ * const value = await counter.view<number>("get", [
59
+ * fixture.accounts.deployer.accountAddress.toString()
60
+ * ]);
61
+ *
62
+ * expect(value).to.equal(0);
63
+ * });
64
+ *
65
+ * it("alice can increment counter", async () => {
66
+ * const tx = await fixture.contracts.counter.call(
67
+ * fixture.accounts.alice,
68
+ * "increment",
69
+ * []
70
+ * );
71
+ *
72
+ * expect(tx.success).to.be.true;
73
+ * });
74
+ *
75
+ * after(async () => {
76
+ * await teardownTestFixture();
77
+ * });
78
+ * });
79
+ * ```
80
+ */
81
+ export async function setupTestFixture<TModules extends readonly string[]>(
82
+ modules: TModules,
83
+ accountLabels: string[] = ["alice", "bob"],
84
+ options: Partial<LocalTestOptions> = {}
85
+ ): Promise<TestFixture<TModules[number]>> {
86
+ console.log(`\n🧪 Setting up test fixture...`);
87
+ console.log(` Modules to deploy: ${modules.join(", ")}`);
88
+ console.log(` Account labels: deployer, ${accountLabels.join(", ")}\n`);
89
+
90
+ // Ensure 'deployer' is always in the account labels
91
+ const allLabels = ["deployer", ...accountLabels.filter((l) => l !== "deployer")];
92
+
93
+ // Setup local testing environment with auto-deploy
94
+ const setupOptions: LocalTestOptions = {
95
+ ...options,
96
+ accountLabels: allLabels,
97
+ autoDeploy: modules, // Auto-deploy specified modules
98
+ };
99
+
100
+ const mh = await setupLocalTesting(setupOptions);
101
+
102
+ // Get all labeled accounts
103
+ const labeledAccounts = AccountManager.getLabeledAccounts();
104
+
105
+ // Build accounts object with required accounts
106
+ const accounts: any = {
107
+ deployer: labeledAccounts.deployer!,
108
+ };
109
+
110
+ // Add other labeled accounts
111
+ for (const label of accountLabels) {
112
+ accounts[label] = labeledAccounts[label] || AccountManager.getOrCreateLabeled(label);
113
+ }
114
+
115
+ // Build contracts object - TypeScript will infer the correct type
116
+ const contracts = {} as Record<TModules[number], MoveContract>;
117
+
118
+ for (const moduleName of modules) {
119
+ // Get deployed contract instance
120
+ const deploymentAddress = mh.getDeploymentAddress(moduleName);
121
+
122
+ if (!deploymentAddress) {
123
+ throw new Error(
124
+ `Module "${moduleName}" was not deployed. Check auto-deploy logs.`
125
+ );
126
+ }
127
+
128
+ contracts[moduleName as TModules[number]] = mh.getContract(deploymentAddress, moduleName);
129
+ console.log(` ✓ Contract "${moduleName}" ready at ${deploymentAddress}`);
130
+ }
131
+
132
+ console.log(`\n✅ Test fixture ready!\n`);
133
+
134
+ return {
135
+ mh,
136
+ accounts,
137
+ contracts,
138
+ } as TestFixture<TModules[number]>;
139
+ }
140
+
141
+ /**
142
+ * Teardown test fixture and cleanup resources
143
+ *
144
+ * Call this in your test suite's `after` hook to properly cleanup:
145
+ * - Stops fork server
146
+ * - Clears account pool
147
+ *
148
+ * @example
149
+ * ```typescript
150
+ * after(async () => {
151
+ * await teardownTestFixture();
152
+ * });
153
+ * ```
154
+ */
155
+ export async function teardownTestFixture(): Promise<void> {
156
+ console.log(`\n🧹 Tearing down test fixture...`);
157
+
158
+ // Stop fork server
159
+ await stopLocalTesting();
160
+
161
+ // Clear account pool for test isolation
162
+ AccountManager.clearPool();
163
+
164
+ console.log(`✓ Teardown complete\n`);
165
+ }
166
+
167
+ /**
168
+ * Create a minimal test fixture without auto-deployment
169
+ * Useful when you want to deploy contracts manually in tests
170
+ *
171
+ * @param accountLabels Account labels to create (defaults to ['alice', 'bob'])
172
+ * @param options Optional LocalTestOptions
173
+ * @returns Partial TestFixture (without contracts)
174
+ *
175
+ * @example
176
+ * ```typescript
177
+ * let fixture: Partial<TestFixture>;
178
+ *
179
+ * before(async function () {
180
+ * this.timeout(30000);
181
+ * fixture = await setupMinimalFixture(['alice', 'bob', 'charlie']);
182
+ * });
183
+ *
184
+ * it("should deploy contract manually", async () => {
185
+ * await fixture.mh!.deployContract("counter");
186
+ * // ...
187
+ * });
188
+ * ```
189
+ */
190
+ export async function setupMinimalFixture(
191
+ accountLabels: string[] = ["alice", "bob"],
192
+ options: Partial<LocalTestOptions> = {}
193
+ ): Promise<Omit<TestFixture, "contracts">> {
194
+ console.log(`\n🧪 Setting up minimal test fixture (no auto-deploy)...`);
195
+
196
+ const allLabels = ["deployer", ...accountLabels.filter((l) => l !== "deployer")];
197
+
198
+ const setupOptions: LocalTestOptions = {
199
+ ...options,
200
+ accountLabels: allLabels,
201
+ autoDeploy: [], // No auto-deploy
202
+ };
203
+
204
+ const mh = await setupLocalTesting(setupOptions);
205
+
206
+ const labeledAccounts = AccountManager.getLabeledAccounts();
207
+
208
+ const accounts: any = {
209
+ deployer: labeledAccounts.deployer!,
210
+ };
211
+
212
+ for (const label of accountLabels) {
213
+ accounts[label] = labeledAccounts[label] || AccountManager.getOrCreateLabeled(label);
214
+ }
215
+
216
+ console.log(`\n✅ Minimal fixture ready!\n`);
217
+
218
+ return {
219
+ mh,
220
+ accounts,
221
+ };
222
+ }