Mining
There are two ways to mine Liqua, and they serve different goals. The SOMA-PoW miner produces the chain's native L1 blocks and is driven from a CLI + live dashboard. The Ethash pool is the GPU-mineable surface — point a real rig at it, and every pool block is bridged into a real L1 block that credits miners on-chain. Pick the one that matches what you have.
Both miners live under liqua/ and share its dependencies. Run npm install once in
liqua/ (Node ≥18) before the commands below.
Which path?
| Path A — SOMA-PoW miner | Path B — Ethash pool | |
|---|---|---|
| What it mines | the native L1 SOMA-PoW seal (§4) | real Ethash (GPU-mineable), bridged to L1 |
| Who it's for | running / testing the chain itself | real GPU rigs & pool operators |
| You drive it with | a CLI + live dashboard | a Stratum miner (or the sim rig) |
| Reward | §4 emission per block | §4 emission split PPLNS across miners |
| Port | :8010 | stratum :3333 · dash :8030 |
Path A · the SOMA-PoW miner
A continuous, throttleable block producer over the molecular primitives in chain/. Each block runs
the pipeline: candidate → grind → §1e witness attest → metaVerify K-of-N → extend → §4 emission →
§5 reward-instance. Start it and a live dashboard comes up on :8010 with a block explorer at
/explorer.
node miner/cli.mjs run
# with options:
node miner/cli.mjs run --port 8010 --bits 18 --cap 8000 --target 2500 --validators 4
| Flag | Meaning · default |
|---|---|
--port <n> | dashboard + control API port · 8010 |
--bits <n> | PoW difficulty in leading-zero bits · 18. Passing this disables auto-retarget. |
--cap <H/s> | hashrate cap (throttle) · 0 = unlimited |
--target <ms> | target block time for auto-retarget · 2500 |
--validators <n> | §1e witness count for the K-of-N quorum · 4 |
Control it live
While it runs, the same CLI drives it over the control API — speed up or slow down hashing, retune difficulty, pause, and tail the full streaming log:
node miner/cli.mjs status # height · hashrate · difficulty · emission · posture
node miner/cli.mjs speed 2 # multiply the hash cap (faster)
node miner/cli.mjs slow 1.5 # divide the hash cap (slower)
node miner/cli.mjs max # remove the cap entirely (unthrottle)
node miner/cli.mjs cap 6000 # set an exact H/s ceiling
node miner/cli.mjs bits 20 # set difficulty · harder / easier nudge ±1
node miner/cli.mjs auto on # auto-retarget to --target · target <ms> to change
node miner/cli.mjs pause # pause / resume production
node miner/cli.mjs log -f # follow the full log (ctrl-c to stop)
All of these are thin wrappers over POST /api/control on the miner — see the
API reference for the raw actions. Add --port to any command to target a
miner on a non-default port.
The dashboard & explorer
Open http://localhost:8010 for the live UI — hashrate sparkline, difficulty and
emission gauges, the blocks table, the full streaming log, and speed/slow/difficulty buttons that drive the miner
in real time. The block explorer is at http://localhost:8010/explorer.
The cap / speed / slow controls let you watch §4's bounded fast-twitch
band react inside the decay envelope — push the cap up and emission posture responds, without ever breaking the
continuous schedule.
Verify it
npm run mine:test # deterministic gate · exit 0:
# 6 blocks · PoW target met · §1e quorum · §4 emission decays · §5 instance/block
(a) ASIC-resistance is not active. chain/block.mjs imports the memory-hard
powSeal ("door 3"), but the live miner grinds the plain soma() root — so the memory-hard
path is a dead import for now. Wiring it in is a consensus change + difficulty recalibration.
(b) §5 claim is now wired. A block's reward can be escrowed to a per-block instance via
RewardEscrow.sol, and the miner's L2 getting-tool
(evm/claim-tool.mjs) re-derives the instance and claims it after the gate — verified by
npm run claim:test. What's left is a toggle: today
produceBlock credits the coinbase directly (so the pool/validator keep native liquidity); making
seal() the default emission path is the remaining switch.
Path B · the GPU Ethash pool
The pool mines real Ethash so real rigs (or the sim) can point at it. Each accepted share that clears the network target seals a pool block; the reward is split PPLNS across recent shares, written to a persistent ledger, and bridged into a real Liqua L1 block that credits each miner's on-chain address (state root = SOMA, conservation asserted to the wei).
GPU rig / sim ──Stratum──▶ pool server ──verify (real Ethash)──▶ share
│ accepted share clears net target → BLOCK
├─▶ PPLNS split (rolling last-N-shares window)
├─▶ payout LEDGER (persistent balances + history)
└─▶ L1 BRIDGE → evm/produceBlock → §4 emission split on-chain
Start the pool
npm run pool # stratum tcp://:3333 · dashboard http://:8030
# server flags:
node pool/server.mjs --stratum 3333 --http 8030 \
--net-diff 500000000 --share-diff 6000000 --reward 2 --payout pplns
Open http://localhost:8030 for the live pool UI — pool stats, the on-chain balances
table, sealed blocks, and the full log. The ▸ CONNECT A MINER panel (and
GET /api/connect) shows copy-paste connect commands with the pool's
detected LAN IP already filled in.
Connect a miner
The username the pool credits is your LIQUA payout address (0x…), optionally with a
.worker label. Prove the pool end-to-end with the sim rig (no GPU needed):
# sim rig — grinds real Ethash in software, submits shares
node pool/sim-miner.mjs --host <ip> --port 3333 --wallet 0xYou --worker rig-1
# …or speak the NiceHash EthereumStratum/1.0.0 dialect
npm run sim -- --pool localhost:3333 --name rig-2 --nicehash
A real GPU rig uses the same fields — the username is wallet.worker:
lolMiner --algo ETHASH --pool <ip>:3333 --user 0xYou.rig-1
teamredminer -a ethash -o stratum+tcp://<ip>:3333 -u 0xYou.rig-1 -p x
| Miner input | sim flag | Meaning |
|---|---|---|
| host | --host <ip> | pool IP / hostname (use the LAN IP for a rig on another box) |
| port | --port <n> | stratum port · 3333 |
| wallet | --wallet 0x… | your LIQUA payout address — the username the pool credits |
| worker | --worker <label> | per-rig label (e.g. rig-1) |
| dialect | --nicehash | speak EthereumStratum/1.0.0 instead of the simple dialect |
Payout · PPLNS, on-chain
A block's reward is split across the rolling window of the last N accepted shares
(N ≈ pplnsFactor × networkDiff) by each miner's share count — measured from real shares, never a
hardcoded ratio. PPLNS smooths variance and resists pool-hopping versus per-round prop. The split
sums to the reward exactly (rounding dust → the block finder).
Each pool block then calls the verified evm/produceBlock: the §4 emission is split across the pooled
miners' on-chain addresses, so pool rewards become real LIQUA balances in the molecular world-state.
A miner's on-chain address is a 0x… name as-is, else
keccak256("liqua-miner:"+name)[-20:]. Because the bridge produces a real L1 block, a wallet can read
those balances over the pool's EVM JSON-RPC at /rpc.
Verify it
npm run pool:test # PASS exit 0: shares verified · bad share rejected · PPLNS split ·
# ledger = blocks×reward · one L1 block per pool block · conservation holds
npm run verify-ethash # real Ethash self-check (builds the 16 MB epoch-0 cache once)
- Pure-JS Ethash ≈ 6 H/s (a real GPU does MH/s) → keep devnet difficulties low. The 16 MB epoch-0 cache builds once (~14 s) then persists to
pool/.cacheand loads instantly. - Real-rig Stratum is to-spec but UNTESTED on hardware. The EthereumStratum/1.0.0 framing is faithful, but a real NiceHash miner applies the boundary
2²⁵⁶/(diff·2³²)— so an operator must scale the configured difficulty by2³²for physical rigs. - The L1 chain is in-memory (devnet); the payout ledger persists, the L1 block history does not yet. Persisting the L1 chain + a withdraw/settlement path are next.
Self-mine into locked liquidity
A third option for what to do with what you mine: instead of taking emission as spendable balance, route it straight into time-locked AMM liquidity on the exchange. Enroll a wallet, point your miner's payout at it, and every block is auto-zapped into the LIQUA/mUSDC pool with the LP locked for you on a rolling term.
Full walkthrough — enroll, the zap, the rolling lock, withdraw, and the relayer — is on
Self-Mine into Locked Liquidity. TL;DR (from orderbook-dex/):
npm run deploy:local && npm run relayer # exchange + UI on :8080
npm run self-mine # mined LIQUA → locked LP · enroll at :8080/self-mine.html
Files
| File | Role |
|---|---|
miner/engine.mjs | continuous, throttleable SOMA-PoW producer over chain/ |
miner/server.mjs · cli.mjs | HTTP+SSE control server · the management CLI |
miner/dashboard.html · explorer.html | live control UI · block explorer |
pool/engine.mjs | jobs · per-share real verify · dedup/stale · PPLNS split |
pool/verify-ethash.mjs | real @ethereumjs/ethash hashimoto-light verification |
pool/ledger.mjs · bridge.mjs | persistent balances/history · Ethash→SOMA L1 bridge |
pool/server.mjs · sim-miner.mjs | dual-dialect Stratum + HTTP/SSE + JSON-RPC · the sim rig |
Next: stake and validate to secure what you mine, or see every endpoint in the API reference.