Mining Guide
Anvil256 — Mining Guide
How Anvil256 Mining Works — Mathematical Summary
Anvil256 uses Keccak-Cascade PoW with Temporal Miner Binding. The puzzle for each epoch incorporates two values that are undetermined until specific on-chain events occur:
1. (epoch entropy):
is produced by the sequencer when epoch closes — a future event at any earlier time. By collision resistance of , no precomputed nonce is valid with probability better than (see SECURITY.md Theorem 4.1).
2. (miner epoch counter):
Strictly monotonic, non-transferable, permanently tied to address . A new address has and receives a distinct but equally hard challenge.
Full puzzle validity condition:
The CLI fetches both and from the contract at the start of each epoch before dispatching a kernel job.
Hardware
Expected hashrate and mines-per-day estimates assume:
- Global hashrate
- Target epoch time
- Expected mines per day for hashrate :
| Tier | GPU | (mines/day) | ||
|---|---|---|---|---|
| Entry | RTX 3060 | 560 MH/s | 0.56% | |
| Mid | RTX 4060 Ti | 1.6 GH/s | 1.6% | |
| High | RTX 4090 | 4.4 GH/s | 4.4% | |
| Datacenter | H100 | 9.6 GH/s | 9.6% | |
| AMD | RX 7900 XTX | 2.6 GH/s | 2.6% | |
| CPU (24-core) | Ryzen 9 7950X | 35 MH/s | 0.035% |
No GPU? The CPU fallback (
miner-cpu) runs on any Linux/Windows/macOS machine. Hashrate is ~20–80 MH/s depending on core count. Profitable only at very low global hashrate (early network) or as a learning/test setup.
Why Cascade is ~20% slower than single-pass Keccak. The kernel computes two Keccak passes per nonce:
Pass 2 requires held in GPU constant memory. This is not avoidable: pass 2 is the anti-precompute layer (Theorem 4.1, SECURITY.md). The 20% overhead is the mathematical price of structural precomputation immunity.
Software Requirements
| Component | Requirement |
|---|---|
| OS | Linux kernel ≥ 5.10 (Ubuntu 22.04+ recommended), Windows 10/11, macOS 14+ |
| CUDA (GPU path) | CUDA Toolkit 11.8 or 12.x, driver ≥ 520 |
| CPU path | Any x86-64 or ARM64, C++17 compiler, pthreads |
| Rust (CLI build) | rustc ≥ 1.75 (stable), cargo |
| Solidity (contracts) | Foundry (forge, cast) |
| Wallet | ETH on Base L2 (≈0.108/mine) |
| RPC | Base L2 endpoint (Alchemy, Infura, or self-hosted) |
Mining Economics — Exact Fee and Reserve Split
Each successful mine() requires the oracle-priced protocol fee:
At ETH =\2{,}500fee_{wei}=4\times10^{13}$0.10$. The fee is split inside the contract:
The miner receives only the miner reward:
The liquidity reserve receives an additional token mint:
At epoch 0:
Miner break-even before gas and electricity is therefore:
This is not a profit guarantee. It is only the fee break-even price.
Option A — Build from Source (Recommended)
Prebuilt tar releases are not available yet. Build locally from source so you can inspect the code, reproduce the binaries, and trust your own toolchain.
1. Clone
git clone https://github.com/anvil256xyz/anvil256.gitcd anvil2562. Build the kernel
For NVIDIA GPU mining, use the default CUDA target:
cd kernelmakecd ..Equivalent explicit GPU command:
cd kernelmake cuda ARCHS="89" # change ARCHS for your GPU generationcd ..For CPU-only mining:
cd kernelmake cpucd ..Expected outputs:
ls -lh cli/bin/# miner # CUDA GPU kernel, if built# miner-cpu # CPU fallback kernel, if built3. Build the CLI
Install Rust first if cargo is not available:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | shsource "$HOME/.cargo/env"cd clicargo build --release --lockedThe binary is written to:
./target/release/anvil256-cliKeep running commands from cli/; the CLI expects kernel binaries under
cli/bin/ by default.
4. Configure
cp .env.example .envnano .env # or: vim .env / code .envMinimum required fields in .env:
# Base L2 RPC endpoint (get a free key from https://www.alchemy.com)BASE_RPC_URL=https://base-mainnet.g.alchemy.com/v2/YOUR_API_KEY
# Your mining wallet private key — NEVER share or commit thisPRIVATE_KEY=0xYOUR_PRIVATE_KEY_HERE
# Deployed Anvil256 contract addressANVIL256_ADDRESS=0xYOUR_CONTRACT_ADDRESS_HEREFull optional tuning reference:
| Variable | Default | Description |
|---|---|---|
MAX_GAS_USD | 0.05 | Abort round if L2 gas cost exceeds this USD cap |
MAX_FEE_USD | 0.15 | Abort round if protocol fee exceeds this USD cap |
PRIORITY_FEE_GWEI | 0.001 | EIP-1559 miner tip (negligible on Base) |
ETH_PRICE_USD | 2500.0 | Fallback ETH price for gas estimation (overridden by oracle) |
MINER_BIN | auto | Path to kernel binary (auto-detects GPU→CPU fallback) |
MINER_DEVICES | all | Comma-separated GPU device indices, e.g. 0,1,2 |
MINER_NPT | 128 | Nonces per thread per kernel launch |
MINER_TARGET_BATCH_MS | 250 | Target ms per kernel batch |
MINER_PROGRESS_MS | 750 | How often the kernel emits progress logs (CPU miner) |
MINER_THREADS | nproc | Worker threads (CPU miner only) |
MAX_SEARCH_SECS | 300 | Abandon search after this many seconds (retries next epoch) |
KEEP_MINING | true | Set to false to mine exactly one epoch then exit |
LOG_FORMAT | text | Set to json for structured logging (Datadog, Loki, etc.) |
5. Run
./target/release/anvil256-cliNormal startup output:
INFO Anvil256 CLI starting rpc=https://… contract=0x…INFO signer ready miner_addr=0xa0ee…INFO kernel binary selected bin="./bin/miner"INFO waiting for kernel ready eventINFO kernel device index=0 name="NVIDIA GeForce RTX 4060 Ti" cu=34 wg=128 grid=272 npt=128INFO chain snapshot epoch=0 gamma=0 reward_wei=50000000000000000000 fee_wei=43454266557161 difficulty_hex="0x000000…"INFO mining device=RTX 4060 Ti hashes=200000000 hashrate_ghs=1.600 elapsed_ms=125INFO FOUND device=RTX 4060 Ti nonce=8539386912708195224 hash=0x000000… hashrate_ghs=1.601 elapsed_ms=1234INFO submitting mine() fee_wei=43454266557161 gas_limit=198741 gas_cost_usd=0.0086INFO mine() confirmed tx_hash=0xbef7…INFO chain snapshot epoch=1 gamma=1 …Kernel Build Reference
The CLI auto-detects the kernel binary at runtime:
- First tries
./bin/miner(CUDA) - Falls back to
./bin/miner-cpu(CPU) - Fails with a clear error if neither exists
GPU kernel (CUDA)
Requires CUDA Toolkit 11.8+ and nvcc in your PATH.
cd kernel
# Check nvcc is availablenvcc --version
# Build for your GPU architecture:# sm_75 = Turing (RTX 20xx)# sm_80 = Ampere (A100, RTX 30xx)# sm_86 = Ampere (RTX 3060–3090)# sm_89 = Ada (RTX 40xx)# sm_90 = Hopper (H100)
# Single arch (fastest compile, smallest binary):make cuda ARCHS="89"
# Multi-arch fat binary (distributable, works on multiple GPU gens):make cuda ARCHS="75 80 86 89 90"
# Output:ls -lh ../cli/bin/minerCPU kernel (no GPU required)
cd kernel # if not already there
# Requires: g++ with C++17 supportg++ --version # must be ≥ 7.0
make cpu
# Output:ls -lh ../cli/bin/miner-cpuBuild both GPU + CPU
cd kernelmake all ARCHS="86 89"
ls -lh ../cli/bin/# miner ← CUDA# miner-cpu ← CPU fallbackMakefile reference
make — same as `make cuda` (default)make cuda — NVIDIA GPU kernel → cli/bin/minermake cpu — CPU fallback kernel → cli/bin/miner-cpumake opencl — OpenCL kernel → cli/bin/miner-opencl (AMD/Intel)make all — cuda + cpumake clean — remove all built binaries from cli/bin/make help — print this reference
# Variables:make cuda ARCHS="75 80 86 89 90" — multi-arch fat binarymake cuda NVCC=/usr/local/cuda/bin/nvcc — explicit nvcc pathmake cpu CXX=clang++ — use clang instead of g++Run the built CLI
cd clicp .env.example .envnano .envcargo build --release --locked./target/release/anvil256-cliOr if you want to use the compiled binary in-place:
# From repo root:cd cliANVIL256_ADDRESS=0x… \BASE_RPC_URL=https://… \PRIVATE_KEY=0x… \ ./target/release/anvil256-cliOption B — Prebuilt Tar Releases
Prebuilt tarballs are intentionally not documented as the primary path yet. Until signed release artifacts are published, users should build locally from source using Option A.
When official tarballs exist, this section will include:
- release URL;
sha256sums.txtverification;- cosign/Sigstore verification;
- exact extracted file layout.
Reproducible build (CI/attestation)
The repo ships scripts/reproducible-build.sh which pins compiler versions
and produces sha256sums.txt matching the official release:
# Requires Docker and the builder imageTAG=v0.1.0 bash scripts/reproducible-build.sh
# Outputs to ./dist/:# anvil256-cli# anvil256-miner# sha256sums.txtEpoch Lifecycle — Mathematical Steps
Step 1: CLI reads γ(m) = minerEpochCount[m] (eth_call)Step 2: CLI reads ε[n] = epochEntropy[n] (eth_call)
Step 3: CLI computes: τ(m,n) = H(m ∥ γ(m) ∥ genesisBlockhash) ι(m,n) = H(τ(m,n) ∥ n)
Step 4: CLI dispatches JOB(ι, ε[n], D[n]) → kernel
Step 5: Kernel iterates ν = 0, 1, 2, …: mid = H(ι ∥ ν_be32) result = H(mid ∥ ε[n]) if result < D[n]: FOUND(ν)
Expected iterations: E[iter] = 2^256 / D[n]
Step 6: CLI submits mine(ν) with msg.value = currentFeeWei()
Step 7: On-chain: contract verifies κ(m, ν, n) < D[n] mints R(n) = R₀ >> ⌊n/H⌋ ANVL to msg.sender mints 0.1R(n) ANVL to the POL reserve, subject to MAX_SUPPLY splits fee: 50% dev recipient, 50% lpReserveEthWei γ(m)++ (I13) ε[n+1] ← H(blockhash(block) ∥ D[n+1])
Step 8: Repeat from Step 1 with new γ(m) and new ε[n+1]Useful cast Commands (on-chain inspection)
All commands use cast from the Foundry toolchain.
Replace $CONTRACT and $RPC with your values.
CONTRACT=0x8ea35B3e757d36dDeD989844aa49B56bb9C48bC9RPC=https://mainnet.base.org
# Current epoch numbercast call $CONTRACT "currentEpoch()(uint256)" --rpc-url $RPC
# Current reward (in wei; divide by 1e18 for ANVL)cast call $CONTRACT "currentReward()(uint256)" --rpc-url $RPC
# Current difficulty (uint256)cast call $CONTRACT "currentDifficulty()(uint256)" --rpc-url $RPC
# Current protocol fee in wei (~$0.10 in ETH)cast call $CONTRACT "currentFeeWei()(uint256)" --rpc-url $RPC
# Your ANVL token balancecast call $CONTRACT \ "balanceOf(address)(uint256)" 0xYOUR_ADDRESS \ --rpc-url $RPC
# Total ANVL minted so farcast call $CONTRACT "totalSupply()(uint256)" --rpc-url $RPC
# Your gamma (mine count for this address)cast call $CONTRACT \ "minerEpochCount(address)(uint64)" 0xYOUR_ADDRESS \ --rpc-url $RPC
# Check if ETH fees got stuck (should be 0 normally)cast call $CONTRACT "stuckFeesWei()(uint256)" --rpc-url $RPC
# Sweep any stuck fees (permissionless, sends to feeRecipient)cast send $CONTRACT "sweepStuckFees()" \ --private-key 0xYOUR_KEY --rpc-url $RPC
# Claim a pending refund (if mine() over-pay was queued)cast send $CONTRACT "claimRefund()" \ --private-key 0xYOUR_KEY --rpc-url $RPCMaximising Yield
Low-latency RPC. The per-epoch and fetches are mandatory round-trips. If RPC latency is ms, then ms of hashrate is lost per epoch transition. At and :
Negligible vs. expected iterations, but grows at high global hashrates. Self-host a Base node for sub-10ms latency.
Distinct address per physical rig. Multiple machines on the same address race the same challenge — they do not add hashrates. Effective hashrate for machines on the same address remains , not . Use separate wallets for independent rigs.
This also lowers the NCT concentration signal: each distinct address lowers , which reduces and thus lowers difficulty for the entire network.
Tune MINER_TARGET_BATCH_MS. At ,
a 250 ms batch processes nonces. Smaller batches improve
epoch-transition reaction time; larger batches improve GPU utilisation.
Tradeoff: reaction latency .
Priority fee. Base L2 at 0.001 gwei tip lands in the next block
of the time. Increasing this provides diminishing returns because
epoch slots go to the first valid nonce, not the highest fee.
Cost Model
Per Mine (RTX 4060 Ti, Epoch 0)
| Component | Formula | Typical |
|---|---|---|
| L2 gas | $66{,}500 \times 0.05;\text{gwei} \times 10^{-9} \times p_$$ | \approx\0.008$ |
| Protocol fee | at ETH = \2{,}500$ | \0.10$ |
| Total per mine | \approx\0.108$ | |
| GPU power per day | 160\;\text{W} \times 24\;\text{h} \times \0.12/\text{kWh}$ | \0.46$ |
Break-Even Analysis
Daily revenue at p_{\text{ANVL}} = \0.50\mu = 11.5;\text{mines/day}$:
Daily cost:
Hardware payback at RTX 4060 Ti \approx \450$:
Price floor. A rig breaks even when revenue cost:
Below this floor, hashrate exits, the PI controller reduces , decreases, increases — a self-stabilising closed-loop property.
These figures are examples, not forecasts. They assume a market price, global hashrate, gas price, and power cost; the protocol guarantees none of them.
Troubleshooting
no miner binary found
fatal error: no miner binary found: tried ./bin/miner (CUDA) and ./bin/miner-cpu (CPU).Build with `make cuda` or `make cpu`.Fix: Build the kernel first:
cd kernelmake cpu # CPU-only (no GPU needed)# ormake cuda ARCHS="89" # for RTX 40xxANVIL256_ADDRESS not set
fatal error: config: ANVIL256_ADDRESS not setFix: Add it to .env or export it:
export ANVIL256_ADDRESS=0x…FeeOracleStale — retrying
WARN retryable error: protocol fee oracle stale or unreachable: …; sleeping 2sThe Chainlink ETH/USD feed has not updated within 1 hour. This is rare on Base. The CLI retries automatically every 2 seconds — no action needed.
GasCapExceeded
WARN retryable error: gas budget exceeded: estimated $0.06, cap $0.05; sleeping 2sBase L2 base fee spiked. Either wait for it to drop, or raise MAX_GAS_USD in .env:
MAX_GAS_USD=0.10EpochChanged — nonce raced
WARN retryable error: epoch changed mid-search (old=5, new=6); sleeping 2sAnother miner won the epoch while your kernel was searching. Normal — the CLI immediately starts the next epoch. No ANVL or ETH was lost.
kernel emitted no events; declaring it dead
The GPU kernel process stopped emitting output for 30 seconds. The CLI kills and respawns it automatically. Check GPU temperature and VRAM usage:
nvidia-smiMine confirms but ANVL balance is 0
Check you are querying the correct contract address:
cast call $CONTRACT "totalSupply()(uint256)" --rpc-url $RPCcast call $CONTRACT "balanceOf(address)(uint256)" 0xYOUR_ADDRESS --rpc-url $RPCIf totalSupply is non-zero but your balance is 0, the $CONTRACT variable
may be pointing at an old deployment. Check the CLI startup log:
INFO Anvil256 CLI starting contract=0x… ← this is the address being usedFAQ
Q: What is and why does it matter?
is your wallet’s on-chain mine counter. It enters the Cascade puzzle as:
A new wallet has , giving a distinct from established wallets () — but no cheaper puzzle. Difficulty is uniform across all callers.
Q: Does switching to a new wallet help?
No. A fresh address has and receives a fresh challenge. Difficulty is unchanged. The new address must pay \0.10$ to register in the NCT window. There is no mining advantage from rotation (see SECURITY.md §4.3, Theorem 4.3).
Q: Can two machines mine with the same wallet?
Yes, but they race the same challenge with the same . Their hashrates do not add — they compete. The probability of the first machine finding a valid nonce is . Use separate wallets for independent physical rigs.
Q: What is an “epoch”?
One successful mine() call anywhere on the network. The protocol does
not use block numbers, time, or any external clock. Epoch count is the
only unit of protocol timekeeping. The PI controller’s setpoint of 120 s
is a target for the observed epoch duration, not a protocol-enforced value.
Q: Why is Cascade ~20% slower than single-pass Keccak?
Two Keccak evaluations per nonce. Pass 2 uses in constant memory — unavoidable by Theorem 4.1. The ~20% overhead cannot be eliminated without removing the anti-precompute property.
Q: Does the CLI send telemetry?
No. The CLI contacts only your configured BASE_RPC_URL. No telemetry,
no update checks, no third-party endpoints.
Q: What if Base L2 is unavailable?
Mining halts gracefully. The CLI retries with exponential backoff.
No ANVL is at risk: only advances on successful
mine() calls. The controller state is unchanged until the next
period boundary.
Q: GPU not detected — only CPU cores showing?
The kernel binary was built with make cpu, not make cuda. Rebuild:
cd kernel && make cuda ARCHS="YOUR_SM_VERSION"Or explicitly set MINER_BIN=./bin/miner in .env to force the CUDA binary.
Q: How do I run multiple GPUs?
By default all detected GPUs are used. To use specific ones:
MINER_DEVICES=0,1,2 # use GPU 0, 1, and 2 onlyEach device runs an independent search starting at a different nonce base
(device_index × 2^56), so they cover non-overlapping nonce space.
Q: What happens at halving?
At epoch 210,000 the reward drops from 50 ANVL to 25 ANVL automatically
on-chain. No miner action needed. The CLI reads currentReward() each
epoch and logs the correct value.