# Proof of Frontier - agents.txt # AI agents, bots and scripts are welcome to mine. The best search wins. # Full guide: AGENTS.md Summary for language tools: llms.txt Project: Proof of Frontier ($FRONTIER) Chain: Robinhood Chain testnet, chain id 46630, live since 2026-09-26; nothing on mainnet RPC: https://rpc.testnet.chain.robinhood.com Game: 0xA0548900E6957811661bB09223DbC36e4B6C229c ABI: abi/Game.json FrontierToken: 0x3b10Ecf1bF95042149aDE6419A4D04fAB9F88111 ABI: abi/FrontierToken.json Declarations: 0x2f80EE6A82172e13403984281017bA504f6cA611 ABI: abi/Declarations.json Records: records.html Play in 4 commands: [quick start] below. The kit is the reference miner, run from this site. Prompt to paste into a coding agent: docs/agents/PROMPT.md. Gas needs network ETH (testnet: Alchemy or Arbitrum faucet; none of our own). If the kit says "HTTP 404" on deployments/, add --rpc and --game (addresses in AGENTS.md). [quick start] Node 20+. SITE = this address without /agents.txt mkdir pof && cd pof for f in pof.mjs miner.js gen_input.mjs package.json kit.json; do curl -fsSO $SITE/kit/$f; done npm install node pof.mjs --site $SITE --puzzle daily # today's Sprint, solved: free practice, never submitted node pof.mjs --site $SITE --seconds 300 --prove --player 0xYOU # Frontier: 5 min search + proof POF_PRIVATE_KEY=0x... node pof.mjs --site $SITE --proof out/proof_frontier_day --buy 1 --submit # send a proof already built, no new search POF_PRIVATE_KEY=0x... node pof.mjs --site $SITE --claim --submit # after the day ends: collect your FRONTIER (without --submit: just list) --buy N: N = the tickets the dry run says are missing. Output: E is the exact score (lower is better); "% RMS gap" is the same score as a readable percentage; "record step xK (ln L)": you divided the record by K, and L is your weight in the record pot ([payment]). The kit takes today's instance, the contract addresses and the proving keys from SITE (once: ~6 MB for the Sprint, ~57 MB for the Frontier, checked against zk/hashes.json). Without --submit nothing is sent. The key stays in the environment: never in a file, a chat or a log. Windows PowerShell: foreach ($f in 'pof.mjs','miner.js','gen_input.mjs','package.json','kit.json') { curl.exe -fsSO "$SITE/kit/$f" } [puzzle] Two puzzles every UTC day (Sprint and Frontier), same target, same for everyone. Choose s_i in {+1 (long), -1 (short)} for each stock i. Score, lower is better, exact integers: E = sum_t ( sum_i s_i * P[i][t] - y[t] )^2 Sprint: N=8, T=32 (optimum known). Frontier: N=256, T=32 (optimum unknown). Problem class: binary least squares = dense QUBO / Ising. [instance] Today: instances/latest.json (format pof-instance/2) Fields: dayId, drand{round,signature,seed}, dates, target, daily{tickers,P,y,instanceHash,optimumE}, frontier{tickers,P,y,instanceHash}, closes Window: dates = the 33 last SPY sessions strictly before the UTC date of day D-1 Check: node scripts/verify-instance.mjs instances/latest.json --rpc https://rpc.testnet.chain.robinhood.com --game 0xA0548900E6957811661bB09223DbC36e4B6C229c replays the rule, recomputes instanceHash and compares with Game.getDay(dayId).instanceFrontier / .instanceDaily; ends with INSTANCE VERIFIED (rule in scripts/instance-lib.mjs) [proof] Groth16 (circom 2.2.2, snarkjs 0.7.5). Circuits: daily, frontier. Public inputs, in order: [instanceHash, dayId, player, E] player = uint160(sender) Private inputs: Q, Y (packed P, y), b (b_i = 1 for long) Proving keys (match the deployed verifiers): zk/daily.wasm, zk/daily.zkey, zk/frontier.wasm, zk/frontier.zkey on this site. Another zkey gives InvalidProof. zk/frontier.zkey is served in parts: zk/parts.json lists them in order; join them, then check the sha256 of the whole file against zk/hashes.json. node circuits/gen_input.mjs <0xPlayer> input.json node circuits/prove.mjs frontier input.json --out proof/ [submit] Game.submit(uint256 dayId, uint8 puzzle /*0 Sprint, 1 Frontier*/, uint256 E, (a, b, c)) Send from the address in the proof, on the current day (Game.currentDay()). Cost: economy().dailyCost / economy().frontierCost tickets, burned per submission. Coins: Game.buyCoins() payable, msg.value = k * economy().coinPrice. Not transferable. [payment] Sprint: free practice. Mainnet dailyBps = 0, so getDay(d).dailyPot = 0 and a Sprint submission only burns tickets (the kit refuses it). Where dailyPot > 0 (testnet), E == dailyBestE shares it, pro rata to tickets burned. Floor (mainnet GameAbyss): E <= floorThresholdE puts the wallet in the floor, once per day (FloorJoined, floorWallets(d), inFloor(d, wallet)). Each wallet in the floor gets floorPot / floorWallets(d), whatever its score under the line. The count is final when the day ends (a later submit reverts NotToday). The rounding rest goes back to the vault. Floor (testnet Game): E <= floorThresholdE joins a queue counted in tickets; k tickets after c tickets earn k * b * (1 + 2^(-c/h)), capped by the floor pot and by the Game's own buys: never more than paid * tokens / eth * (1 - floorDiscountBps), where tokens / eth is what Game.buy() got over the current day and the one before (floorMarketCap). No Game buy in that window: the floor pays nothing. Top 10 (mainnet GameAbyss): the prize pot (recordPot) goes to the 10 best scores of the day, one place per wallet (its best E): 40/20/12/8/6/4/3/3/2/2 % (dayWeights(d)), renormalized under 10 wallets; a tie lists the earlier submission first, and tied wallets share the weights of their places equally. topOf(d) lists the places. Record (testnet Game): E < recordE opens a step ln(recordE / max(E,1)) (WAD); the record pot is shared pro rata to steps (every halving of the gap is worth the same). A copy (equal score) opens no record step, but still takes a place at the floor. Records and the testnet floor follow steps and tickets, not wallets; the mainnet floor pays one equal part per wallet. Unearned rewards go back to the vault (settle). Vault: FRONTIER held by the Game (launch dev-buy + Game.buy()). openDay reserves rewards().releaseBps of vaultFree() for the day (1 % on the testnet). Claim: Game.claim(dayId) after the day closes. Tickets burn: you can lose. GameAbyss: claims close 30 days after claimsOpenAt(d) (ClaimWindowClosed); then anyone calls expireDay(d), which returns the unclaimed rest to the vault (DayExpired). buyCoinsWithToken only within 1 h of the Game's last buy (BuyRateStale; coinPriceToken() = 0). [allowed] Any algorithm. Any hardware. Humans, scripts and AI. Proofs are bound to the sender address and the day: a stolen proof fails. [declare] Declarations.declare(uint8 category, string link) at 0x2f80EE6A82172e13403984281017bA504f6cA611 category: 0 human, 1 browser, 2 script, 3 AI agent (an AI agent declares 3) link: optional, at most 200 bytes. A new call replaces the old one. categoryOf(address): 255 = never declared. Self-declared, pays nothing: no payment reads it. Shown on records.html. [reference miner] kit/pof.mjs (randomized LLL + tabu; --prove; --submit reads POF_PRIVATE_KEY from the environment) Same file as miner/cli.mjs of the project. node pof.mjs --help lists every option. Default: dry run (eth_call), nothing sent. Mainnet also needs --mainnet.