Fetch tokens from IPFS hash on load / node change
This commit is contained in:
parent
7f21b2014f
commit
c62ce80f1d
|
@ -132,3 +132,21 @@ export function web3UnsetNode(): interfaces.Web3UnsetNodeAction {
|
|||
type: TypeKeys.CONFIG_NODE_WEB3_UNSET
|
||||
};
|
||||
}
|
||||
|
||||
export function updateTokensRequested(
|
||||
payload: interfaces.UpdateTokensRequested['payload']
|
||||
): interfaces.UpdateTokensRequested {
|
||||
return { type: TypeKeys.CONFIG_UPDATE_TOKENS_REQUESTED, payload };
|
||||
}
|
||||
|
||||
export function updateTokensSucceeded(
|
||||
payload: interfaces.UpdateTokensSucceeded['payload']
|
||||
): interfaces.UpdateTokensSucceeded {
|
||||
return { type: TypeKeys.CONFIG_UPDATE_TOKENS_SUCCEEDED, payload };
|
||||
}
|
||||
|
||||
export function updateTokensFailed(
|
||||
payload: interfaces.UpdateTokensFailed['payload']
|
||||
): interfaces.UpdateTokensFailed {
|
||||
return { type: TypeKeys.CONFIG_UDPATE_TOKENS_FAILED, payload };
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { TypeKeys } from './constants';
|
||||
import { CustomNodeConfig, StaticNodeConfig } from 'types/node';
|
||||
import { CustomNetworkConfig } from 'types/network';
|
||||
import { CustomNetworkConfig, Token, StaticNetworkIds } from 'types/network';
|
||||
|
||||
export interface SetOnlineAction {
|
||||
type: TypeKeys.CONFIG_SET_ONLINE;
|
||||
|
@ -93,6 +93,23 @@ export interface Web3setNodeAction {
|
|||
payload: { id: 'web3'; config: StaticNodeConfig };
|
||||
}
|
||||
|
||||
export interface UpdateTokensRequested {
|
||||
type: TypeKeys.CONFIG_UPDATE_TOKENS_REQUESTED;
|
||||
payload: { id: StaticNetworkIds };
|
||||
}
|
||||
|
||||
export interface UpdateTokensSucceeded {
|
||||
type: TypeKeys.CONFIG_UPDATE_TOKENS_SUCCEEDED;
|
||||
payload: { tokens: Token[]; id: StaticNetworkIds; hash: string | null };
|
||||
}
|
||||
|
||||
export interface UpdateTokensFailed {
|
||||
type: TypeKeys.CONFIG_UDPATE_TOKENS_FAILED;
|
||||
payload: { id: StaticNetworkIds };
|
||||
}
|
||||
|
||||
export type UpdateTokensAction = UpdateTokensRequested | UpdateTokensSucceeded | UpdateTokensFailed;
|
||||
|
||||
export type CustomNetworkAction = AddCustomNetworkAction | RemoveCustomNetworkAction;
|
||||
|
||||
export type CustomNodeAction = AddCustomNodeAction | RemoveCustomNodeAction;
|
||||
|
|
|
@ -20,5 +20,9 @@ export enum TypeKeys {
|
|||
CONFIG_REMOVE_CUSTOM_NODE = 'CONFIG_REMOVE_CUSTOM_NODE',
|
||||
|
||||
CONFIG_ADD_CUSTOM_NETWORK = 'CONFIG_ADD_CUSTOM_NETWORK',
|
||||
CONFIG_REMOVE_CUSTOM_NETWORK = 'CONFIG_REMOVE_CUSTOM_NETWORK'
|
||||
CONFIG_REMOVE_CUSTOM_NETWORK = 'CONFIG_REMOVE_CUSTOM_NETWORK',
|
||||
|
||||
CONFIG_UPDATE_TOKENS_REQUESTED = 'CONFIG_UPDATE_TOKENS_REQUESTED',
|
||||
CONFIG_UPDATE_TOKENS_SUCCEEDED = 'CONFIG_UPDATE_TOKENS_SUCCEEDED',
|
||||
CONFIG_UDPATE_TOKENS_FAILED = 'CONFIG_UPDATE_TOKENS_FAILED'
|
||||
}
|
||||
|
|
|
@ -5,6 +5,10 @@ import { GasPriceSetting } from 'types/network';
|
|||
import { makeExplorer } from 'utils/helpers';
|
||||
import NewTabLink from 'components/ui/NewTabLink';
|
||||
|
||||
// tslint:disable-next-line:no-http-string
|
||||
export const IPFS_GATEWAY = 'http://127.0.0.1:8080';
|
||||
export const IPFS_MULTISIG_CONTRACT_ADDRESS = '0x230ead3dbbd50248456a57afdd6570cdc8b4e439';
|
||||
|
||||
export const languages = require('./languages.json');
|
||||
export const discordURL = 'https://discord.gg/VSaTXEA';
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import Contract from 'libs/contracts';
|
||||
|
||||
interface ABIFunc<T, K = void> {
|
||||
export interface ABIFunc<T, K = void> {
|
||||
encodeInput(x: T): string;
|
||||
decodeInput(argStr: string): T;
|
||||
decodeOutput(argStr: string): K;
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
import { IPFS_GATEWAY, IPFS_MULTISIG_CONTRACT_ADDRESS } from 'config';
|
||||
import { Token, StaticNetworkConfig } from 'shared/types/network';
|
||||
import Contract from 'libs/contracts';
|
||||
import { ABIFunc } from './erc20';
|
||||
import { shepherdProvider } from './nodes';
|
||||
import bs58 from 'bs58';
|
||||
import { handleValues } from './units';
|
||||
const { processTokenJson } = require('../../shared/update-tokens-utils');
|
||||
|
||||
function makeIpfsGatewayUrl(ipfsHash: string) {
|
||||
return `${IPFS_GATEWAY}/ipfs/${ipfsHash}`;
|
||||
}
|
||||
|
||||
function getTokenList(ipfsHash: string): Promise<Token[]> {
|
||||
return fetch(makeIpfsGatewayUrl(ipfsHash))
|
||||
.then(r => r.json())
|
||||
.then(processTokenJson);
|
||||
}
|
||||
|
||||
type uint256 = any;
|
||||
type bytes32 = any;
|
||||
type uint8 = any;
|
||||
|
||||
interface GetEntryResponse {
|
||||
digest: bytes32;
|
||||
hashfunction: uint8;
|
||||
size: uint8;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a multihash structure into base58 encoded multihash string
|
||||
*
|
||||
* @param {Multihash} multihash
|
||||
* @returns {(string|null)} base58 encoded multihash string
|
||||
*/
|
||||
function getMultihashFromBytes32(multihash: GetEntryResponse) {
|
||||
const { digest, hashfunction, size } = multihash;
|
||||
if (size === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// prepend hashFunction and digest size
|
||||
const multihashBytes = Buffer.alloc(2 + digest.length);
|
||||
multihashBytes[0] = hashfunction;
|
||||
multihashBytes[1] = size;
|
||||
multihashBytes.set(digest, 2);
|
||||
|
||||
return bs58.encode(multihashBytes);
|
||||
}
|
||||
|
||||
function fetchLatestIpfsHash(key: number) {
|
||||
interface IContract {
|
||||
getEntry: ABIFunc<{ _key: uint256 }, GetEntryResponse>;
|
||||
}
|
||||
|
||||
const abi = [
|
||||
{
|
||||
name: 'getEntry',
|
||||
type: 'function',
|
||||
constant: true,
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
inputs: [
|
||||
{
|
||||
name: '_key',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
name: 'digest',
|
||||
type: 'bytes32'
|
||||
},
|
||||
{
|
||||
name: 'hashfunction',
|
||||
type: 'uint8'
|
||||
},
|
||||
{
|
||||
name: 'size',
|
||||
type: 'uint8'
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const contract = (new Contract(abi) as any) as IContract;
|
||||
|
||||
return shepherdProvider
|
||||
.sendCallRequest({
|
||||
data: contract.getEntry.encodeInput({ _key: key }),
|
||||
to: IPFS_MULTISIG_CONTRACT_ADDRESS
|
||||
})
|
||||
.then(contract.getEntry.decodeOutput)
|
||||
.then(({ digest, hashfunction, size }) => ({
|
||||
digest,
|
||||
hashfunction: handleValues(hashfunction).toNumber(),
|
||||
size: handleValues(size).toNumber()
|
||||
}))
|
||||
.then(getMultihashFromBytes32);
|
||||
}
|
||||
|
||||
export async function updateTokensForNetwork(network: StaticNetworkConfig) {
|
||||
const { tokens: currTokenList, chainId: key, tokenListHash } = network;
|
||||
if (!network.shouldCheckForTokenUpdates) {
|
||||
return { tokens: currTokenList, hash: null };
|
||||
}
|
||||
const latestHash = await fetchLatestIpfsHash(key);
|
||||
|
||||
if (!latestHash) {
|
||||
return { tokens: currTokenList, hash: null };
|
||||
}
|
||||
|
||||
if (tokenListHash === latestHash) {
|
||||
return { tokens: currTokenList, hash: null };
|
||||
}
|
||||
|
||||
const latestList = await getTokenList(latestHash);
|
||||
|
||||
return { tokens: latestList, hash: latestHash };
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
import { Wei, TokenValue } from 'libs/units';
|
||||
import { IHexStrTransaction } from 'libs/transaction';
|
||||
import { Token } from 'types/network';
|
||||
import { TransactionData, TransactionReceipt } from 'types/transactions';
|
||||
import { IRPCProvider } from 'mycrypto-shepherd/dist/lib/types';
|
||||
import { Token } from 'shared/types/network';
|
||||
import { TokenValue } from 'libs/units';
|
||||
|
||||
export interface TxObj {
|
||||
to: string;
|
||||
|
@ -13,16 +12,8 @@ interface TokenBalanceResult {
|
|||
error: string | null;
|
||||
}
|
||||
|
||||
export interface INode {
|
||||
ping(): Promise<boolean>;
|
||||
getBalance(address: string): Promise<Wei>;
|
||||
// alias for compatibility
|
||||
export type INode = IRPCProvider & {
|
||||
getTokenBalance(address: string, token: Token): Promise<TokenBalanceResult>;
|
||||
getTokenBalances(address: string, tokens: Token[]): Promise<TokenBalanceResult[]>;
|
||||
estimateGas(tx: Partial<IHexStrTransaction>): Promise<Wei>;
|
||||
getTransactionCount(address: string): Promise<string>;
|
||||
getTransactionByHash(txhash: string): Promise<TransactionData>;
|
||||
getTransactionReceipt(txhash: string): Promise<TransactionReceipt>;
|
||||
sendRawTx(tx: string): Promise<string>;
|
||||
sendCallRequest(txObj: TxObj): Promise<string>;
|
||||
getCurrentBlock(): Promise<string>;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { shepherd, redux } from 'mycrypto-shepherd';
|
||||
import { INode } from '.';
|
||||
import { tokenBalanceHandler } from './tokenBalanceProxy';
|
||||
import { IProviderConfig } from 'mycrypto-shepherd/dist/lib/ducks/providerConfigs';
|
||||
import { INode } from './INode';
|
||||
|
||||
type DeepPartial<T> = Partial<{ [key in keyof T]: Partial<T[key]> }>;
|
||||
const { selectors, store } = redux;
|
||||
|
|
|
@ -29,6 +29,10 @@ export default class RpcNode implements INode {
|
|||
this.requests = new RPCRequests();
|
||||
}
|
||||
|
||||
public getNetVersion(): Promise<string> {
|
||||
return this.client.call(this.requests.getNetVersion()).then(({ result }) => result);
|
||||
}
|
||||
|
||||
public ping(): Promise<boolean> {
|
||||
return this.client
|
||||
.call(this.requests.getNetVersion())
|
||||
|
@ -42,6 +46,14 @@ export default class RpcNode implements INode {
|
|||
.then(isValidCallRequest)
|
||||
.then(response => response.result);
|
||||
}
|
||||
|
||||
public sendCallRequests(txObjs: TxObj[]): Promise<string[]> {
|
||||
return this.client
|
||||
.batch(txObjs.map(this.requests.ethCall))
|
||||
.then(r => r.map(isValidCallRequest))
|
||||
.then(r => r.map(({ result }) => result));
|
||||
}
|
||||
|
||||
public getBalance(address: string): Promise<Wei> {
|
||||
return this.client
|
||||
.call(this.requests.getBalance(address))
|
||||
|
|
|
@ -18,9 +18,10 @@ import {
|
|||
TOMO_DEFAULT,
|
||||
UBQ_DEFAULT
|
||||
} from 'config/dpaths';
|
||||
import { ConfigAction } from 'actions/config';
|
||||
import { TypeKeys, UpdateTokensAction } from 'actions/config';
|
||||
import { makeExplorer } from 'utils/helpers';
|
||||
import { StaticNetworksState as State } from './types';
|
||||
import { StaticNetworkConfig } from 'shared/types/network';
|
||||
|
||||
const testnetDefaultGasPrice = {
|
||||
min: 0.1,
|
||||
|
@ -44,6 +45,9 @@ export const INITIAL_STATE: State = {
|
|||
address: ETHTokenExplorer
|
||||
},
|
||||
tokens: require('config/tokens/eth.json'),
|
||||
updatingTokens: false,
|
||||
shouldCheckForTokenUpdates: true,
|
||||
tokenListHash: '',
|
||||
contracts: require('config/contracts/eth.json'),
|
||||
dPathFormats: {
|
||||
[SecureWalletName.TREZOR]: ETH_TREZOR,
|
||||
|
@ -64,6 +68,9 @@ export const INITIAL_STATE: State = {
|
|||
origin: 'https://ropsten.etherscan.io'
|
||||
}),
|
||||
tokens: require('config/tokens/ropsten.json'),
|
||||
updatingTokens: false,
|
||||
shouldCheckForTokenUpdates: true,
|
||||
tokenListHash: '',
|
||||
contracts: require('config/contracts/ropsten.json'),
|
||||
isTestnet: true,
|
||||
dPathFormats: {
|
||||
|
@ -84,6 +91,9 @@ export const INITIAL_STATE: State = {
|
|||
origin: 'https://kovan.etherscan.io'
|
||||
}),
|
||||
tokens: require('config/tokens/ropsten.json'),
|
||||
updatingTokens: false,
|
||||
shouldCheckForTokenUpdates: true,
|
||||
tokenListHash: '',
|
||||
contracts: require('config/contracts/ropsten.json'),
|
||||
isTestnet: true,
|
||||
dPathFormats: {
|
||||
|
@ -104,6 +114,9 @@ export const INITIAL_STATE: State = {
|
|||
origin: 'https://rinkeby.etherscan.io'
|
||||
}),
|
||||
tokens: require('config/tokens/rinkeby.json'),
|
||||
updatingTokens: false,
|
||||
shouldCheckForTokenUpdates: true,
|
||||
tokenListHash: '',
|
||||
contracts: require('config/contracts/rinkeby.json'),
|
||||
isTestnet: true,
|
||||
dPathFormats: {
|
||||
|
@ -125,6 +138,9 @@ export const INITIAL_STATE: State = {
|
|||
addressPath: 'addr'
|
||||
}),
|
||||
tokens: require('config/tokens/etc.json'),
|
||||
updatingTokens: false,
|
||||
shouldCheckForTokenUpdates: true,
|
||||
tokenListHash: '',
|
||||
contracts: require('config/contracts/etc.json'),
|
||||
dPathFormats: {
|
||||
[SecureWalletName.TREZOR]: ETC_TREZOR,
|
||||
|
@ -148,6 +164,9 @@ export const INITIAL_STATE: State = {
|
|||
origin: 'https://ubiqscan.io/en'
|
||||
}),
|
||||
tokens: require('config/tokens/ubq.json'),
|
||||
updatingTokens: false,
|
||||
shouldCheckForTokenUpdates: true,
|
||||
tokenListHash: '',
|
||||
contracts: require('config/contracts/ubq.json'),
|
||||
dPathFormats: {
|
||||
[SecureWalletName.TREZOR]: UBQ_DEFAULT,
|
||||
|
@ -171,6 +190,9 @@ export const INITIAL_STATE: State = {
|
|||
origin: 'https://www.gander.tech'
|
||||
}),
|
||||
tokens: require('config/tokens/exp.json'),
|
||||
updatingTokens: false,
|
||||
shouldCheckForTokenUpdates: true,
|
||||
tokenListHash: '',
|
||||
contracts: require('config/contracts/exp.json'),
|
||||
dPathFormats: {
|
||||
[SecureWalletName.TREZOR]: EXP_DEFAULT,
|
||||
|
@ -196,6 +218,9 @@ export const INITIAL_STATE: State = {
|
|||
blockPath: 'blocks/block'
|
||||
}),
|
||||
tokens: [],
|
||||
updatingTokens: false,
|
||||
shouldCheckForTokenUpdates: false,
|
||||
tokenListHash: '',
|
||||
contracts: [],
|
||||
dPathFormats: {
|
||||
[SecureWalletName.TREZOR]: POA_DEFAULT,
|
||||
|
@ -219,6 +244,9 @@ export const INITIAL_STATE: State = {
|
|||
origin: 'https://explorer.tomocoin.io/#'
|
||||
}),
|
||||
tokens: [],
|
||||
updatingTokens: false,
|
||||
shouldCheckForTokenUpdates: false,
|
||||
tokenListHash: '',
|
||||
contracts: [],
|
||||
dPathFormats: {
|
||||
[SecureWalletName.LEDGER_NANO_S]: ETH_LEDGER,
|
||||
|
@ -243,6 +271,9 @@ export const INITIAL_STATE: State = {
|
|||
origin: 'https://explorer.ellaism.org'
|
||||
}),
|
||||
tokens: [],
|
||||
updatingTokens: false,
|
||||
shouldCheckForTokenUpdates: false,
|
||||
tokenListHash: '',
|
||||
contracts: [],
|
||||
dPathFormats: {
|
||||
[SecureWalletName.TREZOR]: ELLA_DEFAULT,
|
||||
|
@ -256,8 +287,42 @@ export const INITIAL_STATE: State = {
|
|||
}
|
||||
};
|
||||
|
||||
export const staticNetworks = (state: State = INITIAL_STATE, action: ConfigAction) => {
|
||||
function networkReducerHelper(state: State, action: UpdateTokensAction) {
|
||||
const { payload } = action;
|
||||
const networkId = payload.id;
|
||||
const networkToUpdate = state[networkId];
|
||||
return { networkId, networkToUpdate, payload };
|
||||
}
|
||||
|
||||
export const staticNetworks = (state: State = INITIAL_STATE, action: UpdateTokensAction) => {
|
||||
switch (action.type) {
|
||||
case TypeKeys.CONFIG_UPDATE_TOKENS_REQUESTED: {
|
||||
const { networkId, networkToUpdate } = networkReducerHelper(state, action);
|
||||
const nextNetwork: StaticNetworkConfig = { ...networkToUpdate, updatingTokens: true };
|
||||
|
||||
return { ...state, [networkId]: nextNetwork };
|
||||
}
|
||||
|
||||
case TypeKeys.CONFIG_UPDATE_TOKENS_SUCCEEDED: {
|
||||
const { networkId, networkToUpdate } = networkReducerHelper(state, action);
|
||||
const { tokens, hash } = action.payload;
|
||||
|
||||
const nextNetwork: StaticNetworkConfig = {
|
||||
...networkToUpdate,
|
||||
updatingTokens: false,
|
||||
tokens,
|
||||
tokenListHash: hash || networkToUpdate.tokenListHash
|
||||
};
|
||||
|
||||
return { ...state, [networkId]: nextNetwork };
|
||||
}
|
||||
|
||||
case TypeKeys.CONFIG_UDPATE_TOKENS_FAILED: {
|
||||
const { networkId, networkToUpdate } = networkReducerHelper(state, action);
|
||||
const nextNetwork: StaticNetworkConfig = { ...networkToUpdate, updatingTokens: false };
|
||||
|
||||
return { ...state, [networkId]: nextNetwork };
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { network } from './network';
|
||||
import { node } from './node';
|
||||
import { web3 } from './web3';
|
||||
import { updateTokens } from './update-tokens';
|
||||
import { all } from 'redux-saga/effects';
|
||||
import { SagaIterator } from 'redux-saga';
|
||||
|
||||
export default function*(): SagaIterator {
|
||||
yield all([...network, ...node, ...web3]);
|
||||
yield all([...network, ...node, ...web3, ...updateTokens]);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
import { getNetworkConfig, getOffline } from 'selectors/config';
|
||||
import { select, call, put, fork, takeEvery } from 'redux-saga/effects';
|
||||
import { UnwrapEffects, ExtPromise } from 'shared/types/sagaHelpers';
|
||||
import {
|
||||
updateTokensSucceeded,
|
||||
UpdateTokensRequested,
|
||||
updateTokensFailed,
|
||||
updateTokensRequested,
|
||||
TypeKeys
|
||||
} from 'actions/config';
|
||||
import { updateTokensForNetwork } from 'libs/ipfs-tokens';
|
||||
import { delay } from 'redux-saga';
|
||||
|
||||
export function* handleUpdatingTokens({ payload: { id } }: UpdateTokensRequested) {
|
||||
const config: UnwrapEffects<typeof getNetworkConfig> = yield select(getNetworkConfig);
|
||||
if (config.isCustom) {
|
||||
return;
|
||||
}
|
||||
|
||||
const appOffline: UnwrapEffects<typeof getOffline> = yield select(getOffline);
|
||||
if (appOffline) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const { hash, tokens }: ExtPromise<UnwrapEffects<typeof updateTokensForNetwork>> = yield call(
|
||||
updateTokensForNetwork,
|
||||
config
|
||||
);
|
||||
yield put(updateTokensSucceeded({ id, tokens, hash }));
|
||||
} catch (e) {
|
||||
console.error((e as Error).message);
|
||||
yield put(updateTokensFailed({ id }));
|
||||
}
|
||||
}
|
||||
|
||||
export function* fireTokenUpdate() {
|
||||
const config: UnwrapEffects<typeof getNetworkConfig> = yield select(getNetworkConfig);
|
||||
if (config.isCustom) {
|
||||
return;
|
||||
}
|
||||
// allow shepherdProvider async init to complete. TODO - don't export shepherdProvider as promise
|
||||
yield call(delay, 300);
|
||||
yield put(updateTokensRequested({ id: config.name }));
|
||||
}
|
||||
|
||||
export const updateTokens = [
|
||||
takeEvery(TypeKeys.CONFIG_UPDATE_TOKENS_REQUESTED, handleUpdatingTokens),
|
||||
fork(fireTokenUpdate),
|
||||
takeEvery([TypeKeys.CONFIG_NODE_CHANGE], fireTokenUpdate)
|
||||
];
|
|
@ -24,7 +24,8 @@ export function* handleNonceRequest(): SagaIterator {
|
|||
const base10Nonce = Nonce(retrievedNonce);
|
||||
yield put(inputNonce(base10Nonce.toString()));
|
||||
yield put(getNonceSucceeded(retrievedNonce));
|
||||
} catch {
|
||||
} catch (e) {
|
||||
console.error('Handle nonce request error:', e);
|
||||
yield put(showNotification('warning', 'Your addresses nonce could not be fetched'));
|
||||
yield put(getNonceFailed());
|
||||
}
|
||||
|
|
|
@ -11,11 +11,14 @@ import {
|
|||
getLanguageSelection,
|
||||
getCustomNodeConfigs,
|
||||
getSelectedNode,
|
||||
getCustomNetworkConfigs
|
||||
getCustomNetworkConfigs,
|
||||
getStaticNetworkConfigs
|
||||
} from 'selectors/config';
|
||||
import RootReducer, { AppState } from 'reducers';
|
||||
import { CustomNodeConfig } from 'types/node';
|
||||
import { shepherd, makeProviderConfig, shepherdProvider, isAutoNode } from 'libs/nodes';
|
||||
import { StaticNetworkConfig, StaticNetworkIds } from 'shared/types/network';
|
||||
import { StaticNetworksState } from 'reducers/config/networks';
|
||||
const appInitialState = RootReducer(undefined as any, { type: 'inital_state' });
|
||||
|
||||
type DeepPartial<T> = { [P in keyof T]?: DeepPartial<T[P]> };
|
||||
|
@ -26,7 +29,8 @@ export function getConfigAndCustomTokensStateToSubscribe(
|
|||
meta: { languageSelection: getLanguageSelection(state) },
|
||||
nodes: { customNodes: getCustomNodeConfigs(state), selectedNode: getSelectedNode(state) },
|
||||
networks: {
|
||||
customNetworks: getCustomNetworkConfigs(state)
|
||||
customNetworks: getCustomNetworkConfigs(state),
|
||||
staticNetworks: getStaticNetworkConfigs(state)
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -94,6 +98,24 @@ function rehydrateNetworks(
|
|||
): ConfigState['networks'] {
|
||||
const nextNetworkState = { ...initialState };
|
||||
nextNetworkState.customNetworks = savedState.customNetworks;
|
||||
|
||||
// merge the states so that we just grab the tokenlist hash and tokens
|
||||
const nextStaticNetworkState = Object.entries(savedState.staticNetworks).reduce<
|
||||
StaticNetworksState
|
||||
>(
|
||||
(prev, [networkId, networkConfig]: [StaticNetworkIds, StaticNetworkConfig]) => {
|
||||
const initialConfig = initialState.staticNetworks[networkId];
|
||||
const nextConfig: StaticNetworkConfig = {
|
||||
...initialConfig,
|
||||
tokenListHash: networkConfig.tokenListHash,
|
||||
tokens: networkConfig.tokens
|
||||
};
|
||||
return { ...prev, [networkId]: nextConfig };
|
||||
},
|
||||
{} as StaticNetworksState
|
||||
);
|
||||
|
||||
nextNetworkState.staticNetworks = nextStaticNetworkState;
|
||||
return nextNetworkState;
|
||||
}
|
||||
|
||||
|
|
32
package.json
32
package.json
|
@ -16,10 +16,12 @@
|
|||
"bip39": "2.5.0",
|
||||
"bn.js": "4.11.8",
|
||||
"bootstrap-sass": "3.3.7",
|
||||
"bs58": "4.0.1",
|
||||
"classnames": "2.2.5",
|
||||
"electron-updater": "2.21.4",
|
||||
"ethereum-blockies-base64": "1.0.1",
|
||||
"ethereumjs-abi": "git://github.com/ethereumjs/ethereumjs-abi.git#09c3c48fd3bed143df7fa8f36f6f164205e23796",
|
||||
"ethereumjs-abi":
|
||||
"git://github.com/ethereumjs/ethereumjs-abi.git#09c3c48fd3bed143df7fa8f36f6f164205e23796",
|
||||
"ethereumjs-tx": "1.3.4",
|
||||
"ethereumjs-util": "5.1.5",
|
||||
"ethereumjs-wallet": "0.6.0",
|
||||
|
@ -60,6 +62,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@types/bip39": "2.4.0",
|
||||
"@types/bs58": "3.0.30",
|
||||
"@types/classnames": "2.2.3",
|
||||
"@types/enzyme": "3.1.8",
|
||||
"@types/enzyme-adapter-react-16": "1.0.1",
|
||||
|
@ -156,13 +159,19 @@
|
|||
"prebuild": "check-node-version --package",
|
||||
"build:downloadable": "webpack --mode=production --config webpack_config/webpack.html.js",
|
||||
"prebuild:downloadable": "check-node-version --package",
|
||||
"build:electron": "webpack --config webpack_config/webpack.electron-prod.js && node webpack_config/buildElectron.js",
|
||||
"build:electron:osx": "webpack --config webpack_config/webpack.electron-prod.js && ELECTRON_OS=osx node webpack_config/buildElectron.js",
|
||||
"build:electron:windows": "webpack --config webpack_config/webpack.electron-prod.js && ELECTRON_OS=windows node webpack_config/buildElectron.js",
|
||||
"build:electron:linux": "webpack --config webpack_config/webpack.electron-prod.js && ELECTRON_OS=linux node webpack_config/buildElectron.js",
|
||||
"build:electron":
|
||||
"webpack --config webpack_config/webpack.electron-prod.js && node webpack_config/buildElectron.js",
|
||||
"build:electron:osx":
|
||||
"webpack --config webpack_config/webpack.electron-prod.js && ELECTRON_OS=osx node webpack_config/buildElectron.js",
|
||||
"build:electron:windows":
|
||||
"webpack --config webpack_config/webpack.electron-prod.js && ELECTRON_OS=windows node webpack_config/buildElectron.js",
|
||||
"build:electron:linux":
|
||||
"webpack --config webpack_config/webpack.electron-prod.js && ELECTRON_OS=linux node webpack_config/buildElectron.js",
|
||||
"prebuild:electron": "check-node-version --package",
|
||||
"jenkins:build:linux": "webpack --config webpack_config/webpack.electron-prod.js && ELECTRON_OS=JENKINS_LINUX node webpack_config/buildElectron.js",
|
||||
"jenkins:build:mac": "webpack --config webpack_config/webpack.electron-prod.js && ELECTRON_OS=JENKINS_MAC node webpack_config/buildElectron.js",
|
||||
"jenkins:build:linux":
|
||||
"webpack --config webpack_config/webpack.electron-prod.js && ELECTRON_OS=JENKINS_LINUX node webpack_config/buildElectron.js",
|
||||
"jenkins:build:mac":
|
||||
"webpack --config webpack_config/webpack.electron-prod.js && ELECTRON_OS=JENKINS_MAC node webpack_config/buildElectron.js",
|
||||
"jenkins:upload": "node jenkins/upload",
|
||||
"test:coverage": "jest --config=jest_config/jest.config.json --coverage",
|
||||
"test": "jest --config=jest_config/jest.config.json",
|
||||
|
@ -175,13 +184,16 @@
|
|||
"predev": "check-node-version --package",
|
||||
"dev:https": "HTTPS=true node webpack_config/devServer.js",
|
||||
"predev:https": "check-node-version --package",
|
||||
"dev:electron": "concurrently --kill-others --names 'webpack,electron' 'BUILD_ELECTRON=true node webpack_config/devServer.js' 'webpack --config webpack_config/webpack.electron-dev.js && electron dist/electron-js/main.js'",
|
||||
"dev:electron":
|
||||
"concurrently --kill-others --names 'webpack,electron' 'BUILD_ELECTRON=true node webpack_config/devServer.js' 'webpack --config webpack_config/webpack.electron-dev.js && electron dist/electron-js/main.js'",
|
||||
"tslint": "tslint --project . --exclude common/vendor/**/*",
|
||||
"tscheck": "tsc --noEmit",
|
||||
"start": "npm run dev",
|
||||
"precommit": "lint-staged",
|
||||
"formatAll": "find ./common/ -name '*.ts*' | xargs prettier --write --config ./.prettierrc --config-precedence file-override",
|
||||
"prettier:diff": "prettier --write --config ./.prettierrc --list-different \"common/**/*.ts\" \"common/**/*.tsx\"",
|
||||
"formatAll":
|
||||
"find ./common/ -name '*.ts*' | xargs prettier --write --config ./.prettierrc --config-precedence file-override",
|
||||
"prettier:diff":
|
||||
"prettier --write --config ./.prettierrc --list-different \"common/**/*.ts\" \"common/**/*.tsx\"",
|
||||
"prepush": "npm run tslint && npm run tscheck",
|
||||
"update:tokens": "ts-node scripts/update-tokens"
|
||||
},
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { GitCommit } from './types/GitCommit';
|
||||
import { CommitStatus } from './types/CommitStatus';
|
||||
import { RawTokenJSON } from './types/TokensJson';
|
||||
import { GitCommit } from 'shared/types/GitCommit';
|
||||
import { CommitStatus } from 'shared/types/CommitStatus';
|
||||
import { RawTokenJSON } from 'shared/types/TokensJson';
|
||||
|
||||
const { processTokenJson } = require('./update-tokens-utils');
|
||||
const { processTokenJson } = require('../shared/update-tokens-utils');
|
||||
const https = require('https');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
|
|
@ -47,6 +47,9 @@ export interface GasPriceSetting {
|
|||
|
||||
interface StaticNetworkConfig {
|
||||
isCustom: false; // used for type guards
|
||||
shouldCheckForTokenUpdates: boolean;
|
||||
tokenListHash: string;
|
||||
updatingTokens: boolean;
|
||||
name: StaticNetworkIds;
|
||||
unit: string;
|
||||
color?: string;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { RawTokenJSON, ValidatedTokenJSON, NormalizedTokenJSON } from './types/TokensJson';
|
||||
import { Token } from '../shared/types/network';
|
||||
import { RawTokenJSON, ValidatedTokenJSON, NormalizedTokenJSON } from 'shared/types/TokensJson';
|
||||
interface StrIdx<T> {
|
||||
[key: string]: T;
|
||||
}
|
66
yarn.lock
66
yarn.lock
|
@ -43,23 +43,33 @@
|
|||
core-js "^2.5.3"
|
||||
regenerator-runtime "^0.11.1"
|
||||
|
||||
"@parity/qr-signer@0.2.0":
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@parity/qr-signer/-/qr-signer-0.2.0.tgz#5c6c41c03265608c117346f0a9d7ab89699352fa"
|
||||
"@parity/qr-signer@0.2.1":
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@parity/qr-signer/-/qr-signer-0.2.1.tgz#f8b0e0ff5d8ee90b1788951c8524cdf8b1aadf27"
|
||||
dependencies:
|
||||
qrcode-generator "1.3.1"
|
||||
react-qr-reader "2.0.1"
|
||||
react-qr-reader "2.1.0"
|
||||
|
||||
"@sindresorhus/is@^0.7.0":
|
||||
version "0.7.0"
|
||||
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd"
|
||||
|
||||
"@types/base-x@*":
|
||||
version "1.0.29"
|
||||
resolved "https://registry.yarnpkg.com/@types/base-x/-/base-x-1.0.29.tgz#8a2d73e4a5c3121757a5f8870cfa4509655186b6"
|
||||
|
||||
"@types/bip39@2.4.0":
|
||||
version "2.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/bip39/-/bip39-2.4.0.tgz#eee31a14abc8ebbb41a1ff14575c447b18346cbc"
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/bs58@3.0.30":
|
||||
version "3.0.30"
|
||||
resolved "https://registry.yarnpkg.com/@types/bs58/-/bs58-3.0.30.tgz#916ba10992465e56f81afc735e7935fd749893e1"
|
||||
dependencies:
|
||||
"@types/base-x" "*"
|
||||
|
||||
"@types/cheerio@*":
|
||||
version "0.22.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/cheerio/-/cheerio-0.22.7.tgz#4a92eafedfb2b9f4437d3a4410006d81114c66ce"
|
||||
|
@ -1329,7 +1339,7 @@ base-x@^1.1.0:
|
|||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/base-x/-/base-x-1.1.0.tgz#42d3d717474f9ea02207f6d1aa1f426913eeb7ac"
|
||||
|
||||
base-x@^3.0.4:
|
||||
base-x@^3.0.2, base-x@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.4.tgz#94c1788736da065edb1d68808869e357c977fa77"
|
||||
dependencies:
|
||||
|
@ -1660,6 +1670,12 @@ browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6:
|
|||
caniuse-db "^1.0.30000639"
|
||||
electron-to-chromium "^1.2.7"
|
||||
|
||||
bs58@4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a"
|
||||
dependencies:
|
||||
base-x "^3.0.2"
|
||||
|
||||
bs58@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/bs58/-/bs58-2.0.1.tgz#55908d58f1982aba2008fa1bed8f91998a29bf8d"
|
||||
|
@ -6579,9 +6595,9 @@ jsprim@^1.2.2:
|
|||
json-schema "0.2.3"
|
||||
verror "1.10.0"
|
||||
|
||||
"jsqr@https://github.com/cozmo/jsQR.git":
|
||||
jsqr@^1.0.1:
|
||||
version "1.0.4"
|
||||
resolved "https://github.com/cozmo/jsQR.git#d37c764bf43a41ed7c6aa1c17cbdf21b7f9cb69e"
|
||||
resolved "https://registry.yarnpkg.com/jsqr/-/jsqr-1.0.4.tgz#e2ea353fa81007708efab7d95b2652a7254c10dd"
|
||||
|
||||
jssha@2.3.1:
|
||||
version "2.3.1"
|
||||
|
@ -7434,9 +7450,9 @@ mute-stream@0.0.7:
|
|||
version "0.0.7"
|
||||
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
|
||||
|
||||
mycrypto-shepherd@1.3.1:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/mycrypto-shepherd/-/mycrypto-shepherd-1.3.1.tgz#901d86e2278c08fd9a3a0aee7a13dea971dbbc38"
|
||||
mycrypto-shepherd@1.3.3:
|
||||
version "1.3.3"
|
||||
resolved "https://registry.yarnpkg.com/mycrypto-shepherd/-/mycrypto-shepherd-1.3.3.tgz#3bdefec7604f4d6f094e0282b807001d87fd98b6"
|
||||
dependencies:
|
||||
"@types/jest" "^22.2.2"
|
||||
"@types/node" "^9.6.2"
|
||||
|
@ -9077,17 +9093,17 @@ react-markdown@3.3.0:
|
|||
unist-util-visit "^1.3.0"
|
||||
xtend "^4.0.1"
|
||||
|
||||
react-onclickoutside@6.7.1, react-onclickoutside@^6.5.0:
|
||||
react-onclickoutside@^6.5.0:
|
||||
version "6.7.1"
|
||||
resolved "https://registry.yarnpkg.com/react-onclickoutside/-/react-onclickoutside-6.7.1.tgz#6a5b5b8b4eae6b776259712c89c8a2b36b17be93"
|
||||
|
||||
react-qr-reader@2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/react-qr-reader/-/react-qr-reader-2.0.1.tgz#f7be785e8c880d7e68423fc129802994f70b6b58"
|
||||
react-qr-reader@2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/react-qr-reader/-/react-qr-reader-2.1.0.tgz#f429c196675a710926da1cc9057223b79358da75"
|
||||
dependencies:
|
||||
jsqr "https://github.com/cozmo/jsQR.git"
|
||||
jsqr "^1.0.1"
|
||||
prop-types "^15.5.8"
|
||||
webrtc-adapter "^5.0.6"
|
||||
webrtc-adapter "^6.1.1"
|
||||
|
||||
react-reconciler@^0.7.0:
|
||||
version "0.7.0"
|
||||
|
@ -9786,7 +9802,7 @@ rst-selector-parser@^2.2.3:
|
|||
lodash.flattendeep "^4.4.0"
|
||||
nearley "^2.7.10"
|
||||
|
||||
rtcpeerconnection-shim@^1.1.13:
|
||||
rtcpeerconnection-shim@^1.2.10:
|
||||
version "1.2.11"
|
||||
resolved "https://registry.yarnpkg.com/rtcpeerconnection-shim/-/rtcpeerconnection-shim-1.2.11.tgz#df2b2456020365daf26bf8c135523bca2deb252b"
|
||||
dependencies:
|
||||
|
@ -9950,10 +9966,14 @@ scss-tokenizer@^0.2.3:
|
|||
js-base64 "^2.1.8"
|
||||
source-map "^0.4.2"
|
||||
|
||||
sdp@^2.3.0, sdp@^2.6.0:
|
||||
sdp@^2.6.0:
|
||||
version "2.7.0"
|
||||
resolved "https://registry.yarnpkg.com/sdp/-/sdp-2.7.0.tgz#02b64ea0c29d73179afa19794e466b123b1b29f3"
|
||||
|
||||
sdp@^2.7.0:
|
||||
version "2.7.4"
|
||||
resolved "https://registry.yarnpkg.com/sdp/-/sdp-2.7.4.tgz#cac76b0e2f16f55243d25bc0432f6bbb5488bfc1"
|
||||
|
||||
secp256k1@^3.0.1:
|
||||
version "3.5.0"
|
||||
resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-3.5.0.tgz#677d3b8a8e04e1a5fa381a1ae437c54207b738d0"
|
||||
|
@ -11803,12 +11823,12 @@ webpack@4.5.0:
|
|||
watchpack "^1.5.0"
|
||||
webpack-sources "^1.0.1"
|
||||
|
||||
webrtc-adapter@^5.0.6:
|
||||
version "5.0.6"
|
||||
resolved "https://registry.yarnpkg.com/webrtc-adapter/-/webrtc-adapter-5.0.6.tgz#7946fca194dadf869bb6c8cae1011dfda03f40c7"
|
||||
webrtc-adapter@^6.1.1:
|
||||
version "6.1.5"
|
||||
resolved "https://registry.yarnpkg.com/webrtc-adapter/-/webrtc-adapter-6.1.5.tgz#df72e4af5cb6675656c896db0d1187695359220b"
|
||||
dependencies:
|
||||
rtcpeerconnection-shim "^1.1.13"
|
||||
sdp "^2.3.0"
|
||||
rtcpeerconnection-shim "^1.2.10"
|
||||
sdp "^2.7.0"
|
||||
|
||||
what-input@5.0.5:
|
||||
version "5.0.5"
|
||||
|
|
Loading…
Reference in New Issue