Spout's 0% Loan Costs 15.6%

Spout’s 0% Loan Costs 15.6%

Spout Finance lets you borrow stablecoins against tokenized US equities at 0% interest. The 0% is real as a line item and misleading as an economic claim. The borrower doesn’t pay interest in stablecoins; the borrower pays in sold optionality on their own collateral, and by Spout’s own published numbers that cost is roughly 15.6% annualized — higher than the margin loan the product is positioned to replace.

Everything below comes from Spout’s public documentation and from a hands-on session on their devnet beta on 17 September 2026. No insider information, no assumptions the docs don’t support.


The arithmetic

Spout’s tranche walkthrough gives a complete worked example. Take it at face value:

Input (Spout’s own example) Value
Lending pool $10,000,000
Gross options premium collected $30,000 / week
Protocol fee 20% of gross
Lender share 80% of gross

The calls are written against borrower collateral. From the Option Assignment page: “Because locked collateral backs covered calls written by the protocol.” At the maximum 50% LTV, a $10m stablecoin pool that is fully drawn is backed by $20m of borrower equities.

So:

  • Annual gross premium = $30,000 × 52 = $1,560,000
  • That premium is extracted from $20m of borrower-owned shares → 7.8% of collateral notional per year
  • Borrowers received $10m of loans for it → effective cost of funds = 1,560,000 / 10,000,000 = 15.6% APR

Every dollar of that premium goes to lenders (80%) and the protocol (20%). The borrower receives none of it. The borrower’s compensation is the loan itself.

For comparison: Interactive Brokers margin runs about 5–6%. Aave USDC sits around 5–8%. A private-bank securities-backed line of credit is 6–8%. Spout’s borrower is paying roughly double the incumbent rate, and the pricing is invisible because it’s denominated in foregone upside rather than in dollars.

This is not a bug in the protocol. It’s a bug in how the protocol is described.

The 0.5% number measures something else

Spout’s FAQ states the covered-call cost to borrowers “historically averages around 0.5% annualized across the portfolio.”

That figure is the realized assignment loss — the ex-post cost of calls that finished in the money. It is not the cost of the position. When you sell an option you pay its full value at the moment of sale, whether or not it is later exercised. The ex-ante cost is the premium: 7.8% of notional.

Quoting realized assignment loss as “the cost” is the same error as quoting an insurer’s claims paid in a quiet year as the price of the policy.

The gap between 0.5% and 7.8% is precisely the variance risk premium Spout says it is harvesting. Spout is correct that the VRP exists. It is simply telling lenders that the VRP is their return while telling borrowers it costs them almost nothing. Both cannot be true. It is the same dollar.

Two documentation pages contradict each other

From Strike Selection Philosophy:

“If the underlying rallies modestly, the call expires worthless and the borrower keeps everything: the premium, the shares, and the full upside of the move.”

From Fees:

“Protocol fee: 20% of gross premium. Taken from each cycle’s options premium before distribution; lenders receive the other 80%.”

The borrower cannot keep the premium if the protocol takes 20% and lenders take 80%. One of these pages is wrong. Based on the tranche math, the settlement flow, and the loss waterfall — all of which route premium to lenders — the error is in Strike Selection.

It’s a one-line fix: “the call expires worthless and the borrower keeps the shares and the full upside of the move.” As written, it is the single most load-bearing sentence in the borrower-facing docs, and it is false.

Liquidation is offline about 80% of the time

From Oracles and Price Feeds:

“prices update in real time during US market hours and at reduced frequency during off-hours, since the underlying equities only trade during market sessions.”

US equity regular sessions run 09:30–16:00 ET, five days a week: 32.5 of 168 hours, or 19.3% of the week. The debt is a stablecoin obligation on Solana, live 24/7. The collateral has a continuously meaningful price less than a fifth of the time.

Overlay Spout’s own worked example. NVDA is given a liquidation line at 58.8% LTV — an 8.8 percentage-point buffer above the 50% entry. The example then walks NVDA down in two orderly weekly steps (−8%, then −7.6%) and partial liquidation catches it cleanly.

That is not how single-name equities move. NVDA’s post-earnings gaps have repeatedly exceeded 15% overnight, and gap moves are the dominant loss mode for concentrated single-name collateral. A 15% overnight gap takes a position from 50% LTV through the 58.8% line to roughly 69% before any price the protocol can act on exists.

So partial liquidation — the core borrower-protection mechanism — is structurally unable to engage on exactly the risk that matters most. The docs demonstrate it against a slow grind, which is the scenario it handles well and the scenario that rarely produces insolvency.

Things the docs should answer and don’t: what happens to a position already below the liquidation line when the market opens; whether there is a pre-market or post-market liquidation window and at what price source; whether earnings dates are handled specially.

Auto-Roll buys high after selling low

From Option Assignment, with Auto-Roll on by default:

“The shares are sold at the strike… the residual proceeds are automatically used to rebuy the same asset and re-enroll it in the next cycle.”

Assignment happens only when the stock closes above the strike. So the sequence is: sell at the strike, then immediately repurchase at the prevailing market price, which is by construction higher. The difference is a realized loss, and it recurs every time the position is assigned — that is, every time the borrower’s thesis on the stock is most right.

Add the 0.20% buy/sell fee on each leg and the default setting converts strong rallies into a mechanized sell-low/buy-high loop with a transaction cost attached.

This is the mechanic that most directly falsifies the homepage promise — “keep your upside and unlock liquidity without selling” — because the position is in fact sold, at a capped price, on a recurring schedule. Auto-Roll is a reasonable feature. Defaulting it on without surfacing the realized cost per roll is the problem.


The beta prints the wrong number on screen

All of the above is a reading of the documentation. I expected the app to be silent on the borrower’s real cost. It isn’t silent — it’s worse than silent.

The trade table carries a column labelled “Borrow Cost”, in percent per year:

NVDA BSOL SMCI GLD PFE AAPL GOOG IBIT MSTR XOM GS
0.00%/yr 0.00 0.00 0.06 0.07 0.37 0.54 0.55 0.58 0.80 0.86

The buy widget repeats it: “Est. borrower cost/yr — $0.00.”

These are realized-assignment-loss numbers, presented as the cost of borrowing. The column header has no tooltip. The widget’s “About borrower cost” control opens nothing, on click or on hover. There is no definition of the term anywhere in the interface.

A borrower reading “NVDA — borrow cost 0.00%/yr” is being quoted zero for something that, by the protocol’s own tranche example, costs about fifteen percent a year.

That’s the whole thesis of this post, rendered on screen. The gap between the engineering and the marketing isn’t an interpretation of the docs. It’s a column in a table.

Two bugs worth reporting

I couldn’t complete a transaction. Two separate defects stopped it, and both are cheap to fix.

The production CSP blocks the app’s own Solana RPC. No wallet or login needed to reproduce:

$ curl -sSI https://beta.spout.finance/buy | grep -i content-security-policy
... connect-src 'self' https://auth.privy.io https://*.rpc.privy.systems
    https://explorer-api.walletconnect.com wss://relay.walletconnect.com
    wss://relay.walletconnect.org wss://www.walletlink.org
    https://*.withpersona.com https://axartrdqynqtfclakxru.supabase.co
    wss://axartrdqynqtfclakxru.supabase.co ...

No Solana RPC host appears in connect-src. The client calls https://api.devnet.solana.com/ and the browser refuses it. The console repeats, roughly every 15 seconds:

Connecting to 'https://api.devnet.solana.com/' violates the following
Content Security Policy directive: "connect-src 'self' ..."

useAssetPrice(NVDA): Error: failed to get info about account
ERhwDiUmByKM3UrmVxibyjrr9MKzYHsKgjp3Bs8BjrwQ: TypeError: Failed to fetch

There is no fallback RPC — filtering the network log for any Solana endpoint returns zero requests. The calls never leave the browser. Prices still render only because they come from the app’s own origin, not from chain.

What this looks like in use: 20 test USDC sitting in the wallet, confirmed on chain, and the app showing $0 USDC, an “Insufficient Balance” modal, and a “Refresh Balance” button that cannot ever succeed. Stripping the CSP header in the browser and reloading makes $20 USDC appear immediately. One header line is the entire defect.

It also explains a bug other reviewers have already published — “sell flow reads zero balance on held assets” is exactly what a blocked getAccountInfo produces. That’s a symptom. This is the cause.

The deeper fix is to treat an RPC read failure as an error state. Right now a total loss of chain connectivity renders as a confident $0, “No Holdings”, “No Positions” — indistinguishable from a genuinely empty account. A user with real collateral would be told, without caveat, that they hold nothing.

Buying fails silently after you sign. With the CSP bypassed so the flow could proceed, a $10 NVDA market buy failed four times out of four, and the interface reported success every time:

  1. POST /api/orders/buy returns 200, with "preflight":{"blockers":[],"warnings":[]} and an unsigned transaction.
  2. The wallet modal says “Buy NVDA — Spend $10.00 on NVDA at the market price.” You sign.
  3. POST /api/orders/submit returns 400 Bad Request.
  4. Console: [order] transaction failed: Simulation failed. Message: Transaction simulation failed: Blockhash not found.
  5. The modal displays: “Order signed — Submitting it to Solana now — this can take a moment.” No error is ever shown.

Nothing lands. Across the session the wallet’s only on-chain transactions were two faucet transfers and one identity enrollment; the USDC balance never moved. The transaction’s validity window was 822 blocks, so expiry isn’t the explanation — “Blockhash not found” against a freshly-minted blockhash points at the submitting node not having seen the blockhash the backend issued.

A transaction that fails is a bug. A transaction that fails while the interface says it succeeded is a trust problem: the user walks away believing they own NVDA. On a devnet beta that costs nothing. The same code path on mainnet loses money and confidence at once.

One more thing worth flagging: the only application transaction that did land in the whole session decodes to CreateIdentityForUser + SetVerified — identity enrollment, no USDC transfer, no share mint. The dialog that produced it said “Buy NVDA — Spend $10.00.” Bundling a silent identity write behind a button labelled as a purchase deserves its own screen and its own consent, particularly for a product whose compliance story is a selling point.

What works

The onboarding is genuinely good, and it’s worth saying so plainly. Email and passcode, an access card, a Privy email OTP, and you’re in — no browser extension anywhere in the flow. The wallet modal is the best-executed screen in the product: address, copy button, and both faucets linked with one plain sentence each explaining what the token is for. “What orders are priced in.” “Pays the network fee on every trade.” That is better testnet onboarding than most protocols ship.

The navigation carries a live market clock — “Market: Open. Closes in 6:15:42” — which is the right instinct for a product whose collateral only trades 19.3% of the week.

And the app answers a question the documentation doesn’t. Under the buy widget: “Held 1:1 at Alpaca Securities · Reserves 100.2%.” The Oracles page refers to “the regulated broker” and never names it. The application names it in eight words. That belongs in the docs too.

Open questions

  • Under what exemption are spAssets offered, and which jurisdictions are excluded? A live question for any non-US participant, including me.
  • Is custody bankruptcy-remote from Spout, and what is the claim path for a token holder if the broker fails? Who performs the Proof of Reserve attestation? The attestor is unnamed in both the docs and the UI.
  • Proof of Reserve verifies supply, not encumbrance. If shares backing spAssets are simultaneously pledged against written calls, an attestation that the shares exist doesn’t establish that they are unencumbered. What fraction of reserve is committed to open options positions at attestation time?
  • The Junior tranche — 15% of the pool, ~32.8% APY, second-loss, with a 45-day withdrawal notice — is a short-volatility carry position sold as a yield product. A short-vol position whose holders cannot exit for 45 days during a volatility event is the exact configuration that turns a drawdown into a cascade. The backtest disclaimer (“a loss large enough to reach Senior has not occurred in any historical scenario we have tested”) is doing heavy lifting against a launch roster of 11 names weighted to large-cap tech, AI and crypto-linked ETFs. Publish the backtest window and the worst weekly drawdown observed, and run the scenario with correlation set to 1.0.

None of this is an argument that Spout is badly built. The risk architecture is documented more carefully than most of its peers, the tranching math is coherent, and the team publishes enough detail that an outsider can check their work — which is exactly how I was able to write this.

The problem is that the number the borrower is quoted and the number the borrower pays are not the same number, and the interface currently quotes the wrong one with more confidence than the documentation does.

Session run 17 September 2026 against beta.spout.finance on Solana devnet. All on-chain claims are verifiable against api.devnet.solana.com for wallet DwiBvACxP4JinBXpFju323j78cKe35U4W1ErWwrzSVh1.

Your AI Editor Doesn't Know What You're Doing

Your AI Editor Doesn’t Know What You’re Doing

Your editor knows your codebase. It doesn’t know what you’re doing.

This is a structural limitation of how AI tooling is currently built, and the distinction matters more than it seems. Knowing a codebase means having a model of state — the functions, the types, the call graph, what exists and how it connects. Knowing what a programmer is doing means having a model of process — the intent behind the current sequence of edits, the problem being solved, the direction of movement. State is a snapshot. Process is a vector.

Current tools are good at the snapshot. They index your codebase, retrieve relevant context, and generate suggestions calibrated to what’s there. The Cursor-style approach has gotten remarkably far on this. But it consistently fails at a specific class of problem: situations where the right suggestion depends not on what the code looks like right now, but on what the programmer has been trying to do for the last twenty minutes.

The obvious fix is to send edit history as context. If the model sees the last hundred edits, it can infer intent. The problem is cost. Edit history is verbose, the context window fills fast, and most of what’s in that history is noise — intermediate states, backtracking, exploratory deletions. Sending raw history to a remote model on every keystroke isn’t a product, it’s a billing catastrophe.


The architecture I want to propose inverts the problem.

Instead of sending more context up, you add a layer of compression locally. A small model runs inside the editor — lightweight enough to respond in real time, trained specifically to track edit sequences and produce structured summaries. Not natural language summaries. Embeddings. The local model watches every edit, maintains a running representation of programmer intent as a vector, and sends that vector upstream when the remote model needs it.

The remote model receives not a history of raw edits but a compressed semantic trace of what the programmer has been doing. It uses this to generate suggestions that are calibrated to process, not just state.

This is already an improvement. But the interesting part is what happens when you make the communication bidirectional.

The remote model doesn’t just receive from the local model — it can dispatch to it. Before generating a suggestion, the remote model can send an investigation instruction: check whether this function is called anywhere else, find all implementations of this interface, trace how this variable is threaded through the codebase. The local model executes the investigation against the indexed codebase and returns the result. The remote model incorporates this into the suggestion.

Two cases where this matters concretely. First: the remote model is about to suggest a refactoring. Before suggesting it, it dispatches — verify this change won’t break callers it can’t see from the current file. Second: it recognizes a pattern in the edit trace that commonly leads to a specific class of bug. It dispatches — check whether the conditions for that bug are present here. The suggestion, if it comes, arrives with actual grounding.

The criterion for dispatch isn’t general uncertainty — it’s consequential uncertainty. The remote model investigates when the suggestion has implications it can’t evaluate from the context it already has. This is partially mapped, not solved. There are more cases than the two above, and the boundary conditions between “investigate” and “suggest directly” aren’t fully defined. I’m naming this as open.


The embedding layer is also open, and in a deeper way.

The premise is that the local model compresses edit sequences into vectors that the remote model can interpret. But these are different models, potentially different architectures, certainly trained on different data. Their embedding spaces aren’t compatible by default. A vector that means “the programmer is decomposing a monolith” in the local model’s geometry doesn’t mean the same thing in the remote model’s geometry.

The solution is probably a trained alignment layer between the two spaces — essentially, fine-tuning both models jointly on the specific task of intent communication. This requires data that doesn’t quite exist yet: annotated edit sequences with ground-truth intent labels, enough of them to produce a local model worth running. This isn’t an insurmountable problem. It’s an expensive one.

What does exist is the architecture. The shape of what would need to be built. And the observation that the bottleneck in AI-assisted programming isn’t intelligence — the remote models are already remarkable — it’s the channel. What gets sent, when, how it’s compressed, who decides what to investigate. That’s the unsolved layer.


It’s worth being concrete about what this unlocks, because it’s a different category of tool than what exists today.

A model that tracks process — not just state — can tell you there’s a better way to solve the problem you’re currently solving. It can notice you’re heading toward a race condition before you’ve written the code that introduces it. It can recognize that a library exists which would eliminate the pattern you’ve been manually implementing for the last forty minutes. It can see that the abstraction you’re building in this file is going to collide with a decision made in another file three sessions ago.

None of this is next-line prediction. It’s next-hour guidance. The difference between a tool that completes your sentences and one that understands what you’re trying to say.

The editor sees your code. It should be able to see you working.

if you feel the world is collapsing, read this

I may not have a perfect solution, but is what I have. I’m trying. Do the same

We are amidst the collapse of our current civilization. Either if you went down of the rabbit role of researching and understanding the significance of current events and how they can end up collapsing the way our current society is structured (maybe even went as deep as watching professor Jiangs grim predictions of how it will all unfold) or maybe you’re just perceptive/sensitive and feel in your bones that we are close to the end; you understand that we are entering a very turbulent phase of human history.

This may make you feel somewhat desperate. Not full blown panic attack, but a certain deep anxiety that you can’t take too seriously because its just a hunch, but something you can’t fully ignore.

This creeping anxiety, specially if you delved into spiritual practices in the past to better understand the subjective experience, may make you go after spiritual answers. Simulation theory, the perspective that your consciousness creates and transforms reality, or to find someway to spiritually detach from this reality. But all of that feels like escaping instead of confronting, and the solutions marketed and deep ancient truths boil down to positive thinking.

But how to confront this when, thinking about it logically, there’s no real solution. Changing course would need everyone to start being conscious of where we are and how we got here and how to get out, and that would need them to come to therms with beliefs they have that aren’t enough anymore to explain reality.

Ignoring the fact that I don’t even know if most people have the cognitive capacity to understanding all this because cognitive abilities are not uniformly distributed within the human race; and assuming that everyone has the capacity, truth of the matter, most people didn’t have the privilege to have an upbringing that stimulates critical thinking nor the time to develop that critical thinking.

Being discouraged, desperate and thinking of giving up are not personal failures, are just very good options once you understand how we are utterly hopeless.

And before I event start talking what I think are the solutions, we do not even feel safe to create communities to talk about this because it all feels like personal responsibility. And that feeling comes from the system making us feel that every problem and suffering the system throws at us and makes us suffers is our responsibility for not dealing with it, even when the problem is completely unreasonable and not our faults, but all the same we feel guilt. So lets break from that.

If this resonates with you, join the telegram group I created: ending and no solution in mind (yet)

I am by no means saying that doing nothing is the correct solution, and if it sound like that it’s because I didn’t articulate my thoughts enough. That being said, truth is that something things can be extracted from spiritual knowledge that don’t boil down to have faith in the universe conspire in your favor: that you are not your mind, you are the system that observers you mind, not even that, you are the system that observes the observation and chooses when to engage with the observations of the mind, even deeper, you are the system that know that it is simply the universe looking at itself from a particular perspective. And that’s the only thing you can call yours and be proud of having built it: your understanding of yourself and reality. If you can do even more, you’ll help others improve theirs. Ideally we could construct communities where we, those that understand this, help each other in this journey.

But with all of that you realize: reality will change. Many things may worsen. Some will die or suffer. Maybe you. Maybe someone you love. Suffering may increase. But your goal has not changed: survive for as long as you can, absorb and process information as much as you can and grow. Write about it. Eventually we will build technologies where we can find the writings of others doing the same thing.

None of this resolves the issue. But makes us able to still sane while dealing with it.

The closes thing a modern thinker can have to a philosophical system is a method of not going insane while absorbing information - me, now

Keep going, do what you think is wright, there’s no right or wrong solutions. Do that for as long as you can. And when you can’t anymore, I won’t judge your for anything you choose to do. That’s the best love and compassion I can give to you. Only you know what is too much for you

the favelization of the human mind

without proper planning or resources, we house more ideas each day

In previous generations, thinkers, those who reflect about the nature of reality and themselves, could develop their way of thinking into a coherent and structured system. Their worlds were static enough that they had the time to build an effective lens to understand it as it was. With the increasing rate of change of modern society, this has become not only increasingly more difficult, but ineffective.

We, modern thinkers, come in contact very early in our lives with a plethora of ways and systems of thinking, be it by active study or by passive absorption of their “ghosts” in the collective consciousness. These ways of thinking come in various forms: some spiritual, some philosophical, some practical; each with their own set of beliefs, each with their own limitation.

The problem is that we are constantly bombarded with so much information that whenever we try to adopt a particular way of analyzing reality, it quickly shows itself to us more than the lens can observe.

All models are wrong, but some are useful. — George Box

Over time, we become so attuned to this process, that whenever we try to structure a solid way of understanding reality, before even beginning, we already found its limitations. This makes our minds become less of a temple or palace that you plan, build and maintain; and more of a favela: forced to keep growing and changing to house way more inhabitants than it ever should.

favelization mind map A place of constant change and creation. A place that each day has more inhabitants than the less. A violent place where inhabitants constantly die. A place where the lack of resources make many struggle. A place of adaptation.

This is our minds.

incomplete

as it will ever be

Moved by desire, I tried to save. But to do that, I’d need to know what I crave.

For it is not that of what is good that I am after. It’s knowing of suffering that I need to master.

I wish my pain was the only that I felt. I wish my grudges were the only I held.

Maybe surrender is the victory attainable. But for sure to keep fighting is not sustainable. At least not alone, but in my ranks there are none. But there’s so little I can do and so much to be done.

I wish to keep on fighting, but am without weapons to wield. But to attain them I would need to lose fights that I will not yield.