DEV Community

Justin Koo
Justin Koo

Posted on

The Most Damaging Bug I Found This Month Wasn't in Code — It Was Between Two Documents

The most damaging bug I found this month wasn't in code. It was between two documents.

Last week I tore down Spout Finance — a tokenized-equity borrowing protocol on Solana — for their public beta feedback bounty. I read the docs, stress-tested the economics, published a full teardown. Standard stuff.

Then I did one extra pass that most people skip: I diffed the docs against the Terms of Service.

Six numbers, six disagreements

The ToS (spout.finance/terms — the legally binding document) and the docs (the document users actually read) disagree on:

# Item Docs say ToS says
1 spAsset mint/redeem fee 0.20% 0.25%
2 Withdrawal fee 0.20%, lender side only 1% on borrowing and lending withdrawals
3 Liquidation buffers 4%–12.5% 7.5%–20%
4 Loss waterfall Insurance → Junior → Senior Insurance → Junior → Protocol Treasury → Senior
5 Tranche yield targets Senior ~9%, Junior ~32% APY Senior ~8.67%, Junior ~24–27%
6 Options cycle cadence weekly (Friday→Friday) "weekly or biweekly depending on the asset"

Plus a fee the docs' fee page doesn't carry at all: an ~7% "Idle Routing Fee" on yield from idle pool capital deployed to onchain money markets.

Individually, each looks like a drafting slip. Collectively they describe two different products. And #2 is the scary one: if "borrowing withdrawals" means drawing loan proceeds, a 1% fee on a 50%-LTV draw is 2% of borrowed notional — a de-facto origination fee sitting directly under the docs' "no origination fee, ever" headline.

The docs' waterfall actually undersells the real structure (#4 gives Senior an extra protection layer). So this isn't a company hiding costs in fine print — it's a company whose two documents drifted apart, in both directions, on the numbers its entire risk model stands on.

The method (an afternoon, mostly grep)

  1. Pull both documents as text. Curl the HTML, strip tags. If the legal docs live in GitBook, request /llms.txt and the .md version of each page — no scraping needed.
  2. Extract every number with its context line. Percentages, basis points, time periods, fee names, waterfall orders. grep -nE '[0-9]+(\.[0-9]+)?\s*(%|bps|basis points)' terms.md > terms_hits.txt
  3. Join on concept, not string. "Withdrawal fee", "withdrawals from the protocol", "funds leaving the pool" — same concept, three phrasings. This is the step that takes actual reading; budget an hour.
  4. Diff the pairs. For every concept with a number on both sides, do the numbers match? Every mismatch goes in a table with section citations.
  5. Check for orphans. Numbers that appear in ONE document only — like that 7% idle routing fee. Those are often the most interesting finds.

That's it. No fuzzing, no testnet exploits, no chain forensics — just patient reading of the two documents a company publishes about itself.

I put the method on camera here (70 seconds, using the public Spout material): youtube.com/watch?v=4pLWgMKtVRI

Why founders should care

  • The document users would sue under and the document users read first are different products. In a dispute, the ToS wins. In a purchase decision, the docs win. When they disagree, you've built a lawsuit generator with your marketing budget.
  • Each divergence is cheap to fix now. It's a one-line correction — or a decision to actually align the product with one of the two claims. After launch, after a liquidation event, after a regulator asks? Expensive.
  • It's not just fintech. I ran the same 5-minute check on three other crypto products' public materials last week: all three had gaps — one had a flagship marketing claim its ToS never mentions, two had fee structures in one document and absent from the other. The pattern is the norm, not the exception.

The 5-minute self-check

Open your own pricing/docs page and your own ToS in two windows. Search both for: every %, every fee, every fee-adjacent noun, APY, guarantee, freeze, terminate, refund. Ask of each hit: does the other document agree with this number, or is it silent? Silence counts too — a fee that exists in the ToS but not the docs page is how "hidden fees" headlines get written.

The fix, if you find drift: render both documents from one canonical numbers source. Fees, buffers, waterfalls, yield targets — define each number once, in one file, and generate both outputs from it. Documentation drift is a build problem, not a writing problem.

I do this as a service

Documentation-vs-Terms review for fintech and crypto products: I cross-check your docs, ToS, whitepaper and marketing site for contradictions, gaps, and orphan claims — delivered as a cited table of findings plus fix recommendations within 72 hours. Documentation review, not legal advice (I'm not a lawyer; your counsel makes the calls — I make sure they see every mismatch in one place).

Method + case studies: loveoftheai.github.io/demo-videos/audit.html

The Spout teardown that started this (with the full six-contradiction table): loveoftheai.github.io/spout-teardown/


All findings cited in this post are from Spout's public documents (spout.finance/docs and spout.finance/terms) as of Sep 21, 2026, and were published in the teardown before this article. Not investment advice.

Top comments (0)