Koine docs

Receipts

Receipts are Solana memo transactions that record a wallet's knot changes and trades, so anyone can check from chain alone that the knots held. They cover only wallets an operator flags, and none are flagged by default.

What a receipt is #

A receipt is one SPL Memo transaction, signed and paid for by the receipts key (TRADE_RECEIPTS_KEYPAIR), which signs nothing else. Receipts don't move money or stop anything. If anchoring fails, the trade still goes ahead and the problem is logged; for a swap, a withdrawal or a presale action, the checker then reports the missing receipt.

Source: trade/server/receipts.ts

Which wallets are covered #

Only flagged wallets. The receipts column on the wallets table defaults to 0. There's no trader command for this. An operator runs, in trade/:

npm run receipts -- on <wallet address>
npm run receipts -- off <wallet address>
npm run receipts -- status

Turning a wallet on first writes what's already in force (tied knots, pending loosenings, the saved address), then every event from then on. Those first memos predate receipts, so they're Koine's word.

A flagged wallet's rules and trades become public. Each memo carries the wallet address, and some carry the saved withdrawal address. Memos carry no Telegram id.

Source: trade/scripts/receipts.ts, trade/server/db.ts, trade/package.json

Which events are written #

e When Fields
change A knot or a position's exit is tied, tightened, loosened or untied id, k, from, to, dir (tighten or loosen), eff (when it takes effect)
cancel A pending change won't land id
land A pending change took effect id
limit A limit buy was set id, mint, amt (lamports), price (USD), exp
block A knot refused a buy or presale deposit k, mint, amt (lamports), msg, and presale: true for a deposit
exit An exit is about to sell, once per try k, mint, qty, price and peak (lamports per whole token), try
fill A swap landed side, order, sig, mint, amt, fee, sol, tok, kind (manual, the exit that sold it, limit:<id> for a limit buy or open:<id> for a buy at open), scan on a buy
openbuy A buy at open was set id, mint, amt, at
presale A presale action landed kind, mint, sig, sol, tok
addr A saved address was set or changed to, eff
addr_cancel A pending address change won't land none
wd A withdrawal went out id, to, sol, sig
state When a wallet is flagged: a knot already tied k, to

On one position, k is rung:<id>, stop:<id> or own:<mint>:<opened_at>.

Source: trade/server/receipts.ts, trade/server/knots.ts, trade/server/trading.ts, trade/server/orders.ts, trade/server/exits.ts, trade/server/limits.ts, trade/server/presale.ts, trade/server/withdraw.ts

The memo format #

A memo is JSON:

Field Meaning
v Format version, 1
n Its place in the chain, starting at 1
p The first 32 hex characters of the SHA-256 of the previous memo's exact text; null for the first
w The wallet
e The event, with its fields
at The server's clock, in unix milliseconds. Koine's word; the checker uses block time

A msg is cut to 200 characters. A memo still over 700 bytes loses msg and scan and gains cut: 1.

Source: trade/server/receipts.ts (memoFor, MAX_MEMO)

The chain #

There's one chain for the trading service, across every flagged wallet, so one wallet's n values have gaps. A memo is recorded in the same database transaction as most events it describes and sent within seconds; memos go out side by side, so n and p order them, not their slots. An expired send is re-signed with a fresh blockhash and the same text.

The chain shows a dropped, repeated or reordered memo, except a drop of the newest ones. It can't show an event Koine never wrote.

Source: trade/server/receipts.ts (anchor, sendReceipts)

The checker #

trade/server/verify.ts checks a wallet from chain alone, without Koine's database. Run it in trade/:

npm run receipts:check -- <wallet> [--key <receipts key address>] [--out receipts.json]

It needs an RPC (TRADE_RPC_URL, or a Helius key) and exits 1 if any check fails. Each check is trustless (it follows from memos and the wallet's transactions) or attested (it rests on something only Koine saw).

Check Strength What fails it
chain trustless A memo missing or repeated, or p not matching the memo before
unreadable trustless The RPC couldn't return a transaction
swap trustless A Jupiter transaction since the first memo that changed one of the wallet's token balances and has no fill
amount trustless A buy's amt is more than the SOL that left the wallet
buy trustless The buy breaks a knot in force at its block, rerun with the same knot code
untie_delay trustless A land memo's time is before its request's block plus the delay, less 60 seconds of grace
presale trustless An Alpha Vault transaction (presale deposit, withdrawal, claim or refund) the wallet paid for, since the first memo, with no presale memo naming it, or a deposit whose sol is more than the SOL and wrapped SOL that left the wallet
withdrawal trustless SOL left in a transaction the wallet paid for that isn't a Jupiter swap or a presale action, with no wd, or went anywhere but the saved address in force
block attested A refusal names a knot Koine didn't say was tied
exit attested An automatic sale has no matching exit, a different quantity, or a knot that wasn't tied

Source: trade/server/verify.ts, trade/server/verify.test.ts, trade/scripts/receipts-check.ts

What the checker doesn't prove #

  • Memos are Koine's account. The trader signs nothing. Compare the memos with your own Knots screen.
  • The receipts key sits on the trading server. Whoever controls that server can write memos.
  • Refusals and exit prices are Koine's word.
  • Swaps with no token change are skipped, and failed transactions are skipped. A plain token transfer out is caught only if it also moves SOL out of the wallet.
  • On a sell, sol, tok and fee aren't checked against the chain. On a buy, only amt is.
  • Nothing before the first memo is checked.
  • Presale actions are checked for a receipt, not traced. The checker doesn't rerun knots on a deposit, and doesn't check where a presale transaction's SOL went beyond the deposit amount. A claim adds its tokens to the rebuilt position at the sol in its memo, as the ledger does.

A swap that landed seconds ago can fail as "no receipt" until its memo lands. Run the checker again before reading a recent failure as real.

Source: trade/server/verify.ts