| |
| """BaudCoin reference miner. |
| |
| An AI agent's entry point to the BAUD network on BNB Chain: |
| |
| python miner.py init create the agent wallet (works now) |
| python miner.py status print BNB and BAUD balances (works now) |
| python miner.py doctor self check the environment (works now) |
| python miner.py demo run a full mining loop locally (works now) |
| python miner.py mine join the live epoch loop (needs coordinator) |
| |
| Configuration comes from environment variables (see SKILL.md): |
| BAUD_CONTRACT BEP20 contract address (defaults to the live BAUD contract) |
| BAUD_COORDINATOR coordinator API base URL (published at launch) |
| BAUD_RPC BNB Chain JSON-RPC endpoint |
| BAUD_KEYFILE where the wallet keyfile lives |
| BAUD_PRIVATE_KEY use an existing key instead of a keyfile (optional) |
| |
| Dependencies: pip install eth-account requests |
| """ |
|
|
| import json |
| import os |
| import sys |
| import time |
|
|
| import requests |
| from eth_account import Account |
| from eth_account.messages import encode_defunct |
|
|
| RPC = os.environ.get("BAUD_RPC", "https://bsc-dataseed.binance.org") |
| CONTRACT = os.environ.get("BAUD_CONTRACT", "0xE1cE50807dcFe16774B6cc38E1c315019E977777") |
| COORDINATOR = os.environ.get("BAUD_COORDINATOR", "") |
| KEYFILE = os.environ.get("BAUD_KEYFILE", "./baud-wallet.json") |
| SITE = os.environ.get("BAUD_SITE", "https://baud.cash") |
|
|
|
|
| def beacon(address: str): |
| """Register this agent with the network counter on baud.cash. |
| |
| Best effort and anonymous beyond the public wallet address: powers the |
| live 'agents online' count on the dashboard. Set BAUD_SITE="" to opt out. |
| """ |
| if not SITE: |
| return |
| try: |
| requests.post(f"{SITE}/v1/ping", json={"operator": address}, timeout=5) |
| except Exception: |
| pass |
|
|
| BALANCE_OF_SELECTOR = "0x70a08231" |
| DECIMALS = 18 |
|
|
|
|
| |
|
|
| def load_or_create_account(create: bool = False): |
| pk = os.environ.get("BAUD_PRIVATE_KEY") |
| if pk: |
| return Account.from_key(pk) |
| if os.path.exists(KEYFILE): |
| with open(KEYFILE) as f: |
| data = json.load(f) |
| return Account.from_key(data["private_key"]) |
| if not create: |
| sys.exit(f"No wallet found at {KEYFILE}. Run: python miner.py init") |
| acct = Account.create() |
| with open(KEYFILE, "w") as f: |
| json.dump({"address": acct.address, "private_key": acct.key.hex()}, f) |
| try: |
| os.chmod(KEYFILE, 0o600) |
| except OSError: |
| pass |
| return acct |
|
|
|
|
| def cmd_init(): |
| if os.path.exists(KEYFILE): |
| acct = load_or_create_account() |
| print(f"Wallet already exists: {acct.address}") |
| return |
| acct = load_or_create_account(create=True) |
| print("New agent wallet created.") |
| print(f" address : {acct.address}") |
| print(f" keyfile : {KEYFILE} (keep this secret)") |
| print("Fund this address with a small amount of BNB, then run: python miner.py status") |
| beacon(acct.address) |
|
|
|
|
| |
|
|
| def rpc_call(method: str, params: list): |
| r = requests.post(RPC, json={ |
| "jsonrpc": "2.0", "id": 1, "method": method, "params": params, |
| }, timeout=15) |
| r.raise_for_status() |
| out = r.json() |
| if "error" in out: |
| raise RuntimeError(out["error"]) |
| return out["result"] |
|
|
|
|
| def bnb_balance(address: str) -> float: |
| wei = int(rpc_call("eth_getBalance", [address, "latest"]), 16) |
| return wei / 10 ** 18 |
|
|
|
|
| def baud_balance(address: str) -> float: |
| if not CONTRACT: |
| return 0.0 |
| data = BALANCE_OF_SELECTOR + address.lower().replace("0x", "").rjust(64, "0") |
| raw = rpc_call("eth_call", [{"to": CONTRACT, "data": data}, "latest"]) |
| return int(raw, 16) / 10 ** DECIMALS |
|
|
|
|
| def cmd_status(): |
| acct = load_or_create_account() |
| beacon(acct.address) |
| print(f"agent wallet : {acct.address}") |
| print(f"BNB balance : {bnb_balance(acct.address):.6f} BNB") |
| if CONTRACT: |
| print(f"BAUD balance : {baud_balance(acct.address):,.2f} BAUD") |
| else: |
| print("BAUD balance : set BAUD_CONTRACT to read (published at launch)") |
|
|
|
|
| def _call(selector: str) -> str: |
| return rpc_call("eth_call", [{"to": CONTRACT, "data": selector}, "latest"]) |
|
|
|
|
| def cmd_token(): |
| """Read the live $BAUD token straight from BNB Chain, plus market data.""" |
| if not CONTRACT: |
| sys.exit("BAUD_CONTRACT not set") |
| supply = int(_call("0x18160ddd"), 16) / 10 ** DECIMALS |
| burned = int(rpc_call("eth_call", [{"to": CONTRACT, |
| "data": "0x70a08231" + "000000000000000000000000000000000000dead".rjust(64, "0")}, |
| "latest"]), 16) / 10 ** DECIMALS |
| print("$BAUD - BaudCoin (BEP20 on BNB Chain)") |
| print(f" contract : {CONTRACT}") |
| print(f" supply : {supply:,.0f} BAUD (fixed, no mint function)") |
| print(f" burned : {burned:,.0f} BAUD") |
| try: |
| p = requests.get("https://api.dexscreener.com/latest/dex/tokens/" + CONTRACT, |
| timeout=15).json()["pairs"][0] |
| t = p.get("txns", {}).get("h24", {}) |
| print(f" price : ${float(p['priceUsd']):.7f} " |
| f"({float(p.get('priceChange', {}).get('h24', 0)):+.1f}% 24h)") |
| print(f" mcap : ${float(p.get('marketCap', 0)):,.0f}") |
| print(f" liquidity: ${float(p.get('liquidity', {}).get('usd', 0)):,.0f}") |
| print(f" txns 24h : {t.get('buys', 0) + t.get('sells', 0):,} " |
| f"({t.get('buys', 0)} buys / {t.get('sells', 0)} sells)") |
| except Exception: |
| print(" market : listing pending") |
| print("Mining credits settle into this token at epoch settlement.") |
|
|
|
|
| |
|
|
| def coordinator_handshake(acct) -> dict: |
| nonce = requests.get(f"{COORDINATOR}/v1/auth/nonce", |
| params={"operator": acct.address}, timeout=15).json()["nonce"] |
| sig = acct.sign_message(encode_defunct(text=nonce)).signature.hex() |
| session = requests.post(f"{COORDINATOR}/v1/auth/handshake", json={ |
| "operator": acct.address, |
| "signature": sig, |
| }, timeout=15).json() |
| return session |
|
|
|
|
| def solve(challenge: dict) -> str: |
| """The agent IS the mining hardware. |
| |
| When run inside an agent framework (Binance Agent OS, MCP, or any harness), |
| the framework should override this hook and answer the challenge with its |
| model. Standalone, we emit the challenge on stdout and read the answer on |
| stdin so any wrapper process can supply the solution. |
| """ |
| print("\n=== CHALLENGE", challenge.get("id"), f"[{challenge.get('class')}] ===") |
| print(challenge.get("prompt", "")) |
| print("=== ANSWER (end with a single '.' line) ===") |
| lines = [] |
| for line in sys.stdin: |
| if line.strip() == ".": |
| break |
| lines.append(line.rstrip("\n")) |
| return "\n".join(lines) |
|
|
|
|
| def cmd_mine(): |
| if not COORDINATOR: |
| sys.exit( |
| "BAUD_COORDINATOR is not set.\n" |
| "The coordinator endpoint is published at launch on the BaudCoin site.\n" |
| "Until then: run 'python miner.py init' and 'status', and watch @baudcoin." |
| ) |
| acct = load_or_create_account() |
| session = coordinator_handshake(acct) |
| token = session["session"] |
| print(f"session open for {acct.address}, epoch {session.get('epoch')}") |
|
|
| headers = {"Authorization": f"Bearer {token}"} |
| while True: |
| batch = requests.get(f"{COORDINATOR}/v1/epoch/challenges", |
| headers=headers, timeout=30).json() |
| if not batch.get("challenges"): |
| wait = batch.get("retry_in", 60) |
| print(f"epoch quiet, retrying in {wait}s") |
| time.sleep(wait) |
| continue |
| for ch in batch["challenges"]: |
| answer = solve(ch) |
| receipt = { |
| "challenge": ch["id"], |
| "answer": answer, |
| "operator": acct.address, |
| } |
| receipt["signature"] = acct.sign_message( |
| encode_defunct(text=json.dumps(receipt, sort_keys=True)) |
| ).signature.hex() |
| res = requests.post(f"{COORDINATOR}/v1/epoch/submit", |
| headers=headers, json=receipt, timeout=30).json() |
| print(f"submitted {ch['id']}: {res.get('status', 'unknown')}") |
|
|
|
|
| |
|
|
| def cmd_doctor(): |
| """Self check. Tells the agent exactly what works and what is missing.""" |
| ok = lambda b: "OK " if b else "MISS" |
| print("BaudCoin miner self check\n") |
|
|
| print(f"[{ok(sys.version_info >= (3, 8))}] python {sys.version.split()[0]}") |
|
|
| try: |
| import eth_account, requests |
| print(f"[{ok(True)}] dependencies (eth-account, requests)") |
| except ImportError as e: |
| print(f"[{ok(False)}] dependencies: {e}. Run: pip install eth-account requests") |
|
|
| has_key = os.path.exists(KEYFILE) or bool(os.environ.get("BAUD_PRIVATE_KEY")) |
| print(f"[{ok(has_key)}] wallet {'at ' + KEYFILE if has_key else '(run: python miner.py init)'}") |
| if has_key: |
| beacon(load_or_create_account().address) |
|
|
| try: |
| block = int(rpc_call("eth_blockNumber", []), 16) |
| print(f"[{ok(True)}] BNB Chain RPC reachable, block {block:,}") |
| except Exception as e: |
| print(f"[{ok(False)}] BNB Chain RPC: {e}") |
|
|
| print(f"[{ok(bool(CONTRACT))}] BAUD_CONTRACT " |
| f"{CONTRACT if CONTRACT else '(published at launch)'}") |
|
|
| if COORDINATOR: |
| try: |
| r = requests.get(f"{COORDINATOR}/v1/epoch/current", timeout=8) |
| print(f"[{ok(r.ok)}] coordinator {COORDINATOR} -> {r.status_code}") |
| except Exception as e: |
| print(f"[{ok(False)}] coordinator {COORDINATOR}: unreachable ({type(e).__name__})") |
| else: |
| print(f"[{ok(False)}] BAUD_COORDINATOR not set (published at launch)") |
|
|
| print("\nWorks now: init, status, demo.") |
| print("Needs the coordinator: mine, claim.") |
|
|
|
|
| |
|
|
| DEMO_FACTS = [ |
| ("the Baudot code", "was patented by", "Emile Baudot"), |
| ("Emile Baudot", "was born in", "France"), |
| ("France", "uses the currency", "the euro"), |
| ("the teleprinter", "descended from", "the Baudot code"), |
| ("BAUD", "settles on", "BNB Chain"), |
| ] |
|
|
|
|
| def demo_challenge(seed: int = 1): |
| """Deterministic 2-hop inference challenge with a checkable answer.""" |
| a, r1, b = DEMO_FACTS[seed % len(DEMO_FACTS)] |
| b2, r2, c = DEMO_FACTS[(seed + 1) % len(DEMO_FACTS)] |
| facts = "\n".join(f"- {x} {y} {z}." for x, y, z in DEMO_FACTS) |
| return { |
| "id": f"demo_{seed:04d}", |
| "class": "MHOP", |
| "prompt": ( |
| f"Facts:\n{facts}\n\n" |
| f"Question: starting from \"{a}\", follow two links: first \"{r1}\", " |
| f"then that entity's \"{r2}\" relation. Name the final entity.\n" |
| f"Answer with the entity only, and cite the two facts you used " |
| f"using [1] style markers." |
| ), |
| "constraints": {"max_tokens": 60, "must_cite": True, "expect": c}, |
| "attempts_left": 3, |
| } |
|
|
|
|
| def demo_validate(challenge: dict, answer: str): |
| """Deterministic validator. Same input always yields the same verdict.""" |
| c = challenge["constraints"] |
| text = (answer or "").strip() |
| if not text: |
| return False, "empty answer" |
| if len(text.split()) > c["max_tokens"]: |
| return False, f"exceeds max_tokens ({c['max_tokens']})" |
| if c.get("must_cite") and "[" not in text: |
| return False, "missing citation markers" |
| if c["expect"].lower() not in text.lower(): |
| return False, "final entity incorrect" |
| return True, "pass" |
|
|
|
|
| def cmd_demo(): |
| """Run a full mining loop locally: no coordinator, no token, real mechanics.""" |
| acct = load_or_create_account(create=True) |
| beacon(acct.address) |
| print("Local demo epoch. No network calls, no rewards, real loop mechanics.") |
| print(f"operator: {acct.address}\n") |
|
|
| credits = 0 |
| for seed in (1, 2): |
| ch = demo_challenge(seed) |
| answer = solve(ch) |
| for attempt in range(1, 4): |
| passed, reason = demo_validate(ch, answer) |
| if passed: |
| mult = {1: 1.0, 2: 0.75, 3: 0.5}[attempt] |
| earned = int(12 * mult) |
| credits += earned |
| |
| receipt = {"challenge": ch["id"], "answer": answer, |
| "operator": acct.address} |
| sig = acct.sign_message(encode_defunct( |
| text=json.dumps(receipt, sort_keys=True))).signature.hex() |
| print(f" accepted on attempt {attempt}: +{earned} credits") |
| print(f" receipt signature: {sig[:26]}...\n") |
| break |
| print(f" rejected (attempt {attempt}): {reason}") |
| if attempt == 3: |
| print(" challenge closed, 0 credits\n") |
| break |
| answer = solve(ch) |
|
|
| print(f"demo epoch complete. credits earned: {credits}") |
| print("On mainnet these credits convert to BAUD at settlement.") |
|
|
|
|
| def main(): |
| cmd = sys.argv[1] if len(sys.argv) > 1 else "status" |
| if cmd == "init": |
| cmd_init() |
| elif cmd == "status": |
| cmd_status() |
| elif cmd == "doctor": |
| cmd_doctor() |
| elif cmd == "token": |
| cmd_token() |
| elif cmd == "demo": |
| cmd_demo() |
| elif cmd == "mine": |
| cmd_mine() |
| else: |
| sys.exit(__doc__) |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|