Skip to content

Fix crash when adding contract tokens from stellar.expert search (#799)#801

Merged
CassioMG merged 7 commits intov1.14.25from
cg-fix-add-custom-token
Apr 4, 2026
Merged

Fix crash when adding contract tokens from stellar.expert search (#799)#801
CassioMG merged 7 commits intov1.14.25from
cg-fix-add-custom-token

Conversation

@CassioMG
Copy link
Copy Markdown
Contributor

@CassioMG CassioMG commented Apr 3, 2026

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 asset field is a raw contract ID (e.g. CC64WBDGS6QQP22QTTIACYIXT3WF7BBQEYOQPLTP7GTKYY7PZ74QYGSL) instead of the classic CODE-ISSUER-TYPE format. Our parsing split asset by -, which meant the full contract ID ended up as tokenCode. This caused new 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:

  1. Go to Add Token screen
  2. Search for "dejaaa"
  3. Tap "Add" on the second result (the one showing the contract ID)
  4. App crashes

Root cause: formatTokensFromSearchResults in useTokenLookup assumed all stellar.expert results use the CODE-ISSUER-TYPE format. Contract tokens don't.

Changes

  • useTokenLookup.ts — Detect contract tokens via isContractId(result.asset) and parse them using the new top-level fields (code, decimals, token_name) that stellar.expert provides, with tomlInfo as fallback. Also guard the icon reduction against missing tomlInfo.
  • config/types.ts — Update SearchTokenResponse to match stellar.expert's current schema (tomlInfo optional, new code/token_name/decimals fields).
  • AddTokenBottomSheetContent.tsx — Defense-in-depth: also check isContractId(tokenCode) before calling new 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

  • This PR does not mix refactoring changes with feature changes (break it down into smaller PRs if not).
  • This PR has reasonably narrow scope (break it down into smaller PRs if not).
  • This PR includes relevant before and after screenshots/videos highlighting these changes.
  • I took the time to review my own PR.

Testing

  • These changes have been tested and confirmed to work as intended on Android.
  • These changes have been tested and confirmed to work as intended on iOS.
  • These changes have been tested and confirmed to work as intended on small iOS screens.
  • These changes have been tested and confirmed to work as intended on small Android screens.
  • I have tried to break these changes while extensively testing them.
  • This PR adds tests for the new functionality or fixes.

Release

  • This is not a breaking change.
  • This PR updates existing JSDocs when applicable.
  • This PR adds JSDocs to new functionalities.

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>
@CassioMG CassioMG linked an issue Apr 3, 2026 that may be closed by this pull request
@CassioMG CassioMG marked this pull request as ready for review April 3, 2026 22:04
@CassioMG CassioMG self-assigned this Apr 3, 2026
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>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 useTokenLookup to detect contract IDs from stellar.expert results and map contract token metadata from the new top-level fields (with tomlInfo fallback).
  • Update SearchTokenResponse typing to reflect the updated stellar.expert response shape (tomlInfo optional; new code/token_name/decimals fields).
  • Add a defense-in-depth guard in AddTokenBottomSheetContent to avoid constructing Asset when 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.

CassioMG and others added 2 commits April 3, 2026 16:12
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"),
Copy link
Copy Markdown
Contributor Author

@CassioMG CassioMG Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mobile is also crashing when you try to Swap custom tokens so I'm jamming in this fix here which will display the error message below instead:

Image

CassioMG and others added 2 commits April 3, 2026 16:23
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>
@piyalbasu
Copy link
Copy Markdown
Contributor

Code review

No 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",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this a possible response?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a blocker, was just curious

Copy link
Copy Markdown
Contributor

@piyalbasu piyalbasu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking good to me! Just left 1 question

@CassioMG CassioMG merged commit 94cfe78 into v1.14.25 Apr 4, 2026
25 checks passed
@CassioMG CassioMG deleted the cg-fix-add-custom-token branch April 4, 2026 00:28
@CassioMG CassioMG mentioned this pull request Apr 4, 2026
5 tasks
CassioMG added a commit that referenced this pull request Apr 4, 2026
* 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Update asset search to handle contract tokens

3 participants