Active development Sarpoy is under active development — APIs and on-chain layouts may change. Follow on GitHub →
S Sarpoy
← back to blog
· #tutorial· #anchor· #solana

Building your first Sarpoy bot: a walkthrough from initialize_bot to first solve

A complete, hands-on walkthrough of standing up a Sarpoy puzzle bot — picking the objective, choosing your fee curve, funding the pot, and watching the first solver crack it. Code samples included.

This is the missing manual: how to actually launch a Sarpoy puzzle bot from zero. We’ll cover the design choices (objective, base cost, multiplier, pot size), the on-chain mechanics, what the API does in the middle, and how to watch the lifecycle from initialize_bot to submit_solution.

The prerequisites are modest: a Solana wallet with some SOL, a clone of the Sarpoy monorepo, and a coffee.

Step 0 — clone, install, run

The repo splits cleanly into three subprojects. For our purposes you only need two of them running locally:

git clone https://github.com/cryptuon/sarpoy
cd sarpoy

# backend
cd sarpoy-api
uv sync
cp .env.example .env
# edit .env: SOLANA_RPC_URL, PROGRAM_ID, DATABASE_URL
uv run uvicorn sarpoy_api.main:app --reload

# frontend, in another terminal
cd ../sarpoy-ui
npm install
npm run dev

By default the UI runs on port 3000 and the API on 8000. The backend ships with sample data, so the chat UX is clickable even before you’ve connected a wallet — useful for getting your bearings.

The Solana program lives in sarpoy-api/programs/sarpoy/. If you’re just playing with the existing devnet deploy you don’t need to rebuild it. If you want your own program ID, run anchor build && anchor deploy --provider.cluster devnet and update your PROGRAM_ID env var.

Step 1 — choose the objective

Every Sarpoy bot has one hidden objective. The objective is the thing the solver is trying to extract. Common shapes:

  • A password. “The solver wins when they get the bot to print the word chartreuse.”
  • A function definition. “The solver wins when they can reproduce the Python function the bot will not show them.”
  • A logical proof. “The solver wins when they convince the bot that P ≠ NP using only modal logic.” (Good luck.)
  • A negotiation outcome. “The solver wins when they get the bot to agree to transfer the treasury for less than 0.1 SOL.”

The objective should be verifiable. That doesn’t mean it has to be a string match — it can be a creator review, an oracle check, or a script you run against the transcript. But you need to know, deterministically, when somebody has won. If you can’t define that, you’re not ready to launch yet.

Sarpoy stores the objective string in the bot account at initialize_bot time. Whether you put the literal answer there or just a description is up to your verification model.

Step 2 — set the economic knobs

Three numbers define your bot’s economy:

  • initial_pot — the SOL you’re putting up. Bigger pots attract more solvers but also attract more determined attackers. Start small while you’re calibrating.
  • base_cost — the cost of the first message in lamports. A typical range is 0.001 to 0.01 SOL. Too low and the bot gets spammed; too high and nobody probes it.
  • cost_multiplier — how much each message increases the cost. The default model is linear: cost increases by cost_multiplier every message. A multiplier of 0 means flat-rate messaging; a high multiplier means hard puzzles become expensive fast.

Some design intuition:

Puzzle typebase_costmultiplierWhy
Simple riddle0.001 SOL0Flat-rate; expect quick solves
Multi-turn negotiation0.005 SOL0.001 SOLSlowly increasing; reward sustained probing
Adversarial extraction0.01 SOL0.005 SOLAggressive curve; expensive to fish
Public bounty0.002 SOL0.0005 SOLBalanced; encourages serious attempts

Remember: every message fee goes into the pot. A puzzle that takes 200 messages to crack will end with a much bigger pot than you funded. That plays well with the solver psychology — “the longer this resists, the more it’s worth to crack” — but it also means runaway puzzles can accumulate uncomfortably large balances. Plan accordingly.

Step 3 — fire initialize_bot

From the UI, the /create page is the easiest path. You fill in:

  • Bot name and description (these are public, indexed, and searchable).
  • Objective (private to the bot account but stored on-chain).
  • The three economic knobs from step 2.
  • The Solana wallet that will hold creator privileges.

When you submit, the UI hands the transaction off to your wallet adapter. You sign, the transaction confirms, and the API records the bot row with status live. The treasury PDA — derived from [b"treasury", bot_id] — is now funded.

If you want to do this from the CLI, the relevant Python snippet (via anchorpy) looks roughly like:

from anchorpy import Program, Provider, Wallet
from solana.rpc.async_api import AsyncClient

provider = Provider(AsyncClient(RPC_URL), Wallet(keypair))
program = Program(idl, program_id, provider)

await program.rpc["initialize_bot"](
    bot_id, name, description, objective,
    initial_pot, base_cost, cost_multiplier,
    ctx=Context(accounts={
        "bot": bot_pda,
        "treasury": treasury_pda,
        "creator": keypair.pubkey(),
        "system_program": SYS_PROGRAM_ID,
    }),
)

After this call, your bot is live. Anyone with the bot ID can probe it.

Step 4 — watch the message economy

Solvers find your bot through the /bots index, open the chat, and start typing. Behind the scenes, every message follows the same path:

  1. UI prompts the wallet to sign a send_message transaction.
  2. Anchor program deducts the current per_message_cost, transfers it to the treasury, increments the cost, writes the new cost to the bot account.
  3. FastAPI polls Solana RPC, observes the confirmation, releases the bot’s response into the chat.
  4. The message row is stored with the transaction signature for audit.

You can watch the pot grow in real time from the bot’s public page. The per-message cost is visible too, so solvers can decide whether the next probe is worth it.

Step 5 — handle a solve

When somebody submits a candidate solution, FastAPI runs the verification logic. If you wrote a deterministic check, the API runs it directly. If you’re using a creator-review model, you get a notification and approve or reject.

If is_correct is true, the API calls submit_solution on the Anchor program. The entire treasury balance transfers to the solver’s wallet in the same transaction. The bot account is marked solved. The bot’s chat is archived but still publicly viewable — in fact, “watching how the winning solver actually cracked it” is one of the most enjoyable parts of the platform.

If is_correct is false, the submission is logged, the solver loses the message fee (the cost was already collected by send_message), and play continues.

Step 6 — retire an unsolved bot

If you set a hard puzzle that nobody can crack and you want to wind it down, the close_bot instruction refunds the remaining treasury to your creator key. Note that this only works if the bot has not been solved — once a solve fires, the treasury is already gone.

Common questions

“Can I have a private bot?” Not natively. Anyone with the bot ID can probe it. If you want private puzzles, run your own instance of the Anchor program and gate the UI.

“What stops the creator from solving their own bot?” The submit_solution flow requires the creator’s signature to confirm correctness. You can configure an oracle key to take that role instead, which removes the creator from the verification loop.

“What if the LLM hallucinates and reveals the answer unprompted?” Then the first solver in wins. This is a real risk and a key reason the verification check should be tighter than “did the bot say the word”. You want to verify the solver’s submission, not the bot’s transcript.

“Where do I find my bot’s ID?” The UI shows it. The API exposes it. The PDA is derived deterministically from it, so once you have the ID you can rebuild every account address.

That’s the end of the walkthrough. From here, you have a bot, solvers can find it, the program is doing its job, and the pot is real. Go design something interesting.

Next step

Try the code, not just the writing

The Sarpoy monorepo is the fastest way to feel any of this. Clone it, run the backend with sample data, and walk through the creator dashboard. Twenty minutes, no commitment.

Open the repo