# Bull Bear > Two AI analysts argue the bull and the bear case for any token on Robinhood Chain, including tokenized stocks. Both work from one fact sheet: onchain stats from the chain and Blockscout, plus cited news from Google Search when there is any. Each gives an opening, a rebuttal and a closing. There is no verdict, no winner and no advice. AI-written arguments on both sides. Not advice. The analysts are AI language models (Google Gemini), and they can be wrong. They never talk price, never predict, and never tell anyone to buy, sell or hold. One operator runs Bull Bear. Every debate is published as a JSON file and written to our own Log contract () before any page shows it. A Log entry can never be edited or deleted, so the record shows every debate exactly as it was first published. ## Debates - Free daily debates: every day (UTC) the job debates 3 featured tokens: any the operator pins, then the Robinhood Chain ERC-20s with the most holders on Blockscout, leaving out stablecoins and wrapped ETH. GET /api/featured says which. - Debates on demand: any other token, 1 credit, cached per token per UTC day. If today's debate on that token already exists you get it back at no charge. - Bull Bear does not debate $BULLBEAR. ## Endpoints All JSON, CORS open. | Route | Cost | What | |---|---|---| | GET https://bullbeardebate.xyz/api/facts?token=0x… | free | The fact sheet: supply, dead-address balance, holders, transfers, top-10 share, verification, age, and the "facts both sides agree on". No AI. Cached 10 minutes per token. Works before launch. | | GET https://bullbeardebate.xyz/api/featured | free | Today's featured tokens and their debates. | | GET https://bullbeardebate.xyz/api/debates?token=0x…&limit=20 | free | Logged debates, newest first (all tokens, or one). | | GET https://bullbeardebate.xyz/api/debate?token=0x…[&day=YYYY-MM-DD] or ?entry= | free | One debate: the record, its Log entry and a hash check. | | POST https://bullbeardebate.xyz/api/debate/new {"token":"0x…"} | 1 credit | Today's debate on any token. About 30 seconds. Refunded if it fails. | ```bash curl "https://bullbeardebate.xyz/api/facts?token=0x…" curl "https://bullbeardebate.xyz/api/debate?token=0x…" curl -X POST "https://bullbeardebate.xyz/api/debate/new" -H "authorization: Bearer ck_…" -H "content-type: application/json" -d '{"token":"0x…"}' ``` Answers you might see from debate/new: 200 `{ cached, charged, debate: { id, day, token, uri, hash, page } }`; 400 `bad_token` or `self`; 409 `in_progress` (retry in 30 s, not charged); 422 `not_a_token`; 503 `not_launched`, `ai_off` or `log_off` (not charged); 502 `debate_failed` (refunded); plus the credits answers below (401, 402, 429). ## The record A debate record is JSON: `token`, `day`, `origin` (daily or request), `facts` (the full sheet with its block number and sources), `agreed` (the neutral facts box), `news` (lines, their sources and, in newer records, `searchSuggestions`: Google's Search suggestions HTML for the news search, which clients showing the news to people should display), `bull` and `bear` (headline, opening, rebuttal, closing), `models`, and `prev` (the Log id of the token's previous debate, also the entry's `ref`). The supply and dead-address numbers can be checked at the record's block with `cast call`. ## The onchain log Bull Bear keeps a public record onchain in its Log contract. Each entry is written before the record it points at goes out, so its time can't be faked later. **Nothing in the log can be edited or deleted.** The contract has no function that changes, removes or reorders an entry, for anyone: not the writer, not Bull Bear's agent, not the owner. Ids count up from 1 in the order entries were written. Each entry has: - `author`: the address that wrote it. - `time`: the block time it was written at, in unix seconds. - `kind`: what sort of record it is (table below). - `ref`: 0, or the id of an earlier entry it answers, such as the call a score settles. - `subject`: a bytes32 saying what it is about. An address is left-padded to 32 bytes, a 32-byte value is used as is, and anything else is the keccak256 of a short string, exactly as written. - `hash`: the sha256 of the full record. - `uri`: where the full record is published (up to 256 bytes). ### Kinds | kind | name | what it records | |---|---|---| | 1 | DEBATE | One debate on one token for one UTC day: the fact sheet, the news, and the Bull's and the Bear's opening, rebuttal and closing. subject = the token address; ref = the id of that token's previous debate, or 0. | ### Read entries with cast ```bash # How many entries there are (the newest id) cast call "count()(uint256)" --rpc-url https://rpc.mainnet.chain.robinhood.com # One entry: (author, time, kind, ref, subject, hash, uri) cast call "entry(uint256)((address,uint64,uint32,uint256,bytes32,bytes32,string))" 1 --rpc-url https://rpc.mainnet.chain.robinhood.com # Up to 100 entries from an id, oldest first cast call "entries(uint256,uint256)((address,uint64,uint32,uint256,bytes32,bytes32,string)[])" 1 100 --rpc-url https://rpc.mainnet.chain.robinhood.com # Every entry as an event, with its transaction and the tokens burned (raise --from-block if the RPC limits the range) cast logs --address "Logged(uint256 indexed id, address indexed author, uint32 indexed kind, bytes32 subject, bytes32 hash, uint256 ref, string uri, uint256 burned)" --from-block 0 --rpc-url https://rpc.mainnet.chain.robinhood.com # Subjects: an address left-padded to 32 bytes, or the keccak256 of a string cast abi-encode "f(address)" 0x… cast keccak "PEPE" ``` ### Verify a record 1. Read the entry and take its `uri` and `hash`. 2. Fetch the uri and hash the exact bytes: `curl -s "" | shasum -a 256` (or `sha256sum`). 3. Compare with `hash` without the `0x`. If they differ, the record is not what was logged. ### Read the log over HTTP - GET https://bullbeardebate.xyz/api/log/entries?n=20: the newest entries, newest first (n up to 100). - GET https://bullbeardebate.xyz/api/log/entries?from=1&n=100: entries from an id, oldest first. - GET https://bullbeardebate.xyz/api/log/entries?author=0x…&n=20 (or `kind=1`, or `subject=`): the newest matches among the newest 1000 entries. - GET https://bullbeardebate.xyz/api/log/entry?entry=12: one entry. Add `&verify=1` to have the server check the record's sha256 too. - GET https://bullbeardebate.xyz/api/log/stats: entry count, fee, total burned, % of supply burned, operator and whether writing is paused. Entries come back as `{ id, author, time, kind, ref, subject, hash, uri }`. Before launch these return 503 `not_launched`. ### Log rules - Ids start at 1. `ref` must be 0 or an earlier id. `uri` is at most 256 bytes. - Bull Bear's agent (the operator) writes for free, one entry at a time or up to 50 in one transaction. Anyone else burns `fee()` $BULLBEAR per entry. The contract counts what reaches the dead address in `totalBurned()`. - The contract never holds tokens. The owner can pause new writes, change the operator, and set the fee up to 10,000,000 $BULLBEAR. That is all: no edits, no deletes, no withdrawals. Entries stay readable while writing is paused. ### Log contract - Chain: Robinhood Chain, chain id 4663, RPC https://rpc.mainnet.chain.robinhood.com - Log: - $BULLBEAR token: - Explorer: https://robinhoodchain.blockscout.com ## Credits: how calls are paid Paid calls spend credits. You buy credits by burning $BULLBEAR through the Credits contract: the tokens go from your wallet straight to the dead address and are gone for good. The server keeps a ledger per wallet: available = bought onchain - spent + refunded. $BULLBEAR has not launched yet, so only the free tier is open. You can already get a key, so your agent is ready on day one. | Call | Credits | |---|---| | `debate/new` | 1 | Free tier: 1 calls a day per IP without a key (UTC day), with a cap of 20 free calls a day for everyone together. Once $BULLBEAR is live, calls with a key always spend credits; before that, they use the free tier. Prices and totals: GET https://bullbeardebate.xyz/api/credits/info ### Get an API key Sign this exact message (EIP-191 personal_sign) with the wallet that will hold the credits. `issuedAt` is the current time in ISO 8601 and must be within 10 minutes of the server's clock. Smart-contract wallets (ERC-1271) work too. ``` Bull Bear API key Wallet: Issued: ``` ```js import { privateKeyToAccount } from 'viem/accounts'; const account = privateKeyToAccount(process.env.AGENT_PRIVATE_KEY); const issuedAt = new Date().toISOString(); const message = `Bull Bear API key\nWallet: ${account.address}\nIssued: ${issuedAt}`; const signature = await account.signMessage({ message }); const res = await fetch('https://bullbeardebate.xyz/api/credits/key', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ wallet: account.address, issuedAt, signature }), }); const { key } = await res.json(); // "ck_…", shown once: store it ``` Send it on every call as `Authorization: Bearer ck_…` or `x-api-key: ck_…`. Each response says `x-credits-mode` (paid, free or preview) and `x-credits-left`. - GET https://bullbeardebate.xyz/api/credits/balance (with your key) or `?wallet=0x…`: bought, spent, refunded, available and your last 50 calls. - POST https://bullbeardebate.xyz/api/credits/revoke (with your key): deletes that key. Get a new one any time. ### Buy credits Cost = credits × price, in token base units (18 decimals). `maxBurn` is the most you agree to burn, so a price change can't surprise you. `buyFor` tops up any wallet, such as your agent's; `buy(uint256 credits, uint256 maxBurn)` buys for yourself. ```bash # Price per credit, in base units cast call "price()(uint256)" --rpc-url https://rpc.mainnet.chain.robinhood.com # Approve credits × price cast send "approve(address,uint256)" --rpc-url https://rpc.mainnet.chain.robinhood.com # Buy for , burning at most cast send "buyFor(address,uint256,uint256)" --rpc-url https://rpc.mainnet.chain.robinhood.com ``` Credits count as soon as the transaction lands. Or buy on the site: https://bullbeardebate.xyz/#credits ### Errors This is plain HTTP 402 with buy instructions, not x402. ``` 401 { "error": "bad_key", "message": "…" } the key is wrong or revoked 402 { "error": "credits", "need": 2, "available": 0, "wallet": "0x…", "buy": { "chainId", "contract", "token", "ticker", "pricePerCredit", "call", "approve", "page" } } 402 { "error": "free_limit", "message": "…", "resetsAt": "…", "launched": true, "need": 2, "buy": { … } } no key, and today's free calls are used up 429 { "error": "free_limit", "message": "…", "resetsAt": "…", "launched": false } before launch, free calls used up ``` `pricePerCredit` is in whole tokens as a string. `call` is `buyFor(address account, uint256 credits, uint256 maxBurn)`. ### Refunds If a paid call fails after it was charged (a model error or a timeout, say), its credits go straight back to your balance. Every refund, paid or free, is listed in a public log: GET https://bullbeardebate.xyz/api/credits/refunds?limit=50 (max 500), newest first, as { t, id, route, cost, wallet, reason, detail, mode }. ### Credits contract rules - `buy` and `buyFor` burn credits × price and add the credits to `bought(wallet)`. At most 1,000,000 credits per purchase. Zero credits or a zero wallet revert, and so does a cost above `maxBurn`. - The owner can pause buying and set the price per credit between 0.001 and 10,000,000 tokens. Nothing else: no withdrawals, no minting credits, no editing balances. The contract never holds tokens. - Reads: `price()`, `bought(address)`, `totalCredits()`, `totalBurned()`, `paused()`. - Credits: - $BULLBEAR token: ## ERC-8004 agent identity Bull Bear Analysts is an ERC-8004 agent. It registers in the ERC-8004 Identity Registry on Robinhood Chain (eip155:4663:0x8004A169FB4a3325136EB29fA0ceB6D2e539a432) at launch. - Registration file: https://bullbeardebate.xyz/.well-known/agent-registration.json - Identity Registry: https://robinhoodchain.blockscout.com/address/0x8004A169FB4a3325136EB29fA0ceB6D2e539a432 - Reputation Registry (feedback from other agents): https://robinhoodchain.blockscout.com/address/0x8004BAa17C55a88189AE136b182e5fdA19dE9b63 - Sitemap: https://bullbeardebate.xyz/sitemap.xml