diff --git a/common/actions/config/actionCreators.ts b/common/actions/config/actionCreators.ts index 1fd60c9b..2f067e70 100644 --- a/common/actions/config/actionCreators.ts +++ b/common/actions/config/actionCreators.ts @@ -32,7 +32,7 @@ export function changeNode( ): interfaces.ChangeNodeAction { return { type: TypeKeys.CONFIG_NODE_CHANGE, - payload: { nodeSelection, node, network } + payload: { nodeSelection, nodeName, networkName } }; } diff --git a/common/actions/config/actionTypes.ts b/common/actions/config/actionTypes.ts index 5552661e..56d2290f 100644 --- a/common/actions/config/actionTypes.ts +++ b/common/actions/config/actionTypes.ts @@ -1,5 +1,6 @@ import { TypeKeys } from './constants'; -import { NodeConfig, CustomNodeConfig, NetworkConfig, CustomNetworkConfig } from 'config'; +import { CustomNodeConfig } from 'config'; +import { CustomNetworkConfig } from 'reducers/config/networks/typings'; /*** Toggle Offline ***/ export interface ToggleOfflineAction { @@ -21,9 +22,8 @@ export interface ChangeNodeAction { type: TypeKeys.CONFIG_NODE_CHANGE; // FIXME $keyof? payload: { - nodeSelection: string; - node: NodeConfig; - network: NetworkConfig; + nodeName: string; + networkName: string; }; } @@ -41,25 +41,25 @@ export interface ChangeNodeIntentAction { /*** Add Custom Node ***/ export interface AddCustomNodeAction { type: TypeKeys.CONFIG_ADD_CUSTOM_NODE; - payload: CustomNodeConfig; + payload: { id: string; config: CustomNodeConfig }; } /*** Remove Custom Node ***/ export interface RemoveCustomNodeAction { type: TypeKeys.CONFIG_REMOVE_CUSTOM_NODE; - payload: CustomNodeConfig; + payload: { id: string }; } /*** Add Custom Network ***/ export interface AddCustomNetworkAction { type: TypeKeys.CONFIG_ADD_CUSTOM_NETWORK; - payload: CustomNetworkConfig; + payload: { id: string; config: CustomNetworkConfig }; } /*** Remove Custom Network ***/ export interface RemoveCustomNetworkAction { type: TypeKeys.CONFIG_REMOVE_CUSTOM_NETWORK; - payload: CustomNetworkConfig; + payload: { id: string }; } /*** Set Latest Block ***/ @@ -73,17 +73,18 @@ export interface Web3UnsetNodeAction { type: TypeKeys.CONFIG_NODE_WEB3_UNSET; } -/*** Union Type ***/ -export type ConfigAction = - | ChangeNodeAction +export type CustomNetworkAction = AddCustomNetworkAction | RemoveCustomNetworkAction; + +export type CustomNodeAction = AddCustomNodeAction | RemoveCustomNodeAction; + +export type NodeAction = ChangeNodeAction | ChangeNodeIntentAction | Web3UnsetNodeAction; + +export type MetaAction = | ChangeLanguageAction | ToggleOfflineAction | ToggleAutoGasLimitAction | PollOfflineStatus - | ChangeNodeIntentAction - | AddCustomNodeAction - | RemoveCustomNodeAction - | AddCustomNetworkAction - | RemoveCustomNetworkAction - | SetLatestBlockAction - | Web3UnsetNodeAction; + | SetLatestBlockAction; + +/*** Union Type ***/ +export type ConfigAction = CustomNetworkAction | CustomNodeAction | NodeAction | MetaAction; diff --git a/common/config/networks.ts b/common/config/networks.ts deleted file mode 100644 index 98d74f70..00000000 --- a/common/config/networks.ts +++ /dev/null @@ -1,176 +0,0 @@ -import { EtherscanNode, InfuraNode, RPCNode, Web3Node } from 'libs/nodes'; -import { networkIdToName } from 'libs/values'; - -export interface CustomNodeConfig { - name: string; - url: string; - port: number; - network: string; - auth?: { - username: string; - password: string; - }; -} - -export interface NodeConfig { - network: NetworkKeys; - lib: RPCNode | Web3Node; - service: string; - estimateGas?: boolean; - hidden?: boolean; -} - -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; -} - -type NodeConfigs = NonWeb3NodeConfigs & Web3NodeConfig; - -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 { - 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 MyEtherWallet 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 { - 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 { - const { networkId, lib } = await setupWeb3Node(); - const web3: NodeConfigOverride = { - network: networkIdToName(networkId), - service: Web3Service, - lib, - estimateGas: false, - hidden: true - }; - - NODES.web3 = web3; -} diff --git a/common/reducers/config.ts b/common/reducers/config.ts index bf73c880..0f59d06b 100644 --- a/common/reducers/config.ts +++ b/common/reducers/config.ts @@ -1,36 +1,13 @@ -import { - ChangeLanguageAction, - ChangeNodeAction, - AddCustomNodeAction, - RemoveCustomNodeAction, - AddCustomNetworkAction, - RemoveCustomNetworkAction, - SetLatestBlockAction, - ConfigAction -} from 'actions/config'; +import { ChangeLanguageAction, SetLatestBlockAction, ConfigAction } from 'actions/config'; import { TypeKeys } from 'actions/config/constants'; -import { - NODES, - NETWORKS, - NodeConfig, - CustomNodeConfig, - NetworkConfig, - CustomNetworkConfig -} from 'config'; -import { makeCustomNodeId } from 'utils/node'; -import { makeCustomNetworkId } from 'utils/network'; export interface State { // FIXME languageSelection: string; nodeSelection: string; - node: NodeConfig; - network: NetworkConfig; isChangingNode: boolean; offline: boolean; autoGasLimit: boolean; - customNodes: CustomNodeConfig[]; - customNetworks: CustomNetworkConfig[]; latestBlock: string; } @@ -55,23 +32,6 @@ function changeLanguage(state: State, action: ChangeLanguageAction): State { }; } -function changeNode(state: State, action: ChangeNodeAction): State { - return { - ...state, - nodeSelection: action.payload.nodeSelection, - node: action.payload.node, - network: action.payload.network, - isChangingNode: false - }; -} - -function changeNodeIntent(state: State): State { - return { - ...state, - isChangingNode: true - }; -} - function toggleOffline(state: State): State { return { ...state, @@ -86,44 +46,6 @@ function toggleAutoGasLimitEstimation(state: State): State { }; } -function addCustomNode(state: State, action: AddCustomNodeAction): State { - const newId = makeCustomNodeId(action.payload); - return { - ...state, - customNodes: [ - ...state.customNodes.filter(node => makeCustomNodeId(node) !== newId), - action.payload - ] - }; -} - -function removeCustomNode(state: State, action: RemoveCustomNodeAction): State { - const id = makeCustomNodeId(action.payload); - return { - ...state, - customNodes: state.customNodes.filter(cn => cn !== action.payload), - nodeSelection: id === state.nodeSelection ? defaultNode : state.nodeSelection - }; -} - -function addCustomNetwork(state: State, action: AddCustomNetworkAction): State { - const newId = makeCustomNetworkId(action.payload); - return { - ...state, - customNetworks: [ - ...state.customNetworks.filter(node => makeCustomNetworkId(node) !== newId), - action.payload - ] - }; -} - -function removeCustomNetwork(state: State, action: RemoveCustomNetworkAction): State { - return { - ...state, - customNetworks: state.customNetworks.filter(cn => cn !== action.payload) - }; -} - function setLatestBlock(state: State, action: SetLatestBlockAction): State { return { ...state, @@ -135,22 +57,12 @@ export function config(state: State = INITIAL_STATE, action: ConfigAction): Stat switch (action.type) { case TypeKeys.CONFIG_LANGUAGE_CHANGE: return changeLanguage(state, action); - case TypeKeys.CONFIG_NODE_CHANGE: - return changeNode(state, action); - case TypeKeys.CONFIG_NODE_CHANGE_INTENT: - return changeNodeIntent(state); + case TypeKeys.CONFIG_TOGGLE_OFFLINE: return toggleOffline(state); case TypeKeys.CONFIG_TOGGLE_AUTO_GAS_LIMIT: return toggleAutoGasLimitEstimation(state); - case TypeKeys.CONFIG_ADD_CUSTOM_NODE: - return addCustomNode(state, action); - case TypeKeys.CONFIG_REMOVE_CUSTOM_NODE: - return removeCustomNode(state, action); - case TypeKeys.CONFIG_ADD_CUSTOM_NETWORK: - return addCustomNetwork(state, action); - case TypeKeys.CONFIG_REMOVE_CUSTOM_NETWORK: - return removeCustomNetwork(state, action); + case TypeKeys.CONFIG_SET_LATEST_BLOCK: return setLatestBlock(state, action); default: diff --git a/common/reducers/config/networks/customNetworks.ts b/common/reducers/config/networks/customNetworks.ts index e69de29b..557c64d6 100644 --- a/common/reducers/config/networks/customNetworks.ts +++ b/common/reducers/config/networks/customNetworks.ts @@ -0,0 +1,33 @@ +import { + AddCustomNetworkAction, + RemoveCustomNetworkAction, + CustomNetworkAction, + TypeKeys +} from 'actions/config'; +import { CustomNetworkConfig } from 'reducers/config/networks/typings'; + +export interface State { + [customNetworkId: string]: CustomNetworkConfig; +} + +const addCustomNetwork = (state: State, { payload }: AddCustomNetworkAction): State => ({ + ...state, + [payload.id]: payload.config +}); + +function removeCustomNetwork(state: State, { payload }: RemoveCustomNetworkAction): State { + const stateCopy = { ...state }; + Reflect.deleteProperty(stateCopy, payload.id); + return stateCopy; +} + +export const customNetworks = (state: State = {}, action: CustomNetworkAction) => { + switch (action.type) { + case TypeKeys.CONFIG_ADD_CUSTOM_NETWORK: + return addCustomNetwork(state, action); + case TypeKeys.CONFIG_REMOVE_CUSTOM_NETWORK: + return removeCustomNetwork(state, action); + default: + return state; + } +}; diff --git a/common/reducers/config/networks/defaultNetworks.ts b/common/reducers/config/networks/defaultNetworks.ts index 03f30893..b88ff8bf 100644 --- a/common/reducers/config/networks/defaultNetworks.ts +++ b/common/reducers/config/networks/defaultNetworks.ts @@ -12,13 +12,15 @@ import { import { NetworkConfig, BlockExplorerConfig, - DefaultNetworkKeys + DefaultNetworkNames } from 'reducers/config/networks/typings'; +import { ConfigAction } from 'actions/config'; -export type State = { [key in DefaultNetworkKeys]: NetworkConfig }; +export type State = { [key in DefaultNetworkNames]: NetworkConfig }; // Must be a website that follows the ethplorer convention of /tx/[hash] and // address/[address] to generate the correct functions. +// TODO: put this in utils / libs function makeExplorer(origin: string): BlockExplorerConfig { return { origin, @@ -27,7 +29,7 @@ function makeExplorer(origin: string): BlockExplorerConfig { }; } -const INITIAL_STATE = { +const INITIAL_STATE: State = { ETH: { name: 'ETH', unit: 'ETH', @@ -134,3 +136,10 @@ const INITIAL_STATE = { } } }; + +export const defaultNetworks = (state: State = INITIAL_STATE, action: ConfigAction) => { + switch (action.type) { + default: + return state; + } +}; diff --git a/common/reducers/config/networks/index.ts b/common/reducers/config/networks/index.ts index e69de29b..0338e3e1 100644 --- a/common/reducers/config/networks/index.ts +++ b/common/reducers/config/networks/index.ts @@ -0,0 +1,16 @@ +import { customNetworks, State as CustomNetworksState } from './customNetworks'; +import { defaultNetworks, State as DefaultNetworksState } from './defaultNetworks'; +import { selectedNetwork, State as SelectedNetworkState } from './selectedNetwork'; +import { combineReducers } from 'redux'; + +export interface State { + customNetworks: CustomNetworksState; + defaultNetworks: DefaultNetworksState; + selectedNetwork: SelectedNetworkState; +} + +export const networks = combineReducers({ + customNetworks, + defaultNetworks, + selectedNetwork +}); diff --git a/common/reducers/config/networks/selectedNetwork.ts b/common/reducers/config/networks/selectedNetwork.ts index e69de29b..8dfc9888 100644 --- a/common/reducers/config/networks/selectedNetwork.ts +++ b/common/reducers/config/networks/selectedNetwork.ts @@ -0,0 +1,22 @@ +import { NodeAction, TypeKeys, ChangeNodeAction } from 'actions/config'; +import { DefaultNetworkNames } from 'reducers/config/networks/typings'; +import { INITIAL_STATE as INITIAL_NODE_STATE } from '../nodes/selectedNode'; // could probably consolidate this in the index file of 'nodes' to make it easier to import +import { INITIAL_STATE as INITIAL_DEFAULT_NODE_STATE } from '../nodes/defaultNodes'; +import { NonWeb3NodeConfigs } from 'reducers/config/nodes/typings'; + +const initalNode = + INITIAL_DEFAULT_NODE_STATE[INITIAL_NODE_STATE.nodeName as keyof NonWeb3NodeConfigs]; + +export type State = string | DefaultNetworkNames; +const INITIAL_STATE: State = initalNode.networkName; + +const handleNodeChange = (_: State, { payload }: ChangeNodeAction) => payload.networkName; + +export const selectedNetwork = (state: State = INITIAL_STATE, action: NodeAction) => { + switch (action.type) { + case TypeKeys.CONFIG_NODE_CHANGE: + return handleNodeChange(state, action); + default: + break; + } +}; diff --git a/common/reducers/config/networks/typings.ts b/common/reducers/config/networks/typings.ts index 7f31d1b0..d6449ade 100644 --- a/common/reducers/config/networks/typings.ts +++ b/common/reducers/config/networks/typings.ts @@ -1,6 +1,6 @@ import { DPath } from 'config/dpaths'; -export type DefaultNetworkKeys = 'ETH' | 'Ropsten' | 'Kovan' | 'Rinkeby' | 'ETC' | 'UBQ' | 'EXP'; +export type DefaultNetworkNames = 'ETH' | 'Ropsten' | 'Kovan' | 'Rinkeby' | 'ETC' | 'UBQ' | 'EXP'; export interface BlockExplorerConfig { origin: string; @@ -16,7 +16,7 @@ export interface Token { } export interface NetworkContract { - name: DefaultNetworkKeys; + name: DefaultNetworkNames; address?: string; abi: string; } @@ -29,7 +29,7 @@ export interface DPathFormats { export interface NetworkConfig { // TODO really try not to allow strings due to custom networks - name: DefaultNetworkKeys; + name: DefaultNetworkNames; unit: string; color?: string; blockExplorer?: BlockExplorerConfig; diff --git a/common/reducers/config/nodes/customNodes.ts b/common/reducers/config/nodes/customNodes.ts new file mode 100644 index 00000000..58a04638 --- /dev/null +++ b/common/reducers/config/nodes/customNodes.ts @@ -0,0 +1,33 @@ +import { CustomNodeConfig } from 'reducers/config/nodes/typings'; +import { + TypeKeys, + CustomNodeAction, + AddCustomNodeAction, + RemoveCustomNodeAction +} from 'actions/config'; + +export interface State { + [customNodeId: string]: CustomNodeConfig; +} + +const addCustomNode = (state: State, { payload }: AddCustomNodeAction): State => ({ + ...state, + [payload.id]: payload.config +}); + +function removeCustomNode(state: State, { payload }: RemoveCustomNodeAction): State { + const stateCopy = { ...state }; + Reflect.deleteProperty(stateCopy, payload.id); + return stateCopy; +} + +export const customNodes = (state: State = {}, action: CustomNodeAction): State => { + switch (action.type) { + case TypeKeys.CONFIG_ADD_CUSTOM_NODE: + return addCustomNode(state, action); + case TypeKeys.CONFIG_REMOVE_CUSTOM_NODE: + return removeCustomNode(state, action); + default: + return state; + } +}; diff --git a/common/reducers/config/nodes/defaultNodes.ts b/common/reducers/config/nodes/defaultNodes.ts new file mode 100644 index 00000000..b1be9ae3 --- /dev/null +++ b/common/reducers/config/nodes/defaultNodes.ts @@ -0,0 +1,87 @@ +import { NonWeb3NodeConfigs, Web3NodeConfig } from 'reducers/config/nodes/typings'; +import { EtherscanNode, InfuraNode, RPCNode } from 'libs/nodes'; +import { ConfigAction } from 'actions/config'; + +export type State = NonWeb3NodeConfigs & Web3NodeConfig; + +export const INITIAL_STATE: State = { + eth_mew: { + networkName: 'ETH', + lib: new RPCNode('https://api.myetherapi.com/eth'), + service: 'MyEtherWallet', + estimateGas: true + }, + eth_mycrypto: { + networkName: 'ETH', + lib: new RPCNode('https://api.mycryptoapi.com/eth'), + service: 'MyCrypto', + estimateGas: true + }, + eth_ethscan: { + networkName: 'ETH', + service: 'Etherscan.io', + lib: new EtherscanNode('https://api.etherscan.io/api'), + estimateGas: false + }, + eth_infura: { + networkName: 'ETH', + service: 'infura.io', + lib: new InfuraNode('https://mainnet.infura.io/mew'), + estimateGas: false + }, + rop_mew: { + networkName: 'Ropsten', + service: 'MyEtherWallet', + lib: new RPCNode('https://api.myetherapi.com/rop'), + estimateGas: false + }, + rop_infura: { + networkName: 'Ropsten', + service: 'infura.io', + lib: new InfuraNode('https://ropsten.infura.io/mew'), + estimateGas: false + }, + kov_ethscan: { + networkName: 'Kovan', + service: 'Etherscan.io', + lib: new EtherscanNode('https://kovan.etherscan.io/api'), + estimateGas: false + }, + rin_ethscan: { + networkName: 'Rinkeby', + service: 'Etherscan.io', + lib: new EtherscanNode('https://rinkeby.etherscan.io/api'), + estimateGas: false + }, + rin_infura: { + networkName: 'Rinkeby', + service: 'infura.io', + lib: new InfuraNode('https://rinkeby.infura.io/mew'), + estimateGas: false + }, + etc_epool: { + networkName: 'ETC', + service: 'Epool.io', + lib: new RPCNode('https://mewapi.epool.io'), + estimateGas: false + }, + ubq: { + networkName: 'UBQ', + service: 'ubiqscan.io', + lib: new RPCNode('https://pyrus2.ubiqscan.io'), + estimateGas: true + }, + exp_tech: { + networkName: 'EXP', + service: 'Expanse.tech', + lib: new RPCNode('https://node.expanse.tech/'), + estimateGas: true + } +}; + +export const defaultNodes = (state: State = INITIAL_STATE, action: ConfigAction) => { + switch (action.type) { + default: + return state; + } +}; diff --git a/common/reducers/config/nodes/index.ts b/common/reducers/config/nodes/index.ts new file mode 100644 index 00000000..bc9b24c7 --- /dev/null +++ b/common/reducers/config/nodes/index.ts @@ -0,0 +1,12 @@ +import { customNodes, State as CustomNodeState } from './customNodes'; +import { defaultNodes, State as DefaultNodeState } from './defaultNodes'; +import { selectedNode, State as SelectedNodeState } from './selectedNode'; +import { combineReducers } from 'redux'; + +export interface State { + customNodes: CustomNodeState; + defaultNodes: DefaultNodeState; + selectedNode: SelectedNodeState; +} + +export const nodes = combineReducers({ customNodes, defaultNodes, selectedNode }); diff --git a/common/reducers/config/nodes/selectedNode.ts b/common/reducers/config/nodes/selectedNode.ts new file mode 100644 index 00000000..12a747dd --- /dev/null +++ b/common/reducers/config/nodes/selectedNode.ts @@ -0,0 +1,39 @@ +import { ChangeNodeAction, ChangeNodeIntentAction, NodeAction, TypeKeys } from 'actions/config'; + +interface NodeLoaded { + pending: false; + nodeName: string; +} + +interface NodeChangePending { + pending: true; + nodeName: null; +} + +export type State = NodeLoaded | NodeChangePending; + +export const INITIAL_STATE: NodeLoaded = { + nodeName: 'eth_mew', + pending: false +}; + +const changeNode = (_: State, { payload }: ChangeNodeAction): State => ({ + nodeName: payload.networkName, + pending: false +}); + +const changeNodeIntent = (_: State, _2: ChangeNodeIntentAction): State => ({ + nodeName: null, + pending: true +}); + +export const selectedNode = (state: State = INITIAL_STATE, action: NodeAction) => { + switch (action.type) { + case TypeKeys.CONFIG_NODE_CHANGE: + return changeNode(state, action); + case TypeKeys.CONFIG_NODE_CHANGE_INTENT: + return changeNodeIntent(state, action); + default: + return state; + } +}; diff --git a/common/reducers/config/nodes/typings.ts b/common/reducers/config/nodes/typings.ts new file mode 100644 index 00000000..470e0b61 --- /dev/null +++ b/common/reducers/config/nodes/typings.ts @@ -0,0 +1,103 @@ +import { RPCNode, Web3Node } from 'libs/nodes'; +import { networkIdToName } from 'libs/values'; +import { DefaultNetworkNames } from 'reducers/config/networks/typings'; + +export interface CustomNodeConfig { + name: string; + url: string; + port: number; + network: string; + auth?: { + username: string; + password: string; + }; +} + +export interface DefaultNodeConfig { + networkName: DefaultNetworkNames; + lib: RPCNode | Web3Node; + service: string; + estimateGas?: boolean; + hidden?: boolean; +} + +export enum DefaultNodeName { + 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' +} + +export type NonWeb3NodeConfigs = { [key in DefaultNodeName]: DefaultNodeConfig }; + +export interface Web3NodeConfig { + web3?: DefaultNodeConfig; +} + +/** + * TODO: Put this in a saga that runs on app mount + */ +interface Web3NodeInfo { + networkId: string; + lib: Web3Node; +} + +export async function setupWeb3Node(): Promise { + 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 MyEtherWallet 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 { + try { + await setupWeb3Node(); + return true; + } catch (e) { + return false; + } +} + +export const Web3Service = 'MetaMask / Mist'; + +export interface NodeConfigOverride extends DefaultNodeConfig { + networkName: any; +} + +export async function initWeb3Node(): Promise { + const { networkId, lib } = await setupWeb3Node(); + const web3: NodeConfigOverride = { + network: networkIdToName(networkId), + service: Web3Service, + lib, + estimateGas: false, + hidden: true + }; + + NODES.web3 = web3; +}