# 8BALL ETH Wallet Association 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.** --- ## How it works 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. --- ## Prerequisites - 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](#setting-up-cloudflare-r2) below) - Python 3.10+ with dependencies: `pip install -r requirements.txt` --- ## Setting up Cloudflare R2 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`). ### 1. Create a bucket 1. Log into the [Cloudflare dashboard](https://dash.cloudflare.com) 2. Go to **R2 Object Storage** in the sidebar 3. Click **Create bucket** and give it a name (e.g., `8ball-bindings`) ### 2. Enable public access 1. Open your bucket's **Settings** tab 2. Under **Public access**, click **Allow Access** 3. This gives your bucket a public URL like `https://pub-xxxxxxxxxxxx.r2.dev` 4. Note this URL -- you'll need it for your `.env` file ### 3. Create an API token 1. Go to **R2 Object Storage** > **Manage R2 API Tokens** 2. Click **Create API token** 3. Give it **Object Read & Write** permission for your bucket 4. 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). --- ## Registration ### Step 1: Create your `.env` file Put your ETH private keys and R2 credentials in a `.env` file: ```bash # 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.dev ``` You 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. ### Step 2: Run register.py ```bash python register.py \ --netuid 125 \ --wallet-name my_miner \ --wallet-hotkey default \ --env-file .env ``` This does everything in one shot: 1. Loads your ETH private keys from `.env` 2. Derives the ETH addresses from the keys 3. Generates binding messages and signs them automatically 4. Builds the bundle and verifies all signatures 5. Uploads the bundle to your R2 bucket (object key = your hotkey) 6. 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.** ### Dry run (test without uploading) To verify your `.env` is correct and signatures pass validation without actually uploading or committing: ```bash python register.py \ --netuid 125 \ --wallet-name my_miner \ --wallet-hotkey default \ --env-file .env \ --dry-run ``` This prints the verified bundle JSON and exits. No network calls are made. ### After registration **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. --- ## Contracts All markets live on Polygon (chain ID 137): | Contract | Address | |----------|---------| | OutcomeRegistry | `0xA69Ee0a1B0E25151684161CE875745b94ed170ab` | | PredictionCLOB (orderbook) | `0x906e949ADE80C322c5DF704Aa1F2B9A982648d8E` | ### Incentivized markets 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? | --- ## How rewards will be distributed 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.** ### Optimal strategy 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. ### Transition to Valley 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](https://8Ball125.com) --- ## Verifying your binding After registering, confirm the bundle is publicly accessible and valid: ```python 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: ```bash curl -s https://pub-xxxxxxxxxxxx.r2.dev/5YourHotkey... | python -m json.tool ``` --- ## Bundle format reference ```json { "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_sign` signature over the binding message - The bundle is uploaded as-is to R2; any modification is detectable by re-verifying the signatures --- ## URL requirements Validators enforce these rules on commit URLs: | Rule | Requirement | |------|-------------| | Scheme | HTTPS only | | Host | Must be `*.r2.dev` or `*.r2.cloudflarestorage.com` | | Path | Exactly `/` -- single segment, must match the miner's hotkey | Any commit URL that doesn't match these rules is ignored during reward distribution. --- ## Files | 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 |