Verified Calls docs
v1.0.0
Live demo Get help
● Ship · Seal, anchor, verify

How the proof works
seal, anchor, verify.

How a call becomes part of a sealed chain, how you anchor seals on Solana from your own wallet, how anyone checks a call, and how a follower's result is measured.

01Sealing

Every SEAL_INTERVAL_MIN minutes (default 10, console → Proof), if there are new calls or coverage events, the server builds a Merkle tree:

  • Each leaf is one call: its id, chat, caller, token, message id, time and the SHA-256 of its original text.
  • Coverage events (the bot joined or left a chat, a chat was approved, paused or resumed) are sealed too, so gaps in coverage are visible.
  • The seal header joins the root, the range of calls, the time and the previous seal's root. Its hash is the seal hash.
  • Because every seal includes the previous root, the seals form a chain: removing or changing an old call breaks every later seal.

The bot then posts Seal #12 · 40 calls · <seal hash> to your seal chats. Keep the interval short: until a call is sealed, the server owner could still drop it unseen.

02Anchoring on Solana (optional)

  1. Set your walletConsole → Proof → Operator wallet: your wallet's public address. It pays the memo's network fee.
  2. Open SealsConsole → Monitor → Seals shows seals awaiting a signature, oldest first.
  3. Sign with walletThe server builds an unsigned transaction with one memo, mvc1:<seal hash>, paid by your wallet. Your browser wallet asks you to approve, signs and sends it.
  4. Server checkThe server fetches the transaction and checks it succeeded, that your wallet paid for it and that the memo is exact. Only then does it store the signature. The call pages then link to it on Solscan.

The server never holds a key and never submits a transaction. Each memo costs a normal Solana network fee.

Monitor, Seals screen with seals awaiting signature
Monitor → Seals · synthetic data
Not tested on mainnet here

Building the memo and checking a signed transaction are covered by automated tests. No seal was anchored on mainnet while writing these docs.

03How anyone verifies

  • In the browser. Every call has /verify/<call id>: the leaf, the Merkle path, the seal header, the Telegram post and the Solana memo. A script recomputes leaf → root → seal hash with the browser's own crypto and shows a tick or a cross for each step. /verify also answers "is this call in the record?".
  • Offline, the whole record. Download /export/calls.jsonl and /export/seals.json, or let the script fetch them. It needs no dependencies and exits 0 only if everything matches.
  • API. /api/v1/calls/<id> returns the call, its outcomes and its proof as JSON. CORS is open.
node scripts/verify-export.mjs --site https://calls.example.com
# or with downloaded files
node scripts/verify-export.mjs calls.jsonl seals.json

Tested against a local copy with 160 synthetic calls and 136 seals: RESULT: OK — every leaf, root, seal hash, chain link and seal range matches.

It checks every leaf, root and seal hash, the chain of previous roots, that call and seal numbers have no gaps, and that each call's text matches its stored hash. Compare the seal hashes with the Telegram posts and Solana memos, which cannot be edited afterwards.

Verify page for one call with each proof step
/verify/<call id> · synthetic data

04How a follower's result is measured

  • Prices: 1-minute candles of the token's most liquid pool from GeckoTerminal (no key); DexScreener helps find the pool and cross-checks it. When the sources disagree too much, the call is unpriceable rather than guessed.
  • Entry: the first trade at least 30 seconds after the post. No trade within 5 minutes after that: "unpriceable", kept on the record, not counted in the median.
  • Exits: at 1 hour, 24 hours and 7 days, volume-weighted over the last minutes of the window. An open window shows the time left and is never guessed.
  • Costs: 1 % fee per side and slippage of at least 1.5 % per side, more for thin pools (liquidity-aware, using the liquidity recorded at the call).
  • Labels, display only: "low liquidity" and "called after a run-up".
  • Ranking: each caller's median result, pulled toward the group median until they have enough calls; re-shills and paid promotions are shown but not scored.

Exact formulas and every default: SPEC.md and your site's /methodology, which is generated from the settings in use.

05Exit liquidity

  1. A caller links a wallet/linkwallet in the bot opens a one-time page where they sign a message with that wallet.
  2. You add a data sourceYour Helius key (Integrations), or "Check exit liquidity with my own RPC" with your own RPC (Data sources).
  3. The checkEvery 5 minutes the server looks for a sale of the called token from that wallet within 60 minutes of the call and flags "exit liquidity".

Without a data source or a linked wallet the call shows "not checked", which means "we don't know", never "did not sell". A sale from a wallet that was never linked cannot be seen.

06What the proof cannot show

  • The seals prove the record was not changed after it was sealed. They cannot prove the bot saw every message in the first place, or anything in a chat that has not added it.
  • Nothing before the bot joined a chat is recorded.
  • GeckoTerminal serves roughly the last 180 days of minute candles and can be a few minutes late on new tokens. For a pump.fun token that migrated, the new pool may lack candles from the bonding-curve phase; such calls can be unpriceable.