Skip to content

Commit

Permalink
[detectTokens] Refactor by extracting three methods:`#getCorrectChain…
Browse files Browse the repository at this point in the history
…IdAndNetworkClientId`, `#getTokenListAndSlicesOfTokensToDetect`, `#addDetectedTokens`

- Fixes #1614
- Maintains distinction between private class fields `#{chainId,selectedAddress,networkClientId}{,AgainstWhichToDetect}`, so that `detectTokens` method can be used independently of polling/passive detection (convert to static method?). Is this expected behavior?
  • Loading branch information
MajorLift committed Feb 20, 2024
1 parent 035ced3 commit c5709b8
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 62 deletions.
4 changes: 2 additions & 2 deletions packages/assets-controllers/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ module.exports = merge(baseConfig, {
global: {
branches: 88.53,
functions: 96.98,
lines: 97.15,
statements: 97.21,
lines: 97.17,
statements: 97.23,
},
},

Expand Down
166 changes: 106 additions & 60 deletions packages/assets-controllers/src/TokenDetectionController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,12 @@ export class TokenDetectionController extends StaticIntervalPollingController<

#networkClientId: NetworkClientId;

#chainIdAgainstWhichToDetect: Hex;

#addressAgainstWhichToDetect: string;

#networkClientIdAgainstWhichToDetect: NetworkClientId;

#disabled: boolean;

#isUnlocked: boolean;
Expand Down Expand Up @@ -229,14 +235,18 @@ export class TokenDetectionController extends StaticIntervalPollingController<
this.#disabled = disabled;
this.setIntervalLength(interval);

this.#networkClientId = networkClientId;
this.#selectedAddress =
selectedAddress ??
this.messagingSystem.call('AccountsController:getSelectedAccount')
.address;
const { chainId } =
this.#addressAgainstWhichToDetect = this.#selectedAddress;

const { chainId, networkClientId: correctNetworkClientId } =
this.#getCorrectChainIdAndNetworkClientId(networkClientId);
this.#chainId = chainId;
this.#chainIdAgainstWhichToDetect = this.#chainId;
this.#networkClientId = correctNetworkClientId;
this.#networkClientIdAgainstWhichToDetect = this.#networkClientId;

const { useTokenDetection: defaultUseTokenDetection } =
this.messagingSystem.call('PreferencesController:getState');
Expand Down Expand Up @@ -483,33 +493,54 @@ export class TokenDetectionController extends StaticIntervalPollingController<
return;
}

const addressAgainstWhichToDetect = accountAddress ?? this.#selectedAddress;
const {
chainId: chainIdAgainstWhichToDetect,
networkClientId: networkClientIdAgainstWhichToDetect,
} = this.#getCorrectChainIdAndNetworkClientId(
networkClientId ?? this.#networkClientId,
);
this.#addressAgainstWhichToDetect = accountAddress ?? this.#selectedAddress;
const { chainId, networkClientId: selectedNetworkClientId } =
this.#getCorrectChainIdAndNetworkClientId(
networkClientId ?? this.#networkClientId,
);
this.#chainIdAgainstWhichToDetect = chainId;
this.#networkClientIdAgainstWhichToDetect = selectedNetworkClientId;

if (!isTokenDetectionSupportedForNetwork(chainIdAgainstWhichToDetect)) {
if (
!isTokenDetectionSupportedForNetwork(this.#chainIdAgainstWhichToDetect)
) {
return;
}

if (
!this.#isDetectionEnabledFromPreferences &&
chainIdAgainstWhichToDetect !== ChainId.mainnet
this.#chainIdAgainstWhichToDetect !== ChainId.mainnet
) {
return;
}
const { tokenList, slicesOfTokensToDetect } =
this.#getTokenListAndSlicesOfTokensToDetect();
for (const tokensSlice of slicesOfTokensToDetect) {
if (tokensSlice.length === 0) {
break;
}
await this.#addDetectedTokens({
tokenList,
tokensSlice,
});
}
}

#getTokenListAndSlicesOfTokensToDetect(): {
tokenList: Record<
string,
Pick<TokenListToken, 'address' | 'decimals' | 'symbol'>
>;
slicesOfTokensToDetect: string[][];
} {
const isTokenDetectionInactiveInMainnet =
!this.#isDetectionEnabledFromPreferences &&
chainIdAgainstWhichToDetect === ChainId.mainnet;
this.#chainIdAgainstWhichToDetect === ChainId.mainnet;
const { tokensChainsCache } = this.messagingSystem.call(
'TokenListController:getState',
);
const tokenList = isTokenDetectionInactiveInMainnet
? STATIC_MAINNET_TOKEN_LIST
: tokensChainsCache[chainIdAgainstWhichToDetect]?.data ?? {};
: tokensChainsCache[this.#chainIdAgainstWhichToDetect]?.data ?? {};

const { allTokens, allDetectedTokens, allIgnoredTokens } =
this.messagingSystem.call('TokensController:getState');
Expand All @@ -519,9 +550,12 @@ export class TokenDetectionController extends StaticIntervalPollingController<
allIgnoredTokens,
].map((tokens) =>
(
tokens[chainIdAgainstWhichToDetect]?.[addressAgainstWhichToDetect] ?? []
tokens[this.#chainIdAgainstWhichToDetect]?.[
this.#addressAgainstWhichToDetect
] ?? []
).map((value) => (typeof value === 'string' ? value : value.address)),
);

const tokensToDetect: string[] = [];
for (const tokenAddress of Object.keys(tokenList)) {
if (
Expand All @@ -539,60 +573,72 @@ export class TokenDetectionController extends StaticIntervalPollingController<
tokensToDetect.push(tokenAddress);
}
}

const slicesOfTokensToDetect = [];
slicesOfTokensToDetect[0] = tokensToDetect.slice(0, 1000);
slicesOfTokensToDetect[1] = tokensToDetect.slice(
1000,
tokensToDetect.length - 1,
);
for (const tokensSlice of slicesOfTokensToDetect) {
if (tokensSlice.length === 0) {
break;

return { tokenList, slicesOfTokensToDetect };
}

async #addDetectedTokens({
tokenList,
tokensSlice,
}: {
tokenList: Record<
string,
Partial<TokenListToken> & Pick<Token, 'address' | 'symbol' | 'decimals'>
>;
tokensSlice: string[];
}): Promise<void> {
await safelyExecute(async () => {
const balances = await this.#getBalancesInSingleCall(
this.#addressAgainstWhichToDetect,
tokensSlice,
this.#networkClientIdAgainstWhichToDetect,
);

const tokensWithBalance: Token[] = [];
const eventTokensDetails: string[] = [];
for (const nonZeroTokenAddress of Object.keys(balances)) {
const { decimals, symbol, aggregators, iconUrl, name } =
tokenList[nonZeroTokenAddress];
eventTokensDetails.push(`${symbol} - ${nonZeroTokenAddress}`);
tokensWithBalance.push({
address: nonZeroTokenAddress,
decimals,
symbol,
aggregators,
image: iconUrl,
isERC721: false,
name,
});
}

await safelyExecute(async () => {
const balances = await this.#getBalancesInSingleCall(
addressAgainstWhichToDetect,
tokensSlice,
networkClientIdAgainstWhichToDetect,
if (tokensWithBalance.length) {
this.#trackMetaMetricsEvent({
event: 'Token Detected',
category: 'Wallet',
properties: {
tokens: eventTokensDetails,
token_standard: 'ERC20',
asset_type: 'TOKEN',
},
});

await this.messagingSystem.call(
'TokensController:addDetectedTokens',
tokensWithBalance,
{
selectedAddress: this.#addressAgainstWhichToDetect,
chainId: this.#chainIdAgainstWhichToDetect,
},
);
const tokensWithBalance: Token[] = [];
const eventTokensDetails: string[] = [];
for (const nonZeroTokenAddress of Object.keys(balances)) {
const { decimals, symbol, aggregators, iconUrl, name } =
tokenList[nonZeroTokenAddress];
eventTokensDetails.push(`${symbol} - ${nonZeroTokenAddress}`);
tokensWithBalance.push({
address: nonZeroTokenAddress,
decimals,
symbol,
aggregators,
image: iconUrl,
isERC721: false,
name,
});
}
if (tokensWithBalance.length) {
this.#trackMetaMetricsEvent({
event: 'Token Detected',
category: 'Wallet',
properties: {
tokens: eventTokensDetails,
token_standard: 'ERC20',
asset_type: 'TOKEN',
},
});
await this.messagingSystem.call(
'TokensController:addDetectedTokens',
tokensWithBalance,
{
selectedAddress: addressAgainstWhichToDetect,
chainId: chainIdAgainstWhichToDetect,
},
);
}
});
}
}
});
}
}

Expand Down

0 comments on commit c5709b8

Please sign in to comment.