Skip to content

Commit

Permalink
Fix detected tokens added to wrong network (#22814)
Browse files Browse the repository at this point in the history
## **Description**

Fixes a race condition during token detection after switching networks.

During the network switch, some controllers state are still on the old
chain id, and others are on the new chain id. This can cause different
issues depending which controllers win the race. The worst case is that
detected tokens are added to the wrong network (see related issues):


![image](https://github.com/MetaMask/metamask-extension/assets/3500406/c4dc4a21-9234-44a5-a915-7d2910a28a60)

In that ^ screenshot there are 2 mainnet tokens that we have a balance
of, but they incorrectly appear under linea.

There's a fix for each of the relevant controllers
(`DetectTokensController`, `TokensController`,
`AssetsContractController`) ensuring we use the chain ID being switched
*to*.

## **Related issues**

[Auto token detection list collision with other networks
#22512](#22512)

[Autodetect tokens display Mainnet tokens on another network
#7587](MetaMask/metamask-mobile#7587)


## **Manual testing steps**

It takes a few minutes of switching networks back and forth to reproduce
the bug. But basically keep doing that and we should not see tokens hop
from 1 network to the other.

## **Screenshots/Recordings**

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->

### **Before**

<!-- [screenshots/recordings] -->

### **After**

<!-- [screenshots/recordings] -->

## **Pre-merge author checklist**

- [ ] I’ve followed [MetaMask Coding
Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md).
- [ ] I've clearly explained what problem this PR is solving and how it
is solved.
- [ ] I've linked related issues
- [ ] I've included manual testing steps
- [ ] I've included screenshots/recordings if applicable
- [ ] I’ve included tests if applicable
- [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.
- [ ] I’ve properly set the pull request status:
  - [ ] In case it's not yet "ready for review", I've set it to "draft".
- [ ] In case it's "ready for review", I've changed it from "draft" to
"non-draft".

## **Pre-merge reviewer checklist**

- [ ] 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.
  • Loading branch information
bergeron authored Feb 13, 2024
1 parent 89d0287 commit b0bd096
Showing 1 changed file with 14 additions and 18 deletions.
32 changes: 14 additions & 18 deletions app/scripts/controllers/detect-tokens.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,7 @@ export default class DetectTokensController extends StaticIntervalPollingControl
this.useTokenDetection =
this.preferences?.store.getState().useTokenDetection;
this.selectedAddress = getCurrentSelectedAccount().address;
this.tokenAddresses = this.tokensController?.state.tokens.map((token) => {
return token.address;
});
this.setIntervalLength(interval);
this.hiddenTokens = this.tokensController?.state.ignoredTokens;
this.detectedTokens = this.tokensController?.state.detectedTokens;
this.chainId = this.getChainIdFromNetworkStore();
this._trackMetaMetricsEvent = trackMetaMetricsEvent;

Expand Down Expand Up @@ -101,15 +96,6 @@ export default class DetectTokensController extends StaticIntervalPollingControl
}
});

tokensController?.subscribe(
({ tokens = [], ignoredTokens = [], detectedTokens = [] }) => {
this.tokenAddresses = tokens.map((token) => {
return token.address;
});
this.hiddenTokens = ignoredTokens;
this.detectedTokens = detectedTokens;
},
);
messenger.subscribe('NetworkController:stateChange', () => {
if (this.chainId !== this.getChainIdFromNetworkStore()) {
const chainId = this.getChainIdFromNetworkStore();
Expand Down Expand Up @@ -169,13 +155,19 @@ export default class DetectTokensController extends StaticIntervalPollingControl
const tokensToDetect = [];
for (const tokenAddress in tokenListUsed) {
if (
!this.tokenAddresses.find((address) =>
!this.tokensController.state.allTokens?.[chainIdAgainstWhichToDetect]?.[
addressAgainstWhichToDetect
]?.find(({ address }) =>
isEqualCaseInsensitive(address, tokenAddress),
) &&
!this.hiddenTokens.find((address) =>
!this.tokensController.state.allIgnoredTokens?.[
chainIdAgainstWhichToDetect
]?.[addressAgainstWhichToDetect]?.find((address) =>
isEqualCaseInsensitive(address, tokenAddress),
) &&
!this.detectedTokens.find(({ address }) =>
!this.tokensController.state.allDetectedTokens?.[
chainIdAgainstWhichToDetect
]?.[addressAgainstWhichToDetect]?.find(({ address }) =>
isEqualCaseInsensitive(address, tokenAddress),
)
) {
Expand All @@ -192,6 +184,9 @@ export default class DetectTokensController extends StaticIntervalPollingControl
result = await this.assetsContractController.getBalancesInSingleCall(
addressAgainstWhichToDetect,
tokensSlice,
this.network.findNetworkClientIdByChainId(
chainIdAgainstWhichToDetect,
),
);
} catch (error) {
warn(
Expand Down Expand Up @@ -247,7 +242,8 @@ export default class DetectTokensController extends StaticIntervalPollingControl
*/
restartTokenDetection({ selectedAddress, chainId } = {}) {
const addressAgainstWhichToDetect = selectedAddress ?? this.selectedAddress;
const chainIdAgainstWhichToDetect = chainId ?? this.chainId;
const chainIdAgainstWhichToDetect =
chainId ?? this.getChainIdFromNetworkStore();
if (!(this.isActive && addressAgainstWhichToDetect)) {
return;
}
Expand Down

0 comments on commit b0bd096

Please sign in to comment.