Fix crash when adding contract tokens from stellar.expert search (#799)#801
Fix crash when adding contract tokens from stellar.expert search (#799)#801
Conversation
stellar.expert now returns Soroban contract tokens alongside classic assets. The `asset` field for these is a raw contract ID instead of the "CODE-ISSUER-TYPE" format, which caused `new Asset()` to throw when the 56-char contract ID was used as an asset code. Handle contract tokens in `formatTokensFromSearchResults` by detecting contract IDs and reading metadata from the new top-level fields (`code`, `decimals`, `token_name`) with `tomlInfo` as fallback. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Xcode 26.4 ships a stricter Clang that breaks the `fmt` CocoaPod with consteval errors. Cap the auto-selected Xcode at 26.3 until the Pod is updated. The cap is a single variable (MAX_XCODE_VERSION) at the top of the script for easy bumping. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Test that search results containing Soroban contract tokens (where `asset` is a raw contract ID) are correctly parsed using the new top-level fields, and that missing metadata falls back gracefully. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Fixes a crash when adding Soroban contract tokens returned by stellar.expert asset search by correctly detecting/parsing contract-token results and adding additional guards in the Add Token UI.
Changes:
- Update
useTokenLookupto detect contract IDs from stellar.expert results and map contract token metadata from the new top-level fields (withtomlInfofallback). - Update
SearchTokenResponsetyping to reflect the updated stellar.expert response shape (tomlInfooptional; newcode/token_name/decimalsfields). - Add a defense-in-depth guard in
AddTokenBottomSheetContentto avoid constructingAssetwhen the token is actually a contract ID; update the Xcode selection script to exclude versions above a maximum.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/hooks/useTokenLookup.ts | Adds contract-token detection/formatting for stellar.expert search results and hardens icon reduction when tomlInfo is missing. |
| src/config/types.ts | Updates stellar.expert search response types for contract token fields and optional tomlInfo. |
| src/components/screens/AddTokenScreen/AddTokenBottomSheetContent.tsx | Adds runtime guard to avoid new Asset() for contract tokens (defense-in-depth). |
| scripts/setup-xcode-latest-stable | Introduces a maximum allowed Xcode version filter when selecting the latest stable Xcode. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/components/screens/AddTokenScreen/AddTokenBottomSheetContent.tsx
Outdated
Show resolved
Hide resolved
Custom tokens cannot be swapped via Horizon's classic DEX path finder. Detect them early in findSwapPath and show a user-friendly error instead of letting getTokenForPayment throw an unhandled exception. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove activeError from the dependency array of the pathError useEffect. activeError was both set by and a dependency of this effect, creating an infinite loop: setActiveError creates a new object → triggers the effect → setActiveError again → forever. The effect only needs to react to pathError, sourceAmount, and destinationTokenId changes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
| if (isSourceCustom || isDestCustom) { | ||
| set({ | ||
| isLoadingPath: false, | ||
| pathError: t("swapScreen.errors.customTokenSwapNotSupported"), |
Use explicit isContractId checks instead of || fallback to avoid picking a non-empty classic issuer (G...) when tokenCode holds the contract ID. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Code reviewNo issues found. Checked for bugs and CLAUDE.md compliance. 🤖 Generated with Claude Code - If this code review was useful, please react with 👍. Otherwise, react with 👎. |
| records: [ | ||
| { | ||
| asset: "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC", | ||
| domain: "example.com", |
There was a problem hiding this comment.
is this a possible response?
There was a problem hiding this comment.
do you mean a response containing only asset and domain ? It shouldn't happen. I think the main goal of this test case is asserting that the app is able to handle a search using the contract address + a response containing the contract address as the asset field
There was a problem hiding this comment.
Yeah, a response with just those 2 fields. I wasn't quite sure what this covers that stub above doesn't. Both have asset as a contract id and domain as a string
There was a problem hiding this comment.
Not a blocker, was just curious
piyalbasu
left a comment
There was a problem hiding this comment.
This is looking good to me! Just left 1 question
* chore: bump app version to v1.14.25 * Fix crash when adding contract tokens from stellar.expert search (#799) (#801) * Fix crash when adding contract tokens from stellar.expert search (#799) stellar.expert now returns Soroban contract tokens alongside classic assets. The `asset` field for these is a raw contract ID instead of the "CODE-ISSUER-TYPE" format, which caused `new Asset()` to throw when the 56-char contract ID was used as an asset code. Handle contract tokens in `formatTokensFromSearchResults` by detecting contract IDs and reading metadata from the new top-level fields (`code`, `decimals`, `token_name`) with `tomlInfo` as fallback. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Cap CI Xcode version at 26.3 to fix iOS build Xcode 26.4 ships a stricter Clang that breaks the `fmt` CocoaPod with consteval errors. Cap the auto-selected Xcode at 26.3 until the Pod is updated. The cap is a single variable (MAX_XCODE_VERSION) at the top of the script for easy bumping. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Add tests for contract token parsing from stellar.expert Test that search results containing Soroban contract tokens (where `asset` is a raw contract ID) are correctly parsed using the new top-level fields, and that missing metadata falls back gracefully. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Prevent crash when swapping custom/Soroban tokens Custom tokens cannot be swapped via Horizon's classic DEX path finder. Detect them early in findSwapPath and show a user-friendly error instead of letting getTokenForPayment throw an unhandled exception. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix infinite useEffect loop in SwapAmountScreen Remove activeError from the dependency array of the pathError useEffect. activeError was both set by and a dependency of this effect, creating an infinite loop: setActiveError creates a new object → triggers the effect → setActiveError again → forever. The effect only needs to react to pathError, sourceAmount, and destinationTokenId changes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix tokenContractId selection to prefer explicit contract field Use explicit isContractId checks instead of || fallback to avoid picking a non-empty classic issuer (G...) when tokenCode holds the contract ID. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * add comment --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Cássio Marcos Goulart <3228151+CassioMG@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

What
Closes #799
Fix crash when adding Soroban contract tokens returned by stellar.expert search results.
Why
stellar.expert now returns Soroban contract tokens alongside classic assets. For contract tokens, the
assetfield is a raw contract ID (e.g.CC64WBDGS6QQP22QTTIACYIXT3WF7BBQEYOQPLTP7GTKYY7PZ74QYGSL) instead of the classicCODE-ISSUER-TYPEformat. Our parsing splitassetby-, which meant the full contract ID ended up astokenCode. This causednew Asset()to throw"Asset code is invalid (maximum alphanumeric, 12 characters at max)"when the user tapped "Add" on a contract token result.Steps to reproduce:
Root cause:
formatTokensFromSearchResultsinuseTokenLookupassumed all stellar.expert results use theCODE-ISSUER-TYPEformat. Contract tokens don't.Changes
useTokenLookup.ts— Detect contract tokens viaisContractId(result.asset)and parse them using the new top-level fields (code,decimals,token_name) that stellar.expert provides, withtomlInfoas fallback. Also guard the icon reduction against missingtomlInfo.config/types.ts— UpdateSearchTokenResponseto match stellar.expert's current schema (tomlInfooptional, newcode/token_name/decimalsfields).AddTokenBottomSheetContent.tsx— Defense-in-depth: also checkisContractId(tokenCode)before callingnew Asset().Adding using both token name and contract id as search term
Simulator.Screen.Recording.-.iPhone.17.Pro.-.2026-04-03.at.15.00.47.mov
Known limitations
N/A
Checklist
PR structure
Testing
Release