Repository navigation
fix(dapp): phantom-signed solana swaps, dollar cash-outs, and raydium beside jupiter - #157
Merged
Merged
Conversation
…check Phantom adds Lighthouse guard instructions to a transaction before signing it. The submit route checked the signed bytes against the venue's program list, so a Jupiter swap that passed at prepare was refused at submit with "calls a program Jupiter should not need" (seen on prod, POST /api/chain/solana/submit, 6 November). The check now runs on a copy without the Lighthouse instructions. What is sent is what the wallet signed, guards included. Any other added program, and bytes after the message, are still refused.
… any model Only "$0.8 sol" was read as dollars off Stellar. "$0.8 worth of sol", "$0.8 of sol" and "0.8 dollars worth of sol" fell through to the model, so "offramp $0.8 worth of sol to naira" could come back as 0.8 SOL. Both the chain parser and the cash-out parser now rewrite those to "$0.8 sol" first. A bare "of" counts only before a token, so "$110 of profit" is left alone, and a dollar of USDC is still one USDC.
Raydium's Trade API quotes a swap and builds the transaction, so no SDK is added. The swap lands through the Solana module's own submit, since Raydium has no endpoint that sends. Its router's source is not public, so the check reads the three route instructions the API built for SOL to USDC, USDC to SOL and USDC to USDT on 6 November: one swap of exactly the amount asked for no less than the quote's floor, a SOL wrap of that same amount only when SOL is spent, and one closing step. Anything else is refused. A quote is held to the same slippage, price impact and value checks as Jupiter's. Ships with Raydium's logo.
✅ Deploy Preview for useintent ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for useintentadapp ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Depo-dev
pushed a commit
that referenced
this pull request
Nov 6, 2026
…it check (#158) #157 fixed the chain submit route only. The cash-out deposit has its own submit, which checked the signed bytes as they came from the wallet, so a Phantom user's swap-then-cash-out sent the swap and then refused the deposit: "calls a program a USDC deposit does not need". The deposit check now reads the same guard-free copy, and sends what the wallet signed. A test now fails on any check that reads raw signed Solana bytes, so a third route can't miss it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phantom-signed Jupiter swaps were refused
A 0.007 SOL to USDC swap through Jupiter failed with "This transaction calls a program Jupiter should not need."
Prod logs put the refusal at
POST /api/chain/solana/submit, not prepare. The transaction our server built passed its check. A live Jupiter order for the same swap, from a wallet with and without a USDC account, called only Compute Budget, System, Token, Associated Token and Jupiter's aggregator.What changed it was the wallet. Phantom adds Lighthouse guard instructions to a transaction before signing (Phantom's post). These are assertions that can only make a transaction fail. Submit re-checked the signed bytes against the venue's program list, so it refused them.
Fix:
withoutWalletGuardstakes Lighthouse instructions (L2TExMFK…, executable on mainnet) out of a copy used only for the check. What is sent is exactly what the wallet signed. Every other added program is still refused, and so are bytes after the message.Covered by unit tests on real captured transactions, plus a submit-route test through the real Solana module.
Not proven: Jupiter's
/executeaccepting a guarded transaction. No wallet has signed one through this."$0.8 worth of sol" read as 0.8 SOL
Off Stellar, only "$0.8 sol" was read as dollars. "$0.8 worth of sol", "$0.8 of sol" and "0.8 dollars worth of sol" all fell through to the model, for swaps and cash-outs alike.
Fix: both the chain parser and the cash-out parser now rewrite these to "$0.8 sol" first. A bare "of" counts only before a token, so "$110 of profit" is left alone, and a dollar of USDC is still one USDC.
A Playwright spec types both messages from the report, "swap $0.8 worth of sol to naira" and "offramp $0.8 worth of sol to naira". In each, the chat says "$0.8 of SOL at $90.24 is 0.00886524 SOL." and races 0.00886524 SOL.
Raydium as a second Solana swap venue
Raydium's Trade API quotes a swap and builds the transaction, so no SDK is added. The swap lands through the Solana module's own submit.
Raydium's router source isn't public, so the check reads the three route instructions its API builds:
Anything else is refused. A quote gets the same slippage (1%), price impact (3%) and value-loss (5%) checks as Jupiter's.
Checked live on 6 November: SOL→USDC, USDC→SOL and USDC→USDT all quoted, built and passed every check. Ships with Raydium's logo.
Orca is not in this PR. It has no swap-building API, only its
@orca-so/whirlpoolsSDK, so it needs a new dependency and its own install. That will be a follow-up PR.Checks