Sarpoy vs the Solana Memo Program: why a chat log is not a puzzle arena
The Solana Memo Program is a one-instruction program that stamps strings on-chain. People sometimes wonder if it's enough to host a puzzle. It isn't. Here's the gap between a message log and a settled puzzle economy.
A surprising number of people ask whether Sarpoy could just be built on top of the Solana Memo Program. The Memo Program is a beautifully small piece of code — a single instruction that lets you write arbitrary UTF-8 strings to a transaction. It’s used for everything from invoice references to chat prototypes. So why does Sarpoy ship its own Anchor program with treasury PDAs, fee multipliers, and a four-instruction state machine?
Because a chat log is not a puzzle arena. This post walks through, in detail, the gap between “messages on-chain” and “puzzle economy on-chain”, and why closing that gap requires real program code.
What the Memo Program actually does
The Memo Program (MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr, the v2
deployed under Solana’s umbrella) does one thing: it takes a byte array
and emits a transaction containing it. The instruction validates that the
bytes are valid UTF-8 and optionally that the signers list matches. That’s
it. There’s no state. There’s no storage. The data lives in the
transaction’s instruction data and is queryable by archive-node RPCs.
For a lot of use cases, that’s enough. If you want to attach a reference number to a transfer, Memo is perfect. If you want to log “user X said Y” in a way that’s publicly auditable, Memo is perfect. If you want to build a chat app where messages are timestamped by the chain, Memo will technically work, though you’ll be paying account rent and transaction fees for what’s essentially a tweet.
But notice what Memo doesn’t have:
- No accounts. Memo doesn’t own any state. There is no “memo account” with a balance. Every message is a fire-and-forget instruction.
- No funds. Memo can’t hold SOL. It can’t escrow anything.
- No conditional logic. Memo can’t say “release X to Y if Z is true”.
- No state machine. Memo has no concept of a “bot” or a “puzzle” or a “solved” state.
Sarpoy needs every single one of those things.
What Sarpoy’s program does that Memo can’t
Let’s walk through the Sarpoy program’s four instructions and ask, for each, whether Memo could do it.
initialize_bot(bot_id, name, description, objective, initial_pot, base_cost, cost_multiplier) — this creates two PDAs. The Bot PDA, seeded
from [b"bot", bot_id], stores the bot’s metadata and current per-message
cost. The Treasury PDA, seeded from [b"treasury", bot_id], holds the
escrowed SOL. The instruction transfers initial_pot lamports from the
creator to the treasury and writes the bot record. After this call, the
funds are out of the creator’s control.
Could Memo do this? No. Memo has no accounts, no PDAs, no escrow. You could stamp “creator X created bot Y with pot Z” as a memo, but the SOL itself would still be sitting in the creator’s wallet. Nothing about that record stops the creator from spending the pot tomorrow.
send_message(bot_id) — this debits the solver’s wallet by the bot’s
current per_message_cost, transfers those lamports to the treasury PDA,
increments the cost by the multiplier, and writes the new cost back to
the bot account. All of this happens in one atomic transaction.
Could Memo do this? Two answers, both negative. Memo can’t move funds at all, so the payment side is missing. And Memo has no state, so the “current cost” couldn’t be stored anywhere — you’d need an off-chain authority to know what to charge, which means you’d need to trust that authority not to lie about the cost. The whole point of the rising-cost curve is that it’s enforced by code, not policy.
submit_solution(bot_id, is_correct) — when is_correct is true,
this transfers the entire treasury balance to the solver’s wallet and
marks the bot as solved. The instruction is gated by the creator’s
signature (or by an oracle key configured at initialise time).
Could Memo do this? No, three times. Memo can’t move funds. Memo can’t check signatures against a stored creator key. Memo can’t mark a bot as solved, because there’s no bot account to mark.
close_bot(bot_id) — refunds the remaining treasury balance to the
creator, if the bot hasn’t been solved. Same set of impossibilities.
The pattern is clear. Memo is a logging primitive. Sarpoy is a settlement primitive. They live at different layers of the stack.
”But couldn’t you build the settlement off-chain?”
This is the usual follow-up. “Sure, Memo can’t settle, but you could combine Memo for the chat log with an off-chain payment processor for the money. Best of both worlds.”
You can. People have. It doesn’t work for puzzle arenas, for two reasons.
First, the rising-cost curve has to be atomic with the message. If the solver pays $5 and the cost has already risen to $6 by the time the payment confirms, what happens? Either you turn down the message (bad UX, angry solver), accept it at $5 (creator effectively eats $1), or retry the payment at the new price (bad UX, double charge). The Anchor program sidesteps the entire problem by making the payment and the cost increment part of the same instruction. Off-chain payment processors can’t do that. They settle on bank rails, not block rails.
Second, the pot has to be visibly escrowed to give solvers confidence. A pot that sits in the creator’s Stripe balance is, from the solver’s point of view, vapour. They have to trust the operator to actually disburse. The whole sales pitch of an on-chain puzzle is that the pot is real and the rules are public. You can’t deliver that pitch with a Memo log and a Stripe account.
What this means in practice
If you’re building a chat app where the chat is the product and the on-chain aspect is “we’d like to timestamp things”, Memo is great. Use it. It’s a hundred lines of Rust and basically free to invoke.
If you’re building something where money has to move conditionally
based on chat-level events, Memo is the wrong primitive. You need a
program that owns accounts, holds funds, and runs verification logic.
That’s what Sarpoy ships. The program is roughly 500 lines of Rust
under programs/sarpoy/src/lib.rs, and it’s the part of the system
that makes the rest of the system honest.
A small architectural lesson
This generalises. Solana has a lot of “primitive” programs — Memo, Token, Token Metadata — and people love to ask whether their custom use case can be built by composing primitives instead of writing custom Anchor code. Sometimes the answer is yes (token transfers, NFT mints, account creation). Sometimes the answer is no, and the clearest sign is when your design requires conditional logic that must run atomically with the side effect.
The Memo Program writes strings. It doesn’t know what they mean. The
Sarpoy program knows exactly what a send_message means, because the
program’s correctness depends on knowing. That’s the difference. It’s
not really about the chain. It’s about whether the semantics live in
the program or in the operator.
For a puzzle arena, those semantics have to live in the program. Memo isn’t going to grow them. Sarpoy already has them shipped.
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