movehat
v0.2.4
Published
Hardhat-like development framework for Movement L1 smart contracts
Maintainers
Readme
Movehat
A Hardhat-like development framework for Movement L1 smart contracts.
Write your tests and deployment scripts in TypeScript while building Move contracts.
Features
- Hardhat-style Harness API —
Harness.createLocal,createFork,createLivefactory methods with explicit lifecycle (cleanup()) and use-after-cleanup safety (Proxy poisoning). - Three execution modes — full local blockchain, read-only fork of a remote network, or live testnet/mainnet binding.
- Auto-deploy in tests + auto-detect named addresses — contracts compile and deploy automatically; no manual address wiring.
- Native fork system — local JSON-backed snapshots of Movement L1 state, no BCS compatibility issues.
- TypeScript-first — single
PRIVATE_KEYacross all networks (Hardhat-style); deployments tracked per-network indeployments/. - SLSA-provenance releases — every npm release ships with Trusted Publishers provenance. Verify with
npm view movehat@<version>. - Security hardening built-in — path-traversal, command-injection, and YAML-injection protections at every boundary.
Quick Start
Prerequisites
- Node.js v20+ (download)
- Movement CLI — required for compiling and deploying Move code (install guide). For the exact revision Movehat tests against, see
MOVEMENT_CLI_COMPAT.md.
Installation
npm install -g movehat
# or
pnpm install -g movehatFive commands to a working test
npx movehat init my-project # 1. Scaffold project
cd my-project
npm install # 2. Install dependencies
npx movehat compile # 3. Compile contracts (auto-detects addresses)
npm test # 4. Run tests (auto-starts local node, deploys, runs)That's it — local blockchain starts automatically, accounts get funded, contracts deploy, tests run.
For more depth (project layout, configuration, account model, deployment tracking), browse the full docs.
Configuration
A minimal movehat.config.ts looks like this:
import dotenv from "dotenv";
dotenv.config();
export default {
defaultNetwork: "testnet",
networks: {
testnet: { url: process.env.MOVEMENT_RPC_URL || "https://testnet.movementnetwork.xyz/v1", chainId: "testnet" },
mainnet: { url: "https://mainnet.movementnetwork.xyz/v1", chainId: "mainnet" },
local: { url: "http://localhost:8080/v1", chainId: "local" },
},
accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [],
moveDir: "./move",
};A single PRIVATE_KEY is reused across networks (Hardhat-style). For testing, accounts are auto-generated and funded from the local faucet — no .env needed.
Full reference: /docs/getting-started/configuration.
Writing Tests
The canonical pattern uses Harness.createLocal:
// tests/Counter.test.ts
import { describe, it, before, after } from "mocha";
import { expect } from "chai";
import { Harness } from "movehat";
import type { MoveContract } from "movehat/helpers";
describe("Counter Contract", () => {
let harness: Harness;
let counter: MoveContract;
let counterAddr: string;
before(async function () {
this.timeout(60000); // Local node startup + autoDeploy
harness = await Harness.createLocal({
accountLabels: ["deployer", "alice"],
autoDeploy: ["counter"],
});
counterAddr = harness.runtime.getDeploymentAddress("counter");
counter = harness.runtime.getContract(counterAddr, "counter");
});
after(async () => { await harness.cleanup(); });
it("alice can increment her own counter", async () => {
const alice = harness.runtime.getAccountByLabel("alice");
await counter.call(alice, "increment", []);
const [value] = await harness.runViewFunction({
function: `${counterAddr}::counter::get`,
functionArguments: [alice.accountAddress.toString()],
});
expect(parseInt(value as string)).to.equal(1);
});
});Run with npm test (interactive menu) or movehat test --ts (TypeScript suite, starts local node).
setupTestFixturefrommovehat/helpersis a lighter-weight alternative for tests that don't need the Harness lifecycle — both styles are documented at/docs/guides/testing.
Writing Deployment Scripts
Deploy as a code object with Harness.createLive:
// scripts/deploy-counter.ts
import { Harness } from "movehat";
async function main() {
const harness = await Harness.createLive(process.env.MOVEHAT_NETWORK ?? "testnet");
try {
const deployment = await harness.deployCodeObject({
moduleName: "counter",
});
console.log(`Deployed: ${deployment.address}::counter`);
console.log(`Tx: ${deployment.txHash}`);
// Initialize the freshly deployed module
const counter = harness.runtime.getContract(deployment.address, "counter");
await counter.call(harness.runtime.account, "init", []);
} finally {
await harness.cleanup();
}
}
main().catch((err) => { console.error(err); process.exit(1); });Run with movehat run scripts/deploy-counter.ts --network testnet.
Re-running fails with ModuleAlreadyDeployedError (local record at deployments/{network}/counter.json). Set MH_CLI_REDEPLOY=true to force a re-deploy.
Requires
movehat@^0.2.0. Full deploy guide (named addresses, code-object semantics, redeploy flow, deployment tracking):/docs/guides/deployment.
Fork System
Movehat ships a native fork system for testing against real Movement L1 state without deploying to testnet. Forks are JSON-backed local snapshots that lazy-load resources as you read them, and Harness.createFork(network) binds a Harness to one.
Full fork docs: FORK_GUIDE.md (in-repo, comprehensive) or /docs/guides/fork (live site).
CLI Reference
| Command | Description | Docs |
|---|---|---|
| movehat init [name] | Scaffold a new Movehat project | /docs/cli/init |
| movehat compile | Compile Move contracts via Movement CLI | /docs/cli/compile |
| movehat test [--move\|--ts\|--all] | Run Move and/or TypeScript tests (interactive menu by default) | /docs/cli/test |
| movehat run <script> | Execute a TypeScript deployment / interaction script | /docs/cli/run |
| movehat fork <subcmd> | Manage local network forks (create / list / serve / fund / view-resource) | /docs/cli/fork |
| movehat update | Check npm for a newer version and upgrade | — |
Troubleshooting
| Error | Solution |
|---|---|
| movement: command not found | Install Movement CLI per the Movement docs |
| Module "X" is already deployed on Y | Set MH_CLI_REDEPLOY=true to force a re-deploy |
| Configuration file not found | Create movehat.config.ts or run movehat init |
| No accounts configured | Set PRIVATE_KEY in .env |
| Cannot find package 'dotenv' | Run npm install or pnpm install in the project dir |
For anything not on this list, open an issue on GitHub.
Contributing
See CONTRIBUTING.md for development setup, the dual-tier test infrastructure, and the PR workflow.
License
MIT — see LICENSE.
Links
- Full documentation — guides, CLI reference, auto-generated API reference (50 pages from TypeDoc)
- Fork system guide — in-repo deep-dive
- GitHub repository
- NPM package
- Movement Network — the L1 Movehat targets
- Movement Network docs
Author
Gilberts Ahumada
