Commit 7274b18
authored
fix(card): missing tokens when revoking using external tools (#30209)
<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until this PR meets the canonical
Definition of Ready For Review in `docs/readme/ready-for-review.md`.
In short: the template must be materially complete (not just section
titles
present), all status checks must be currently passing, and the only
expected
follow-up commits must be reviewer-driven.
-->
## **Description**
This branch fixes MetaMask Card **available funding assets** so that
after a user revokes a token allowance (e.g. via revoke.cash) or when
another linked wallet still holds an active delegation, the **currently
selected wallet** still gets a correct **Inactive / not enabled** row
for supported tokens from Baanx delegation settings—instead of the token
disappearing or being incorrectly deduplicated across wallets.
**What changed (high level):**
1. **`BaanxProvider.buildSupportedTokens`** — Replaces wallet-blind
deduplication (`address` + `chainId` only) with
**`hasPlaceholderForCurrentWallet`**: an existing row only blocks adding
an Inactive placeholder for the **same** wallet (or legacy empty
`walletAddress`). Another wallet’s Active/Limited row no longer
suppresses the current wallet’s placeholder. New Inactive placeholders
are stamped with **`walletAddress: currentWalletAddress`** (passed from
`getCardHomeData`).
2. **`selectCardAvailableTokens`** — **Active** and **Limited** assets
from any linked wallet stay visible; **Inactive** rows are shown only
when they belong to the **selected EVM account** (or have no
`walletAddress`), avoiding duplicate “not enabled” noise for other
accounts.
3. **Tests** — `BaanxProvider.test.ts` covers `buildSupportedTokens`
(empty funding list, same-wallet dedup, multi-wallet core case, null /
empty `delegationSettings`, contract enrichment).
`cardController.test.ts` covers the selector filtering behaviour with
mocked selected account.
**Intentionally not included:** A feature-flag fallback that synthesizes
tokens without `delegationSettings` (would lack jurisdiction-correct
`delegationContract`). When `delegationSettings` is missing or has no
`networks`, behaviour stays **return `fundingAssets` as-is** (no
synthetic list).
### Why
- **`/v1/wallet/external`** can be empty after revocation while
**`/v1/delegation/chain/config`** still lists supported networks/tokens.
Placeholders must be built from delegation settings **per selected
wallet**, not deduplicated globally across wallets.
- **UI** reads **`availableFundingAssets`** via
**`selectCardAvailableTokens`**; filtering Inactive by account keeps the
asset list accurate for account switching and multi-wallet Baanx
linkage.
### What changed (scoped paths)
| Area | Files / behaviour |
| ---- | ----------------- |
| **Baanx provider** |
[`BaanxProvider.ts`](app/core/Engine/controllers/card-controller/providers/BaanxProvider.ts):
`getCardHomeData(address, …)` passes `address` into
`buildSupportedTokens`; wallet-aware placeholder dedup; Inactive
`walletAddress` set to current address. |
| **Selectors** |
[`cardController.ts`](app/selectors/cardController.ts):
`selectCardAvailableTokens` uses `selectSelectedEvmAccount` and filters
Inactive by current address. |
| **Tests** |
[`BaanxProvider.test.ts`](app/core/Engine/controllers/card-controller/providers/BaanxProvider.test.ts),
[`cardController.test.ts`](app/selectors/cardController.test.ts). |
### Out of scope (intentional)
- Feature-flag–only token list without `delegationSettings` /
`delegationContract`.
- Changes to unauthenticated `getOnChainAssets` (still bypasses
`buildSupportedTokens`).
## **Changelog**
CHANGELOG entry: Fixed Card available asset list so supported tokens
show per-wallet “not enabled” state after revocation or when another
linked wallet still has an active delegation; inactive rows are scoped
to the selected account in the token picker.
## **Related issues**
Fixes:
<!-- Add ticket ID(s), e.g. Fixes: MUSD-xxx or #12345 -->
## **Manual testing steps**
```gherkin
Feature: Card available tokens after revocation / multi-wallet
Background:
Given I am authenticated with the MetaMask Card (Baanx) backend
And delegation chain config returns supported networks (e.g. Linea USDC)
And I may have more than one EVM wallet linked to the same card account
Scenario: revoke on external tool then open Card
Given I had delegated USDC on Linea from wallet A
When I revoke the allowance (e.g. revoke.cash) so wallet external API returns no rows for that delegation
And I select wallet A in MetaMask
When I open Card home / spending limit asset list
Then USDC on Linea still appears as not enabled (Inactive) for wallet A when delegation settings still list the token
And I can re-enable delegation from that row (contract comes from delegation settings)
Scenario: another wallet still delegated
Given wallet B still has an active USDC Linea delegation in Baanx wallet external data
When I select wallet A (revoked or never delegated)
Then I still see a not-enabled / Inactive row for USDC Linea for wallet A
And I still see wallet B’s Active (or Limited) row for awareness
Scenario: account switch
Given both wallets have their own Inactive placeholders for the same token
When I switch the selected EVM account in the app
Then the available token list shows Inactive rows only for the selected account (plus all Active/Limited from any wallet)
```
## **Screenshots/Recordings**
### **Before**
<!-- Token missing from Card asset list after revoke, or wrong wallet’s
row only. -->
### **After**
<!-- Same token visible as not enabled for current wallet; other
wallet’s active row still visible if applicable. -->
## **Pre-merge author checklist**
<!--
Every checklist item must be consciously assessed before marking this PR
as
"Ready for review". A checked box means you deliberately considered that
responsibility, not that you literally performed every action listed.
Unchecked boxes are ambiguous: they are not an implicit "N/A" and they
are not
a silent "skip". See `docs/readme/ready-for-review.md` for the full
checklist
semantics.
-->
- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I've included tests if applicable
- [x] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.
#### Performance checks (if applicable)
- [ ] I've tested on Android
- Ideally on a mid-range device; emulator is acceptable
- [ ] I've tested with a power user scenario
- Use these [power-user
SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93)
to import wallets with many accounts and tokens
- [ ] I've instrumented key operations with Sentry traces for production
performance metrics
- See [`trace()`](/app/util/trace.ts) for usage and
[`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274)
for an example
For performance guidelines and tooling, see the [Performance
Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers).
## **Pre-merge reviewer checklist**
<!--
Reviewer checklist items follow the same semantics as the author
checklist: an
unchecked box is ambiguous, a checked box means the reviewer consciously
assessed that responsibility. See `docs/readme/ready-for-review.md`.
-->
- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Changes multi-wallet token placeholder and filtering logic for Card
funding assets, which can affect what users see and can re-enable
funding; risk is mitigated by added unit coverage but may impact edge
cases around wallet selection and legacy empty `walletAddress` rows.
>
> **Overview**
> Fixes Card `availableFundingAssets` generation and display for
multi-wallet scenarios so a supported token can still appear as
*Inactive/not enabled* for the **currently selected wallet** even if
another linked wallet has an Active/Limited entry or the external-wallet
API returns no rows.
>
> `BaanxProvider.getCardHomeData` now passes the current `address` into
`buildSupportedTokens`, which dedupes placeholders by `address + chainId
+ walletAddress` (with a legacy fallback for empty wallet) and stamps
new inactive placeholders with the current wallet’s address while still
enriching existing assets with the network `delegationContract`.
>
> `selectCardAvailableTokens` now filters `Inactive` rows to the
selected EVM account (or empty `walletAddress`) while always showing
Active/Limited rows from any linked wallet, reducing duplicate “not
enabled” entries; new tests cover the wallet-aware placeholder and
selector filtering behavior.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
730da51. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->1 parent 0b1fa04 commit 7274b18
4 files changed
Lines changed: 330 additions & 12 deletions
File tree
- app
- core/Engine/controllers/card-controller/providers
- selectors
Lines changed: 186 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
378 | 378 | | |
379 | 379 | | |
380 | 380 | | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
| 417 | + | |
| 418 | + | |
| 419 | + | |
| 420 | + | |
| 421 | + | |
| 422 | + | |
| 423 | + | |
| 424 | + | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
| 428 | + | |
| 429 | + | |
| 430 | + | |
| 431 | + | |
| 432 | + | |
| 433 | + | |
| 434 | + | |
| 435 | + | |
| 436 | + | |
| 437 | + | |
| 438 | + | |
| 439 | + | |
| 440 | + | |
| 441 | + | |
| 442 | + | |
| 443 | + | |
| 444 | + | |
| 445 | + | |
| 446 | + | |
| 447 | + | |
| 448 | + | |
| 449 | + | |
| 450 | + | |
| 451 | + | |
| 452 | + | |
| 453 | + | |
| 454 | + | |
| 455 | + | |
| 456 | + | |
| 457 | + | |
| 458 | + | |
| 459 | + | |
| 460 | + | |
| 461 | + | |
| 462 | + | |
| 463 | + | |
| 464 | + | |
| 465 | + | |
| 466 | + | |
| 467 | + | |
| 468 | + | |
| 469 | + | |
| 470 | + | |
| 471 | + | |
| 472 | + | |
| 473 | + | |
| 474 | + | |
| 475 | + | |
| 476 | + | |
| 477 | + | |
| 478 | + | |
| 479 | + | |
| 480 | + | |
| 481 | + | |
| 482 | + | |
| 483 | + | |
| 484 | + | |
| 485 | + | |
| 486 | + | |
| 487 | + | |
| 488 | + | |
| 489 | + | |
| 490 | + | |
| 491 | + | |
| 492 | + | |
| 493 | + | |
| 494 | + | |
| 495 | + | |
| 496 | + | |
| 497 | + | |
| 498 | + | |
| 499 | + | |
| 500 | + | |
| 501 | + | |
| 502 | + | |
| 503 | + | |
| 504 | + | |
| 505 | + | |
| 506 | + | |
| 507 | + | |
| 508 | + | |
| 509 | + | |
| 510 | + | |
| 511 | + | |
| 512 | + | |
| 513 | + | |
| 514 | + | |
| 515 | + | |
| 516 | + | |
| 517 | + | |
| 518 | + | |
| 519 | + | |
| 520 | + | |
| 521 | + | |
| 522 | + | |
| 523 | + | |
| 524 | + | |
| 525 | + | |
| 526 | + | |
| 527 | + | |
| 528 | + | |
| 529 | + | |
| 530 | + | |
| 531 | + | |
| 532 | + | |
| 533 | + | |
| 534 | + | |
| 535 | + | |
| 536 | + | |
| 537 | + | |
| 538 | + | |
| 539 | + | |
| 540 | + | |
| 541 | + | |
| 542 | + | |
| 543 | + | |
| 544 | + | |
| 545 | + | |
| 546 | + | |
| 547 | + | |
| 548 | + | |
| 549 | + | |
| 550 | + | |
| 551 | + | |
| 552 | + | |
| 553 | + | |
| 554 | + | |
| 555 | + | |
| 556 | + | |
| 557 | + | |
| 558 | + | |
| 559 | + | |
| 560 | + | |
| 561 | + | |
| 562 | + | |
| 563 | + | |
| 564 | + | |
| 565 | + | |
| 566 | + | |
381 | 567 | | |
Lines changed: 28 additions & 10 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
368 | 368 | | |
369 | 369 | | |
370 | 370 | | |
371 | | - | |
| 371 | + | |
372 | 372 | | |
373 | 373 | | |
374 | 374 | | |
| |||
456 | 456 | | |
457 | 457 | | |
458 | 458 | | |
| 459 | + | |
459 | 460 | | |
460 | 461 | | |
461 | 462 | | |
| |||
1532 | 1533 | | |
1533 | 1534 | | |
1534 | 1535 | | |
1535 | | - | |
1536 | | - | |
| 1536 | + | |
| 1537 | + | |
| 1538 | + | |
1537 | 1539 | | |
1538 | 1540 | | |
1539 | 1541 | | |
1540 | 1542 | | |
| 1543 | + | |
1541 | 1544 | | |
1542 | 1545 | | |
1543 | 1546 | | |
1544 | 1547 | | |
1545 | 1548 | | |
| 1549 | + | |
| 1550 | + | |
| 1551 | + | |
| 1552 | + | |
| 1553 | + | |
| 1554 | + | |
| 1555 | + | |
| 1556 | + | |
| 1557 | + | |
| 1558 | + | |
| 1559 | + | |
| 1560 | + | |
| 1561 | + | |
| 1562 | + | |
| 1563 | + | |
| 1564 | + | |
| 1565 | + | |
| 1566 | + | |
1546 | 1567 | | |
1547 | 1568 | | |
1548 | 1569 | | |
| |||
1567 | 1588 | | |
1568 | 1589 | | |
1569 | 1590 | | |
1570 | | - | |
1571 | | - | |
1572 | | - | |
1573 | | - | |
1574 | | - | |
1575 | | - | |
| 1591 | + | |
| 1592 | + | |
| 1593 | + | |
1576 | 1594 | | |
1577 | 1595 | | |
1578 | 1596 | | |
| |||
1587 | 1605 | | |
1588 | 1606 | | |
1589 | 1607 | | |
1590 | | - | |
| 1608 | + | |
1591 | 1609 | | |
1592 | 1610 | | |
1593 | 1611 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
403 | 403 | | |
404 | 404 | | |
405 | 405 | | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
| 417 | + | |
| 418 | + | |
| 419 | + | |
| 420 | + | |
| 421 | + | |
| 422 | + | |
| 423 | + | |
| 424 | + | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
| 428 | + | |
| 429 | + | |
| 430 | + | |
| 431 | + | |
| 432 | + | |
| 433 | + | |
| 434 | + | |
| 435 | + | |
| 436 | + | |
| 437 | + | |
| 438 | + | |
| 439 | + | |
| 440 | + | |
406 | 441 | | |
407 | 442 | | |
408 | 443 | | |
| |||
422 | 457 | | |
423 | 458 | | |
424 | 459 | | |
| 460 | + | |
| 461 | + | |
| 462 | + | |
| 463 | + | |
| 464 | + | |
| 465 | + | |
| 466 | + | |
| 467 | + | |
| 468 | + | |
| 469 | + | |
| 470 | + | |
| 471 | + | |
| 472 | + | |
| 473 | + | |
| 474 | + | |
| 475 | + | |
| 476 | + | |
| 477 | + | |
| 478 | + | |
| 479 | + | |
| 480 | + | |
| 481 | + | |
| 482 | + | |
| 483 | + | |
| 484 | + | |
| 485 | + | |
| 486 | + | |
| 487 | + | |
| 488 | + | |
| 489 | + | |
| 490 | + | |
| 491 | + | |
| 492 | + | |
| 493 | + | |
| 494 | + | |
| 495 | + | |
| 496 | + | |
| 497 | + | |
| 498 | + | |
| 499 | + | |
| 500 | + | |
| 501 | + | |
| 502 | + | |
| 503 | + | |
| 504 | + | |
| 505 | + | |
| 506 | + | |
| 507 | + | |
| 508 | + | |
| 509 | + | |
| 510 | + | |
| 511 | + | |
| 512 | + | |
| 513 | + | |
| 514 | + | |
| 515 | + | |
| 516 | + | |
| 517 | + | |
| 518 | + | |
| 519 | + | |
| 520 | + | |
| 521 | + | |
425 | 522 | | |
426 | 523 | | |
427 | 524 | | |
| |||
0 commit comments