Skip to content

fix: handle ContractNotFound and TokenNotFound in add token screen#718

Draft
thepastaclaw wants to merge 1 commit intodashpay:v1.0-devfrom
thepastaclaw:fix/token-search-not-found
Draft

fix: handle ContractNotFound and TokenNotFound in add token screen#718
thepastaclaw wants to merge 1 commit intodashpay:v1.0-devfrom
thepastaclaw:fix/token-search-not-found

Conversation

@thepastaclaw
Copy link
Copy Markdown
Collaborator

@thepastaclaw thepastaclaw commented Mar 10, 2026

Issue

Fixes #691

Description

The Add/Search Token screen gets stuck searching indefinitely when a contract or token is not found on the network.

Root cause: AddTokenByIdScreen::display_task_result has a _ => {} wildcard that silently swallows BackendTaskSuccessResult::ContractNotFound and BackendTaskSuccessResult::TokenNotFound. When either variant is returned, the UI stays in Searching(timestamp) state forever with no user feedback.

Fix:

  • ContractNotFound — falls back to interpreting the input as a Token ID (via the existing try_token_id_next mechanism), matching the existing fallback pattern elsewhere in the screen
  • TokenNotFound — shows an error via MessageBanner and transitions to Error state
  • Both paths use the unified MessageBanner system consistent with other error handling in the same file

Validation

  • cargo check
  • cargo clippy -- -D warnings
  • Manual trace: confirmed ContractNotFound and TokenNotFound are returned by the backend task handlers in src/backend_task/tokens/mod.rs (lines 523, 549, 553) and were previously swallowed by the wildcard

Summary by CodeRabbit

  • Bug Fixes
    • Added a one-time fallback: if a contract lookup fails for a valid identifier, the app will automatically try a token-ID lookup.
    • Unified and clearer error banner: "No contract or token found for the given identifier".
    • Prevents repeated retry loops after failures and resets lookup state after successful "Add Another" flows.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 10, 2026

Warning

Rate limit exceeded

@thepastaclaw has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 29 minutes and 44 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 03a9cbbf-3710-4b05-b7d0-79a7c3438ae6

📥 Commits

Reviewing files that changed from the base of the PR and between 3717f62 and 326e1da.

📒 Files selected for processing (1)
  • src/ui/tokens/add_token_by_id_screen.rs
📝 Walkthrough

Walkthrough

Adds a one-time fallback from contract-ID to token-ID lookup when a contract lookup returns not-found and the input is valid Base58; introduces tried_token_id_lookup: bool to prevent retry loops and resets that flag on new searches, resets, and after "Add Another" flows. Error messaging unified for missing contract/token.

Changes

Cohort / File(s) Summary
Search Fallback and State
src/ui/tokens/add_token_by_id_screen.rs
Adds tried_token_id_lookup: bool to AddTokenByIdScreen; reset on new/search/reset flows. On ContractNotFound, if input is Base58 and fallback not tried, set try_token_id_next and tried_token_id_lookup=true for a single token-ID lookup; otherwise emit unified "No contract or token found for the given identifier" error. Handles TokenNotFound similarly and preserves existing invalid-format error behavior. Public struct signature updated to include the new field.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped the logs and sniffed each id,
One cautious hop if contracts hide.
A single fallback, no looping chase,
Tried flag set — I rest in place.
Searches end tidy, carrot in my space.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly addresses the main change: handling ContractNotFound and TokenNotFound results in the add token screen, which is the primary objective of this PR.
Linked Issues check ✅ Passed The PR comprehensively addresses issue #691 by introducing fallback token-ID lookup with guard, unified error messaging, and preventing infinite retry loops.
Out of Scope Changes check ✅ Passed All changes are scoped to AddTokenByIdScreen to resolve the stuck-searching issue; no unrelated modifications are present.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@thepastaclaw
Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 10, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/ui/tokens/add_token_by_id_screen.rs`:
- Around line 302-317: The ContractNotFound branch currently always sets
try_token_id_next and causes a loop; add a boolean field tried_token_id_lookup
to the AddTokenByIdScreen state, initialize it false in the constructor and
reset it to false in show_success_screen (and whenever starting a new lookup),
then update display_task_result's BackendTaskSuccessResult::ContractNotFound
handling to only set try_token_id_next = true and set tried_token_id_lookup =
true when tried_token_id_lookup is false (i.e., the initial contract-ID
fallback), and if tried_token_id_lookup is already true treat it as an error
(call MessageBanner::set_global and set self.status = AddTokenStatus::Error) so
a ContractNotFound from the FetchTokenByTokenId path does not loop.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7328d80d-4ac4-4db1-a36d-9046705536c2

📥 Commits

Reviewing files that changed from the base of the PR and between 314e347 and 9032752.

📒 Files selected for processing (1)
  • src/ui/tokens/add_token_by_id_screen.rs

thepastaclaw added a commit to thepastaclaw/dash-evo-tool that referenced this pull request Mar 10, 2026
…otFound

Add tried_token_id_lookup field to track whether the token ID fallback
has already been attempted. Without this, when FetchTokenByTokenId finds
a token but its subsequent contract fetch fails (returning ContractNotFound),
the handler would set try_token_id_next = true again, creating an infinite
loop.

The guard ensures ContractNotFound only triggers the token ID fallback
once. On subsequent ContractNotFound results, it shows an error banner
and transitions to Error state.

Addresses CodeRabbit review feedback on dashpay#718.
@thepastaclaw thepastaclaw force-pushed the fix/token-search-not-found branch from 9032752 to baff28e Compare March 10, 2026 22:44
@thepastaclaw
Copy link
Copy Markdown
Collaborator Author

Addressed the infinite loop concern — added a tried_token_id_lookup guard that limits the ContractNotFound → token ID fallback to a single attempt. On subsequent ContractNotFound results (e.g., token found but its contract fetch fails), it shows an error and transitions to Error state instead of looping.

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 10, 2026

@thepastaclaw Thanks for the update! The tried_token_id_lookup guard approach sounds like a clean solution — using a boolean flag to allow exactly one fallback attempt and then transitioning to Error state on any subsequent ContractNotFound is a well-established pattern for preventing infinite retry loops. I'll kick off a review of the latest changes now.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/ui/tokens/add_token_by_id_screen.rs (1)

272-281: Consider adding the same guard to display_message for consistency.

The display_message method also sets try_token_id_next = true when handling "Contract not found" string messages (line 278), but doesn't check tried_token_id_lookup. While the typed BackendTaskSuccessResult::ContractNotFound now goes through display_task_result correctly, this string-based path could theoretically trigger the same infinite loop if messages arrive via this route.

♻️ Suggested fix for consistency
             } else if msg.contains("Contract not found") {
                 // Contract not found, try as token ID
-                if let Ok(_identifier) =
+                if !self.tried_token_id_lookup && Identifier::from_string(&self.contract_or_token_id_input, Encoding::Base58)
-                    Identifier::from_string(&self.contract_or_token_id_input, Encoding::Base58)
+                    .is_ok()
                 {
-                {
                     // We'll initiate a token ID search
                     self.try_token_id_next = true;
+                    self.tried_token_id_lookup = true;
                 } else {
                     self.status = AddTokenStatus::Error;
                 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/ui/tokens/add_token_by_id_screen.rs` around lines 272 - 281, The
display_message handler sets try_token_id_next = true when
msg.contains("Contract not found") but lacks the same guard used elsewhere;
update display_message (the method handling string messages) to check the
tried_token_id_lookup flag before setting try_token_id_next (the same guard used
when processing BackendTaskSuccessResult::ContractNotFound) and only set
try_token_id_next = true if tried_token_id_lookup is false, otherwise set status
to AddTokenStatus::Error to avoid re-triggering the lookup loop; locate
display_message and modify the "Contract not found" branch accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/ui/tokens/add_token_by_id_screen.rs`:
- Around line 272-281: The display_message handler sets try_token_id_next = true
when msg.contains("Contract not found") but lacks the same guard used elsewhere;
update display_message (the method handling string messages) to check the
tried_token_id_lookup flag before setting try_token_id_next (the same guard used
when processing BackendTaskSuccessResult::ContractNotFound) and only set
try_token_id_next = true if tried_token_id_lookup is false, otherwise set status
to AddTokenStatus::Error to avoid re-triggering the lookup loop; locate
display_message and modify the "Contract not found" branch accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 82c1ccc3-68af-49a1-b587-1e175bb8c5e1

📥 Commits

Reviewing files that changed from the base of the PR and between 9032752 and baff28e.

📒 Files selected for processing (1)
  • src/ui/tokens/add_token_by_id_screen.rs

@thepastaclaw thepastaclaw force-pushed the fix/token-search-not-found branch from d8c603d to 3717f62 Compare March 10, 2026 23:14
@thepastaclaw
Copy link
Copy Markdown
Collaborator Author

Also added the tried_token_id_lookup guard to the string-based display_message path (was only in the typed display_task_result handler). Both paths now consistently prevent the infinite fallback loop. Squashed into one commit.

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 10, 2026

@thepastaclaw Thanks for the update! Applying the tried_token_id_lookup guard to the display_message path as well ensures both handlers are consistent — the infinite fallback loop prevention is now complete across all code paths. Triggering a review of the latest squashed commit now.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/ui/tokens/add_token_by_id_screen.rs (1)

272-290: Consider extracting the shared fallback logic to reduce duplication.

The ContractNotFound handling logic in display_message (lines 272-290) and display_task_result (lines 315-335) is nearly identical. Extracting this into a private helper method would improve maintainability and ensure both paths stay consistent.

♻️ Optional: Extract helper method
+    /// Handles contract-not-found by attempting a one-time fallback to token ID lookup.
+    fn handle_contract_not_found(&mut self) {
+        if !self.tried_token_id_lookup
+            && Identifier::from_string(&self.contract_or_token_id_input, Encoding::Base58).is_ok()
+        {
+            self.try_token_id_next = true;
+            self.tried_token_id_lookup = true;
+        } else {
+            MessageBanner::set_global(
+                self.app_context.egui_ctx(),
+                "No contract or token found for the given identifier",
+                MessageType::Error,
+            );
+            self.status = AddTokenStatus::Error;
+        }
+    }

Then simplify both call sites:

// In display_message():
} else if msg.contains("Contract not found") {
    self.handle_contract_not_found();
}

// In display_task_result():
BackendTaskSuccessResult::ContractNotFound => {
    self.handle_contract_not_found();
}

Also applies to: 315-335

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/ui/tokens/add_token_by_id_screen.rs` around lines 272 - 290, Extract the
duplicated "Contract not found" fallback into a private helper on the same impl
(e.g., fn handle_contract_not_found(&mut self)) that performs the
Identifier::from_string(&self.contract_or_token_id_input,
Encoding::Base58).is_ok() check, sets self.try_token_id_next = true and
self.tried_token_id_lookup = true when valid, otherwise calls
MessageBanner::set_global(self.app_context.egui_ctx(), "No contract or token
found for the given identifier", MessageType::Error) and sets self.status =
AddTokenStatus::Error; then replace the inline blocks in display_message and the
BackendTaskSuccessResult::ContractNotFound arm inside display_task_result with
calls to this new handle_contract_not_found method so both code paths share the
same logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/ui/tokens/add_token_by_id_screen.rs`:
- Around line 272-290: Extract the duplicated "Contract not found" fallback into
a private helper on the same impl (e.g., fn handle_contract_not_found(&mut
self)) that performs the
Identifier::from_string(&self.contract_or_token_id_input,
Encoding::Base58).is_ok() check, sets self.try_token_id_next = true and
self.tried_token_id_lookup = true when valid, otherwise calls
MessageBanner::set_global(self.app_context.egui_ctx(), "No contract or token
found for the given identifier", MessageType::Error) and sets self.status =
AddTokenStatus::Error; then replace the inline blocks in display_message and the
BackendTaskSuccessResult::ContractNotFound arm inside display_task_result with
calls to this new handle_contract_not_found method so both code paths share the
same logic.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: aeba571b-22c6-4920-9ae4-ca45bd9b2aa5

📥 Commits

Reviewing files that changed from the base of the PR and between baff28e and 3717f62.

📒 Files selected for processing (1)
  • src/ui/tokens/add_token_by_id_screen.rs

The display_task_result method was missing handlers for
BackendTaskSuccessResult::ContractNotFound and
BackendTaskSuccessResult::TokenNotFound variants. These fell through
to the wildcard _ => {} match, causing the screen to stay stuck in
the Searching state indefinitely.

Now:
- ContractNotFound triggers fallback to token ID lookup (existing
  try_token_id_next logic), or shows an error banner if the input
  is not a valid identifier
- TokenNotFound shows an error banner and transitions to Error state

Fixes dashpay#691
@thepastaclaw thepastaclaw force-pushed the fix/token-search-not-found branch from 3717f62 to 326e1da Compare March 11, 2026 05:14
@thepastaclaw
Copy link
Copy Markdown
Collaborator Author

Extracted the duplicated ContractNotFound fallback logic into a handle_contract_not_found() helper method, called by both display_message and display_task_result. Pure refactoring, no behavior change.

@thepastaclaw
Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 19, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Collaborator Author

@thepastaclaw thepastaclaw left a comment

Choose a reason for hiding this comment

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

Code Review

Clean, well-targeted fix for issue #691. The display_task_result wildcard was silently swallowing ContractNotFound and TokenNotFound, leaving the UI stuck in Searching state. The fix correctly handles both variants with a one-time fallback to token-ID lookup and proper Error state transitions. Both agents confirm no issues found.

Reviewed commit: 326e1da

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.

Add/Search Token doesn't work

1 participant