Active development Sarpoy is under active development — APIs and on-chain layouts may change. Follow on GitHub →
S Sarpoy
quickstart

From clone to first solve

Sarpoy is open source and runs locally. Clone the monorepo, bring up the backend and UI, connect a wallet, and either escrow a puzzle pot as a creator or chase one as a solver.

  1. 1

    Clone the monorepo

    git clone https://github.com/cryptuon/sarpoy — the FastAPI backend, Nuxt UI, and Anchor program all live in one tree.

  2. 2

    Run the backend and UI

    Start the FastAPI service (SQLite + Aerich migrations in dev) and the Nuxt 3 SPA. Sample bot data ships with the backend, so the UI is clickable without a live cluster.

  3. 3

    Connect a Solana wallet

    Open the UI and connect any standard wallet adapter — Phantom, Solflare, or Backpack. Auth is a nonce you sign; no passwords or signups.

  4. 4

    Create a bot or solve one

    As a creator, escrow a pot with initialize_bot and set the fee curve. As a solver, buy clues with send_message and win the pot with submit_solution.

as a creator

Escrow a pot and open the arena

One instruction locks the pot in the treasury PDA and sets the rising fee curve.

create_bot.py
from sarpoy_client import SarpoyClient, LAMPORTS_PER_SOL

client = SarpoyClient(rpc_url, creator_keypair)

# 1. Escrow a 5 SOL pot and set the rising fee curve
bot = await client.initialize_bot(
    bot_id="riddle-of-the-treasury",
    objective_hash=hash_secret("the seed is 'treasury'"),
    initial_pot=5 * LAMPORTS_PER_SOL,
    base_cost=int(0.01 * LAMPORTS_PER_SOL),   # first message
    cost_multiplier=int(0.002 * LAMPORTS_PER_SOL),  # +per message
)
print("Treasury PDA:", bot.treasury_pda)
as a solver

Buy clues and claim the pot

Authenticate with a signed nonce, probe the bot at the current fee, then submit your answer.

solve_bot.py
from sarpoy_client import SarpoyClient

client = SarpoyClient(rpc_url, solver_keypair)

# 1. Authenticate: sign the server nonce -> short-lived JWT
nonce = await client.request_nonce()
await client.login(sign(nonce, solver_keypair))

# 2. Buy clues — each call costs more than the last
reply = await client.send_message("riddle-of-the-treasury",
                                   "Is the seed a keyword?")

# 3. Submit the answer — settles the pot on success
sig = await client.submit_solution("riddle-of-the-treasury",
                                    "treasury")
print("Payout tx:", sig)

Keep going

Understand the lifecycle in depth, or read the source.