Skip to content

Commit

Permalink
Merge pull request #2255 from MyCryptoHQ/fixit/no-duplicate-tokens
Browse files Browse the repository at this point in the history
[BUGSQUASH] Prevent duplicate token additions
  • Loading branch information
ConnorBryan authored Nov 16, 2018
2 parents 6974eab + d21f606 commit 1c484d7
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 6 deletions.
6 changes: 3 additions & 3 deletions common/components/BalanceSidebar/TokenBalances/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ interface StateProps {
isOffline: AppState['config']['meta']['offline'];
}
interface ActionProps {
addCustomToken: customTokensActions.TAddCustomToken;
attemptAddCustomToken: customTokensActions.TAttemptAddCustomToken;
removeCustomToken: customTokensActions.TRemoveCustomToken;
scanWalletForTokens: walletActions.TScanWalletForTokens;
setWalletTokens: walletActions.TSetWalletTokens;
Expand Down Expand Up @@ -85,7 +85,7 @@ class TokenBalances extends React.Component<Props> {
hasSavedWalletTokens={hasSavedWalletTokens}
scanWalletForTokens={this.scanWalletForTokens}
setWalletTokens={this.props.setWalletTokens}
onAddCustomToken={this.props.addCustomToken}
onAddCustomToken={this.props.attemptAddCustomToken}
onRemoveCustomToken={this.props.removeCustomToken}
/>
);
Expand Down Expand Up @@ -121,7 +121,7 @@ function mapStateToProps(state: AppState): StateProps {
}

export default connect(mapStateToProps, {
addCustomToken: customTokensActions.addCustomToken,
attemptAddCustomToken: customTokensActions.attemptAddCustomToken,
removeCustomToken: customTokensActions.removeCustomToken,
scanWalletForTokens: walletActions.scanWalletForTokens,
setWalletTokens: walletActions.setWalletTokens,
Expand Down
8 changes: 8 additions & 0 deletions common/features/customTokens/actions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import { Token } from 'types/network';
import * as types from './types';

export type TAttemptAddCustomToken = typeof attemptAddCustomToken;
export function attemptAddCustomToken(payload: Token): types.AttemptAddCustomTokenAction {
return {
type: types.CustomTokensActions.ATTEMPT_ADD,
payload
};
}

export type TAddCustomToken = typeof addCustomToken;
export function addCustomToken(payload: Token): types.AddCustomTokenAction {
return {
Expand Down
6 changes: 6 additions & 0 deletions common/features/customTokens/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,16 @@ import { Token } from 'types/network';
export type CustomTokensState = Token[];

export enum CustomTokensActions {
ATTEMPT_ADD = 'CUSTOM_TOKENS_ATTEMPT_ADD',
ADD = 'CUSTOM_TOKENS_ADD',
REMOVE = 'CUSTOM_TOKENS_REMOVE'
}

export interface AttemptAddCustomTokenAction {
type: CustomTokensActions.ATTEMPT_ADD;
payload: Token;
}

export interface AddCustomTokenAction {
type: CustomTokensActions.ADD;
payload: Token;
Expand Down
26 changes: 24 additions & 2 deletions common/features/wallet/sagas.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { SagaIterator, delay, Task } from 'redux-saga';
import { apply, call, fork, put, select, takeEvery, take, cancel } from 'redux-saga/effects';

import configTokens from 'config/tokens';
import { translateRaw } from 'translations';
import { INode } from 'libs/nodes/INode';
import { Wei } from 'libs/units';
Expand All @@ -17,6 +18,7 @@ import {
WalletConfig
} from 'libs/wallet';
import { loadWalletConfig, saveWalletConfig } from 'utils/localStorage';
import { getAddressesAndSymbols } from 'utils/tokens';
import { AppState } from 'features/reducers';
import * as derivedSelectors from 'features/selectors';
import {
Expand All @@ -26,7 +28,11 @@ import {
configSelectors
} from 'features/config';
import { notificationsActions } from 'features/notifications';
import { customTokensTypes, customTokensSelectors } from 'features/customTokens';
import {
customTokensTypes,
customTokensActions,
customTokensSelectors
} from 'features/customTokens';
import * as types from './types';
import * as actions from './actions';
import * as selectors from './selectors';
Expand Down Expand Up @@ -288,6 +294,22 @@ export function* unlockMnemonicSaga(action: types.UnlockMnemonicAction): SagaIte
export function* handleCustomTokenAdd(
action: customTokensTypes.AddCustomTokenAction
): SagaIterator {
// Ensure the added token address and symbol doesn't exist in the static tokens.
const { id } = yield select(configSelectors.getNetworkConfig);
const { address, symbol } = action.payload;
const tokenList = (configTokens as any)[id];
const usedAddressesAndSymbols = getAddressesAndSymbols(tokenList);
if (usedAddressesAndSymbols.addresses[address]) {
yield put(notificationsActions.showNotification('danger', translateRaw('CUSTOM_TOKEN_1')));
return;
}
if (usedAddressesAndSymbols.symbols[symbol]) {
yield put(notificationsActions.showNotification('danger', translateRaw('CUSTOM_TOKEN_2')));
return;
}
// Add the custom token.
yield put(customTokensActions.addCustomToken(action.payload));

// Add the custom token to our current wallet's config
const wallet: null | IWallet = yield select(selectors.getWalletInst);
if (!wallet) {
Expand Down Expand Up @@ -316,6 +338,6 @@ export function* walletSaga(): SagaIterator {
takeEvery(types.WalletActions.REFRESH_TOKEN_BALANCES, retryTokenBalances),
// Foreign actions
takeEvery(configMetaTypes.ConfigMetaActions.TOGGLE_OFFLINE, updateBalances),
takeEvery(customTokensTypes.CustomTokensActions.ADD, handleCustomTokenAdd)
takeEvery(customTokensTypes.CustomTokensActions.ATTEMPT_ADD, handleCustomTokenAdd)
];
}
4 changes: 3 additions & 1 deletion common/translations/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,8 @@
"SWAP_2": "Authorize with ShapeShift",
"SWAP_3": "Attempting to authorize with ShapeShift...",
"SWAP_4": "Successfully authorized with ShapeShift.",
"SWAP_5": "Lost ShapeShift access token. Please reauthorize."
"SWAP_5": "Lost ShapeShift access token. Please reauthorize.",
"CUSTOM_TOKEN_1": "A token with this address already exists.",
"CUSTOM_TOKEN_2": "A token with this symbol already exists."
}
}
19 changes: 19 additions & 0 deletions common/utils/tokens.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
import { Token } from 'types/network';

interface AddressesAndSymbols {
addresses: { [address: string]: true };
symbols: { [symbol: string]: true };
}

export function getAddressesAndSymbols(tokenList: any): AddressesAndSymbols {
return tokenList.reduce(
(prev: AddressesAndSymbols, next: any) => {
prev.addresses[next.address] = true;
prev.symbols[next.symbol] = true;
return prev;
},
{
addresses: {},
symbols: {}
}
);
}

export function dedupeCustomTokens(networkTokens: Token[], customTokens: Token[]): Token[] {
if (!customTokens.length) {
return [];
Expand Down

0 comments on commit 1c484d7

Please sign in to comment.