More invalid typings fixed.

This commit is contained in:
aitrean 2018-02-13 20:26:40 -05:00
parent 3efaf68ef8
commit e5d3f932fd
10 changed files with 41 additions and 976 deletions

View File

@ -31,7 +31,9 @@ export interface Option {
export interface ApiResponseObj {
id: string;
options: Option[];
rate: number;
rate: string;
limit?: number;
min?: number;
}
export interface ApiResponse {

View File

@ -1,378 +0,0 @@
import { ethPlorer, ETHTokenExplorer, SecureWalletName, InsecureWalletName } from './data';
import { EtherscanNode, InfuraNode, RPCNode, Web3Node } from 'libs/nodes';
import { networkIdToName } from 'libs/values';
import {
ETH_DEFAULT,
ETH_TREZOR,
ETH_LEDGER,
ETC_LEDGER,
ETC_TREZOR,
ETH_TESTNET,
EXP_DEFAULT,
UBQ_DEFAULT,
DPath
} from 'config/dpaths';
export interface BlockExplorerConfig {
origin: string;
txUrl(txHash: string): string;
addressUrl(address: string): string;
}
export interface Token {
address: string;
symbol: string;
decimal: number;
error?: string | null;
}
export interface NetworkContract {
name: NetworkKeys;
address?: string;
abi: string;
}
export interface DPathFormats {
trezor: DPath;
ledgerNanoS: DPath;
mnemonicPhrase: DPath;
}
export interface NetworkConfig {
// TODO really try not to allow strings due to custom networks
name: NetworkKeys;
unit: string;
color?: string;
blockExplorer?: BlockExplorerConfig;
tokenExplorer?: {
name: string;
address(address: string): string;
};
chainId: number;
tokens: Token[];
contracts: NetworkContract[] | null;
dPathFormats: DPathFormats;
isTestnet?: boolean;
}
export interface CustomNetworkConfig {
name: string;
unit: string;
chainId: number;
dPathFormats: DPathFormats | null;
}
export interface NodeConfig {
network: NetworkKeys;
lib: RPCNode | Web3Node;
service: string;
estimateGas?: boolean;
hidden?: boolean;
}
export interface CustomNodeConfig {
name: string;
url: string;
port: number;
network: string;
auth?: {
username: string;
password: string;
};
}
export interface Networks {
[key: string]: NetworkConfig;
}
// Must be a website that follows the ethplorer convention of /tx/[hash] and
// address/[address] to generate the correct functions.
function makeExplorer(origin: string): BlockExplorerConfig {
return {
origin,
txUrl: hash => `${origin}/tx/${hash}`,
addressUrl: address => `${origin}/address/${address}`
};
}
const ETH: NetworkConfig = {
name: 'ETH',
unit: 'ETH',
chainId: 1,
color: '#0e97c0',
blockExplorer: makeExplorer('https://etherscan.io'),
tokenExplorer: {
name: ethPlorer,
address: ETHTokenExplorer
},
tokens: require('./tokens/eth.json'),
contracts: require('./contracts/eth.json'),
dPathFormats: {
[SecureWalletName.TREZOR]: ETH_TREZOR,
[SecureWalletName.LEDGER_NANO_S]: ETH_LEDGER,
[InsecureWalletName.MNEMONIC_PHRASE]: ETH_DEFAULT
}
};
const Ropsten: NetworkConfig = {
name: 'Ropsten',
unit: 'ETH',
chainId: 3,
color: '#adc101',
blockExplorer: makeExplorer('https://ropsten.etherscan.io'),
tokens: require('./tokens/ropsten.json'),
contracts: require('./contracts/ropsten.json'),
isTestnet: true,
dPathFormats: {
[SecureWalletName.TREZOR]: ETH_TESTNET,
[SecureWalletName.LEDGER_NANO_S]: ETH_TESTNET,
[InsecureWalletName.MNEMONIC_PHRASE]: ETH_TESTNET
}
};
const Kovan: NetworkConfig = {
name: 'Kovan',
unit: 'ETH',
chainId: 42,
color: '#adc101',
blockExplorer: makeExplorer('https://kovan.etherscan.io'),
tokens: require('./tokens/ropsten.json'),
contracts: require('./contracts/ropsten.json'),
isTestnet: true,
dPathFormats: {
[SecureWalletName.TREZOR]: ETH_TESTNET,
[SecureWalletName.LEDGER_NANO_S]: ETH_TESTNET,
[InsecureWalletName.MNEMONIC_PHRASE]: ETH_TESTNET
}
};
const Rinkeby: NetworkConfig = {
name: 'Rinkeby',
unit: 'ETH',
chainId: 4,
color: '#adc101',
blockExplorer: makeExplorer('https://rinkeby.etherscan.io'),
tokens: require('./tokens/rinkeby.json'),
contracts: require('./contracts/rinkeby.json'),
isTestnet: true,
dPathFormats: {
[SecureWalletName.TREZOR]: ETH_TESTNET,
[SecureWalletName.LEDGER_NANO_S]: ETH_TESTNET,
[InsecureWalletName.MNEMONIC_PHRASE]: ETH_TESTNET
}
};
const ETC: NetworkConfig = {
name: 'ETC',
unit: 'ETC',
chainId: 61,
color: '#669073',
blockExplorer: makeExplorer('https://gastracker.io'),
tokens: require('./tokens/etc.json'),
contracts: require('./contracts/etc.json'),
dPathFormats: {
[SecureWalletName.TREZOR]: ETC_TREZOR,
[SecureWalletName.LEDGER_NANO_S]: ETC_LEDGER,
[InsecureWalletName.MNEMONIC_PHRASE]: ETC_TREZOR
}
};
const UBQ: NetworkConfig = {
name: 'UBQ',
unit: 'UBQ',
chainId: 8,
color: '#b37aff',
blockExplorer: makeExplorer('https://ubiqscan.io/en'),
tokens: require('./tokens/ubq.json'),
contracts: require('./contracts/ubq.json'),
dPathFormats: {
[SecureWalletName.TREZOR]: UBQ_DEFAULT,
[SecureWalletName.LEDGER_NANO_S]: UBQ_DEFAULT,
[InsecureWalletName.MNEMONIC_PHRASE]: UBQ_DEFAULT
}
};
const EXP: NetworkConfig = {
name: 'EXP',
unit: 'EXP',
chainId: 2,
color: '#673ab7',
// tslint:disable:no-http-string - Unavailable behind HTTPS right now
blockExplorer: makeExplorer('http://www.gander.tech'),
// tslint:enable:no-http-string
tokens: require('./tokens/exp.json'),
contracts: require('./contracts/exp.json'),
dPathFormats: {
[SecureWalletName.TREZOR]: EXP_DEFAULT,
[SecureWalletName.LEDGER_NANO_S]: EXP_DEFAULT,
[InsecureWalletName.MNEMONIC_PHRASE]: EXP_DEFAULT
}
};
export const NETWORKS: Networks = {
ETH,
Ropsten,
Kovan,
Rinkeby,
ETC,
UBQ,
EXP
};
export type NetworkKeys = keyof typeof NETWORKS;
export enum NodeName {
ETH_MEW = 'eth_mew',
ETH_MYCRYPTO = 'eth_mycrypto',
ETH_ETHSCAN = 'eth_ethscan',
ETH_INFURA = 'eth_infura',
ROP_MEW = 'rop_mew',
ROP_INFURA = 'rop_infura',
KOV_ETHSCAN = 'kov_ethscan',
RIN_ETHSCAN = 'rin_ethscan',
RIN_INFURA = 'rin_infura',
ETC_EPOOL = 'etc_epool',
UBQ = 'ubq',
EXP_TECH = 'exp_tech'
}
type NonWeb3NodeConfigs = { [key in NodeName]: NodeConfig };
interface Web3NodeConfig {
web3?: NodeConfig;
}
export interface NodeConfigs extends NonWeb3NodeConfigs, Web3NodeConfig {
[key: string]: any;
}
export const NODES: NodeConfigs = {
eth_mew: {
network: 'ETH',
lib: new RPCNode('https://api.myetherapi.com/eth'),
service: 'MyEtherWallet',
estimateGas: true
},
eth_mycrypto: {
network: 'ETH',
lib: new RPCNode('https://api.mycryptoapi.com/eth'),
service: 'MyCrypto',
estimateGas: true
},
eth_ethscan: {
network: 'ETH',
service: 'Etherscan.io',
lib: new EtherscanNode('https://api.etherscan.io/api'),
estimateGas: false
},
eth_infura: {
network: 'ETH',
service: 'infura.io',
lib: new InfuraNode('https://mainnet.infura.io/mew'),
estimateGas: false
},
rop_mew: {
network: 'Ropsten',
service: 'MyEtherWallet',
lib: new RPCNode('https://api.myetherapi.com/rop'),
estimateGas: false
},
rop_infura: {
network: 'Ropsten',
service: 'infura.io',
lib: new InfuraNode('https://ropsten.infura.io/mew'),
estimateGas: false
},
kov_ethscan: {
network: 'Kovan',
service: 'Etherscan.io',
lib: new EtherscanNode('https://kovan.etherscan.io/api'),
estimateGas: false
},
rin_ethscan: {
network: 'Rinkeby',
service: 'Etherscan.io',
lib: new EtherscanNode('https://rinkeby.etherscan.io/api'),
estimateGas: false
},
rin_infura: {
network: 'Rinkeby',
service: 'infura.io',
lib: new InfuraNode('https://rinkeby.infura.io/mew'),
estimateGas: false
},
etc_epool: {
network: 'ETC',
service: 'Epool.io',
lib: new RPCNode('https://mewapi.epool.io'),
estimateGas: false
},
ubq: {
network: 'UBQ',
service: 'ubiqscan.io',
lib: new RPCNode('https://pyrus2.ubiqscan.io'),
estimateGas: true
},
exp_tech: {
network: 'EXP',
service: 'Expanse.tech',
lib: new RPCNode('https://node.expanse.tech/'),
estimateGas: true
}
};
interface Web3NodeInfo {
networkId: string;
lib: Web3Node;
}
export async function setupWeb3Node(): Promise<Web3NodeInfo> {
const { web3 } = window as any;
if (!web3 || !web3.currentProvider || !web3.currentProvider.sendAsync) {
throw new Error(
'Web3 not found. Please check that MetaMask is installed, or that MyCrypto is open in Mist.'
);
}
const lib = new Web3Node();
const networkId = await lib.getNetVersion();
const accounts = await lib.getAccounts();
if (!accounts.length) {
throw new Error('No accounts found in MetaMask / Mist.');
}
if (networkId === 'loading') {
throw new Error('MetaMask / Mist is still loading. Please refresh the page and try again.');
}
return { networkId, lib };
}
export async function isWeb3NodeAvailable(): Promise<boolean> {
try {
await setupWeb3Node();
return true;
} catch (e) {
return false;
}
}
export const Web3Service = 'MetaMask / Mist';
export interface NodeConfigOverride extends NodeConfig {
network: any;
}
export async function initWeb3Node(): Promise<void> {
const { networkId, lib } = await setupWeb3Node();
const web3: NodeConfigOverride = {
network: networkIdToName(networkId),
service: Web3Service,
lib,
estimateGas: false,
hidden: true
};
NODES.web3 = web3;
}

View File

@ -29,7 +29,7 @@ export interface NormalizedBityRates {
}
export interface NormalizedShapeshiftRate extends NormalizedRate {
id: string;
id: number;
options: WhitelistedCoins[];
rate: number;
limit: number;

View File

@ -1,279 +0,0 @@
import { delay, SagaIterator } from 'redux-saga';
import {
call,
cancel,
fork,
put,
take,
takeLatest,
takeEvery,
select,
race
} from 'redux-saga/effects';
import {
NODES,
NETWORKS,
NodeConfig,
CustomNodeConfig,
CustomNetworkConfig,
Web3Service,
NodeConfigs
} from 'config';
import {
makeCustomNodeId,
getCustomNodeConfigFromId,
makeNodeConfigFromCustomConfig
} from 'utils/node';
import { makeCustomNetworkId, getNetworkConfigFromId } from 'utils/network';
import { Omit } from 'react-redux';
import {
getNode,
getNodeConfig,
getCustomNodeConfigs,
getCustomNetworkConfigs,
getOffline
} from 'selectors/config';
import { AppState } from 'reducers';
import { TypeKeys } from 'actions/config/constants';
import {
toggleOfflineConfig,
changeNode,
changeNodeIntent,
setLatestBlock,
removeCustomNetwork,
AddCustomNodeAction,
ChangeNodeIntentAction
} from 'actions/config';
import { showNotification } from 'actions/notifications';
import { translateRaw } from 'translations';
import { Web3Wallet } from 'libs/wallet';
import { TypeKeys as WalletTypeKeys } from 'actions/wallet/constants';
import { State as ConfigState, INITIAL_STATE as configInitialState } from 'reducers/config';
import { SetWalletAction } from 'actions/wallet';
export const getConfig = (state: AppState): ConfigState => state.config;
let hasCheckedOnline = false;
export function* pollOfflineStatus(): SagaIterator {
while (true) {
const node: NodeConfig = yield select(getNodeConfig);
const isOffline: boolean = yield select(getOffline);
// If our offline state disagrees with the browser, run a check
// Don't check if the user is in another tab or window
const shouldPing = !hasCheckedOnline || navigator.onLine === isOffline;
if (shouldPing && !document.hidden) {
const { pingSucceeded } = yield race({
pingSucceeded: call(node.lib.ping.bind(node.lib)),
timeout: call(delay, 5000)
});
if (pingSucceeded && isOffline) {
// If we were able to ping but redux says we're offline, mark online
yield put(
showNotification('success', 'Your connection to the network has been restored!', 3000)
);
yield put(toggleOfflineConfig());
} else if (!pingSucceeded && !isOffline) {
// If we were unable to ping but redux says we're online, mark offline
// If they had been online, show an error.
// If they hadn't been online, just inform them with a warning.
if (hasCheckedOnline) {
yield put(
showNotification(
'danger',
`Youve lost your connection to the network, check your internet
connection or try changing networks from the dropdown at the
top right of the page.`,
Infinity
)
);
} else {
yield put(
showNotification(
'info',
'You are currently offline. Some features will be unavailable.',
5000
)
);
}
yield put(toggleOfflineConfig());
} else {
// If neither case was true, try again in 5s
yield call(delay, 5000);
}
hasCheckedOnline = true;
} else {
yield call(delay, 1000);
}
}
}
// Fork our recurring API call, watch for the need to cancel.
export function* handlePollOfflineStatus(): SagaIterator {
const pollOfflineStatusTask = yield fork(pollOfflineStatus);
yield take('CONFIG_STOP_POLL_OFFLINE_STATE');
yield cancel(pollOfflineStatusTask);
}
// @HACK For now we reload the app when doing a language swap to force non-connected
// data to reload. Also the use of timeout to avoid using additional actions for now.
export function* reload(): SagaIterator {
setTimeout(() => location.reload(), 1150);
}
export function* handleNodeChangeIntent(action: ChangeNodeIntentAction): SagaIterator {
const currentNode: string = yield select(getNode);
const currentConfig: NodeConfig = yield select(getNodeConfig);
const customNets: CustomNetworkConfig[] = yield select(getCustomNetworkConfigs);
const currentNetwork =
getNetworkConfigFromId(currentConfig.network, customNets) || NETWORKS[currentConfig.network];
function* bailOut(message: string) {
yield put(showNotification('danger', message, 5000));
yield put(changeNode(currentNode, currentConfig, currentNetwork));
}
let actionConfig = NODES[action.payload];
if (!actionConfig) {
const customConfigs: CustomNodeConfig[] = yield select(getCustomNodeConfigs);
const config = getCustomNodeConfigFromId(action.payload, customConfigs);
if (config) {
actionConfig = makeNodeConfigFromCustomConfig(config);
}
}
if (!actionConfig) {
return yield* bailOut(`Attempted to switch to unknown node '${action.payload}'`);
}
// Grab latest block from the node, before switching, to confirm it's online
// Give it 5 seconds before we call it offline
let latestBlock;
let timeout;
try {
const { lb, to } = yield race({
lb: call(actionConfig.lib.getCurrentBlock.bind(actionConfig.lib)),
to: call(delay, 5000)
});
latestBlock = lb;
timeout = to;
} catch (err) {
// Whether it times out or errors, same message
timeout = true;
}
if (timeout) {
return yield* bailOut(translateRaw('ERROR_32'));
}
const actionNetwork = getNetworkConfigFromId(actionConfig.network, customNets);
if (!actionNetwork) {
return yield* bailOut(
`Unknown custom network for your node '${action.payload}', try re-adding it`
);
}
yield put(setLatestBlock(latestBlock));
yield put(changeNode(action.payload, actionConfig, actionNetwork));
// TODO - re-enable once DeterministicWallet state is fixed to flush properly.
// DeterministicWallet keeps path related state we need to flush before we can stop reloading
// const currentWallet: IWallet | null = yield select(getWalletInst);
// if there's no wallet, do not reload as there's no component state to resync
// if (currentWallet && currentConfig.network !== actionConfig.network) {
const isNewNetwork = currentConfig.network !== actionConfig.network;
const newIsWeb3 = actionConfig.service === Web3Service;
// don't reload when web3 is selected; node will automatically re-set and state is not an issue here
if (isNewNetwork && !newIsWeb3) {
yield call(reload);
}
}
export function* switchToNewNode(action: AddCustomNodeAction): SagaIterator {
const nodeId = makeCustomNodeId(action.payload);
yield put(changeNodeIntent(nodeId));
}
interface INetworksInUse {
[networkName: string]: boolean;
}
// If there are any orphaned custom networks, purge them
export function* cleanCustomNetworks(): SagaIterator {
const customNodes: CustomNodeConfig[] = yield select(getCustomNodeConfigs);
const customNetworks: CustomNetworkConfig[] = yield select(getCustomNetworkConfigs);
const networksInUse = customNodes.reduce(
(prev, conf) => {
prev[conf.network] = true;
return prev;
},
{} as INetworksInUse
);
for (const net of customNetworks) {
if (!networksInUse[makeCustomNetworkId(net)]) {
yield put(removeCustomNetwork(net));
}
}
}
// unset web3 as the selected node if a non-web3 wallet has been selected
export function* unsetWeb3NodeOnWalletEvent(action: SetWalletAction): SagaIterator {
const node = yield select(getNode);
const nodeConfig = yield select(getNodeConfig);
const newWallet = action.payload;
const isWeb3Wallet = newWallet instanceof Web3Wallet;
if (node !== 'web3' || isWeb3Wallet) {
return;
}
// switch back to a node with the same network as MetaMask/Mist
yield put(changeNodeIntent(equivalentNodeOrDefault(nodeConfig)));
}
export function* unsetWeb3Node(): SagaIterator {
const node = yield select(getNode);
if (node !== 'web3') {
return;
}
const nodeConfig: NodeConfig = yield select(getNodeConfig);
const newNode = equivalentNodeOrDefault(nodeConfig);
yield put(changeNodeIntent(newNode));
}
export const equivalentNodeOrDefault = (nodeConfig: NodeConfig): keyof NodeConfigs => {
const node: keyof Omit<NodeConfigs, 'web3'> | '' = Object.keys(NODES)
.filter(key => key !== 'web3')
.reduce<keyof Omit<NodeConfigs, 'web3'> | ''>((found, key: keyof Omit<NodeConfigs, 'web3'>) => {
const config = NODES[key];
if (found.length) {
return found;
}
if (nodeConfig.network === config.network) {
return (found = key);
}
return found;
}, '');
// if no equivalent node was found, use the app default
return node ? node : configInitialState.nodeSelection;
};
export default function* configSaga(): SagaIterator {
yield takeLatest(TypeKeys.CONFIG_POLL_OFFLINE_STATUS, handlePollOfflineStatus);
yield takeEvery(TypeKeys.CONFIG_NODE_CHANGE_INTENT, handleNodeChangeIntent);
yield takeEvery(TypeKeys.CONFIG_LANGUAGE_CHANGE, reload);
yield takeEvery(TypeKeys.CONFIG_ADD_CUSTOM_NODE, switchToNewNode);
yield takeEvery(TypeKeys.CONFIG_REMOVE_CUSTOM_NODE, cleanCustomNetworks);
yield takeEvery(TypeKeys.CONFIG_NODE_WEB3_UNSET, unsetWeb3Node);
yield takeEvery(WalletTypeKeys.WALLET_SET, unsetWeb3NodeOnWalletEvent);
yield takeEvery(WalletTypeKeys.WALLET_RESET, unsetWeb3NodeOnWalletEvent);
}

View File

@ -1,128 +0,0 @@
import throttle from 'lodash/throttle';
import { routerMiddleware } from 'react-router-redux';
import { State as ConfigState, INITIAL_STATE as configInitialState } from 'reducers/config';
import {
State as CustomTokenState,
INITIAL_STATE as customTokensInitialState
} from 'reducers/customTokens';
import { State as SwapState, INITIAL_STATE as swapInitialState } from 'reducers/swap';
import { applyMiddleware, createStore, Store } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import { createLogger } from 'redux-logger';
import createSagaMiddleware from 'redux-saga';
import { loadStatePropertyOrEmptyObject, saveState } from 'utils/localStorage';
import RootReducer, { AppState } from './reducers';
import { getNodeConfigFromId } from 'utils/node';
import { getNetworkConfigFromId } from 'utils/network';
import { dedupeCustomTokens } from 'utils/tokens';
import sagas from './sagas';
const configureStore = () => {
const logger = createLogger({
collapsed: true
});
const sagaMiddleware = createSagaMiddleware();
let middleware;
let store: Store<AppState>;
if (process.env.NODE_ENV !== 'production') {
middleware = composeWithDevTools(
applyMiddleware(sagaMiddleware, logger, routerMiddleware(history as any))
);
} else {
middleware = applyMiddleware(sagaMiddleware, routerMiddleware(history as any));
}
const localSwapState = loadStatePropertyOrEmptyObject<SwapState>('swap');
const swapState =
localSwapState && localSwapState.step === 3
? {
...swapInitialState,
...localSwapState
}
: { ...swapInitialState };
const savedConfigState = loadStatePropertyOrEmptyObject<ConfigState>('config');
// If they have a saved node, make sure we assign that too. The node selected
// isn't serializable, so we have to assign it here.
if (savedConfigState && savedConfigState.nodeSelection) {
const savedNode = getNodeConfigFromId(
savedConfigState.nodeSelection,
savedConfigState.customNodes
);
// If we couldn't find it, revert to defaults
if (savedNode) {
savedConfigState.node = savedNode;
const network = getNetworkConfigFromId(savedNode.network, savedConfigState.customNetworks);
if (network) {
savedConfigState.network = network;
}
} else {
savedConfigState.nodeSelection = configInitialState.nodeSelection;
}
}
// Dedupe custom tokens initially
const savedCustomTokensState =
loadStatePropertyOrEmptyObject<CustomTokenState>('customTokens') || customTokensInitialState;
const initialNetwork =
(savedConfigState && savedConfigState.network) || configInitialState.network;
const customTokens = dedupeCustomTokens(initialNetwork.tokens, savedCustomTokensState);
const persistedInitialState = {
config: {
...configInitialState,
...savedConfigState
},
customTokens,
// ONLY LOAD SWAP STATE FROM LOCAL STORAGE IF STEP WAS 3
swap: swapState
};
// if 'web3' has persisted as node selection, reset to app default
// necessary because web3 is only initialized as a node upon MetaMask / Mist unlock
if (persistedInitialState.config.nodeSelection === 'web3') {
persistedInitialState.config.nodeSelection = configInitialState.nodeSelection;
}
store = createStore<AppState>(RootReducer, persistedInitialState as AppState, middleware);
// Add all of the sagas to the middleware
Object.values(sagas).forEach(saga => {
sagaMiddleware.run(saga);
});
store.subscribe(
throttle(() => {
const state: AppState = store.getState();
saveState({
config: {
nodeSelection: state.config.nodeSelection,
languageSelection: state.config.languageSelection,
customNodes: state.config.customNodes,
customNetworks: state.config.customNetworks
},
swap: {
...state.swap,
options: {
byId: {},
allIds: []
},
bityRates: {
byId: {},
allIds: []
},
shapeshiftRates: {
byId: {},
allIds: []
}
},
customTokens: state.customTokens
});
}, 50)
);
return store;
};
export const configuredStore = configureStore();

View File

@ -1,3 +1,18 @@
declare module 'hdkey' {
export function fromMasterSeed(seed: Buffer): any;
interface HDKeyProps {
versions: any[];
depth: number;
index: number;
_privateKey: any;
_publicKey: any;
chainCode: any;
_fingerprint: any;
parentFingerprint: number;
}
declare class HDCLass<HDKeyProps> {
constructor();
}
declare namespace HDKey {
}

View File

@ -1,122 +0,0 @@
import {
CustomNetworkConfig,
DPathFormats,
InsecureWalletName,
NetworkConfig,
NETWORKS,
SecureWalletName,
WalletName,
walletNames
} from 'config';
import { DPath, EXTRA_PATHS } from 'config/dpaths';
import sortedUniq from 'lodash/sortedUniq';
import difference from 'lodash/difference';
export function makeCustomNetworkId(config: CustomNetworkConfig): string {
return config.chainId ? `${config.chainId}` : `${config.name}:${config.unit}`;
}
export function makeNetworkConfigFromCustomConfig(config: CustomNetworkConfig): NetworkConfig {
// TODO - re-enable this block and classify customConfig after user-inputted dPaths are implemented
// -------------------------------------------------
// this still provides the type safety we want
// as we know config coming in is CustomNetworkConfig
// meaning name will be a string
// then we cast it as any to keep it as a network key
// interface Override extends NetworkConfig {
// name: any;
// }
// -------------------------------------------------
// TODO - allow for user-inputted dPaths so we don't need to use any below and can use supplied dPaths
// In the meantime, networks with an unknown chainId will have HD wallets disabled
const customConfig: any = {
...config,
color: '#000',
tokens: [],
contracts: []
};
return customConfig;
}
export function getNetworkConfigFromId(
id: keyof typeof NETWORKS,
configs: CustomNetworkConfig[]
): NetworkConfig | undefined {
if (NETWORKS[id]) {
return NETWORKS[id];
}
const customConfig = configs.find(conf => makeCustomNetworkId(conf) === id);
if (customConfig) {
return makeNetworkConfigFromCustomConfig(customConfig);
}
}
type PathType = keyof DPathFormats;
type DPathFormat =
| SecureWalletName.TREZOR
| SecureWalletName.LEDGER_NANO_S
| InsecureWalletName.MNEMONIC_PHRASE;
export function getPaths(pathType: PathType): DPath[] {
const networkPaths: DPath[] = [];
Object.values(NETWORKS).forEach(networkConfig => {
const path = networkConfig.dPathFormats ? networkConfig.dPathFormats[pathType] : [];
if (path) {
networkPaths.push(path as DPath);
}
});
const paths = networkPaths.concat(EXTRA_PATHS);
return sortedUniq(paths);
}
export function getSingleDPath(format: DPathFormat, network: NetworkConfig): DPath {
const dPathFormats = network.dPathFormats;
return dPathFormats[format];
}
export function isNetworkUnit(network: NetworkConfig, unit: string) {
const validNetworks = Object.values(NETWORKS).filter((n: NetworkConfig) => n.unit === unit);
return validNetworks.includes(network);
}
export function isWalletFormatSupportedOnNetwork(
format: WalletName,
network: NetworkConfig
): boolean {
const CHECK_FORMATS: DPathFormat[] = [
SecureWalletName.LEDGER_NANO_S,
SecureWalletName.TREZOR,
InsecureWalletName.MNEMONIC_PHRASE
];
const isHDFormat = (f: string): f is DPathFormat => CHECK_FORMATS.includes(f as DPathFormat);
// Ensure DPath's are found
if (isHDFormat(format)) {
const dPath = network.dPathFormats && network.dPathFormats[format];
return !!dPath;
}
// Ensure Web3 is only enabled on ETH or ETH Testnets (MetaMask does not support other networks)
if (format === SecureWalletName.WEB3) {
return isNetworkUnit(network, 'ETH');
}
// All other wallet formats are supported
return true;
}
export function allWalletFormatsSupportedOnNetwork(network: NetworkConfig): WalletName[] {
return walletNames.filter((walletName: WalletName) =>
isWalletFormatSupportedOnNetwork(walletName, network)
);
}
export function unSupportedWalletFormatsOnNetwork(network: NetworkConfig): WalletName[] {
const supportedFormats = allWalletFormatsSupportedOnNetwork(network);
return difference(walletNames, supportedFormats);
}

View File

@ -1,42 +0,0 @@
import { CustomNode } from 'libs/nodes';
import { NODES, NodeConfig, CustomNodeConfig, NodeConfigs } from 'config';
export function makeCustomNodeId(config: CustomNodeConfig): string {
return `${config.url}:${config.port}`;
}
export function getCustomNodeConfigFromId(
id: string,
configs: CustomNodeConfig[]
): CustomNodeConfig | undefined {
return configs.find(node => makeCustomNodeId(node) === id);
}
export function getNodeConfigFromId(
id: keyof NodeConfigs,
configs: CustomNodeConfig[]
): NodeConfig | undefined {
if (NODES[id]) {
return NODES[id];
}
const config = getCustomNodeConfigFromId(id, configs);
if (config) {
return makeNodeConfigFromCustomConfig(config);
}
}
export function makeNodeConfigFromCustomConfig(config: CustomNodeConfig): NodeConfig {
interface Override extends NodeConfig {
network: any;
}
const customConfig: Override = {
network: config.network,
lib: new CustomNode(config),
service: 'your custom node',
estimateGas: true
};
return customConfig;
}

View File

@ -1,12 +1,11 @@
import { BrowserWindow, Menu, shell } from 'electron';
import { URL } from 'url';
import MENU from './menu';
import updater from './updater';
import { APP_TITLE } from '../constants';
const isDevelopment = process.env.NODE_ENV !== 'production';
// Cached reference, preventing recreations
let window: BrowserWindow | null;
let window: any;
// Construct new BrowserWindow
export default function getWindow() {
@ -39,7 +38,7 @@ export default function getWindow() {
window = null;
});
window.webContents.on('new-window', (ev, urlStr) => {
window.webContents.on('new-window', (ev: any, urlStr: any) => {
// Kill all new window requests by default
ev.preventDefault();

View File

@ -23,26 +23,24 @@ describe('ensure whitelist', () => {
describe('swap reducer', () => {
const shapeshiftApiResponse = {
['1SSTANT']: {
id: '1STANT',
options: [
{
id: '1ST',
status: 'available',
image: 'https://shapeshift.io/images/coins/firstblood.png',
name: 'FirstBlood'
},
{
id: 'ANT',
status: 'available',
image: 'https://shapeshift.io/images/coins/aragon.png',
name: 'Aragon'
}
],
rate: '0.24707537',
limit: 5908.29166225,
min: 7.86382979
}
id: '1STANT',
options: [
{
id: '1ST',
status: 'available',
image: 'https://shapeshift.io/images/coins/firstblood.png',
name: 'FirstBlood'
},
{
id: 'ANT',
status: 'available',
image: 'https://shapeshift.io/images/coins/aragon.png',
name: 'Aragon'
}
],
rate: '0.24707537',
limit: 5908.29166225,
min: 7.86382979
};
const bityApiResponse = {