Setup checks and platform notes
Verify the required commands before starting:
{
"git --version\nnode --version\npnpm --version\noffckb --version\nckb-debugger --version"
}
Installing ckb-debugger compiles native dependencies and can
take around ten minutes. Install it with{" "}
cargo install ckb-debugger. If installation reports{" "}
Could not find protoc, install protobuf and retry:
{"# macOS\nbrew install protobuf\ncargo install ckb-debugger"}
Windows:
-
Add the npm global bin directory and{" "}
{"%USERPROFILE%\\.cargo\\bin"} to PATH.
-
If pnpm reports
ERR_PNPM_IGNORED_BUILDS, approve the
required dependency build scripts and reinstall.
-
Keep the repository in a path without
#; Next.js asset
resolution can fail in such paths.
-
If native tooling remains difficult to configure, use WSL or a Linux
Codespace.
## What You Will Build
This tutorial builds a full-stack CKB dApp with a Lock Script named `hash-lock`. The Script stores the hash of a secret in its arguments. A transaction may consume Cells protected by that Script only when its witness contains a preimage whose `blake2b-256` hash matches.
You will:
1. Build and deploy the Script with OffCKB;
2. Derive a CKB address from the deployed code and a preimage hash;
3. Deposit Devnet CKB into Cells protected by that address;
4. Submit the correct preimage and wait for the transaction to commit.
:::danger Educational use only
This hash lock proves **knowledge of a secret**, not identity or ownership. The preimage is disclosed publicly when it is used, so do not use this example with valuable funds. The runnable flow supports Devnet and Testnet.
The example uses JavaScript/TypeScript on [ckb-js-vm](/docs/script/js/js-vm). For production Scripts, start with [Rust](/docs/script/rust/rust-quick-start) and a signature-based authorization design.
## Run the Example on Devnet
### 1. Download the Source
```bash
git clone https://github.com/nervosnetwork/docs.nervos.org.git --depth 1
cd docs.nervos.org/examples/dApp/simple-lock
pnpm install
```
You can also inspect or download the example from the repository.
### 2. Start Devnet
Resetting Devnet creates a new chain. Every OutPoint from the previous chain becomes invalid, so redeploy the contract after a reset.
### 3. Build and Deploy the Script
Build the TypeScript contract into CKB-VM bytecode:
**Command:**
```bash
pnpm build
```
**Response:**
```
Building 1 contract(s): hash-lock
📦 Bundling with esbuild...
🔧 Compiling to bytecode...
✅ Contract 'hash-lock' built successfully!
🔗 Bytecode: dist/hash-lock.bc
```
Deploy it to Devnet:
**Command:**
```bash
pnpm run deploy -- --network devnet
```
**Response:**
```
🌐 Network: devnet
contract hash-lock.bc deployed
wait for tx confirmed on-chain...
tx committed.
🎉 Deployment completed successfully!
🔄 Contract deployment synchronized: frontend/deployment/scripts.json
🔄 System scripts synchronized: frontend/deployment/system-scripts.json
📍 Contract OutPoint: 0x...:0
📍 ckb-js-vm OutPoint: 0x...:15
```
Deployment creates or updates `deployment/scripts.json` and refreshes `deployment/system-scripts.json`. The deploy script validates the `hash-lock.bc` and `ckb-js-vm` entries for Devnet, copies both files to `frontend/deployment/`, and fails if either copy does not match its source.
An OutPoint is a transaction hash plus output index that identifies one specific Cell. OutPoints are network-specific. They become stale after redeployment, a network switch, or a Devnet reset.
### 4. Start the Frontend
```bash
cd frontend
pnpm dev
```
Open [http://localhost:3000](http://localhost:3000). When `NEXT_PUBLIC_NETWORK` is unset, the frontend uses Devnet.
Before enabling transfers, the page calls `getCellLive` for both required dependencies:
- The Cell containing your deployed `hash-lock.bc` bytecode;
- The Cell containing the `ckb-js-vm` interpreter.
If either OutPoint is missing or stale, the page shows recovery guidance instead of constructing a transaction.
### 5. Create and Fund a Hash Lock
Enter a preimage such as `Hello World`. The frontend hashes it and creates a Lock Script that combines:
- The `ckb-js-vm` interpreter code hash;
- The deployed `hash-lock.bc` code hash and hash type;
- The expected preimage hash.
The page displays both a **contract code hash** and a **CKB address**. They are not interchangeable:
- The code hash identifies deployed bytecode;
- The CKB address encodes the complete Lock Script, including its arguments, and is the value you fund.
Copy the generated hash-lock address and deposit Devnet CKB:
```bash
offckb deposit --network devnet