Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding a fallback for tokenList API service based on user preference #517

Merged
merged 7 commits into from
Jul 9, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/ComposableController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ describe('ComposableController', () => {
ipfsGateway: 'https://ipfs.io/ipfs/',
lostIdentities: {},
selectedAddress: '',
useStaticTokenList: false,
},
});
});
Expand Down Expand Up @@ -192,6 +193,7 @@ describe('ComposableController', () => {
properties: { isEIP1559Compatible: false },
provider: { type: 'mainnet', chainId: NetworksChainId.mainnet },
selectedAddress: '',
useStaticTokenList: false,
suggestedAssets: [],
tokens: [],
});
Expand Down
2 changes: 2 additions & 0 deletions src/assets/AssetsDetectionController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,9 @@ describe('AssetsDetectionController', () => {
const messenger = getTokenListMessenger();
tokenList = new TokenListController({
chainId: NetworksChainId.mainnet,
useStaticTokenList: false,
onNetworkStateChange: (listener) => network.subscribe(listener),
onPreferencesStateChange: (listener) => preferences.subscribe(listener),
messenger,
});
await tokenList.start();
Expand Down
166 changes: 166 additions & 0 deletions src/assets/TokenListController.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { stub } from 'sinon';
import nock from 'nock';
import contractmap from '@metamask/contract-metadata';
Copy link
Contributor

Choose a reason for hiding this comment

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

[Q] I agree this is the best thing we can do for a fallback, but is there a more long term fallback option that doesn't require us to maintain this repo anymore? Perhaps if the user could supply their own token list.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I ran that by @omnat and I am quoting her reply here
Yes that’s a valid point and in the long term that’s the plan to allow people to upload their own lists. But for now this fallback is better than no auto detection, even if we don’t keep updating this list

import { ControllerMessenger } from '../ControllerMessenger';
import {
NetworkController,
NetworksChainId,
} from '../network/NetworkController';
import { PreferencesController } from '../user/PreferencesController';
import {
TokenListController,
TokenListStateChange,
Expand All @@ -15,6 +17,13 @@ const name = 'TokenListController';
const TOKEN_END_POINT_API = 'https://token-api.airswap-prod.codefi.network';
const timestamp = Date.now();

const staticTokenList: any = {};
for (const tokenAddress in contractmap) {
const { erc20, logo, ...token } = contractmap[tokenAddress];
if (erc20) {
staticTokenList[tokenAddress] = { ...token, iconUrl: logo };
}
}
const sampleMainnetTokenList = [
{
address: '0xc011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f',
Expand Down Expand Up @@ -464,8 +473,10 @@ function getRestrictedMessenger() {

describe('TokenListController', () => {
let network: NetworkController;
let preferences: PreferencesController;
beforeEach(() => {
network = new NetworkController();
preferences = new PreferencesController();
});
afterEach(() => {
nock.cleanAll();
Expand All @@ -475,7 +486,9 @@ describe('TokenListController', () => {
const messenger = getRestrictedMessenger();
const controller = new TokenListController({
chainId: NetworksChainId.mainnet,
useStaticTokenList: false,
onNetworkStateChange: (listener) => network.subscribe(listener),
onPreferencesStateChange: (listener) => preferences.subscribe(listener),
messenger,
});

Expand All @@ -491,7 +504,9 @@ describe('TokenListController', () => {
const messenger = getRestrictedMessenger();
const controller = new TokenListController({
chainId: NetworksChainId.mainnet,
useStaticTokenList: false,
onNetworkStateChange: (listener) => network.subscribe(listener),
onPreferencesStateChange: (listener) => preferences.subscribe(listener),
messenger,
state: existingState,
});
Expand Down Expand Up @@ -534,7 +549,9 @@ describe('TokenListController', () => {
const messenger = getRestrictedMessenger();
const controller = new TokenListController({
chainId: NetworksChainId.mainnet,
useStaticTokenList: false,
onNetworkStateChange: (listener) => network.subscribe(listener),
onPreferencesStateChange: (listener) => preferences.subscribe(listener),
interval: 100,
messenger,
});
Expand All @@ -551,7 +568,9 @@ describe('TokenListController', () => {
const messenger = getRestrictedMessenger();
const controller = new TokenListController({
chainId: NetworksChainId.mainnet,
useStaticTokenList: false,
onNetworkStateChange: (listener) => network.subscribe(listener),
onPreferencesStateChange: (listener) => preferences.subscribe(listener),
interval: 100,
messenger,
});
Expand All @@ -573,7 +592,9 @@ describe('TokenListController', () => {
const messenger = getRestrictedMessenger();
const controller = new TokenListController({
chainId: NetworksChainId.mainnet,
useStaticTokenList: false,
onNetworkStateChange: (listener) => network.subscribe(listener),
onPreferencesStateChange: (listener) => preferences.subscribe(listener),
interval: 100,
messenger,
});
Expand All @@ -597,7 +618,9 @@ describe('TokenListController', () => {
const messenger = getRestrictedMessenger();
const controller = new TokenListController({
chainId: NetworksChainId.mainnet,
useStaticTokenList: false,
onNetworkStateChange: (listener) => network.subscribe(listener),
onPreferencesStateChange: (listener) => preferences.subscribe(listener),
interval: 100,
messenger,
});
Expand Down Expand Up @@ -626,7 +649,9 @@ describe('TokenListController', () => {
const messenger = getRestrictedMessenger();
const controller = new TokenListController({
chainId: NetworksChainId.mainnet,
useStaticTokenList: false,
onNetworkStateChange: (listener) => network.subscribe(listener),
onPreferencesStateChange: (listener) => preferences.subscribe(listener),
messenger,
});
await controller.start();
Expand Down Expand Up @@ -660,7 +685,9 @@ describe('TokenListController', () => {
const messenger = getRestrictedMessenger();
const controller = new TokenListController({
chainId: NetworksChainId.mainnet,
useStaticTokenList: false,
onNetworkStateChange: (listener) => network.subscribe(listener),
onPreferencesStateChange: (listener) => preferences.subscribe(listener),
messenger,
interval: 100,
});
Expand All @@ -681,7 +708,9 @@ describe('TokenListController', () => {
const messenger = getRestrictedMessenger();
const controller = new TokenListController({
chainId: NetworksChainId.mainnet,
useStaticTokenList: false,
onNetworkStateChange: (listener) => network.subscribe(listener),
onPreferencesStateChange: (listener) => preferences.subscribe(listener),
messenger,
state: existingState,
});
Expand All @@ -699,7 +728,9 @@ describe('TokenListController', () => {
const messenger = getRestrictedMessenger();
const controller = new TokenListController({
chainId: NetworksChainId.mainnet,
useStaticTokenList: false,
onNetworkStateChange: (listener) => network.subscribe(listener),
onPreferencesStateChange: (listener) => preferences.subscribe(listener),
messenger,
});
await controller.start();
Expand Down Expand Up @@ -740,7 +771,9 @@ describe('TokenListController', () => {
const messenger = getRestrictedMessenger();
const controller = new TokenListController({
chainId: NetworksChainId.mainnet,
useStaticTokenList: false,
onNetworkStateChange: (listener) => network.subscribe(listener),
onPreferencesStateChange: (listener) => preferences.subscribe(listener),
messenger,
});
await controller.start();
Expand Down Expand Up @@ -790,7 +823,9 @@ describe('TokenListController', () => {
const messenger = getRestrictedMessenger();
const controller = new TokenListController({
chainId: NetworksChainId.mainnet,
useStaticTokenList: false,
onNetworkStateChange: (listener) => network.subscribe(listener),
onPreferencesStateChange: (listener) => preferences.subscribe(listener),
messenger,
state: outdatedExistingState,
});
Expand All @@ -808,7 +843,9 @@ describe('TokenListController', () => {
const messenger = getRestrictedMessenger();
const controller = new TokenListController({
chainId: NetworksChainId.mainnet,
useStaticTokenList: false,
onNetworkStateChange: (listener) => network.subscribe(listener),
onPreferencesStateChange: (listener) => preferences.subscribe(listener),
messenger,
state: expiredCacheExistingState,
});
Expand Down Expand Up @@ -838,7 +875,9 @@ describe('TokenListController', () => {
const messenger = getRestrictedMessenger();
const controller = new TokenListController({
chainId: NetworksChainId.mainnet,
useStaticTokenList: false,
onNetworkStateChange: (listener) => network.subscribe(listener),
onPreferencesStateChange: (listener) => preferences.subscribe(listener),
messenger,
state: existingState,
interval: 100,
Expand Down Expand Up @@ -868,6 +907,129 @@ describe('TokenListController', () => {
controller.destroy();
});

it('should use static token list when useStaticTokenList flag is disabled', async () => {
NiranjanaBinoy marked this conversation as resolved.
Show resolved Hide resolved
const messenger = getRestrictedMessenger();
const controller = new TokenListController({
chainId: NetworksChainId.mainnet,
useStaticTokenList: true,
onNetworkStateChange: (listener) => network.subscribe(listener),
onPreferencesStateChange: (listener) => preferences.subscribe(listener),
messenger,
state: existingState,
interval: 100,
});
await controller.start();
expect(controller.state.tokenList).toStrictEqual(staticTokenList);
expect(controller.state.tokensChainsCache).toStrictEqual({});

controller.destroy();
});

it('should switch between static and dynamic list based on the prefernce change', async () => {
NiranjanaBinoy marked this conversation as resolved.
Show resolved Hide resolved
nock(TOKEN_END_POINT_API)
.get(`/tokens/${NetworksChainId.mainnet}`)
.reply(200, sampleMainnetTokenList)
.persist();
const messenger = getRestrictedMessenger();
const controller = new TokenListController({
chainId: NetworksChainId.mainnet,
useStaticTokenList: false,
onNetworkStateChange: (listener) => network.subscribe(listener),
onPreferencesStateChange: (listener) => preferences.subscribe(listener),
messenger,
interval: 100,
});
await controller.start();
expect(controller.state.tokenList).toStrictEqual(
sampleSingleChainState.tokenList,
);

preferences.update({
useStaticTokenList: true,
});
await new Promise<void>((resolve) => setTimeout(() => resolve(), 50));
expect(controller.state.tokenList).toStrictEqual(staticTokenList);
expect(controller.state.tokensChainsCache).toStrictEqual({});

preferences.update({
useStaticTokenList: false,
});
await new Promise<void>((resolve) => setTimeout(() => resolve(), 50));
expect(controller.state.tokenList).toStrictEqual(
sampleSingleChainState.tokenList,
);
expect(
controller.state.tokensChainsCache[NetworksChainId.mainnet].data,
).toStrictEqual(sampleMainnetTokenList);

controller.destroy();
});

it('should switch between static and dynamic list when the prefernce change after network change', async () => {
NiranjanaBinoy marked this conversation as resolved.
Show resolved Hide resolved
nock(TOKEN_END_POINT_API)
.get(`/tokens/${NetworksChainId.mainnet}`)
.reply(200, sampleMainnetTokenList)
.get(`/tokens/56`)
.reply(200, sampleBinanceTokenList)
.persist();
const messenger = getRestrictedMessenger();
const controller = new TokenListController({
chainId: NetworksChainId.mainnet,
useStaticTokenList: false,
onNetworkStateChange: (listener) => network.subscribe(listener),
onPreferencesStateChange: (listener) => preferences.subscribe(listener),
messenger,
interval: 100,
});
await controller.start();
expect(controller.state.tokenList).toStrictEqual(
sampleSingleChainState.tokenList,
);
expect(
controller.state.tokensChainsCache[NetworksChainId.mainnet].data,
).toStrictEqual(sampleMainnetTokenList);

network.update({
provider: {
type: 'rpc',
chainId: '56',
},
});
await new Promise<void>((resolve) => setTimeout(() => resolve(), 10));
expect(controller.state.tokenList).toStrictEqual(
sampleTwoChainState.tokenList,
);
expect(
controller.state.tokensChainsCache[NetworksChainId.mainnet].data,
).toStrictEqual(sampleMainnetTokenList);
expect(controller.state.tokensChainsCache['56'].data).toStrictEqual(
sampleBinanceTokenList,
);

preferences.update({
useStaticTokenList: true,
});
await new Promise<void>((resolve) => setTimeout(() => resolve(), 10));
expect(controller.state.tokenList).toStrictEqual(staticTokenList);
expect(controller.state.tokensChainsCache).toStrictEqual({});

preferences.update({
useStaticTokenList: false,
});
await new Promise<void>((resolve) => setTimeout(() => resolve(), 10));
expect(controller.state.tokenList).toStrictEqual(
sampleTwoChainState.tokenList,
);
expect(
controller.state.tokensChainsCache[NetworksChainId.mainnet],
).toBeUndefined();
expect(controller.state.tokensChainsCache['56'].data).toStrictEqual(
sampleBinanceTokenList,
);

controller.destroy();
});

it('should call syncTokens to update the token list in the backend and clears the cache for the next fetch', async () => {
const tokenListBeforeSync = [
{
Expand Down Expand Up @@ -910,7 +1072,9 @@ describe('TokenListController', () => {
const messenger = getRestrictedMessenger();
const controller = new TokenListController({
chainId: NetworksChainId.mainnet,
useStaticTokenList: false,
onNetworkStateChange: (listener) => network.subscribe(listener),
onPreferencesStateChange: (listener) => preferences.subscribe(listener),
messenger,
interval: 200,
});
Expand Down Expand Up @@ -959,7 +1123,9 @@ describe('TokenListController', () => {
const messenger = getRestrictedMessenger();
const controller = new TokenListController({
chainId: NetworksChainId.mainnet,
useStaticTokenList: false,
onNetworkStateChange: (listener) => network.subscribe(listener),
onPreferencesStateChange: (listener) => preferences.subscribe(listener),
messenger,
});
const tokenMeta = await controller.fetchTokenMetadata(
Expand Down
Loading