movehat 0.0.1-alpha.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 +236 -0
- package/bin/movehat.js +21 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +93 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/compile.d.ts +2 -0
- package/dist/commands/compile.d.ts.map +1 -0
- package/dist/commands/compile.js +71 -0
- package/dist/commands/compile.js.map +1 -0
- package/dist/commands/fork/create.d.ts +11 -0
- package/dist/commands/fork/create.d.ts.map +1 -0
- package/dist/commands/fork/create.js +56 -0
- package/dist/commands/fork/create.js.map +1 -0
- package/dist/commands/fork/fund.d.ts +12 -0
- package/dist/commands/fork/fund.d.ts.map +1 -0
- package/dist/commands/fork/fund.js +42 -0
- package/dist/commands/fork/fund.js.map +1 -0
- package/dist/commands/fork/list.d.ts +5 -0
- package/dist/commands/fork/list.d.ts.map +1 -0
- package/dist/commands/fork/list.js +61 -0
- package/dist/commands/fork/list.js.map +1 -0
- package/dist/commands/fork/serve.d.ts +10 -0
- package/dist/commands/fork/serve.d.ts.map +1 -0
- package/dist/commands/fork/serve.js +64 -0
- package/dist/commands/fork/serve.js.map +1 -0
- package/dist/commands/fork/view-resource.d.ts +11 -0
- package/dist/commands/fork/view-resource.d.ts.map +1 -0
- package/dist/commands/fork/view-resource.js +34 -0
- package/dist/commands/fork/view-resource.js.map +1 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +90 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/run.d.ts +2 -0
- package/dist/commands/run.d.ts.map +1 -0
- package/dist/commands/run.js +51 -0
- package/dist/commands/run.js.map +1 -0
- package/dist/commands/test.d.ts +2 -0
- package/dist/commands/test.d.ts.map +1 -0
- package/dist/commands/test.js +35 -0
- package/dist/commands/test.js.map +1 -0
- package/dist/core/config.d.ts +15 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +121 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/contract.d.ts +20 -0
- package/dist/core/contract.d.ts.map +1 -0
- package/dist/core/contract.js +59 -0
- package/dist/core/contract.js.map +1 -0
- package/dist/core/deployments.d.ts +32 -0
- package/dist/core/deployments.d.ts.map +1 -0
- package/dist/core/deployments.js +122 -0
- package/dist/core/deployments.js.map +1 -0
- package/dist/core/shell.d.ts +25 -0
- package/dist/core/shell.d.ts.map +1 -0
- package/dist/core/shell.js +56 -0
- package/dist/core/shell.js.map +1 -0
- package/dist/errors.d.ts +12 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +24 -0
- package/dist/errors.js.map +1 -0
- package/dist/fork/api.d.ts +33 -0
- package/dist/fork/api.d.ts.map +1 -0
- package/dist/fork/api.js +98 -0
- package/dist/fork/api.js.map +1 -0
- package/dist/fork/manager.d.ts +52 -0
- package/dist/fork/manager.d.ts.map +1 -0
- package/dist/fork/manager.js +221 -0
- package/dist/fork/manager.js.map +1 -0
- package/dist/fork/server.d.ts +55 -0
- package/dist/fork/server.d.ts.map +1 -0
- package/dist/fork/server.js +274 -0
- package/dist/fork/server.js.map +1 -0
- package/dist/fork/storage.d.ts +63 -0
- package/dist/fork/storage.d.ts.map +1 -0
- package/dist/fork/storage.js +183 -0
- package/dist/fork/storage.js.map +1 -0
- package/dist/fork/test.d.ts +75 -0
- package/dist/fork/test.d.ts.map +1 -0
- package/dist/fork/test.js +157 -0
- package/dist/fork/test.js.map +1 -0
- package/dist/helpers/assertions.d.ts +7 -0
- package/dist/helpers/assertions.d.ts.map +1 -0
- package/dist/helpers/assertions.js +17 -0
- package/dist/helpers/assertions.js.map +1 -0
- package/dist/helpers/banner.d.ts +3 -0
- package/dist/helpers/banner.d.ts.map +1 -0
- package/dist/helpers/banner.js +38 -0
- package/dist/helpers/banner.js.map +1 -0
- package/dist/helpers/index.d.ts +11 -0
- package/dist/helpers/index.d.ts.map +1 -0
- package/dist/helpers/index.js +7 -0
- package/dist/helpers/index.js.map +1 -0
- package/dist/helpers/setup.d.ts +10 -0
- package/dist/helpers/setup.d.ts.map +1 -0
- package/dist/helpers/setup.js +28 -0
- package/dist/helpers/setup.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/runtime.d.ts +26 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +247 -0
- package/dist/runtime.js.map +1 -0
- package/dist/templates/.env.example +9 -0
- package/dist/templates/.mocharc.json +8 -0
- package/dist/templates/README.md +92 -0
- package/dist/templates/move/Counter.move +64 -0
- package/dist/templates/move/Move.toml +16 -0
- package/dist/templates/movehat.config.ts +37 -0
- package/dist/templates/package.json +24 -0
- package/dist/templates/scripts/deploy-counter.ts +48 -0
- package/dist/templates/tests/Counter.test.ts +75 -0
- package/dist/templates/tsconfig.json +15 -0
- package/dist/templates/types/movehat.d.ts +104 -0
- package/dist/types/config.d.ts +35 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +2 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/fork.d.ts +37 -0
- package/dist/types/fork.d.ts.map +1 -0
- package/dist/types/fork.js +5 -0
- package/dist/types/fork.js.map +1 -0
- package/dist/types/runtime.d.ts +28 -0
- package/dist/types/runtime.d.ts.map +1 -0
- package/dist/types/runtime.js +2 -0
- package/dist/types/runtime.js.map +1 -0
- package/package.json +66 -0
- package/src/cli.ts +106 -0
- package/src/commands/compile.ts +84 -0
- package/src/commands/fork/create.ts +70 -0
- package/src/commands/fork/fund.ts +57 -0
- package/src/commands/fork/list.ts +67 -0
- package/src/commands/fork/serve.ts +77 -0
- package/src/commands/fork/view-resource.ts +46 -0
- package/src/commands/init.ts +150 -0
- package/src/commands/run.ts +59 -0
- package/src/commands/test.ts +42 -0
- package/src/core/config.ts +151 -0
- package/src/core/contract.ts +97 -0
- package/src/core/deployments.ts +164 -0
- package/src/core/shell.ts +66 -0
- package/src/errors.ts +21 -0
- package/src/fork/api.ts +117 -0
- package/src/fork/manager.ts +264 -0
- package/src/fork/server.ts +311 -0
- package/src/fork/storage.ts +224 -0
- package/src/fork/test.ts +195 -0
- package/src/helpers/assertions.ts +29 -0
- package/src/helpers/banner.ts +47 -0
- package/src/helpers/index.ts +26 -0
- package/src/helpers/setup.ts +49 -0
- package/src/index.ts +17 -0
- package/src/runtime.ts +322 -0
- package/src/templates/.env.example +9 -0
- package/src/templates/.mocharc.json +8 -0
- package/src/templates/README.md +92 -0
- package/src/templates/move/Counter.move +64 -0
- package/src/templates/move/Move.toml +16 -0
- package/src/templates/movehat.config.ts +37 -0
- package/src/templates/package.json +24 -0
- package/src/templates/scripts/deploy-counter.ts +48 -0
- package/src/templates/tests/Counter.test.ts +75 -0
- package/src/templates/tsconfig.json +15 -0
- package/src/templates/types/movehat.d.ts +104 -0
- package/src/types/config.ts +36 -0
- package/src/types/fork.ts +41 -0
- package/src/types/runtime.ts +49 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { join } from 'path';
|
|
2
|
+
import { existsSync, readdirSync, statSync } from 'fs';
|
|
3
|
+
import { ForkStorage } from '../../fork/storage.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Fork list command: List all available forks
|
|
7
|
+
*/
|
|
8
|
+
export default async function forkListCommand() {
|
|
9
|
+
try {
|
|
10
|
+
const forksDir = join(process.cwd(), '.movehat', 'forks');
|
|
11
|
+
|
|
12
|
+
if (!existsSync(forksDir)) {
|
|
13
|
+
console.log('\n📂 No forks found\n');
|
|
14
|
+
console.log('Create a fork with:');
|
|
15
|
+
console.log(' movehat fork create --network testnet\n');
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const entries = readdirSync(forksDir);
|
|
20
|
+
const forkDirs = entries.filter((entry) => {
|
|
21
|
+
const fullPath = join(forksDir, entry);
|
|
22
|
+
return statSync(fullPath).isDirectory();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
if (forkDirs.length === 0) {
|
|
26
|
+
console.log('\n📂 No forks found\n');
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
console.log(`\n📂 Found ${forkDirs.length} fork(s):\n`);
|
|
31
|
+
|
|
32
|
+
for (const forkDir of forkDirs) {
|
|
33
|
+
const forkPath = join(forksDir, forkDir);
|
|
34
|
+
const storage = new ForkStorage(forkPath);
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
if (storage.exists()) {
|
|
38
|
+
const metadata = storage.loadMetadata();
|
|
39
|
+
const accounts = storage.listAccounts();
|
|
40
|
+
|
|
41
|
+
console.log(` ${forkDir}`);
|
|
42
|
+
console.log(` Path: ${forkPath}`);
|
|
43
|
+
console.log(` Network: ${metadata.network}`);
|
|
44
|
+
console.log(` Chain ID: ${metadata.chainId}`);
|
|
45
|
+
console.log(` Ledger Version: ${metadata.ledgerVersion}`);
|
|
46
|
+
console.log(` Cached Accounts: ${accounts.length}`);
|
|
47
|
+
console.log(` Created: ${new Date(metadata.createdAt).toLocaleString()}`);
|
|
48
|
+
console.log('');
|
|
49
|
+
} else {
|
|
50
|
+
console.log(` ${forkDir} (invalid - missing metadata)`);
|
|
51
|
+
console.log('');
|
|
52
|
+
}
|
|
53
|
+
} catch (error) {
|
|
54
|
+
console.log(` ${forkDir} (error reading metadata)`);
|
|
55
|
+
console.log('');
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
console.log('Usage:');
|
|
60
|
+
console.log(' movehat fork view-resource --fork <PATH> --account <ADDR> --resource <TYPE>');
|
|
61
|
+
console.log(' movehat fork fund --fork <PATH> --account <ADDR> --amount <AMOUNT>\n');
|
|
62
|
+
|
|
63
|
+
} catch (error: any) {
|
|
64
|
+
console.error(`\n❌ Error: ${error.message}\n`);
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { join } from 'path';
|
|
2
|
+
import { existsSync } from 'fs';
|
|
3
|
+
import { loadUserConfig } from '../../core/config.js';
|
|
4
|
+
import { ForkServer } from '../../fork/server.js';
|
|
5
|
+
|
|
6
|
+
interface ForkServeOptions {
|
|
7
|
+
fork?: string;
|
|
8
|
+
port?: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Fork serve command: Start a local RPC server serving the fork
|
|
13
|
+
*/
|
|
14
|
+
export default async function forkServeCommand(options: ForkServeOptions): Promise<void> {
|
|
15
|
+
try {
|
|
16
|
+
// Determine fork path
|
|
17
|
+
let forkPath: string;
|
|
18
|
+
|
|
19
|
+
if (options.fork) {
|
|
20
|
+
// Use specified path
|
|
21
|
+
forkPath = options.fork;
|
|
22
|
+
} else {
|
|
23
|
+
// Use default fork path based on current network
|
|
24
|
+
const config = await loadUserConfig();
|
|
25
|
+
const networkName = process.env.MH_CLI_NETWORK || config.defaultNetwork || 'testnet';
|
|
26
|
+
|
|
27
|
+
// Lightweight validation: only check if network exists in config
|
|
28
|
+
// Don't validate accounts/keys since fork serve only reads data
|
|
29
|
+
if (!config.networks || !config.networks[networkName]) {
|
|
30
|
+
throw new Error(`Network "${networkName}" not found in config. Available networks: ${Object.keys(config.networks || {}).join(', ')}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
forkPath = join(process.cwd(), '.movehat', 'forks', `${networkName}-fork`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Verify fork exists
|
|
37
|
+
if (!existsSync(join(forkPath, 'metadata.json'))) {
|
|
38
|
+
console.error(`\nError: Fork not found at ${forkPath}`);
|
|
39
|
+
console.error(`\nCreate a fork first with:`);
|
|
40
|
+
console.error(` movehat fork create --network <network> --name <name>`);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Get port (already validated by Commander's parsePort in cli.ts)
|
|
45
|
+
const port = options.port ?? 8080;
|
|
46
|
+
|
|
47
|
+
// Create and start server
|
|
48
|
+
const server = new ForkServer(forkPath, port);
|
|
49
|
+
|
|
50
|
+
// Handle graceful shutdown (use 'once' to prevent duplicate shutdowns)
|
|
51
|
+
const shutdown = async () => {
|
|
52
|
+
console.log('\n\nShutting down...');
|
|
53
|
+
await server.stop();
|
|
54
|
+
process.exit(0);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// Use named handlers so we can remove them if needed
|
|
58
|
+
const sigintHandler = () => { void shutdown(); };
|
|
59
|
+
const sigtermHandler = () => { void shutdown(); };
|
|
60
|
+
|
|
61
|
+
process.once('SIGINT', sigintHandler);
|
|
62
|
+
process.once('SIGTERM', sigtermHandler);
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
// Start server
|
|
66
|
+
await server.start();
|
|
67
|
+
} finally {
|
|
68
|
+
// Remove handlers in case server is stopped by other means
|
|
69
|
+
process.removeListener('SIGINT', sigintHandler);
|
|
70
|
+
process.removeListener('SIGTERM', sigtermHandler);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
} catch (error: any) {
|
|
74
|
+
console.error(`\nError starting fork server:`, error.message);
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { join } from 'path';
|
|
2
|
+
import { ForkManager } from '../../fork/manager.js';
|
|
3
|
+
|
|
4
|
+
interface ForkViewResourceOptions {
|
|
5
|
+
fork?: string;
|
|
6
|
+
account: string;
|
|
7
|
+
resource: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Fork view-resource command: View a resource from the fork
|
|
12
|
+
*/
|
|
13
|
+
export default async function forkViewResourceCommand(options: ForkViewResourceOptions) {
|
|
14
|
+
try {
|
|
15
|
+
if (!options.account) {
|
|
16
|
+
throw new Error('--account is required');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (!options.resource) {
|
|
20
|
+
throw new Error('--resource is required');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Determine fork path
|
|
24
|
+
const forkPath = options.fork || join(process.cwd(), '.movehat', 'forks', 'testnet-fork');
|
|
25
|
+
|
|
26
|
+
console.log(`\n🔍 Viewing resource from fork`);
|
|
27
|
+
console.log(` Fork: ${forkPath}`);
|
|
28
|
+
console.log(` Account: ${options.account}`);
|
|
29
|
+
console.log(` Resource: ${options.resource}\n`);
|
|
30
|
+
|
|
31
|
+
// Load fork
|
|
32
|
+
const forkManager = new ForkManager(forkPath);
|
|
33
|
+
forkManager.load();
|
|
34
|
+
|
|
35
|
+
// Get resource
|
|
36
|
+
const resource = await forkManager.getResource(options.account, options.resource);
|
|
37
|
+
|
|
38
|
+
// Display result
|
|
39
|
+
console.log(JSON.stringify(resource, null, 2));
|
|
40
|
+
console.log('');
|
|
41
|
+
|
|
42
|
+
} catch (error: any) {
|
|
43
|
+
console.error(`\n❌ Error: ${error.message}\n`);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import fs from "fs/promises";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
import prompts from "prompts";
|
|
5
|
+
import { printMovehatBanner } from "../helpers/banner.js";
|
|
6
|
+
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = path.dirname(__filename);
|
|
9
|
+
|
|
10
|
+
export default async function initCommand(projectName?: string) {
|
|
11
|
+
// Show banner only on init command
|
|
12
|
+
printMovehatBanner();
|
|
13
|
+
|
|
14
|
+
// if name is not given
|
|
15
|
+
if (!projectName) {
|
|
16
|
+
const response = await prompts({
|
|
17
|
+
type: 'text',
|
|
18
|
+
name: 'projectName',
|
|
19
|
+
message: 'Project name:',
|
|
20
|
+
initial: 'first-project'
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// If the user cancels (Ctrl+C), exit
|
|
24
|
+
if (!response.projectName) {
|
|
25
|
+
console.log('\n❌ Project initialization cancelled.');
|
|
26
|
+
process.exit(0);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
projectName = response.projectName;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const targetDir = projectName!;
|
|
33
|
+
const projectPath = path.resolve(process.cwd(), targetDir);
|
|
34
|
+
|
|
35
|
+
console.log(`\nInitializing new Movehat project in ${projectPath}...`);
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
await fs.mkdir(projectPath, { recursive: true });
|
|
39
|
+
|
|
40
|
+
const templatesDir = path.join(__dirname, "..", "templates");
|
|
41
|
+
|
|
42
|
+
console.log("📁 Creating project structure...");
|
|
43
|
+
|
|
44
|
+
await copyFile(
|
|
45
|
+
path.join(templatesDir, "package.json"),
|
|
46
|
+
path.join(projectPath, "package.json"),
|
|
47
|
+
{ projectName: projectName! }
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
await copyFile(
|
|
51
|
+
path.join(templatesDir, "tsconfig.json"),
|
|
52
|
+
path.join(projectPath, "tsconfig.json")
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
await copyFile(
|
|
56
|
+
path.join(templatesDir, ".mocharc.json"),
|
|
57
|
+
path.join(projectPath, ".mocharc.json")
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
await copyFile(
|
|
61
|
+
path.join(templatesDir, "movehat.config.ts"),
|
|
62
|
+
path.join(projectPath, "movehat.config.ts")
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
await copyFile(
|
|
66
|
+
path.join(templatesDir, ".env.example"),
|
|
67
|
+
path.join(projectPath, ".env.example")
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
await copyFile(
|
|
71
|
+
path.join(templatesDir, ".gitignore"),
|
|
72
|
+
path.join(projectPath, ".gitignore")
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
await copyFile(
|
|
76
|
+
path.join(templatesDir, "README.md"),
|
|
77
|
+
path.join(projectPath, "README.md"),
|
|
78
|
+
{ projectName: projectName! }
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
// 3. Copiar carpeta move/
|
|
82
|
+
console.log("📦 Setting up Move project...");
|
|
83
|
+
await copyDir(
|
|
84
|
+
path.join(templatesDir, "move"),
|
|
85
|
+
path.join(projectPath, "move")
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
// 4. Copiar scripts/
|
|
89
|
+
console.log("📜 Adding deployment scripts...");
|
|
90
|
+
await copyDir(
|
|
91
|
+
path.join(templatesDir, "scripts"),
|
|
92
|
+
path.join(projectPath, "scripts")
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
// 5. Copiar tests/
|
|
96
|
+
console.log("🧪 Adding test files...");
|
|
97
|
+
await copyDir(
|
|
98
|
+
path.join(templatesDir, "tests"),
|
|
99
|
+
path.join(projectPath, "tests")
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
console.log("\n✅ Project created successfully!\n");
|
|
103
|
+
console.log("📝 Next steps:\n");
|
|
104
|
+
console.log(` cd ${projectName}`);
|
|
105
|
+
console.log(` cp .env.example .env`);
|
|
106
|
+
console.log(` # Edit .env with your credentials`);
|
|
107
|
+
console.log(` npm install`);
|
|
108
|
+
console.log(` npx movehat compile`);
|
|
109
|
+
console.log(` npm test\n`);
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.error(`Failed to initialize project: ${error}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async function copyFile(
|
|
116
|
+
src: string,
|
|
117
|
+
dest: string,
|
|
118
|
+
replacements?: Record<string, string>
|
|
119
|
+
) {
|
|
120
|
+
let content = await fs.readFile(src, "utf-8");
|
|
121
|
+
|
|
122
|
+
if (replacements) {
|
|
123
|
+
for (const [key, value] of Object.entries(replacements)) {
|
|
124
|
+
const regex = new RegExp(`{{${key}}}`, "g");
|
|
125
|
+
content = content.replace(regex, value);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
await fs.writeFile(dest, content);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async function copyDir(src: string, dest: string) {
|
|
132
|
+
await fs.mkdir(dest, { recursive: true });
|
|
133
|
+
const entries = await fs.readdir(src, { withFileTypes: true });
|
|
134
|
+
|
|
135
|
+
for (const entry of entries) {
|
|
136
|
+
// Skip template development files
|
|
137
|
+
if (entry.name === 'types' || entry.name === '.vscode') {
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const srcPath = path.join(src, entry.name);
|
|
142
|
+
const destPath = path.join(dest, entry.name);
|
|
143
|
+
|
|
144
|
+
if (entry.isDirectory()) {
|
|
145
|
+
await copyDir(srcPath, destPath);
|
|
146
|
+
} else {
|
|
147
|
+
await fs.copyFile(srcPath, destPath);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { spawn } from "child_process";
|
|
2
|
+
import { resolve, extname, dirname, join } from "path";
|
|
3
|
+
import { existsSync } from "fs";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
|
|
6
|
+
export default async function runCommand(scriptPath: string) {
|
|
7
|
+
if (!scriptPath) {
|
|
8
|
+
console.error("❌ Error: No script path provided");
|
|
9
|
+
console.error("Usage: movehat run <script-path> [--network <name>]");
|
|
10
|
+
console.error("Example: movehat run scripts/deploy-counter.ts --network testnet");
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const fullPath = resolve(process.cwd(), scriptPath);
|
|
15
|
+
|
|
16
|
+
// Check if file exists
|
|
17
|
+
if (!existsSync(fullPath)) {
|
|
18
|
+
console.error(`❌ Script not found: ${scriptPath}`);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Check if it's a TypeScript or JavaScript file
|
|
23
|
+
const ext = extname(fullPath);
|
|
24
|
+
if (![".ts", ".js", ".mjs"].includes(ext)) {
|
|
25
|
+
console.error(`❌ Unsupported file type: ${ext}`);
|
|
26
|
+
console.error("Supported extensions: .ts, .js, .mjs");
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const network = process.env.MH_CLI_NETWORK;
|
|
31
|
+
console.log(`🚀 Running script: ${scriptPath}`);
|
|
32
|
+
if (network) {
|
|
33
|
+
console.log(` Network: ${network}`);
|
|
34
|
+
}
|
|
35
|
+
console.log();
|
|
36
|
+
|
|
37
|
+
// Find tsx from movehat's node_modules
|
|
38
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
39
|
+
const __dirname = dirname(__filename);
|
|
40
|
+
const tsxPath = join(__dirname, "..", "..", "node_modules", ".bin", "tsx");
|
|
41
|
+
|
|
42
|
+
// Execute script with tsx (handles both .ts and .js files)
|
|
43
|
+
const child = spawn(tsxPath, [fullPath], {
|
|
44
|
+
stdio: "inherit",
|
|
45
|
+
env: {
|
|
46
|
+
...process.env,
|
|
47
|
+
// MH_CLI_NETWORK is already set by the CLI hook
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
child.on("exit", (code) => {
|
|
52
|
+
process.exit(code || 0);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
child.on("error", (error) => {
|
|
56
|
+
console.error(`❌ Failed to execute script: ${error.message}`);
|
|
57
|
+
process.exit(1);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { spawn } from "child_process";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { existsSync } from "fs";
|
|
4
|
+
|
|
5
|
+
export default async function testCommand() {
|
|
6
|
+
const testDir = join(process.cwd(), "tests");
|
|
7
|
+
|
|
8
|
+
if (!existsSync(testDir)) {
|
|
9
|
+
console.error("❌ No tests directory found.");
|
|
10
|
+
console.error(" Create a 'tests' directory with your TypeScript test files.");
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
console.log("🧪 Running TypeScript tests with Mocha...\n");
|
|
15
|
+
|
|
16
|
+
// Find mocha from project's node_modules
|
|
17
|
+
const mochaPath = join(process.cwd(), "node_modules", ".bin", "mocha");
|
|
18
|
+
|
|
19
|
+
if (!existsSync(mochaPath)) {
|
|
20
|
+
console.error("❌ Mocha not found in project dependencies.");
|
|
21
|
+
console.error(" Install it with: npm install --save-dev mocha");
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Run mocha with TypeScript support
|
|
26
|
+
const child = spawn(mochaPath, [], {
|
|
27
|
+
stdio: "inherit",
|
|
28
|
+
env: {
|
|
29
|
+
...process.env,
|
|
30
|
+
// Inherit network if set
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
child.on("exit", (code) => {
|
|
35
|
+
process.exit(code || 0);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
child.on("error", (error) => {
|
|
39
|
+
console.error(`❌ Failed to run tests: ${error.message}`);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { pathToFileURL } from "url";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { existsSync } from "fs";
|
|
4
|
+
import { MovehatConfig, MovehatUserConfig } from "../types/config.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Loads the user's movehat.config.js from the current working directory.
|
|
8
|
+
*
|
|
9
|
+
* @throws {Error} If the configuration file is not found or fails to load
|
|
10
|
+
* @security This function loads and executes code from the current working directory.
|
|
11
|
+
* It should only be called from trusted project directories.
|
|
12
|
+
*/
|
|
13
|
+
export async function loadUserConfig(): Promise<MovehatUserConfig> {
|
|
14
|
+
const cwd = process.cwd();
|
|
15
|
+
|
|
16
|
+
// Try to find config file (.ts first, then .js)
|
|
17
|
+
const possiblePaths = [
|
|
18
|
+
join(cwd, "movehat.config.ts"),
|
|
19
|
+
join(cwd, "movehat.config.js"),
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
let configPath: string | null = null;
|
|
23
|
+
for (const path of possiblePaths) {
|
|
24
|
+
if (existsSync(path)) {
|
|
25
|
+
configPath = path;
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (!configPath) {
|
|
31
|
+
throw new Error(
|
|
32
|
+
"Configuration file not found. Expected 'movehat.config.ts' or 'movehat.config.js' in the current directory."
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
let configModule;
|
|
38
|
+
|
|
39
|
+
if (configPath.endsWith('.ts')) {
|
|
40
|
+
// For TypeScript files, we need to use tsx's import system
|
|
41
|
+
// Register tsx loader for .ts files
|
|
42
|
+
const { register } = await import('tsx/esm/api');
|
|
43
|
+
const unregister = register();
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
const configUrl = pathToFileURL(configPath).href;
|
|
47
|
+
configModule = await import(configUrl + '?t=' + Date.now());
|
|
48
|
+
} finally {
|
|
49
|
+
unregister();
|
|
50
|
+
}
|
|
51
|
+
} else {
|
|
52
|
+
// For .js files, use standard import
|
|
53
|
+
const configUrl = pathToFileURL(configPath).href;
|
|
54
|
+
configModule = await import(configUrl + '?t=' + Date.now());
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const userConfig = configModule.default as MovehatUserConfig;
|
|
58
|
+
|
|
59
|
+
// Validate that networks are defined
|
|
60
|
+
if (!userConfig.networks || Object.keys(userConfig.networks).length === 0) {
|
|
61
|
+
throw new Error(
|
|
62
|
+
"No networks defined in configuration. Add at least one network in the 'networks' field."
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return userConfig;
|
|
67
|
+
} catch (error) {
|
|
68
|
+
throw new Error(`Failed to load configuration file '${configPath}': ${error}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Resolve configuration for a specific network
|
|
74
|
+
* Merges global settings with network-specific settings
|
|
75
|
+
*/
|
|
76
|
+
export async function resolveNetworkConfig(
|
|
77
|
+
userConfig: MovehatUserConfig,
|
|
78
|
+
networkName?: string
|
|
79
|
+
): Promise<MovehatConfig> {
|
|
80
|
+
// Determine which network to use
|
|
81
|
+
const selectedNetwork =
|
|
82
|
+
networkName ||
|
|
83
|
+
process.env.MH_CLI_NETWORK ||
|
|
84
|
+
process.env.MH_DEFAULT_NETWORK ||
|
|
85
|
+
userConfig.defaultNetwork ||
|
|
86
|
+
"testnet";
|
|
87
|
+
|
|
88
|
+
// Check if network exists in config
|
|
89
|
+
const networkConfig = userConfig.networks[selectedNetwork];
|
|
90
|
+
if (!networkConfig) {
|
|
91
|
+
const availableNetworks = Object.keys(userConfig.networks).join(", ");
|
|
92
|
+
throw new Error(
|
|
93
|
+
`Network '${selectedNetwork}' not found in configuration.\nAvailable networks: ${availableNetworks}`
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Get accounts using Hardhat-style resolution:
|
|
98
|
+
// 1. Network-specific accounts (if defined)
|
|
99
|
+
// 2. Global accounts from config (if defined)
|
|
100
|
+
// 3. PRIVATE_KEY environment variable (Hardhat-style, no MH_ prefix)
|
|
101
|
+
// 4. Error if nothing found
|
|
102
|
+
|
|
103
|
+
let accounts: string[] = [];
|
|
104
|
+
|
|
105
|
+
// 1. Check network-specific accounts
|
|
106
|
+
if (networkConfig.accounts && networkConfig.accounts.length > 0) {
|
|
107
|
+
accounts = [...networkConfig.accounts].filter(Boolean);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// 2. If no network-specific accounts, use global accounts
|
|
111
|
+
if (accounts.length === 0 && userConfig.accounts && userConfig.accounts.length > 0) {
|
|
112
|
+
accounts = [...userConfig.accounts].filter(Boolean);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// 3. If still no accounts, check PRIVATE_KEY env var (Hardhat-style)
|
|
116
|
+
if (accounts.length === 0 && process.env.PRIVATE_KEY) {
|
|
117
|
+
accounts = [process.env.PRIVATE_KEY];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// 4. Validate we have at least one account
|
|
121
|
+
if (accounts.length === 0 || !accounts[0]) {
|
|
122
|
+
throw new Error(
|
|
123
|
+
`Network '${selectedNetwork}' has no accounts configured.\n` +
|
|
124
|
+
`Options:\n` +
|
|
125
|
+
` 1. Set PRIVATE_KEY in your .env file (recommended)\n` +
|
|
126
|
+
` 2. Add 'accounts: ["0x..."]' globally in movehat.config.ts\n` +
|
|
127
|
+
` 3. Add 'accounts: ["0x..."]' to the '${selectedNetwork}' network config`
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Merge named addresses (network-specific overrides global)
|
|
132
|
+
const mergedNamedAddresses = {
|
|
133
|
+
...(userConfig.namedAddresses || {}),
|
|
134
|
+
...(networkConfig.namedAddresses || {}),
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// Build resolved config
|
|
138
|
+
const resolvedConfig: MovehatConfig = {
|
|
139
|
+
network: selectedNetwork,
|
|
140
|
+
rpc: networkConfig.url,
|
|
141
|
+
privateKey: accounts[0],
|
|
142
|
+
allAccounts: accounts,
|
|
143
|
+
profile: networkConfig.profile || "default",
|
|
144
|
+
moveDir: userConfig.moveDir || "./move",
|
|
145
|
+
account: "", // Will be derived from privateKey in runtime
|
|
146
|
+
namedAddresses: mergedNamedAddresses,
|
|
147
|
+
networkConfig: networkConfig,
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
return resolvedConfig;
|
|
151
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Account,
|
|
3
|
+
Aptos,
|
|
4
|
+
type InputViewFunctionData,
|
|
5
|
+
type MoveFunctionId,
|
|
6
|
+
} from "@aptos-labs/ts-sdk";
|
|
7
|
+
|
|
8
|
+
export interface TransactionResult {
|
|
9
|
+
hash: string;
|
|
10
|
+
success: boolean;
|
|
11
|
+
vm_status: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class MoveContract {
|
|
15
|
+
constructor(
|
|
16
|
+
private aptos: Aptos,
|
|
17
|
+
private moduleAddress: string,
|
|
18
|
+
private moduleName: string
|
|
19
|
+
) {}
|
|
20
|
+
|
|
21
|
+
async call(
|
|
22
|
+
signer: Account,
|
|
23
|
+
functionName: string,
|
|
24
|
+
args: any[] = [],
|
|
25
|
+
typeArgs: string[] = []
|
|
26
|
+
): Promise<TransactionResult> {
|
|
27
|
+
const functionFullName = `${this.moduleAddress}::${this.moduleName}::${functionName}`;
|
|
28
|
+
|
|
29
|
+
console.log(`📝 Calling ${functionFullName}...`);
|
|
30
|
+
|
|
31
|
+
const transaction = await this.aptos.transaction.build.simple({
|
|
32
|
+
sender: signer.accountAddress,
|
|
33
|
+
data: {
|
|
34
|
+
function: functionFullName as MoveFunctionId,
|
|
35
|
+
typeArguments: typeArgs,
|
|
36
|
+
functionArguments: args,
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const signature = this.aptos.transaction.sign({
|
|
41
|
+
signer,
|
|
42
|
+
transaction,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const committedTxn = await this.aptos.transaction.submit.simple({
|
|
46
|
+
transaction,
|
|
47
|
+
senderAuthenticator: signature,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const response = await this.aptos.waitForTransaction({
|
|
51
|
+
transactionHash: committedTxn.hash,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
console.log(
|
|
55
|
+
`✅ Transaction ${committedTxn.hash} committed with status: ${response.vm_status}\n`
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
hash: committedTxn.hash,
|
|
60
|
+
success: response.success,
|
|
61
|
+
vm_status: response.vm_status,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async view<T = any>(
|
|
66
|
+
functionName: string,
|
|
67
|
+
args: any[] = [],
|
|
68
|
+
typeArgs: string[] = []
|
|
69
|
+
): Promise<T> {
|
|
70
|
+
const functionFullName = `${this.moduleAddress}::${this.moduleName}::${functionName}`;
|
|
71
|
+
|
|
72
|
+
const payload: InputViewFunctionData = {
|
|
73
|
+
function: functionFullName as MoveFunctionId,
|
|
74
|
+
typeArguments: typeArgs,
|
|
75
|
+
functionArguments: args,
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const result = await this.aptos.view({ payload });
|
|
79
|
+
|
|
80
|
+
return (result.length === 1 ? result[0] : result) as T;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
getModuleId(): string {
|
|
84
|
+
return `${this.moduleAddress}::${this.moduleName}`;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Factory function to create a contract instance
|
|
90
|
+
*/
|
|
91
|
+
export function getContract(
|
|
92
|
+
aptos: Aptos,
|
|
93
|
+
moduleAddress: string,
|
|
94
|
+
moduleName: string
|
|
95
|
+
): MoveContract {
|
|
96
|
+
return new MoveContract(aptos, moduleAddress, moduleName);
|
|
97
|
+
}
|