movehat 0.2.6 → 0.2.8
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/cli.js +1 -1
- package/dist/commands/fork/fund.js +3 -1
- package/dist/commands/fork/serve.js +10 -5
- package/dist/commands/test-move.js +6 -3
- package/dist/core/AccountManager.d.ts +121 -139
- package/dist/core/AccountManager.js +217 -158
- package/dist/fork/api.d.ts +1 -1
- package/dist/fork/api.js +9 -4
- package/dist/fork/manager.d.ts +2 -2
- package/dist/fork/manager.js +4 -8
- package/dist/fork/storage.js +5 -4
- package/dist/fork/test.d.ts +1 -1
- package/dist/fork/validation.d.ts +9 -0
- package/dist/fork/validation.js +88 -0
- package/dist/harness/Harness.d.ts +35 -6
- package/dist/harness/Harness.js +36 -5
- package/dist/helpers/index.d.ts +1 -0
- package/dist/helpers/index.js +1 -0
- package/dist/helpers/move-tests.js +8 -5
- package/dist/helpers/setup.js +6 -3
- package/dist/helpers/setupLocalTesting.d.ts +2 -2
- package/dist/helpers/setupLocalTesting.js +55 -24
- package/dist/helpers/testFixtures.d.ts +5 -4
- package/dist/helpers/testFixtures.js +4 -5
- package/dist/helpers/version-check.js +4 -2
- package/dist/index.d.ts +3 -1
- package/dist/node/MoveliteManager.d.ts +18 -0
- package/dist/node/MoveliteManager.js +152 -0
- package/dist/node/NodeProvider.d.ts +9 -0
- package/dist/node/NodeProvider.js +1 -0
- package/dist/runtime.d.ts +13 -0
- package/dist/runtime.js +8 -4
- package/dist/templates/.mocharc.json +1 -0
- package/dist/templates/tests/Counter.test.ts +12 -14
- package/dist/templates/tests/setup.ts +34 -0
- package/dist/types/config.d.ts +2 -0
- package/dist/types/fork.d.ts +24 -0
- package/dist/types/runtime.d.ts +2 -0
- package/package.json +4 -1
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { execSync, spawn } from "child_process";
|
|
2
|
+
import { existsSync } from "fs";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { createRequire } from "module";
|
|
5
|
+
import { logger } from "../ui/index.js";
|
|
6
|
+
export class MoveliteManager {
|
|
7
|
+
process = null;
|
|
8
|
+
port;
|
|
9
|
+
killed = false;
|
|
10
|
+
binaryPath;
|
|
11
|
+
constructor(binaryPath, port = 8090) {
|
|
12
|
+
this.binaryPath = binaryPath;
|
|
13
|
+
this.port = port;
|
|
14
|
+
}
|
|
15
|
+
async start() {
|
|
16
|
+
const binary = this.binaryPath;
|
|
17
|
+
if (!binary) {
|
|
18
|
+
throw new Error("movelite binary not found");
|
|
19
|
+
}
|
|
20
|
+
if (await this.isPortInUse(this.port)) {
|
|
21
|
+
this.port = this.port + 1;
|
|
22
|
+
if (await this.isPortInUse(this.port)) {
|
|
23
|
+
throw new Error(`Ports ${this.port - 1} and ${this.port} are in use`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
logger.step("Starting movelite...");
|
|
27
|
+
this.process = spawn(binary, ["start", "--port", String(this.port), "--no-auth"], {
|
|
28
|
+
stdio: "pipe",
|
|
29
|
+
detached: false,
|
|
30
|
+
});
|
|
31
|
+
this.process.on("exit", () => {
|
|
32
|
+
this.process = null;
|
|
33
|
+
});
|
|
34
|
+
await this.waitForReady();
|
|
35
|
+
logger.success(`movelite ready on port ${this.port}`);
|
|
36
|
+
return this.getNodeInfo();
|
|
37
|
+
}
|
|
38
|
+
async waitForReady() {
|
|
39
|
+
const url = `http://127.0.0.1:${this.port}/v1`;
|
|
40
|
+
const timeout = 15_000;
|
|
41
|
+
const start = Date.now();
|
|
42
|
+
while (Date.now() - start < timeout) {
|
|
43
|
+
try {
|
|
44
|
+
const res = await fetch(url);
|
|
45
|
+
if (res.ok)
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// not ready yet
|
|
50
|
+
}
|
|
51
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
52
|
+
}
|
|
53
|
+
throw new Error(`movelite did not become ready within ${timeout}ms`);
|
|
54
|
+
}
|
|
55
|
+
async fundAccount(address, amount) {
|
|
56
|
+
const res = await fetch(`http://127.0.0.1:${this.port}/mint?address=${address}&amount=${amount}`, { method: "POST" });
|
|
57
|
+
if (!res.ok) {
|
|
58
|
+
throw new Error(`Failed to fund account: ${res.status}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async fundAccounts(accounts, balance) {
|
|
62
|
+
for (const account of accounts) {
|
|
63
|
+
await this.fundAccount(account.accountAddress.toString(), balance);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async stop() {
|
|
67
|
+
if (!this.process || this.killed)
|
|
68
|
+
return;
|
|
69
|
+
this.killed = true;
|
|
70
|
+
this.process.kill("SIGTERM");
|
|
71
|
+
await new Promise((resolve) => {
|
|
72
|
+
const timer = setTimeout(() => {
|
|
73
|
+
if (this.process)
|
|
74
|
+
this.process.kill("SIGKILL");
|
|
75
|
+
resolve();
|
|
76
|
+
}, 5_000);
|
|
77
|
+
if (this.process) {
|
|
78
|
+
this.process.on("exit", () => {
|
|
79
|
+
clearTimeout(timer);
|
|
80
|
+
resolve();
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
clearTimeout(timer);
|
|
85
|
+
resolve();
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
this.process = null;
|
|
89
|
+
}
|
|
90
|
+
async isPortInUse(port) {
|
|
91
|
+
try {
|
|
92
|
+
const res = await fetch(`http://127.0.0.1:${port}/v1`);
|
|
93
|
+
return res.ok;
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
isRunning() {
|
|
100
|
+
return this.process !== null && !this.killed;
|
|
101
|
+
}
|
|
102
|
+
getNodeInfo() {
|
|
103
|
+
return {
|
|
104
|
+
rpcUrl: `http://127.0.0.1:${this.port}`,
|
|
105
|
+
faucetUrl: `http://127.0.0.1:${this.port}`,
|
|
106
|
+
readyUrl: `http://127.0.0.1:${this.port}/v1`,
|
|
107
|
+
testDir: "",
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
export function findMoveliteBinary() {
|
|
112
|
+
if (process.env.MOVELITE_PATH) {
|
|
113
|
+
return existsSync(process.env.MOVELITE_PATH)
|
|
114
|
+
? process.env.MOVELITE_PATH
|
|
115
|
+
: null;
|
|
116
|
+
}
|
|
117
|
+
const platforms = {
|
|
118
|
+
"darwin-arm64": "movelite-darwin-arm64",
|
|
119
|
+
"darwin-x64": "movelite-darwin-x64",
|
|
120
|
+
"linux-x64": "movelite-linux-x64",
|
|
121
|
+
"linux-arm64": "movelite-linux-arm64",
|
|
122
|
+
};
|
|
123
|
+
const key = `${process.platform}-${process.arch}`;
|
|
124
|
+
const pkg = platforms[key];
|
|
125
|
+
if (pkg) {
|
|
126
|
+
try {
|
|
127
|
+
// Resolve through the `movelite` shim (movehat's direct dependency),
|
|
128
|
+
// then resolve the platform package from movelite's own context. The
|
|
129
|
+
// platform package is movelite's dependency, not movehat's, so a direct
|
|
130
|
+
// resolve from here fails under pnpm/yarn strict layouts.
|
|
131
|
+
const req = createRequire(import.meta.url);
|
|
132
|
+
const movelitePkg = req.resolve("movelite/package.json");
|
|
133
|
+
const moveliteReq = createRequire(movelitePkg);
|
|
134
|
+
const pkgPath = moveliteReq.resolve(`${pkg}/package.json`);
|
|
135
|
+
const binPath = join(pkgPath, "..", "bin", "movelite");
|
|
136
|
+
if (existsSync(binPath))
|
|
137
|
+
return binPath;
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
// package not installed
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
try {
|
|
144
|
+
const found = execSync("which movelite", { encoding: "utf-8" }).trim();
|
|
145
|
+
if (found && existsSync(found))
|
|
146
|
+
return found;
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
// not in PATH
|
|
150
|
+
}
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Account } from "@aptos-labs/ts-sdk";
|
|
2
|
+
import type { LocalNodeInfo } from "./LocalNodeManager.js";
|
|
3
|
+
export interface NodeProvider {
|
|
4
|
+
start(): Promise<LocalNodeInfo>;
|
|
5
|
+
stop(): Promise<void>;
|
|
6
|
+
isRunning(): boolean;
|
|
7
|
+
getNodeInfo(): LocalNodeInfo;
|
|
8
|
+
fundAccounts(accounts: Account[], balance: number): Promise<void>;
|
|
9
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/runtime.d.ts
CHANGED
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
import { MovehatRuntime } from "./types/runtime.js";
|
|
2
2
|
import { MovehatUserConfig } from "./types/config.js";
|
|
3
|
+
import { AccountManager } from "./core/AccountManager.js";
|
|
3
4
|
export interface InitRuntimeOptions {
|
|
4
5
|
network?: string;
|
|
5
6
|
accountIndex?: number;
|
|
6
7
|
configOverride?: Partial<MovehatUserConfig>;
|
|
8
|
+
/**
|
|
9
|
+
* Optional pre-constructed `AccountManager` instance. When supplied,
|
|
10
|
+
* the returned runtime's `accountManager` field is THIS instance —
|
|
11
|
+
* so labeled accounts created BEFORE `initRuntime` are visible on
|
|
12
|
+
* the runtime. When omitted, `initRuntime` constructs a fresh
|
|
13
|
+
* `new AccountManager()` per call.
|
|
14
|
+
*
|
|
15
|
+
* `setupLocalTesting` uses this option to thread one manager through
|
|
16
|
+
* `createBatch` → `exportPrivateKeys` → `initRuntime` so the deployer
|
|
17
|
+
* key it extracts ends up on the same manager the runtime exposes.
|
|
18
|
+
*/
|
|
19
|
+
accountManager?: AccountManager;
|
|
7
20
|
}
|
|
8
21
|
/**
|
|
9
22
|
* Initialize the Movehat Runtime Environment.
|
package/dist/runtime.js
CHANGED
|
@@ -35,9 +35,12 @@ export async function initRuntime(options = {}) {
|
|
|
35
35
|
fullnode: config.rpc,
|
|
36
36
|
});
|
|
37
37
|
const aptos = new Aptos(aptosConfig);
|
|
38
|
-
// Setup accounts using AccountManager
|
|
38
|
+
// Setup accounts using AccountManager. Use the caller-supplied
|
|
39
|
+
// instance when threaded in (preserves labels created before
|
|
40
|
+
// initRuntime); otherwise construct a fresh one.
|
|
41
|
+
const accountManager = options.accountManager ?? new AccountManager();
|
|
39
42
|
const accountIndex = options.accountIndex || 0;
|
|
40
|
-
const accounts =
|
|
43
|
+
const accounts = accountManager.loadAccountsFromConfig(config);
|
|
41
44
|
// Primary account (accounts[0] or selected index)
|
|
42
45
|
const account = accounts[accountIndex];
|
|
43
46
|
if (!account) {
|
|
@@ -73,10 +76,10 @@ export async function initRuntime(options = {}) {
|
|
|
73
76
|
return getDeployedAddress(config.network, moduleName);
|
|
74
77
|
};
|
|
75
78
|
const createAccount = () => {
|
|
76
|
-
return
|
|
79
|
+
return accountManager.createAccount();
|
|
77
80
|
};
|
|
78
81
|
const getAccountHelper = (privateKeyHex) => {
|
|
79
|
-
return
|
|
82
|
+
return accountManager.loadAccountFromPrivateKey(privateKeyHex);
|
|
80
83
|
};
|
|
81
84
|
const getAccountByIndex = (index) => {
|
|
82
85
|
const acc = accounts[index];
|
|
@@ -95,6 +98,7 @@ export async function initRuntime(options = {}) {
|
|
|
95
98
|
aptos,
|
|
96
99
|
account,
|
|
97
100
|
accounts,
|
|
101
|
+
accountManager,
|
|
98
102
|
getContract: getContractHelper,
|
|
99
103
|
deployContract,
|
|
100
104
|
getDeployment,
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// @ts-nocheck - This is a template file, dependencies are installed in user projects
|
|
2
2
|
import { describe, it, before, after } from "mocha";
|
|
3
3
|
import { expect } from "chai";
|
|
4
|
-
import { Harness
|
|
4
|
+
import { Harness } from "movehat";
|
|
5
|
+
import { getSharedNode } from "./setup.js";
|
|
5
6
|
|
|
6
7
|
describe("Counter Contract", () => {
|
|
7
8
|
let harness;
|
|
@@ -9,24 +10,21 @@ describe("Counter Contract", () => {
|
|
|
9
10
|
let deployer, alice, bob;
|
|
10
11
|
|
|
11
12
|
before(async function () {
|
|
12
|
-
this.timeout(60000);
|
|
13
|
-
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
// accounts from the local faucet, and (via autoDeploy) builds +
|
|
17
|
-
// publishes the named modules so they're ready to use.
|
|
18
|
-
//
|
|
19
|
-
// The setupTestFixture helper still works if you prefer the older
|
|
20
|
-
// pattern; see `import { setupTestFixture } from "movehat/helpers"`.
|
|
13
|
+
this.timeout(60000);
|
|
14
|
+
|
|
15
|
+
// Reuses the shared Movement node started by root hooks (tests/setup.ts).
|
|
16
|
+
// Each spec still gets its own accounts + deployments for isolation.
|
|
21
17
|
harness = await Harness.createLocal({
|
|
18
|
+
localNode: getSharedNode(),
|
|
22
19
|
accountLabels: ["deployer", "alice", "bob"],
|
|
23
20
|
autoDeploy: ["counter"],
|
|
24
21
|
});
|
|
25
22
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
alice
|
|
29
|
-
|
|
23
|
+
// harness.accounts is a snapshot of the labeled accounts created
|
|
24
|
+
// during Harness construction. Two `Harness.createLocal({ accountLabels:
|
|
25
|
+
// ["alice"] })` calls in the same process now produce DIFFERENT
|
|
26
|
+
// alice accounts (per-Harness isolation introduced in 0.2.7).
|
|
27
|
+
({ deployer, alice, bob } = harness.accounts);
|
|
30
28
|
|
|
31
29
|
const counterAddr = harness.runtime.getDeploymentAddress("counter");
|
|
32
30
|
counter = harness.runtime.getContract(counterAddr, "counter");
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// @ts-nocheck - This is a template file, dependencies are installed in user projects
|
|
2
|
+
import { LocalNodeManager } from "movehat/helpers";
|
|
3
|
+
|
|
4
|
+
let sharedNode;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Returns the shared local Movement node started by root hooks.
|
|
8
|
+
* Pass the result as `{ localNode: getSharedNode() }` to
|
|
9
|
+
* Harness.createLocal() or setupTestFixture().
|
|
10
|
+
*/
|
|
11
|
+
export function getSharedNode() {
|
|
12
|
+
if (!sharedNode || !sharedNode.isRunning()) {
|
|
13
|
+
throw new Error(
|
|
14
|
+
"Shared node not available. " +
|
|
15
|
+
'Ensure tests/setup.ts is listed in .mocharc.json under "require".'
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
return sharedNode;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const mochaHooks = {
|
|
22
|
+
async beforeAll() {
|
|
23
|
+
this.timeout(60000);
|
|
24
|
+
sharedNode = new LocalNodeManager({ forceRestart: true });
|
|
25
|
+
await sharedNode.start();
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
async afterAll() {
|
|
29
|
+
if (sharedNode) {
|
|
30
|
+
await sharedNode.stop();
|
|
31
|
+
sharedNode = undefined;
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
};
|
package/dist/types/config.d.ts
CHANGED
|
@@ -41,6 +41,8 @@ export type LocalTestingMode = 'local-node' | 'fork';
|
|
|
41
41
|
*/
|
|
42
42
|
export interface LocalTestOptions {
|
|
43
43
|
mode?: LocalTestingMode;
|
|
44
|
+
localNode?: import("../node/LocalNodeManager.js").LocalNodeManager;
|
|
45
|
+
useMovelite?: boolean;
|
|
44
46
|
nodeTestDir?: string;
|
|
45
47
|
nodeForceRestart?: boolean;
|
|
46
48
|
nodeFaucetPort?: number;
|
package/dist/types/fork.d.ts
CHANGED
|
@@ -40,3 +40,27 @@ export interface AccountResource {
|
|
|
40
40
|
*/
|
|
41
41
|
data: unknown;
|
|
42
42
|
}
|
|
43
|
+
export interface CoinStore {
|
|
44
|
+
coin: {
|
|
45
|
+
value: string;
|
|
46
|
+
};
|
|
47
|
+
deposit_events: {
|
|
48
|
+
counter: string;
|
|
49
|
+
guid: {
|
|
50
|
+
id: {
|
|
51
|
+
addr: string;
|
|
52
|
+
creation_num: string;
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
withdraw_events: {
|
|
57
|
+
counter: string;
|
|
58
|
+
guid: {
|
|
59
|
+
id: {
|
|
60
|
+
addr: string;
|
|
61
|
+
creation_num: string;
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
frozen: boolean;
|
|
66
|
+
}
|
package/dist/types/runtime.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { Aptos, Account } from "@aptos-labs/ts-sdk";
|
|
|
2
2
|
import { MovehatConfig } from "./config.js";
|
|
3
3
|
import { MoveContract } from "../core/contract.js";
|
|
4
4
|
import { DeploymentInfo } from "../core/deployments.js";
|
|
5
|
+
import type { AccountManager } from "../core/AccountManager.js";
|
|
5
6
|
import type { ChildProcessAdapter } from "../utils/childProcessAdapter.js";
|
|
6
7
|
export interface NetworkInfo {
|
|
7
8
|
name: string;
|
|
@@ -14,6 +15,7 @@ export interface MovehatRuntime {
|
|
|
14
15
|
aptos: Aptos;
|
|
15
16
|
account: Account;
|
|
16
17
|
accounts: Account[];
|
|
18
|
+
accountManager: AccountManager;
|
|
17
19
|
getContract: (address: string, moduleName: string) => MoveContract;
|
|
18
20
|
deployContract: (moduleName: string, options?: {
|
|
19
21
|
packageDir?: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "movehat",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.8",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Hardhat-like development framework for Movement L1 smart contracts",
|
|
6
6
|
"bin": {
|
|
@@ -70,6 +70,9 @@
|
|
|
70
70
|
"prompts": "^2.4.2",
|
|
71
71
|
"tsx": "^4.7.0"
|
|
72
72
|
},
|
|
73
|
+
"optionalDependencies": {
|
|
74
|
+
"movelite": "^0.1.0"
|
|
75
|
+
},
|
|
73
76
|
"devDependencies": {
|
|
74
77
|
"@types/js-yaml": "^4.0.9",
|
|
75
78
|
"@types/node": "^24.10.1",
|