What Spout is
Spout Finance is a brokerage for tokenized US equities on Solana. You buy shares as tokens, lock them as collateral, and borrow stablecoins against them at 0% interest, up to 50% of their value.
The collateral does not sit idle. The protocol writes covered call options against it, and that premium pays the lending side and substitutes for the interest a traditional broker would charge. That is the product's central idea, and it is a good one.
The beta runs on devnet. Every balance, price and position in it is simulated, and Spout's own Testnet Terms say so plainly.
How I tested, and what I will and will not claim
I went through the access gate as an ordinary invited tester, funded a wallet from the two faucets the app links to, read the JavaScript the site serves to the browser, put the in-app assistant through a controlled prompt set, and tried to follow every claim in the documentation back to something I could check myself.
Claims about app behaviour have a screenshot, a console trace or a transaction signature. Claims about economics are arithmetic on Spout's published numbers with the inputs cited. Anything I could not test is an open question, not a defect. Where sources disagree I rank them: on-chain transaction, then captured UI state, then current documentation, then older documentation, then inference. Inference is labelled.
Several times during this test I formed an explanation and had to drop it when a control disproved it. Four of those are in Section 8, because the findings that survived are only worth something if you can see what happened to the ones that did not.
Findings are labelled at the top of each section:
-
Confirmed. Reproduced, with a screenshot, console trace or transaction signature, and steps anyone can follow.
-
Observed. Seen and recorded, but not reproduced enough times to rule out a one-off.
-
Open question. Something I could not resolve with the access I had. Not a defect.
Reading the client JavaScript Spout serves to every visitor is ordinary review work and I did it. I did not attempt authentication bypass, injection, endpoints the interface does not call, or manipulation of requests in flight. This is a product review, not a penetration test.
1. Four orders, no holdings
Confirmed. Reproduced across two days, both market states, and two assets.
To reproduce: fund a devnet wallet from the two faucets the app links to, complete KYC, submit a market buy of any size on any asset. Check Portfolio then Primary Metrics. The order appears under Open Orders with a blank Status and does not move.
This is the finding. Everything else is smaller.
Across two days and both closed and open market states, I submitted four buy orders through the beta. All four remained open, no corresponding spAsset holding appeared, and the one order I tried to cancel remained open after three cancel requests.
| Order | Placed | Market | Amount | Status at end of test |
|---|---|---|---|---|
| NVDA E5Fv...48Hq | 22 Sep, 21:18 | Closed | $5.00 | Open, status blank |
| NVDA 6hz2...Ma3n | 22 Sep, 21:44 | Closed | $8.00 | Open, status blank |
| GOOG AZUR...z5Tp | 22 Sep, 21:46 | Closed | $2.00 | Open, status blank |
| NVDA AWoV...wbJX | 23 Sep, in hours | Open | $5.00 | Open, status blank |
$20 committed. Portfolio Overview reads $0.00, My Holdings reads "No Holdings", Positions reads "No Positions", and my wallet contains no spAsset of any kind.
The first three were placed with the market shut, and each confirmation screen said "Market is closed. Your order fills at 9:30 AM ET, 23 Sep." That time came and went. More than an hour after the opening bell, all three were still sitting there with an empty Status column.
So I placed a fourth during regular trading hours. It behaved identically.
What I ruled out

A single unexplained failure is not worth much. Here is what I eliminated, and how.
Market-on-open queueing. Accepting an order outside session hours is normal brokerage behaviour, and Spout is held at Alpaca, which supports it. If the first three had been stuck purely because they were queued for the open, that would be uninteresting. The fourth order was submitted with the market open and produced the same result.
KYC. Two people in the community channel suggested transactions depend on identity verification. My Settings page reads KYC Status: Confirmed. Verified on-chain, identity account found.
Funding. USDC was debited for every order. Devnet SOL was present throughout and network fees were paid.
Authentication. I was logged in for all four. Every order was accepted and acknowledged.
The signing path. Every order action was signed and landed on-chain without a runtime failure.
What the chain does and does not establish

Each order action coincided with a successful 644-byte Solana devnet transaction, and two of those signatures were surfaced directly by Spout's own confirmation UI.
| Transaction | UTC | Status |
|---|---|---|
| 22 Sep 20:18:39 | Success | |
| 22 Sep 20:44:30 | Success | |
| 22 Sep 20:46:30 | Success | |
| 23 Sep 14:53:07 | Success |
Full account history on Solana Explorer, devnet
I want to be exact about what this proves, because it is less than it looks.
Spout's program is unpublished, so Explorer renders every instruction as "Unknown Program: Unknown Instruction" and I cannot decode them. A Success status establishes that the transaction executed without a Solana runtime failure. It does not establish that the instruction means "buy NVDA", and it does not establish that a brokerage fill should have occurred as part of it.
On-chain success is not brokerage execution. It is entirely possible that the buy transaction only creates an on-chain order record, and that execution is an asynchronous off-chain process that these transactions were never meant to complete.
If that is the case, it does not resolve the finding. It relocates it. The fourth order was submitted with the market open and produced no holding either.
No spAsset token balance appeared in the wallet's token accounts after any of the four orders. That independently corroborates the app's own "No Holdings" state without relying on the confirmation modal.
Cancelling did not work either

I placed the $2 GOOG order as a control, specifically to test the cancel path, and then tried to cancel it.
The app responded: "Cancel requested, the backend processes the refund."
The order stayed open, the status stayed blank, the balance did not return. I tried twice more over the following eighteen hours. Each attempt coincided with a successful devnet transaction of the same size class as the first:
| Transaction | UTC | Status |
|---|---|---|
| 22 Sep 20:49:37 | Success | |
| 23 Sep 07:52:10 | Success | |
| 23 Sep 15:32:37 | Success |
Same caveat applies. Spout did not surface these signatures, so I cannot independently decode them as cancellations. They are consistent with the interaction history.
During my test window, the order neither filled nor could I get the in-app cancel path to remove it.
To be precise about the money: the $2 is not lost and it is not unaccounted for. It is still represented as an open order. What I could not do is recover it. I found no working in-app path that unwound the pending order and restored the committed balance during the test window.
What I am not claiming
I do not know the cause. A broker integration that is not live on devnet, an Alpaca sandbox that acknowledges without executing, an asynchronous fill process that is not running, a stalled worker, or a settlement path deliberately left unimplemented in the beta are all consistent with what I can see from outside.
I am also not claiming funds were taken improperly. This is devnet, the tokens have no value, and Spout says so clearly.
What I am claiming is narrower. In my test, the beta's order path produced zero holdings across four accepted orders, including one submitted during regular market hours, and I could not cancel the one order I tried to withdraw.
And if execution genuinely is not implemented on devnet, then the issue becomes a different one rather than no issue at all: the beta presents a working buy and cancel workflow, commits funds, and displays purchase-success copy for a settlement path that is not active.
Why this took two days to notice
Because the product never surfaced an explicit failure state.
2. The confirmation screen describes a completed purchase

Confirmed. Reproduced on all four orders.
To reproduce: submit any buy order and read the modal that follows.
Here is what the app shows after a buy that never became a holding.
Your purchase has been confirmed
You own 0.02 NVDA
Bought at $228.85
Total paid $5.00
Market is closed. Your order fills at 9:30 AM ET, 23 Sep
"You own 0.02 NVDA" and "Bought at $228.85" describe a completed purchase even though the same modal says the order will fill later. "Total paid $5.00" may accurately describe the amount already committed, but in that position it reinforces the completed-purchase reading.
A brokerage confirming that an order has been received is normal. That is not what this says. "You own" and "Bought at" are settlement semantics, and the same modal acknowledges the opposite one line further down.
The open-market version removes even that qualifier. When I submitted the fourth order during trading hours, the modal read:
Your purchase has been confirmed
You own 0.02 NVDA
Bought at $225.91
Total paid $5.00
No banner, no fill-time note, nothing to qualify it.
The product already has the right words. The Activity tab labels the same order "Order received." That is exactly correct. It is not used on the screen where it matters.
Two things I found while checking that price
The quoted price changes while the market is shut. My first order quoted $228.85. My second, submitted 26 minutes later with the market still closed, quoted $228.29. So "Bought at" is not simply displaying a fixed prior close: the quoted value changed while the market was closed, and it is labelled with past-tense execution language.
Displayed estimated quantities round to two decimal places.
| Committed | Price shown | Implied shares | Displayed |
|---|---|---|---|
| $5.00 | $228.85 | 0.02185 | 0.02 |
| $8.00 | $228.29 | 0.03504 | 0.04 |
| $2.00 | $347.28 | 0.00576 | 0.01 |
At small notionals, rounding fractional quantities to two decimals materially distorts the quantity implied by the displayed notional and price. None of these orders filled, so these are estimates rather than what a user received, but they are the numbers the product presents immediately after order submission.
3. Order and market state become inconsistent across surfaces

Confirmed. All four reproduced on demand.
Once I started looking, the same information went missing in several places.
| Surface | What happens to order state |
|---|---|
| Confirmation modal | Presents a queued order as owned and bought |
| Open Orders, desktop | Status column renders blank |
| Activity, intermediate width | Status column clipped to "Sta" and "O", no horizontal scroll |
| Header, mobile width | "Market: Closed" label dropped, leaving an unlabeled dot and a bare countdown |
These are different implementation defects and I am not going to pretend they share a root cause. They share a user consequence: the information needed to understand whether an order can execute, and what state it is in, is represented inconsistently across different product surfaces and viewport sizes.
The mobile header is the one I would fix first. At narrow widths the market status text disappears and you are left with an amber dot and a number counting down, with nothing saying what it counts down to. On a product where market hours determine whether an order does anything, that is not a spacing issue.
The blank Status column is why I could not diagnose the cancellation. The field exists. It is the one place designed to tell me whether my cancel was pending, failed, or never registered. It is empty.
One more, smaller. At mobile width the Activity view renders a date group header reading "Aug 6, 2026" above a card whose own date reads "22 Sept 2026". There was one item in the account at the time, so the grouping label is inconsistent with the item's displayed date. Desktop does not render the group header at all. I have not read the source and I am not going to guess why.
How to reproduce these
Blank Status column. Submit any buy order. Portfolio, Primary Metrics, scroll to Open Orders. The Status column renders an em dash for every row, at every width, in every order state I saw.
Clipped Status. Portfolio, Activity. Narrow the browser window to roughly tablet width. The table overflows its container with no horizontal scroll and the Status header truncates to "Sta".
Missing market label. Narrow to mobile width on any authenticated page. The header renders an indicator dot and a countdown with no "Market: Open" or "Market: Closed" text. Compare against the same header at desktop width.
Date group header. Portfolio, Activity at mobile width, with at least one activity item. Compare the group header date against the item's own date field.
4. Getting in took ten days
The order path is the finding. But it is worth saying how long it took to reach the point where I could test it, because in a two-week beta that is the constraint that matters.
September 11. First access code rejected as invalid. Second code worked.
September 15. Sign-in blocked. Reported 8:00 PM. Fixed and confirmed by 11:50 AM on the 17th, about forty hours over a weekend with no chasing. Good response.
Mid-window. A funded wallet displaying $0.00. The site's Content Security Policy blocked the outbound request that reads the balance, so the balance could not be fetched. Diagnosed from the browser console. Later resolved.
September 20 to 22. Wallet login failing with "Could not log in with wallet". Similar wallet-verification failures were reported by three other testers in the community channel in the same window, one describing "Devnet verification failed for this wallet".
I found no failed HTTP request corresponding to my login failure. The visible Privy requests returned 200 and 304 while the client surfaced an authentication error. That rules out an obvious transport failure but does not establish why authentication was rejected.
It resolved when I switched browsers. I want to be careful here: I moved from Edge with a large extension set to Chrome, and I did not run the clean-profile control that would separate the browser from an extension conflict. So the honest statement is that login failed in my Edge profile and succeeded in Chrome, and I cannot tell you which of those is the cause.
September 22, 21:18. First submitted order.
Ten days of a two-week window and four separate interruptions in access or funding before I reached a successful buy submission. The September 15 access incident was resolved quickly once support responded. The later failures resolved without enough evidence for me to attribute the fix. Either way, the cumulative effect consumed most of the test period.
5. What held up
A teardown that only lists faults is a complaint, not a test. These are things I tested expecting trouble and did not find it.
Account switching. I switched Phantom accounts without reloading. Spout's wallet panel updated immediately to the new address rather than continuing to display the old one. I could only test address-level state, because I never obtained a filled position.
The funding path. The wallet panel surfaces both tokens a tester needs, each with a one-line explanation: Test USDC, "What orders are priced in"; Devnet SOL, "Pays the network fee on every trade." It states plainly that the tokens have no value and links both faucets.
The borrow panel's empty state. With no collateral, the Amount field and LTV slider are disabled, the button reads "Unavailable", and the panel says: "You don't own any NVDA yet. Buy some on Trade, then deposit it as collateral." Correct behaviour, correct asset, actionable next step.
That message is also the cleanest corroboration of Section 1. With NVDA explicitly selected, after three NVDA orders had been accepted and paid for, the product confirms I hold none.
The borrow tooltips are good, and they cost me a finding.
LTV. "Loan-to-value, how much you borrow relative to your collateral's market value. Capped at 50% so your loan always stays overcollateralised."
Health Factor. "How safe your loan is. It falls as you borrow more against the same collateral; at 1.00 your collateral can be liquidated to cover the debt."
Borrow Cost. "No interest, no monthly payments, a covered call on your stock funds the loan, so borrower cost is just that capped-away upside, annualized."
I went into this expecting to report that the central tradeoff was hidden at the point of consent. It is not hidden. Those three tooltips cover the capped upside, the overcollateralisation and the liquidation risk, in plain words, using the right terms. More on this in Section 8.
Corporate actions are documented better than I expected. Splits propagate to balances, mergers have a documented unwind path, dividends flow to spAsset holders net of withholding, and single-name option cycles that would overlap an earnings release are skipped.
6. What "0% interest" means
Scope. Earn is marked "coming soon" in the beta. The lender-side mechanics and figures below come from Spout's current documentation and Terms, not from anything I could execute in the running application. I am evaluating their internal consistency. I am not claiming to have verified these economics on-chain or in the app, and I never completed a borrow.
Spout documents the debt itself as accruing 0% interest. What funds the loan is a covered call written against your collateral, so the borrower's economic tradeoff is potential foregone upside when a covered call is assigned.
The Fees page gives one lender-side allocation: 20% of gross premium to the protocol and 80% to lenders. The tranche documentation then gives Senior 7% priority, with excess split 25/75.
Elsewhere Spout uses borrower-side language around "premium" and "cycle yield", so I treat the above as the lender documentation's accounting rather than a complete reconciliation of every premium-related cash flow.
Two things follow that a user should understand and that the product does not currently state in one place.
The cost is conditional, not zero. If the call expires below the strike, no upside is surrendered through assignment. If it finishes above the strike and is assigned, upside above the strike is given up. The Borrow Cost column annualizes this, which is the right instinct, but an annualized figure derived from an option premium is a model output rather than a price, and it moves.
The displayed borrower cost varies materially by asset. NVDA showed 0.00%/yr while Goldman Sachs showed 0.86%/yr on the same screen. The application does not expose the calculation inline, so I cannot tell you what drives the spread.
Interest and borrower cost are different quantities. The tooltip explains that distinction. The table does not.

7. The in-app assistant
Confirmed. Reproduced on rephrase.
Spout ships an assistant labelled "Answers from our docs and FAQ". I gave it a controlled prompt set.
The clearest failure was the simplest question I asked it:
Me: can i cancel pending orders
Ask Spout: I could not find an answer to that. Try rephrasing, or ask about buying, borrowing, lending, fees, or risk.

Cancellation is a core control in the beta, and the docs and FAQ assistant could not answer a basic question about how it works. That limits its usefulness for operational questions at exactly the point where a user is managing an open order and looking for help.
8. Four things I got wrong
The findings above are only worth something if you can see the ones that did not survive.
I said the covered-call tradeoff was hidden from users. It is not. The buy panel tooltip discloses it directly and defines borrower cost as the capped-away upside, annualized. I found that tooltip after writing the finding, and the finding died.
I said the product never explains the relationship between "0% interest", "no margin calls" and its own liquidation meter. Also wrong. The Health Factor tooltip states outright that collateral can be liquidated at a health factor of 1.00. All three things I thought were unexplained are explained. What survives is much smaller: the explanations live in tooltips rather than inline, which is an ordinary design choice.
I read a wallet balance of $0.00 as a fix having only partially worked. Three hard refreshes disproved it. All 46 requests returned 200. I had generalised from a single observation and I retracted it.
I used the borrow panel's "You don't own any XOM yet" as evidence that an NVDA order had not filled. The panel was selected to XOM, an asset I never ordered, so that message proves nothing about NVDA. I replaced it with the same message for NVDA specifically, which does.
I kept these four because each was removed only after a control contradicted the initial explanation. That is the same standard I applied to the findings that remain.
9. What I would fix, in order
1. Make execution state reliable. If an order cannot execute, that is the first problem.
2. Do not describe an order as settled until it settles. Use "Order received" on the confirmation screen, the string the Activity tab already uses, because the current copy does not distinguish accepted from filled. Label quantity and price as estimates.
3. Populate the Status column. It exists, it is empty, and it is the one field whose job is to answer the question everything else got wrong. The current UI does not clearly distinguish "received", "pending", "cancel requested", "failed" and "filled" for an open order.
4. Give cancellation a visible outcome. The refund is asynchronous, which the toast admits. Asynchronous with no status is the problem.
5. Show fractional quantities at a useful precision. Two decimals loses most of the information below one share.
6. Keep market status visible at every viewport. The mobile header currently drops it.
7. Teach the assistant about the product's own controls before teaching it anything else.
Two open questions, not findings
Where should committed capital appear? My USDC dropped by $20 across four orders while Portfolio Overview read $0.00 and Metrics read as an empty account. The money appears only as Open Orders rows. An unfilled order is not equity, so this is defensible, but a user whose balance just dropped has nowhere to see that reflected in a total.
What is the guaranteed quantity? The modal says I own 0.02 NVDA, at a price derived from a feed that moves while the market is closed. Would a fill deliver 0.02 shares, or $5 of notional at whatever the open is? I could not find out.
10. Closing
Across four accepted orders, including one submitted during regular market hours, I observed zero holdings. The interface gave me no explicit failure state and continued to describe the orders using completed-purchase language. That combination is the main issue I would address before a wider beta: first make execution state reliable, then make every surface distinguish clearly between received, pending, cancel-requested, filled and failed.
The underlying product model is interesting, and several smaller parts of the beta are already clear: the collateral tooltips, the onboarding copy, the corporate-action documentation. The transaction state needs to reach the same standard.
Appendix: transactions
All on Solana devnet, wallet 6cmBcWJfCfomtQZJ8rficMcQjALxFibdKRAPj6eVx37y. All returned Success.
| Signature | UTC | Likely interaction, instruction not decoded |
|---|---|---|
| 5dSr6kqm...rR18eeYJ | 22 Sep 20:18:39 | $5 NVDA order submission |
| 5RFAeKtB...CwMBvp14 | 22 Sep 20:44:30 | $8 NVDA order submission |
| 3uybh44y...LEXk98tK | 22 Sep 20:46:30 | $2 GOOG order submission |
| 2UjDBztY...Pcbq3r4n | 22 Sep 20:49:37 | cancel request, attempt 1 |
| rRdSRr6e...E1RWHzP | 23 Sep 07:52:10 | cancel request, attempt 2 |
| 56uianBf...sqWSrgKb | 23 Sep 14:53:07 | $5 NVDA order submission, market open |
| 2taNuwk7...PFXa5zA6 | 23 Sep 15:32:37 | cancel request, attempt 3 |
Spout's program is unpublished, so these render as "Unknown Program: Unknown Instruction" in Explorer and I cannot decode them. The readings above are consistent with transaction size, timing, and two signatures that Spout's own confirmation UI surfaced. They are not decoded, and Solana Success does not establish that a brokerage execution occurred.