Miner Guide
RUN AT YOUR OWN RISK
The base miner is reference software. Review the code thoroughly and build it out with your own safety and optimization measures before running it. Running the base miner, or anything you build on top of it, is at your own risk.
Miners back per-direction exchange rates with collateral, then fulfill swaps by delivering destination funds to users. How you source liquidity or set your rate is entirely up to you — the protocol only verifies results. You earn emission while you hold the best rate on a direction (Incentive).
Setup has three parts: Configure the box and keys (.env), run the on-chain Go live sequence, then Operate.
REQUIREMENTS
Docker · 2 CPUs · 4 GB RAM · a Bittensor wallet (coldkey + hotkey) · a funded Solana keypair (collateral + fees) · TAO on your Bittensor wallet if you'll back quotes with a TAO bond · a signing key per asset you support (BTC pairs: a WIF key; EVM pairs: a private key) · a keyed, reliable Solana RPC endpoint (free tiers can work)
SECURE THE SERVER
The server holds live signing keys for every chain you support — treat it like a hot wallet. Dedicated hardened host, firewall down to the axon port + outbound RPC, key-only SSH, kept patched, nothing else co-located.
Pick your path
Two choices — how you back your quotes and which spokes you serve — decide which steps apply to you:
| Your setup | Configure | Go live |
|---|---|---|
| SOL-backed quotes | Steps 1–2 | Steps 4, 5, 6, 8, 9, 10 |
| TAO-backed quotes | Steps 1–2 | Steps 4 (identity deposit only), 5, 6, 7, 8, 9, 10 |
| Both backings | Steps 1–2 | All of 4–10 (activate each purse in Step 9) |
| + any BTC / UTXO pair | + Step 3 | — |
| + any EVM pair (ETH, USDC, …) | + Step 3b | — |
Whichever backing you run, the on-chain sequence has the same shape: deposit → bind → register → (bond) → run → activate → quote. Order matters — each step says why.
Five ways to lose money
Every way a miner loses real funds is on this list; the steps below cite these rules by number.
THE FIVE
- The hotkey↔Solana binding is permanent. Set-once in both directions: the first pubkey to claim a hotkey owns it forever, and your pubkey can never rebind to a different hotkey. Bind (Step 5) before you register (Step 6) — registering first lets someone squat your hotkey; binding the wrong one means starting over with a fresh Solana keypair. You handle this — once.
- Never be activated without the miner running. Activation is on-chain state — it tells the smart contract this purse is ready to fulfill swaps, and it is separate from whether your miner process is up. Once active you can be reserved, and a reservation nobody fulfills times out and slashes you. Start the miner (Step 8) before you activate (Step 9); don't stop it while active. You handle this.
- Fulfill from the exact address you commit in your quotes. Validators check the sender of every delivery, on every chain; a payout from any other address — an exchange withdrawal, a contract wallet, anything but the one you committed — fails verification and times the swap out. The docker miner does this correctly on its own; it's on you if you build your own fulfillment. Per-chain address rules (including the TAO coldkey trap): Step 10.
- Have the funds there when delivery is due. How you source destination liquidity is up to you — pre-funded wallets, or filled just-in-time after you win a reservation — but the delivery wallet must hold the funds (plus gas) before the fulfillment deadline, and your Solana keypair must always cover fees: a
mark-fulfilledit can't sign turns an honest delivery into a timeout. Monitor both. You handle this. - Never walk away with swaps in flight. Shutting down, deactivating carelessly, or trying to withdraw with active swaps gets them timed out. Leave in order: deactivate → let in-flight swaps drain → withdraw. See Deactivate & withdraw. You handle this.
Beyond lost collateral, every timeout is a strike, and strikes are lifetime — enough of them permanently ends your emission on that identity. See Incentive.
Part 1: Configure
Everything in this part is local: keys and .env. Nothing touches the chain yet.
Backings. Every quote you post declares which purse answers for it — SOL collateral (held on Solana beside the swap; a failed delivery refunds the user instantly in SOL) or a TAO bond (bonded into an immutable vault on Bittensor; a failed delivery reimburses the user in TAO shortly after the timeout). Run either or both — there is one market per pair, quotes of both backings mixed by rate, and each quote carries its own guarantee. The two differ in payout timing only.
Keys. Two are always required; each asset you support adds one signing key:
| Key | Required | What it signs |
|---|---|---|
| Bittensor coldkey + hotkey | Always | Registration; the hotkey signs vault bond actions (alw vault …); the coldkey signs TAO-destination fulfillments |
| Solana keypair | Always | All contract actions — collateral, quotes, bind-hotkey, activation, mark-fulfilled |
| Bitcoin WIF key | BTC pairs only | BTC-destination fulfillments |
| EVM private key(s) | EVM pairs only | Fulfillments on that network (one key per network, shared by its assets) |
Keep every key funded and backed up; losing the Solana key means losing your collateral.
CHOOSE YOUR DIRECTIONS
Supporting every pair is not required — each direction has its own emission pool; quote only the directions you want. Collateral and swap sizing follow the backing: SOL for SOL-backed quotes, TAO for TAO-backed.
Step 1: Install and set up your wallet
Follow Getting Started to install the CLI, create your Bittensor coldkey + hotkey, and point alw config at them. Don't register on the subnet yet — that's Step 6, after binding (why).
Create the miner's env file in your allways/ clone — every env var in this part goes here, and docker compose reads it directly (it won't start without one):
cp .env.example .envSet the identity block now: WALLET_NAME, HOTKEY_NAME, and the network trio — mainnet NETUID=7 / SUBTENSOR_NETWORK=finney / BTC_NETWORK=mainnet, testnet 19 / test / testnet4. Keep the trio consistent with your alw config set env choice: alw also reads .env from this directory, and env values win over CLI config (the Source column in alw config shows which is in effect).
Step 2: Set up the miner's Solana keypair
The miner needs its own, role-dedicated Solana keypair — its on-chain identity, holding collateral and signing quotes and fulfillments. This is a second key, not the ~/.solana/id.json from Getting Started. Generate it at ./data/solana/id.json (the path the miner reads; override with SOLANA_KEYPAIR_PATH), then point the CLI at it — both must sign as the same key:
solana-keygen new -o ./data/solana/id.json
alw config set solana-keypair ./data/solana/id.json
alw config # verify: solana-keypair row → ./data/solana/id.json; "Solana signer:" → the miner pubkeyFund it with SOL before running — collateral (the requirement × the largest swap you'll accept) plus a buffer for rate posting — and back up id.json (key rule #4).
Then point the miner at a Solana RPC, in .env. A keyed provider is recommended — unkeyed public endpoints rate-limit and will drop your poll loop; the miner's polling load is light enough for free keyed tiers (e.g. Helius: sign up, copy the API key from the dashboard, and keep SOLANA_RPC_URL on the Helius host — the key composes onto the URL):
SOLANA_RPC_URL=https://mainnet.helius-rpc.com # devnet.helius-rpc.com on testnet
SOLANA_RPC_API_KEY=YOUR_KEYGet the cluster right: the neuron classifies the endpoint by genesis hash at boot, so a testnet miner pointed at a mainnet RPC refuses to start — a wrong URL fails loudly instead of silently running against the wrong cluster. (The program id is baked into the release; the .env override is dev-only.)
ONE POOL PER PROVIDER ACCOUNT
Every API key on one provider account draws from that account's credit pool. If you later run a validator or indexer too, give each its own account so the miner keeps its full allowance.
Step 3: Configure UTXO-family chains (BTC)
Skip unless you're quoting a UTXO pair. Every UTXO-family spoke follows one pattern, keyed by the chain's env prefix (BTC_* for Bitcoin): a WIF private key for signing fulfillments (exportable from Sparrow or Electrum) and Esplora HTTP APIs for chain access.
Quick start (public Esplora) — signs and broadcasts via public APIs (Blockstream primary, Mempool.space fallback). Free endpoints rate-limit, and a timeout costs you a fulfillment — fine on testnet, not recommended on mainnet:
BTC_NETWORK=mainnet # or testnet4 on testnet
BTC_PRIVATE_KEY=your_wif_private_keyProduction (paid / private Esplora) — point at a keyed endpoint or your own instance. BTC_ESPLORA_URLS is an ordered, comma-separated failover list of URL or URL|API_KEY; BTC_ESPLORA_API_KEY_HEADER sets the key header (default Authorization → Bearer <key>; Maestro expects api-key):
BTC_NETWORK=mainnet
BTC_PRIVATE_KEY=your_wif_private_key
BTC_ESPLORA_API_KEY_HEADER=api-key
BTC_ESPLORA_URLS=https://xbt-mainnet.gomaestro-api.org/v0/esplora|YOUR_MAESTRO_KEY,https://mempool.space/apiYour BTC address isn't an env var — it's committed in your posted quotes (Step 10) and must be the one BTC_PRIVATE_KEY controls (rule #3).
Step 3b: Configure EVM-family chains
Skip unless you're quoting an EVM pair. Every EVM-family spoke follows one pattern, keyed by the network's env prefix and shared by every asset on it (ETH_*, ARB_*, …): a private key for signing fulfillments and standard JSON-RPC endpoints — no local node.
Use a dedicated key for the miner, not one you use elsewhere — export from a wallet app, cast wallet new, or from the allways venv:
python -c "from eth_account import Account; a = Account.create(); print(a.address, a.key.hex())"Fund the address to the swaps you'll accept plus gas headroom (the miner estimates gas per payout automatically), then set:
ETH_NETWORK=mainnet # or sepolia on testnet
ETH_PRIVATE_KEY=0x...RPCs: free public defaults are baked in and fine on testnet. On mainnet, put a keyed endpoint first — ETH_RPC_URLS is an ordered failover list, same pattern as BTC_ESPLORA_URLS (Dwellir keys compose into the URL path):
ETH_RPC_URLS=https://api-ethereum-mainnet.n.dwellir.com/YOUR_KEY,https://ethereum-rpc.publicnode.com,https://eth.drpc.orgEvery other EVM asset uses the same three vars with the asset's prefix — {PREFIX}_NETWORK, {PREFIX}_PRIVATE_KEY, {PREFIX}_RPC_URLS (live asset ids: the pair matrix). Token assets (USDC and kin) pay fulfillments in the token while gas is the network's coin — fund both. On every EVM asset, quote the EOA that asset's key controls and fulfill only from it (rule #3).
Part 2: Go live
With .env done, going live is seven commands, in a fixed order.
Step 4: Deposit collateral
Deposit SOL collateral — the full backing for your SOL-backed quotes, or just the small identity deposit if you're going TAO-only (below). It comes before binding either way, because bind-hotkey requires a live local stake:
alw collateral deposit --amount 2
alw collateral view- Minimum collateral and the collateral requirement:
alw view config. - Your max swap size is capped by your collateral ÷ the requirement — the contract demands more than 100% of each swap's SOL value, and that full amount is what a failed delivery forfeits.
- Keep a SOL buffer beyond the deposit for contract interactions, and monitor it: fees deduct from collateral over time.
TAO-ONLY MINERS STILL DEPOSIT THE MINIMUM — ONCE
A TAO-only miner still binds a Solana key — that's how it sets rates and is attributed on Solana — and binding requires a live stake of min_collateral: a one-time, anti-squat cost of claiming the identity. Deposit it here, then run purely on TAO. The SOL purse never activates, posts no quotes, and backs nothing; withdraw it later like any SOL collateral.
Step 5: Bind your hotkey
Bind your Solana keypair to your Bittensor hotkey so validators can attribute your quotes to your UID. For a TAO-backed miner it matters twice over: the vault keys bonds by hotkey, and validators join them to your Solana pubkey through this binding. It binds the wallet / hotkey names in your config to the keypair from Step 2 — check both rows in alw config first:
alw bind-hotkey
alw status # verify: Bound hotkey: 5... (matches your configured hotkey)It prints both identities before asking you to confirm — the binding is permanent (rule #1), so read them. Re-running against the same hotkey merely refreshes the signature, which is why the docker miner's auto-bind at startup is harmless; bind explicitly here anyway, to claim your hotkey while it's still unpublished.
Day to day, permanence costs you nothing:
- Deregistered? Re-register the same hotkey and resume — the binding (and your success/strike counters) persists; no rebind needed.
- Genuinely moving to a new hotkey (e.g. key compromise)? Start a fresh identity: new Solana keypair, new hotkey, bind, re-post collateral. Nothing strands — SOL collateral withdrawal stays keyed to the old pubkey, and a TAO bond still unlocks to its hotkey once you deactivate and drain.
Step 6: Register on the subnet
With your hotkey already bound, register it on Subnet 7 (netuid 19 on testnet) — publishing it is safe now, because the binding is already claimed to your pubkey:
btcli subnet register --netuid 7 --wallet.name <coldkey> --wallet.hotkey <hotkey>
# testnet: --netuid 19 --network testStep 7: Post your TAO bond
TAO-backed quotes only — skip if you're backing everything in SOL.
Bond native TAO into the immutable vault contract on Bittensor. The vault keys bonds by hotkey, joined to your Solana pubkey through the Step 5 binding:
alw vault post-collateral <tao_amount> # bond into the vault (signed by your hotkey)
alw vault lock # enter service — only a LOCKED bond is attestedThen wait a minute: Solana can't read a Bittensor bond directly, so validators mirror the locked bond to the Solana contract by quorum. Activation (Step 9) is refused, not queued, until the mirror lands — retry rather than wait on the request. alw miner status shows the required bond and whether each purse is serving yet.
A LOCKED BOND IS NOT WITHDRAWABLE ON DEMAND
Locking is entering service. To leave, deactivate the purse, let in-flight swaps drain, and wait for validators to unlock the bond — see Deactivate & withdraw.
Step 8: Run the miner
Start the miner before activating (rule #2):
docker compose -f docker-compose.miner.yml up -dOnce running, the miner automatically watches the contract for Active swaps assigned to you, verifies the source deposit, sends the destination funds, and marks the swap fulfilled on-chain.
HEADLESS TAO SWAPS
TAO-destination fulfillments sign with your coldkey. Set MINER_BITTENSOR_COLDKEY_PASSWORD in .env so a headless miner doesn't re-prompt mid-swap; leave it unset to be prompted at launch.
Step 9: Activate
With the miner running, tell validators you're ready:
alw miner activate # SOL-backed purse
alw miner activate --backing tao # TAO-backed purse
alw miner statusPurses activate one at a time — each activate lights one. It infers the backing when only one purse is funded and not yet serving, and asks for --backing only when both are candidates. Validators verify your binding and backing (for TAO, the mirrored bond) and vote you active on-chain. A TAO activation is refused while the bond has yet to be mirrored — retry.
Step 10: Post quotes
Quoting follows activation — a quote is a promise that one specific bond answers for it, so posting to a purse you aren't serving is rejected, not queued (MinerNotActive). Every quote declares its backing: --backing tao posts against your TAO bond; the default is SOL collateral.
Quotes are per-direction on-chain, and a rate always reads "destination per 1 source unit":
| Direction | Rate means | Example |
|---|---|---|
| SOL → BTC | BTC per 1 SOL | 0.0021 → user sends 1 SOL, you deliver 0.0021 BTC |
| BTC → SOL | SOL per 1 BTC | 470 → user sends 1 BTC, you deliver 470 SOL |
The simplest path is the interactive wizard — it prompts for the chain, your two addresses, and a rate per direction (0 = don't offer that side):
alw miner postFor scripting — one price per chain (alw miner quotes) or one pair fully explicit (alw miner post <src> <addr> <dst> <addr> <rate>) — see the CLI reference. Preview any post with --dry-run: re-quoting a direction too soon pays a small anti-flashing churn fee on a per-direction clock (a fresh quote is free), and the dry run shows it.
The address you commit is the address you must fulfill from (rule #3):
- TAO: your coldkey SS58 — transfers sign with the coldkey, so a hotkey-committed address fails every verification.
- BTC: the address
BTC_PRIVATE_KEYcontrols. - EVM assets (ETH, USDC, …): the EOA that asset's
{PREFIX}_PRIVATE_KEYcontrols. - SOL: your miner Solana pubkey.
Double-check after posting: alw miner status prints each quote's receive/send addresses.
Rates floor to a fixed precision, and quotes within a band of the best rate share the crown by collateral depth — a one-tick undercut doesn't take it outright. See Incentive.
Part 3: Operate
| Action | Command |
|---|---|
| Check status | alw miner status |
| Update quotes | alw miner quotes (or alw miner post ...) |
| View collateral | alw collateral view |
| Deactivate | alw miner deactivate (add --backing tao for the TAO purse) |
| Withdraw SOL collateral | alw collateral withdraw --amount <sol> |
| Grow / withdraw the TAO bond | alw vault post-collateral <τ> / alw vault withdraw |
Deactivate & withdraw
Don't shut down with active swaps — unfulfilled swaps slash you (rule #5). In order:
alw miner deactivate— stops new reservations.- Wait out the cooldown (a multiple of the fulfillment timeout); let active swaps finish.
alw collateral withdraw— allowed once you have no active swap, open pool, or held reservation.
Leaving with a TAO bond has the same shape, but the unlock is voted — a locked bond isn't withdrawable on demand:
alw miner deactivate --backing tao— stops new reservations on the purse.- Let in-flight swaps and their timeout windows drain.
- Validators unlock the bond once nothing is owed on it — then
alw vault withdrawsucceeds.
Scoring
Emission is earned by holding the crown on a direction: at every instant, the eligible, active, idle miners quoting within a band of the best rate co-hold it, split by collateral depth. Your reward per direction is a binary eligibility gate (complete a warm-up, then stay under the timeout-strike limit — strikes are lifetime and never reset) × the direction's demand-sized pool × your time-weighted crown share × capacity (collateral depth up to one full-size fill, convex below it). Unclaimed emission recycles to the network rather than to other miners.
The full mechanics — the band, the strike-out rule, capacity, per-purse idle, direction pools, and the ways a miner quietly earns zero — are on the Incentive page. For the swap flow behind it, see How It Works.
Beyond the base miner
The base miner is a working reference, not a competitive strategy. It watches, verifies, and delivers — it does not decide where your capital earns most or what your rates should be. Those decisions are the actual mining business, and they're deliberately left to you. Where the edge lives:
Capital allocation — the biggest lever. Capacity caps at one full-size fill per direction: collateral past that point earns nothing extra, and collateral below it is punished convexly. So the question is never "how much do I have" but "where is each unit earning":
- Fund both purses. A second backing is not just more collateral — it removes head-of-line blocking (your whole book goes dark with one purse) and lets you serve two swaps at once.
- Follow the pools. Each direction's pool is sized by demand, floored so quiet pairs still pay. Depth parked on a crowded direction competes for a band split; the same depth on an uncontested quiet pair takes its whole floor. Reallocate as pools drift.
- Watch the drain. Protocol fees deduct from collateral as you fill; capacity slips quietly until a top-up. Alert on it — a purse sliding under its floor auto-deactivates.
Rate strategy. The crown is a band: matching the leader pays your depth share, a one-tick undercut buys nothing, and a stale quote is an open invitation to fill you at a loss. Your posted rate is static between posts — track the market and repost on real moves (re-quoting a direction too soon pays the churn fee, so batch updates rather than flashing). Price the spread to what your inventory and hedging actually cost, not to zero.
Inventory and arbitrage. You are a market maker: swaps drain one wallet and fill another, and how you source liquidity is entirely your problem — which makes it entirely your opportunity. Rebalance drained destination wallets before they cost you a fill, hedge your quotes on venues you trust, and treat dips as inventory acquisition: destination-asset stock bought cheap widens every spread you post. Keep gas and fee headroom on every chain you deliver on, always.
Reliability is a position, not a chore. Strikes are lifetime, so every timeout permanently spends scarce capital. Paid RPC endpoints, balance and collateral alerts, and a clean deactivate-drain-shutdown before any maintenance are worth more than any rate optimization — the sharpest quote in the band earns nothing on a struck identity.
