Link your Ethereum liquidity wallets to your Bittensor hotkey so you can receive subnet emissions for tightening spreads on 8BALL prediction markets.
If your wallet is not linked, you will not receive rewards when reward distribution goes live.
You provide liquidity on 8BALL markets using an Ethereum wallet. This binding proves you own both, so that you can receive rewards.
ETH wallet (creates limit orders on-chain)
|
| register.py signs a binding message with your private key (EIP-191)
v
Binding bundle (JSON, uploaded to Cloudflare R2)
|
| public URL committed to Bittensor chain
v
Your hotkey (receives emissions)
The binding is cryptographic -- your ETH private key signs a message naming your hotkey, proving you control both. register.py handles signing, uploading the bundle to a public Cloudflare R2 bucket, and committing the URL on-chain. Your Ethereum private keys are only used locally and never leave your machine.
- A registered Bittensor hotkey on subnet 125
- The ETH wallet(s) you use to provide liquidity on the 8BALL orderbook
- A Cloudflare account with an R2 bucket (see Setting up R2 below)
- Python 3.10+ with dependencies:
pip install -r requirements.txt
You need a publicly readable R2 bucket to host your binding bundle. Validators only accept commit URLs hosted on R2 (*.r2.dev or *.r2.cloudflarestorage.com).
- Log into the Cloudflare dashboard
- Go to R2 Object Storage in the sidebar
- Click Create bucket and give it a name (e.g.,
8ball-bindings)
- Open your bucket's Settings tab
- Under Public access, click Allow Access
- This gives your bucket a public URL like
https://pub-xxxxxxxxxxxx.r2.dev - Note this URL -- you'll need it for your
.envfile
- Go to R2 Object Storage > Manage R2 API Tokens
- Click Create API token
- Give it Object Read & Write permission for your bucket
- Save the Access Key ID and Secret Access Key
You'll also need your Cloudflare Account ID (visible in the R2 dashboard URL or the right sidebar of the dashboard overview).
Put your ETH private keys and R2 credentials in a .env file:
# ETH wallet private keys -- register.py derives the addresses automatically
# and signs the binding messages for you. Keys never leave your machine.
ETH_PRIVATE_KEY_1=0xabc123...your_first_wallet_private_key
ETH_PRIVATE_KEY_2=0xdef456...your_second_wallet_private_key
# Cloudflare R2 (from the setup above)
CF_ACCOUNT_ID=your-cloudflare-account-id
CF_R2_ACCESS_KEY=your-r2-access-key
CF_R2_SECRET_KEY=your-r2-secret-key
CF_R2_BUCKET=8ball-bindings
CF_R2_PUBLIC_URL=https://pub-xxxxxxxxxxxx.r2.devYou can bind as many wallets as you want -- just add more ETH_PRIVATE_KEY_N lines.
If you only have one wallet, you can use ETH_PRIVATE_KEY=0x... without a number.
python register.py \
--netuid 125 \
--wallet-name my_miner \
--wallet-hotkey default \
--env-file .envThis does everything in one shot:
- Loads your ETH private keys from
.env - Derives the ETH addresses from the keys
- Generates binding messages and signs them automatically
- Builds the bundle and verifies all signatures
- Uploads the bundle to your R2 bucket (object key = your hotkey)
- Commits the public URL on-chain
Output will look like:
Hotkey: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
ETH wallets: 2 key(s) loaded from .env
Bound: 0xabc123...
Bound: 0xdef456...
Bundle verified: 2 wallet(s), all signatures valid
Uploading bundle to R2: 8ball-bindings/5GrwvaEF5zXb26Fz9rc...
Uploaded: https://pub-xxxxxxxxxxxx.r2.dev/5GrwvaEF5zXb26Fz9rc...
Committing URL to subnet 125...
Committed on-chain: https://pub-xxxxxxxxxxxx.r2.dev/5GrwvaEF5zXb26Fz9rc...
Done. You only need to re-commit if your R2 URL changes.
Your private keys are used locally to sign the binding messages. They are never uploaded, transmitted, or stored anywhere outside your machine.
To verify your .env is correct and signatures pass validation without actually uploading or committing:
python register.py \
--netuid 125 \
--wallet-name my_miner \
--wallet-hotkey default \
--env-file .env \
--dry-runThis prints the verified bundle JSON and exits. No network calls are made.
You only need to commit once. The on-chain commitment points to your R2 URL, and the URL stays the same.
To update your wallets later (add, remove, or change keys), edit your .env and re-run register.py. The bundle at the same URL gets overwritten automatically.
All markets live on Polygon (chain ID 137):
| Contract | Address |
|---|---|
| OutcomeRegistry | 0xA69Ee0a1B0E25151684161CE875745b94ed170ab |
| PredictionCLOB (orderbook) | 0x906e949ADE80C322c5DF704Aa1F2B9A982648d8E |
All 19 active markets will be incentivized equally at validator launch. Every direction (YES bid, YES ask, NO bid, NO ask) within every market carries the same weight.
| ID | Category | Market |
|---|---|---|
| 1 | Politics | GOP retains House in 2026? |
| 2 | Politics | GOP retains Senate in 2026? |
| 3 | Politics | Trump impeachment proceedings in 2026? |
| 4 | Politics | Supreme Court vacancy in 2026? |
| 5 | Politics | New US tariffs on China in 2026? |
| 6 | Sports | USA wins 2026 World Cup? |
| 7 | Sports | USA tops 2026 Winter Olympics medal count? |
| 8 | Sports | Lakers win 2026 NBA Finals? |
| 10 | Sports | Dodgers win 2026 World Series? |
| 11 | Crypto | Bitcoin reaches $150K in 2026? |
| 12 | Crypto | Ethereum reaches $5K in 2026? |
| 13 | Crypto | Crypto market cap reaches $5T in 2026? |
| 14 | Crypto | Solana reaches $500 in 2026? |
| 15 | Crypto | Bitcoin ETF AUM reaches $100B in 2026? |
| 16 | Crypto | TAO drops to $50 in 2026? |
| 17 | Crypto | TAO reaches $1,000 in 2026? |
| 18 | Crypto | TAO reaches $1,500 in 2026? |
| 19 | Crypto | TAO reaches $2,000 in 2026? |
| 20 | Crypto | TAO reaches $5,000 in 2026? |
Incentives are based on the last 72 minutes of liquidity providing. Validators snapshot the orderbook, score every resting order that has been placed during that window, and distribute rewards. This happens every 72 minutes.
Your score depends on three things:
1. Spread tightness -- how close your order is to the market midpoint. Orders within 5% of mid qualify. Tighter is exponentially better:
| Distance from mid | Reward multiplier |
|---|---|
| 0% (at mid) | 1.00x |
| 0.5% | 0.82x |
| 1% | 0.67x |
| 2.5% | 0.37x |
| 5% (boundary) | 0.14x |
| >5% | disqualified |
Quoting at the midpoint earns 7.4x more per dollar than quoting at the 5% boundary.
2. Size -- score scales linearly with the notional value of your order. Deeper liquidity earns proportionally more.
3. Duration -- score scales linearly with how long your order(s) have been placed within the 72-minute window. Cancelling and re-placing resets the clock.
Additionally, orders must be locked (uncancellable) for at least 30 seconds to qualify at all. This prevents cancel/replace gaming.
All markets and all directions (YES bid, YES ask, NO bid, NO ask) are weighted equally. Your total score across all your orders determines your share of emissions for that period. The validator maps your ETH address to your hotkey using the binding you registered here -- which is why linking now matters.
Unlinked wallets forfeit their rewards entirely.
The highest-scoring orders are those placed as close to the midpoint as possible that are not filled during the period. Filled orders stop accumulating duration, so persistent resting liquidity outscores orders that get taken. The ideal position is tight, deep, and untouched for the full 72 minutes.
It should be noted that the current reward system is extremely inefficient, and is intended to be used as a manner to convert miner rewards into usable liquidity in order to enable real users.
Once there is stable trading activity on the contracts, the incentive mechanism will transition to Valley. Valley is a convex optimization system that allocates maker rewards from realized trading fees -- it solves for the optimal flow of liquidity across bid/ask price levels subject to budget and spread constraints, and distributes rewards proportionally to makers whose resting orders facilitated that flow.
Under Valley, rewards are funded entirely by fees collected from trades, so they are necessarily lower than the fees themselves. This is a fundamentally different regime: instead of subsidized emissions for posting liquidity, makers earn a share of actual fee revenue. The transition happens when organic trading volume is sufficient to sustain meaningful maker rebates on its own.
More information on how valley works is available on the website, Eight Ball
After registering, confirm the bundle is publicly accessible and valid:
from wallet_binding import download_bundle, verify_bundle
url = "https://pub-xxxxxxxxxxxx.r2.dev/5YourHotkey..."
bundle = download_bundle(url)
ok, errors = verify_bundle(bundle, expected_hotkey_ss58="5YourHotkey...")
print("Valid" if ok else f"Problems: {errors}")Or just curl the URL and check the JSON looks right:
curl -s https://pub-xxxxxxxxxxxx.r2.dev/5YourHotkey... | python -m json.tool{
"schema": "ethbind",
"version": 2,
"hotkey_ss58": "5GrwvaEF...",
"nonce": "a1b2c3d4e5f6...",
"issued_at": 1700000000,
"wallets": [
{
"eth_address": "0xabc...",
"signature_hex": "0xdef..."
}
]
}nonce-- random 12-byte hex, unique per registration- Each wallet entry carries an EIP-191
personal_signsignature over the binding message - The bundle is uploaded as-is to R2; any modification is detectable by re-verifying the signatures
Validators enforce these rules on commit URLs:
| Rule | Requirement |
|---|---|
| Scheme | HTTPS only |
| Host | Must be *.r2.dev or *.r2.cloudflarestorage.com |
| Path | Exactly /<hotkey> -- single segment, must match the miner's hotkey |
Any commit URL that doesn't match these rules is ignored during reward distribution.
| File | Purpose |
|---|---|
wallet_binding.py |
Core library -- message generation, signing, signature verification, R2 upload/download, URL validation |
register.py |
CLI to register your ETH wallet bindings (reads private keys from .env) |
requirements.txt |
Python dependencies |