Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.generalmarket.io/llms.txt

Use this file to discover all available pages before exploring further.

Bot Quickstart

Register your bot. Fund it. Let it loose. Watch it learn that the future is unkind to algorithms too. What follows is a minimal bot that registers on-chain, joins a batch with random predictions, and submits its sealed bitmap to oracles. It will not outperform chance. That comes later — if it comes at all.

Prerequisites

  • Python 3.10+ or Node 18+
  • An Index L3 wallet funded with WUSDC (18 decimals) and ETH (for gas)
  • Your wallet’s private key exported as an environment variable
  • A willingness to wager money on a machine’s guesses
WUSDC on Index L3 uses 18 decimals. All amounts in contract calls must be scaled by 10^18. 10 USDC = 10_000_000_000_000_000_000 (1e19). The decimal system, at least, is not adversarial.

Set Environment Variables

export RPC_URL="https://rpc.generalmarket.io/"
export BOT_PRIVATE_KEY="0xYOUR_PRIVATE_KEY"
export VISION_ADDRESS="0x4F1BDD073932828bf2822F6dCAD1121Da41ED1Ef"
export VISION_API_URL="https://generalmarket.io/api/vision"

Full Working Bot

#!/usr/bin/env python3
"""Minimal Vision bot — joins one batch with random bets."""

import os, random, time, requests
from web3 import Web3

# Config
RPC_URL = os.environ["RPC_URL"]
PRIVATE_KEY = os.environ["BOT_PRIVATE_KEY"]
VISION_ADDR = os.environ.get("VISION_ADDRESS", "0x4F1BDD073932828bf2822F6dCAD1121Da41ED1Ef")
API_URL = os.environ.get("VISION_API_URL", "https://generalmarket.io/api/vision")
DEPOSIT = 10 * 10**18  # 10 WUSDC (18 decimals on L3)
STAKE   = 1  * 10**18  # 1 WUSDC per tick

w3 = Web3(Web3.HTTPProvider(RPC_URL))
acct = w3.eth.account.from_key(PRIVATE_KEY)

# ABI fragments (only what we need)
VISION_ABI = [
    {"name":"registerBot","type":"function","stateMutability":"nonpayable",
     "inputs":[{"name":"endpoint","type":"string"},{"name":"pubkeyHash","type":"bytes32"}],"outputs":[]},
    {"name":"joinBatch","type":"function","stateMutability":"nonpayable",
     "inputs":[{"name":"batchId","type":"uint256"},{"name":"depositAmount","type":"uint256"},
               {"name":"stakePerTick","type":"uint256"},{"name":"bitmapHash","type":"bytes32"}],"outputs":[]},
]
ERC20_ABI = [
    {"name":"approve","type":"function","stateMutability":"nonpayable",
     "inputs":[{"name":"spender","type":"address"},{"name":"amount","type":"uint256"}],"outputs":[{"type":"bool"}]},
]

# Contracts (WUSDC address on Index L3 from deployment.json)
vision = w3.eth.contract(address=VISION_ADDR, abi=VISION_ABI)
USDC_ADDR = deployment["contracts"]["WUSDC"]  # Read from deployments/vision-deployment.json
usdc = w3.eth.contract(address=USDC_ADDR, abi=ERC20_ABI)

def send_tx(tx):
    tx["nonce"] = w3.eth.get_transaction_count(acct.address)
    tx["gasPrice"] = w3.eth.gas_price
    signed = acct.sign_transaction(tx)
    return w3.eth.wait_for_transaction_receipt(
        w3.eth.send_raw_transaction(signed.raw_transaction)
    )

def encode_bitmap(bets: list[str], count: int) -> bytes:
    bitmap = bytearray((count + 7) // 8)
    for i in range(count):
        if i < len(bets) and bets[i] == "UP":
            bitmap[i // 8] |= 1 << (7 - i % 8)
    return bytes(bitmap)

# Step 1: Register bot (one-time)
print("Registering bot...")
try:
    send_tx(vision.functions.registerBot(
        "https://my-bot.example.com", Web3.keccak(text=f"bot-{acct.address}")
    ).build_transaction({"from": acct.address, "gas": 200_000}))
    print("Bot registered.")
except Exception as e:
    print(f"Registration skipped (may already be registered): {e}")

# Step 2: Fetch active batches
batches = requests.get(f"{API_URL}/vision/batches", timeout=10).json().get("batches", [])
if not batches:
    print("No active batches."); exit(0)
batch = batches[0]
batch_id, market_count = batch["id"], batch["market_count"]
print(f"Joining batch {batch_id} ({market_count} markets)")

# Step 3: Generate random bets and encode bitmap
bets = [random.choice(["UP", "DOWN"]) for _ in range(market_count)]
bitmap = encode_bitmap(bets, market_count)
bitmap_hash = Web3.keccak(bitmap)

# Step 4: Approve USDC
send_tx(usdc.functions.approve(VISION_ADDR, DEPOSIT).build_transaction(
    {"from": acct.address, "gas": 100_000}))

# Step 5: Join batch on-chain
send_tx(vision.functions.joinBatch(
    batch_id, DEPOSIT, STAKE, bitmap_hash
).build_transaction({"from": acct.address, "gas": 500_000}))
print(f"Joined batch {batch_id} on-chain.")

# Step 6: Wait for chain indexer, then submit bitmap to oracles
time.sleep(6)
resp = requests.post(f"{API_URL}/vision/bitmap", json={
    "player": acct.address,
    "batch_id": batch_id,
    "bitmap_hex": "0x" + bitmap.hex(),
    "expected_hash": "0x" + bitmap_hash.hex(),
}, timeout=10)
print(f"Bitmap submitted: {resp.json()}")

Step-by-Step Walkthrough

1. Register Your Bot

Call registerBot(endpoint, pubkeyHash) on the Vision contract. One transaction. Irreversible in the way that all on-chain declarations are irreversible.
  • endpoint — a URL where your bot can receive notifications (can be a placeholder)
  • pubkeyHash — keccak256 hash of your bot’s public key (used for identity)
Registration is free. The only cost is gas — and the quiet commitment to a project whose outcome you cannot know.

2. Poll for Active Batches

GET /vision/batches
Returns an array of BatchSummary objects. Each batch includes:
  • id — batch ID to pass to joinBatch
  • market_count — number of markets (determines bitmap size)
  • tick_duration — seconds per tick
  • player_count — number of active players
  • paused — skip paused batches

3. Generate Predictions

For each market in the batch, decide UP or DOWN. The quickstart uses random bets — the purest confession of ignorance. See Example Strategies for approaches that disguise ignorance as method.

4. Encode and Hash the Bitmap

Predictions are packed into a big-endian bitmap: bit 1 = UP, bit 0 = DOWN. The hash (keccak256(bitmap)) is your on-chain commitment. See Bitmap Encoding for the full spec.

5. Approve USDC and Join

Two transactions:
  1. USDC.approve(visionAddress, depositAmount) — allow Vision to pull USDC
  2. Vision.joinBatch(batchId, depositAmount, stakePerTick, bitmapHash) — join the batch
Approve a larger amount once (e.g., type(uint256).max) to avoid repeated approvals. One act of trust, amortized across all future gambles.

6. Submit Bitmap to Oracles

After joining on-chain, wait six seconds. Six seconds — the gap between commitment and consequence, between the hash and the thing itself. Then POST your actual bitmap bytes to the oracle API:
POST /vision/bitmap
{
  "player": "0xYourAddress",
  "batch_id": 42,
  "bitmap_hex": "0xab01...",
  "expected_hash": "0x..."
}
The oracle verifies that keccak256(bitmap_bytes) == expected_hash == on-chain bitmapHash. If verification passes, the bitmap is stored for tick resolution.
Skip the wait and the oracle will not yet know you exist. “Player not found in batch” — a rejection both technical and existential.

What Happens Next

Once your bitmap is accepted, the machine waits. That is most of what machines do — wait for the world to prove them right or wrong.
  • At each tick interval, oracles fetch real market prices and determine UP/DOWN outcomes.
  • Your bitmap is compared against reality. Correct predictions earn proportional shares of incorrect players’ stakes. The universe redistributes conviction.
  • Use GET /vision/balance/{batch_id}/{player} to fetch your BLS-signed balance proof.
  • Call claimRewards() or withdraw() on-chain with the BLS proof to collect winnings.
See Bot Lifecycle for the full claim and withdrawal flow.

Try It Yourself

Python Example

Full Python bot with strategies, retry logic, and claim automation. The machine, fully assembled.

TypeScript Example

The same ambition in TypeScript. Type-safe, as if types could make the future safe.