web3 actions splitted to blockchain actions

pull/25/head
Szymon Lesisz 6 years ago
parent a913759ef4
commit 26978fe984

@ -2,7 +2,7 @@
import * as ACCOUNT from 'actions/constants/account'; import * as ACCOUNT from 'actions/constants/account';
import type { Action, TrezorDevice } from 'flowtype'; import type { Action, TrezorDevice } from 'flowtype';
import type { State } from 'reducers/AccountsReducer'; import type { Account, State } from 'reducers/AccountsReducer';
export type AccountFromStorageAction = { export type AccountFromStorageAction = {
type: typeof ACCOUNT.FROM_STORAGE, type: typeof ACCOUNT.FROM_STORAGE,
@ -11,11 +11,12 @@ export type AccountFromStorageAction = {
export type AccountCreateAction = { export type AccountCreateAction = {
type: typeof ACCOUNT.CREATE, type: typeof ACCOUNT.CREATE,
device: TrezorDevice, payload: Account,
network: string, }
index: number,
path: Array<number>, export type AccountUpdateAction = {
address: string type: typeof ACCOUNT.UPDATE,
payload: Account,
} }
export type AccountSetBalanceAction = { export type AccountSetBalanceAction = {
@ -36,9 +37,10 @@ export type AccountSetNonceAction = {
export type AccountAction = export type AccountAction =
AccountFromStorageAction AccountFromStorageAction
| AccountCreateAction | AccountCreateAction
| AccountSetBalanceAction | AccountUpdateAction
| AccountSetNonceAction; | AccountSetBalanceAction
| AccountSetNonceAction;
export const setBalance = (address: string, network: string, deviceState: string, balance: string): Action => ({ export const setBalance = (address: string, network: string, deviceState: string, balance: string): Action => ({
type: ACCOUNT.SET_BALANCE, type: ACCOUNT.SET_BALANCE,
@ -55,3 +57,11 @@ export const setNonce = (address: string, network: string, deviceState: string,
deviceState, deviceState,
nonce, nonce,
}); });
<<<<<<< HEAD
=======
export const update = (account: Account): Action => ({
type: ACCOUNT.UPDATE,
payload: account
});
>>>>>>> web3 actions splitted to blockchain actions

@ -116,7 +116,7 @@ export const onBlockMined = (network: string): PromiseAction<void> => async (dis
} else { } else {
response.payload.forEach((a, i) => { response.payload.forEach((a, i) => {
if (a.transactions > 0) { if (a.transactions > 0) {
dispatch( Web3Actions.updateAccount(accounts[i], a, network) ) // dispatch( Web3Actions.updateAccount(accounts[i], a, network) )
} }
}); });
} }
@ -134,16 +134,14 @@ export const onNotification = (payload: any): PromiseAction<void> => async (disp
// const exists = getState().pending.filter(p => p.id === payload.tx.txid && p.address === address); // const exists = getState().pending.filter(p => p.id === payload.tx.txid && p.address === address);
const exists = getState().pending.filter(p => { const exists = getState().pending.filter(p => {
console.warn("CHECK", p.address === address, p.id === payload.tx.txid, p)
return p.address === address; return p.address === address;
}); });
if (exists.length < 1) { if (exists.length < 1) {
console.warn("TXINFO!", txInfo);
if (txInfo) { if (txInfo) {
dispatch({ dispatch({
type: PENDING.ADD, type: PENDING.ADD,
payload: { payload: {
type: 'send',
id: payload.tx.txid, id: payload.tx.txid,
network: payload.coin, network: payload.coin,
currency: "tETH", currency: "tETH",
@ -152,6 +150,7 @@ export const onNotification = (payload: any): PromiseAction<void> => async (disp
tx: {}, tx: {},
nonce: txInfo.nonce, nonce: txInfo.nonce,
address, address,
rejected: false
} }
}); });
} else { } else {

@ -10,8 +10,10 @@ import type {
} from 'flowtype'; } from 'flowtype';
import type { Discovery, State } from 'reducers/DiscoveryReducer'; import type { Discovery, State } from 'reducers/DiscoveryReducer';
import * as AccountsActions from './AccountsActions'; import * as AccountsActions from './AccountsActions';
import * as Web3Actions from './Web3Actions';
import { getNonceAsync, getBalanceAsync } from './Web3Actions'; import * as BlockchainActions from './BlockchainActions';
import { setBalance as setTokenBalance } from './TokenActions';
export type DiscoveryStartAction = { export type DiscoveryStartAction = {
@ -44,15 +46,123 @@ export type DiscoveryAction = {
type: typeof DISCOVERY.FROM_STORAGE, type: typeof DISCOVERY.FROM_STORAGE,
payload: State payload: State
} | DiscoveryStartAction } | DiscoveryStartAction
| DiscoveryWaitingAction | DiscoveryWaitingAction
| DiscoveryStopAction | DiscoveryStopAction
| DiscoveryCompleteAction; | DiscoveryCompleteAction;
export const start = (device: TrezorDevice, network: string, ignoreCompleted?: boolean): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
const selected = getState().wallet.selectedDevice;
if (!selected) {
// TODO: throw error
console.error('Start discovery: no selected device', device);
return;
} if (selected.path !== device.path) {
console.error('Start discovery: requested device is not selected', device, selected);
return;
} if (!selected.state) {
console.warn("Start discovery: Selected device wasn't authenticated yet...");
return;
} if (selected.connected && !selected.available) {
console.warn('Start discovery: Selected device is unavailable...');
return;
}
const discovery: State = getState().discovery;
const discoveryProcess: ?Discovery = discovery.find(d => d.deviceState === device.state && d.network === network);
if (!selected.connected && (!discoveryProcess || !discoveryProcess.completed)) {
dispatch({
type: DISCOVERY.WAITING_FOR_DEVICE,
device,
network,
});
return;
}
const blockchain = getState().blockchain.find(b => b.name === network);
if (blockchain && !blockchain.connected) {
console.error("NO BACKEND!") // TODO
return;
}
if (!discoveryProcess) {
dispatch(begin(device, network))
} else if (discoveryProcess.completed && !ignoreCompleted) {
dispatch({
type: DISCOVERY.COMPLETE,
device,
network,
});
} else if (discoveryProcess.interrupted || discoveryProcess.waitingForDevice) {
// discovery cycle was interrupted
// start from beginning
dispatch(begin(device, network));
} else {
dispatch(discoverAccount(device, discoveryProcess));
}
};
// Because start() is calling begin() and begin() is calling start() one of them must be declared first // first iteration
// otherwise eslint will start complaining // generate public key for this account
let begin; // start discovery process
const begin = (device: TrezorDevice, network: string): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const { config } = getState().localStorage;
const coinToDiscover = config.coins.find(c => c.network === network);
if (!coinToDiscover) return;
dispatch({
type: DISCOVERY.WAITING_FOR_DEVICE,
device,
network,
});
// get xpub from TREZOR
const response = await TrezorConnect.getPublicKey({
device: {
path: device.path,
instance: device.instance,
state: device.state,
},
path: coinToDiscover.bip44,
keepSession: true, // acquire and hold session
useEmptyPassphrase: !device.instance,
});
// handle TREZOR response error
if (!response.success) {
dispatch({
type: NOTIFICATION.ADD,
payload: {
type: 'error',
title: 'Discovery error',
message: response.payload.error,
cancelable: true,
actions: [
{
label: 'Try again',
callback: () => {
dispatch(start(device, network));
},
},
],
},
});
return;
}
// send data to reducer
dispatch({
type: DISCOVERY.START,
network: coinToDiscover.network,
device,
publicKey: response.payload.publicKey,
chainCode: response.payload.chainCode,
basePath,
});
// next iteration
dispatch(start(device, network));
};
const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => { const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const { completed } = discoveryProcess; const { completed } = discoveryProcess;
@ -66,64 +176,53 @@ const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): Asy
// TODO: check if address was created before // TODO: check if address was created before
// verify address with TREZOR try {
const verifyAddress = await TrezorConnect.ethereumGetAddress({ const account = await dispatch( BlockchainActions.discoverAccount(device, ethAddress, network) );
device: { if (discoveryProcess.interrupted) return;
path: device.path,
instance: device.instance,
state: device.state,
},
path,
showOnTrezor: false,
keepSession: true,
useEmptyPassphrase: !device.instance,
});
if (discoveryProcess.interrupted) return;
// TODO: with block-book (Martin) // const accountIsEmpty = account.transactions <= 0 && account.nonce <= 0 && account.balance === '0';
// const discoveryA = await TrezorConnect.accountDiscovery({ const accountIsEmpty = account.nonce <= 0 && account.balance === '0';
// device: { if (!accountIsEmpty || (accountIsEmpty && completed) || (accountIsEmpty && discoveryProcess.accountIndex === 0)) {
// path: device.path,
// instance: device.instance,
// state: device.state
// },
// });
// if (discoveryProcess.interrupted) return;
if (verifyAddress && verifyAddress.success) {
//const trezorAddress: string = '0x' + verifyAddress.payload.address;
const trezorAddress: string = EthereumjsUtil.toChecksumAddress(verifyAddress.payload.address);
if (trezorAddress !== ethAddress) {
// throw inconsistent state error
console.warn('Inconsistent state', trezorAddress, ethAddress);
dispatch({ dispatch({
type: NOTIFICATION.ADD, type: ACCOUNT.CREATE,
payload: { payload: {
type: 'error', index: discoveryProcess.accountIndex,
title: 'Address validation error', loaded: true,
message: `Addresses are different. TREZOR: ${trezorAddress} HDKey: ${ethAddress}`, network,
cancelable: true, deviceID: device.features ? device.features.device_id : '0',
actions: [ deviceState: device.state || '0',
{ addressPath: path,
label: 'Try again', address: ethAddress,
callback: () => { balance: account.balance,
dispatch(start(device, discoveryProcess.network)); nonce: account.nonce,
}, block: account.block,
}, transactions: account.transactions
], }
},
}); });
return;
dispatch( Web3Actions.getTxInput() );
} }
} else {
// handle TREZOR communication error if (accountIsEmpty) {
dispatch( finish(device, discoveryProcess) );
} else {
if (!completed) { dispatch( discoverAccount(device, discoveryProcess) ); }
}
} catch (error) {
dispatch({
type: DISCOVERY.STOP,
device
});
dispatch({ dispatch({
type: NOTIFICATION.ADD, type: NOTIFICATION.ADD,
payload: { payload: {
type: 'error', type: 'error',
title: 'Address validation error', title: 'Account discovery error',
message: verifyAddress.payload.error, message: error.message,
cancelable: true, cancelable: true,
actions: [ actions: [
{ {
@ -135,56 +234,33 @@ const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): Asy
], ],
}, },
}); });
return;
} }
};
const web3instance = getState().web3.find(w3 => w3.network === network); const finish = (device: TrezorDevice, discoveryProcess: Discovery): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
if (!web3instance) return;
const balance = await getBalanceAsync(web3instance.web3, ethAddress); console.warn("FINISH!");
if (discoveryProcess.interrupted) return; await TrezorConnect.getFeatures({
const nonce: number = await getNonceAsync(web3instance.web3, ethAddress); device: {
if (discoveryProcess.interrupted) return; path: device.path,
instance: device.instance,
state: device.state,
},
keepSession: false,
useEmptyPassphrase: !device.instance,
});
const addressIsEmpty = nonce < 1 && !balance.greaterThan(0); await dispatch( BlockchainActions.subscribe(discoveryProcess.network) );
if (!addressIsEmpty || (addressIsEmpty && completed) || (addressIsEmpty && discoveryProcess.accountIndex === 0)) { if (discoveryProcess.interrupted) return;
dispatch({
type: ACCOUNT.CREATE,
device,
network,
index: discoveryProcess.accountIndex,
path,
address: ethAddress,
});
dispatch(
AccountsActions.setBalance(ethAddress, network, device.state || 'undefined', web3instance.web3.fromWei(balance.toString(), 'ether')),
);
dispatch(AccountsActions.setNonce(ethAddress, network, device.state || 'undefined', nonce));
if (!completed) { dispatch(discoverAccount(device, discoveryProcess)); }
}
if (addressIsEmpty) { dispatch({
// release acquired sesssion type: DISCOVERY.COMPLETE,
await TrezorConnect.getFeatures({ device,
device: { network: discoveryProcess.network,
path: device.path, });
instance: device.instance,
state: device.state,
},
keepSession: false,
useEmptyPassphrase: !device.instance,
});
if (discoveryProcess.interrupted) return;
dispatch({ }
type: DISCOVERY.COMPLETE,
device,
network,
});
}
};
export const start = (device: TrezorDevice, network: string, ignoreCompleted?: boolean): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { export const start = (device: TrezorDevice, network: string, ignoreCompleted?: boolean): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
const selected = getState().wallet.selectedDevice; const selected = getState().wallet.selectedDevice;

@ -7,6 +7,9 @@ import type { State, PendingTx } from 'reducers/PendingTxReducer';
export type PendingTxAction = { export type PendingTxAction = {
type: typeof PENDING.FROM_STORAGE, type: typeof PENDING.FROM_STORAGE,
payload: State payload: State
} | {
type: typeof PENDING.ADD,
payload: PendingTx
} | { } | {
type: typeof PENDING.TX_RESOLVED, type: typeof PENDING.TX_RESOLVED,
tx: PendingTx, tx: PendingTx,

@ -27,7 +27,8 @@ import type { State, FeeLevel } from 'reducers/SendFormReducer';
import type { Account } from 'reducers/AccountsReducer'; import type { Account } from 'reducers/AccountsReducer';
import type { Props } from 'views/Wallet/views/AccountSend/Container'; import type { Props } from 'views/Wallet/views/AccountSend/Container';
import * as SessionStorageActions from './SessionStorageActions'; import * as SessionStorageActions from './SessionStorageActions';
import { estimateGas, pushTx } from './Web3Actions'; import { prepareEthereumTx, serializeEthereumTx } from './TxActions';
import * as BlockchainActions from './BlockchainActions';
export type SendTxAction = { export type SendTxAction = {
type: typeof SEND.TX_COMPLETE, type: typeof SEND.TX_COMPLETE,
@ -223,14 +224,13 @@ export const getFeeLevels = (symbol: string, gasPrice: BigNumber | string, gasLi
// initialize component // initialize component
export const init = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { export const init = (): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const { const {
account, account,
network, network,
web3,
} = getState().selectedAccount; } = getState().selectedAccount;
if (!account || !network || !web3) return; if (!account || !network) return;
const stateFromStorage = SessionStorageActions.load(getState().router.location.pathname); const stateFromStorage = SessionStorageActions.load(getState().router.location.pathname);
if (stateFromStorage) { if (stateFromStorage) {
@ -243,7 +243,10 @@ export const init = (): ThunkAction => (dispatch: Dispatch, getState: GetState):
// TODO: check if there are some unfinished tx in localStorage // TODO: check if there are some unfinished tx in localStorage
const gasPrice: BigNumber = new BigNumber(EthereumjsUnits.convert(web3.gasPrice, 'wei', 'gwei')) || new BigNumber(network.defaultGasPrice);
// const gasPrice: BigNumber = new BigNumber(EthereumjsUnits.convert(web3.gasPrice, 'wei', 'gwei')) || new BigNumber(network.defaultGasPrice);
const gasPrice: BigNumber = await dispatch( BlockchainActions.getGasPrice(network.network, network.defaultGasPrice) );
// const gasPrice: BigNumber = new BigNumber(network.defaultGasPrice);
const gasLimit: string = network.defaultGasLimit.toString(); const gasLimit: string = network.defaultGasLimit.toString();
const feeLevels: Array<FeeLevel> = getFeeLevels(network.symbol, gasPrice, gasLimit); const feeLevels: Array<FeeLevel> = getFeeLevels(network.symbol, gasPrice, gasLimit);
@ -709,12 +712,9 @@ export const onNonceChange = (nonce: string): AsyncAction => async (dispatch: Di
const estimateGasPrice = (): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => { const estimateGasPrice = (): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const { const {
web3,
network, network,
} = getState().selectedAccount; } = getState().selectedAccount;
if (!web3 || !network) return; if (!network) return;
const w3 = web3.web3;
const state: State = getState().sendForm; const state: State = getState().sendForm;
const requestedData = state.data; const requestedData = state.data;
@ -732,14 +732,7 @@ const estimateGasPrice = (): AsyncAction => async (dispatch: Dispatch, getState:
return; return;
} }
// TODO: allow data starting with 0x ... const gasLimit: number = await dispatch( BlockchainActions.estimateGasLimit(network.network, state.data, state.amount, state.gasPrice) );
const data: string = `0x${state.data.length % 2 === 0 ? state.data : `0${state.data}`}`;
const gasLimit = await estimateGas(w3, {
to: '0x0000000000000000000000000000000000000000',
data,
value: w3.toHex(w3.toWei(state.amount, 'ether')),
gasPrice: w3.toHex(EthereumjsUnits.convert(state.gasPrice, 'gwei', 'wei')),
});
if (getState().sendForm.data === requestedData) { if (getState().sendForm.data === requestedData) {
dispatch(onGasLimitChange(gasLimit.toString())); dispatch(onGasLimitChange(gasLimit.toString()));
@ -777,56 +770,69 @@ export const onSend = (): AsyncAction => async (dispatch: Dispatch, getState: Ge
const { const {
account, account,
network, network,
web3,
pending, pending,
} = getState().selectedAccount; } = getState().selectedAccount;
if (!account || !web3 || !network) return;
if (!account || !network) return;
const currentState: State = getState().sendForm; const currentState: State = getState().sendForm;
const isToken: boolean = currentState.currency !== currentState.networkSymbol; const isToken: boolean = currentState.currency !== currentState.networkSymbol;
const w3 = web3.web3;
const address_n = account.addressPath; const address_n = account.addressPath;
const pendingNonce: number = stateUtils.getPendingNonce(pending);
let data: string = `0x${currentState.data}`;
let txAmount: string = w3.toHex(w3.toWei(currentState.amount, 'ether'));
let txAddress: string = currentState.address;
if (isToken) {
const token: ?Token = findToken(getState().tokens, account.address, currentState.currency, account.deviceState);
if (!token) return;
const contract = web3.erc20.at(token.address);
const amountValue: string = new BigNumber(currentState.amount).times(Math.pow(10, token.decimals)).toString(10);
data = contract.transfer.getData(currentState.address, amountValue, {
from: account.address,
gasLimit: currentState.gasLimit,
gasPrice: currentState.gasPrice,
});
txAmount = '0x00';
txAddress = token.address;
}
const pendingNonce: number = getPendingNonce(pending);
const nonce = pendingNonce > 0 && pendingNonce >= account.nonce ? pendingNonce : account.nonce; const nonce = pendingNonce > 0 && pendingNonce >= account.nonce ? pendingNonce : account.nonce;
console.warn('NONCE', nonce, account.nonce, pendingNonce); console.warn("NONCE", nonce);
const txData = { const txData = await dispatch( prepareEthereumTx({
address_n, network: network.network,
// from: currentAddress.address token: isToken ? findToken(getState().tokens, account.address, currentState.currency, account.deviceState) : null,
to: txAddress, from: account.address,
value: txAmount, to: currentState.address,
data, amount: currentState.amount,
chainId: web3.chainId, data: currentState.data,
nonce: w3.toHex(nonce), gasLimit: currentState.gasLimit,
gasLimit: w3.toHex(currentState.gasLimit), gasPrice: currentState.gasPrice,
gasPrice: w3.toHex(EthereumjsUnits.convert(currentState.gasPrice, 'gwei', 'wei')), nonce
r: '', }) );
s: '',
v: '', // let data: string = `0x${currentState.data}`;
}; // let txAmount: string = w3.toHex(w3.toWei(currentState.amount, 'ether'));
// let txAddress: string = currentState.address;
// if (isToken) {
// const token: ?Token = findToken(getState().tokens, account.address, currentState.currency, account.deviceState);
// if (!token) return;
// const contract = web3.erc20.at(token.address);
// const amountValue: string = new BigNumber(currentState.amount).times(Math.pow(10, token.decimals)).toString(10);
// data = contract.transfer.getData(currentState.address, amountValue, {
// from: account.address,
// gasLimit: currentState.gasLimit,
// gasPrice: currentState.gasPrice,
// });
// txAmount = '0x00';
// txAddress = token.address;
// }
// console.warn('NONCE', nonce, account.nonce, pendingNonce);
// const txData = {
// address_n,
// // from: currentAddress.address
// to: txAddress,
// value: txAmount,
// data,
// chainId: web3.chainId,
// nonce: w3.toHex(nonce),
// gasLimit: w3.toHex(currentState.gasLimit),
// gasPrice: w3.toHex(EthereumjsUnits.convert(currentState.gasPrice, 'gwei', 'wei')),
// r: '',
// s: '',
// v: '',
// };
const selected: ?TrezorDevice = getState().wallet.selectedDevice; const selected: ?TrezorDevice = getState().wallet.selectedDevice;
if (!selected) return; if (!selected) return;
@ -861,9 +867,17 @@ export const onSend = (): AsyncAction => async (dispatch: Dispatch, getState: Ge
txData.v = signedTransaction.payload.v; txData.v = signedTransaction.payload.v;
try { try {
const tx = new EthereumjsTx(txData); const serializedTx: string = await dispatch( serializeEthereumTx(txData) );
const serializedTx = `0x${tx.serialize().toString('hex')}`; const push = await TrezorConnect.pushTransaction({
const txid: string = await pushTx(w3, serializedTx); tx: serializedTx,
coin: network.network
});
if (!push.success) {
throw new Error( push.payload.error );
}
const txid = push.payload.txid;
dispatch({ dispatch({
type: SEND.TX_COMPLETE, type: SEND.TX_COMPLETE,
@ -871,7 +885,7 @@ export const onSend = (): AsyncAction => async (dispatch: Dispatch, getState: Ge
selectedCurrency: currentState.currency, selectedCurrency: currentState.currency,
amount: currentState.amount, amount: currentState.amount,
total: currentState.total, total: currentState.total,
tx, tx: txData,
nonce, nonce,
txid, txid,
txData, txData,

@ -9,7 +9,7 @@ import type {
import type { State, Token } from 'reducers/TokensReducer'; import type { State, Token } from 'reducers/TokensReducer';
import type { Account } from 'reducers/AccountsReducer'; import type { Account } from 'reducers/AccountsReducer';
import type { NetworkToken } from 'reducers/LocalStorageReducer'; import type { NetworkToken } from 'reducers/LocalStorageReducer';
import { getTokenInfoAsync, getTokenBalanceAsync } from './Web3Actions'; import * as BlockchainActions from './BlockchainActions';
export type TokenAction = { export type TokenAction = {
type: typeof TOKEN.FROM_STORAGE, type: typeof TOKEN.FROM_STORAGE,
@ -42,15 +42,11 @@ export const load = (input: string, network: string): AsyncAction => async (disp
// when options is a large list (>200 items) // when options is a large list (>200 items)
return result.slice(0, 100); return result.slice(0, 100);
} }
const web3instance = getState().web3.find(w3 => w3.network === network);
if (!web3instance) return;
const info = await getTokenInfoAsync(web3instance.erc20, input); const info = await dispatch( BlockchainActions.getTokenInfo(input, network) );
if (info) { if (info) {
return [info]; return [info];
} }
//await resolveAfter(300000);
//await resolveAfter(3000);
}; };
export const setBalance = (tokenAddress: string, ethAddress: string, balance: string): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => { export const setBalance = (tokenAddress: string, ethAddress: string, balance: string): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
@ -68,9 +64,6 @@ export const setBalance = (tokenAddress: string, ethAddress: string, balance: st
}; };
export const add = (token: NetworkToken, account: Account): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => { export const add = (token: NetworkToken, account: Account): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const web3instance = getState().web3.find(w3 => w3.network === account.network);
if (!web3instance) return;
const tkn: Token = { const tkn: Token = {
loaded: false, loaded: false,
deviceState: account.deviceState, deviceState: account.deviceState,
@ -88,7 +81,7 @@ export const add = (token: NetworkToken, account: Account): AsyncAction => async
payload: tkn, payload: tkn,
}); });
const tokenBalance = await getTokenBalanceAsync(web3instance.erc20, tkn); const tokenBalance = await dispatch( BlockchainActions.getTokenBalance(tkn) );
dispatch(setBalance(token.address, account.address, tokenBalance)); dispatch(setBalance(token.address, account.address, tokenBalance));
}; };

@ -1,6 +1,6 @@
/* @flow */ /* @flow */
import TrezorConnect, { import TrezorConnect, {
DEVICE, DEVICE_EVENT, UI_EVENT, TRANSPORT_EVENT, UI, DEVICE, DEVICE_EVENT, UI_EVENT, TRANSPORT_EVENT, BLOCKCHAIN_EVENT
} from 'trezor-connect'; } from 'trezor-connect';
import * as CONNECT from 'actions/constants/TrezorConnect'; import * as CONNECT from 'actions/constants/TrezorConnect';
import * as NOTIFICATION from 'actions/constants/notification'; import * as NOTIFICATION from 'actions/constants/notification';
@ -12,11 +12,13 @@ import { push } from 'react-router-redux';
import type { import type {
DeviceMessage, DeviceMessage,
DeviceMessageType,
UiMessage, UiMessage,
UiMessageType,
TransportMessage, TransportMessage,
DeviceMessageType,
TransportMessageType, TransportMessageType,
UiMessageType, BlockchainMessage,
BlockchainMessageType,
} from 'trezor-connect'; } from 'trezor-connect';
import type { import type {
@ -115,6 +117,15 @@ export const init = (): AsyncAction => async (dispatch: Dispatch, getState: GetS
}); });
}); });
TrezorConnect.on(BLOCKCHAIN_EVENT, (event: BlockchainMessage): void => {
// post event to reducers
const type: BlockchainMessageType = event.type; // assert flow type
dispatch({
type,
payload: event.payload,
});
});
// $FlowIssue LOCAL not declared // $FlowIssue LOCAL not declared
// window.__TREZOR_CONNECT_SRC = typeof LOCAL === 'string' ? LOCAL : 'https://sisyfos.trezor.io/connect/'; // window.__TREZOR_CONNECT_SRC = typeof LOCAL === 'string' ? LOCAL : 'https://sisyfos.trezor.io/connect/';
// window.__TREZOR_CONNECT_SRC = typeof LOCAL === 'string' ? LOCAL : 'https://connect.trezor.io/5/'; // window.__TREZOR_CONNECT_SRC = typeof LOCAL === 'string' ? LOCAL : 'https://connect.trezor.io/5/';

@ -1,13 +1,15 @@
/* @flow */ /* @flow */
import Web3 from 'web3'; import Web3 from 'web3';
import HDKey from 'hdkey';
import type { import BigNumber from 'bignumber.js';
ContractFactory,
EstimateGasOptions, import EthereumjsUtil from 'ethereumjs-util';
TransactionStatus, import EthereumjsUnits from 'ethereumjs-units';
TransactionReceipt, import EthereumjsTx from 'ethereumjs-tx';
} from 'web3'; import InputDataDecoder from 'ethereum-input-data-decoder';
import type BigNumber from 'bignumber.js'; import TrezorConnect from 'trezor-connect';
import type { EstimateGasOptions, TransactionStatus, TransactionReceipt } from 'web3';
import { strip } from 'utils/ethUtils';
import * as WEB3 from 'actions/constants/web3'; import * as WEB3 from 'actions/constants/web3';
import * as PENDING from 'actions/constants/pendingTx'; import * as PENDING from 'actions/constants/pendingTx';
@ -15,6 +17,10 @@ import type {
Dispatch, Dispatch,
GetState, GetState,
AsyncAction, AsyncAction,
PromiseAction,
AccountDiscovery,
EthereumTxRequest,
EthereumPreparedTx
} from 'flowtype'; } from 'flowtype';
import type { Account } from 'reducers/AccountsReducer'; import type { Account } from 'reducers/AccountsReducer';
@ -44,377 +50,335 @@ export type Web3Action = {
} | { } | {
type: typeof WEB3.CREATE, type: typeof WEB3.CREATE,
instance: Web3Instance instance: Web3Instance
} } | Web3UpdateBlockAction
| Web3UpdateBlockAction | Web3UpdateGasPriceAction;
| Web3UpdateGasPriceAction;
export const initWeb3 = (network: string, urlIndex: number = 0): PromiseAction<Web3Instance> => async (dispatch: Dispatch, getState: GetState): Promise<Web3Instance> => {
return new Promise(async (resolve, reject) => {
// check if requested web was initialized before
const instance = getState().web3.find(w3 => w3.network === network);
if (instance && instance.web3.currentProvider.connected) {
resolve(instance);
return;
}
export function init(instance: ?Web3, coinIndex: number = 0): AsyncAction { // requested web3 wasn't initialized or is disconnected
return async (dispatch: Dispatch, getState: GetState): Promise<void> => { // initialize again
const { config, ERC20Abi } = getState().localStorage; const { config, ERC20Abi } = getState().localStorage;
const coin = config.coins.find(c => c.network === network);
const coin = config.coins[coinIndex];
if (!coin) { if (!coin) {
// all instances done // coin not found
dispatch({ reject(new Error(`Network ${ network} not found in application config.`));
type: WEB3.READY,
});
return; return;
} }
const { network } = coin; // get first url
const urls = coin.backends[0].urls; const url = coin.web3[ urlIndex ];
if (!url) {
let web3host: string = urls[0]; reject(new Error('Web3 backend is not responding'));
return;
if (instance) {
const currentHost = instance.currentProvider.host;
const currentHostIndex: number = urls.indexOf(currentHost);
if (currentHostIndex + 1 < urls.length) {
web3host = urls[currentHostIndex + 1];
} else {
console.error(`TODO: Backend ${network} not working`, instance.currentProvider);
dispatch({
type: WEB3.CREATE,
instance: {
network,
web3: instance,
chainId: coin.chainId,
erc20: instance.eth.contract(ERC20Abi),
latestBlock: '0',
gasPrice: '0',
},
});
// try next coin
dispatch(init(null, coinIndex + 1));
return;
}
} }
//const instance = new Web3(window.web3.currentProvider); const web3 = new Web3( new Web3.providers.WebsocketProvider(url) );
const web3 = new Web3(new Web3.providers.HttpProvider(web3host));
// instance = new Web3( new Web3.providers.HttpProvider('https://pyrus2.ubiqscan.io') ); // UBQ
//instance = new Web3( new Web3.providers.HttpProvider('https://node.expanse.tech/') ); // EXP
//instance = new Web3( new Web3.providers.HttpProvider('http://10.34.0.91:8545/') );
//web3 = new Web3(new Web3.providers.HttpProvider("https://api.myetherapi.com/rop"));
//instance = new Web3(new Web3.providers.HttpProvider("https://ropsten.infura.io2/QGyVKozSUEh2YhL4s2G4"));
//web3 = new Web3( new Web3.providers.HttpProvider("ws://34.230.234.51:30303") );
// initial check if backend is running const onConnect = async () => {
if (!web3.currentProvider.isConnected()) {
// try different url
dispatch(init(web3, coinIndex));
return;
}
const erc20 = web3.eth.contract(ERC20Abi); const latestBlock = await web3.eth.getBlockNumber();
const gasPrice = await web3.eth.getGasPrice();
dispatch({ const instance = {
type: WEB3.CREATE,
instance: {
network, network,
web3, web3,
chainId: coin.chainId, chainId: coin.chainId,
erc20, erc20: new web3.eth.Contract(ERC20Abi),
latestBlock: '0', latestBlock,
gasPrice: '0', gasPrice,
}, }
});
// dispatch({
// type: WEB3.GAS_PRICE_UPDATED,
// network,
// gasPrice
// });
// console.log("GET CHAIN", instance.version.network)
// instance.version.getWhisper((err, shh) => {
// console.log("-----whisperrr", error, shh)
// })
// const sshFilter = instance.ssh.filter('latest'); console.warn("CONNECT", web3)
// sshFilter.watch((error, blockHash) => {
// console.warn("SSH", error, blockHash);
// });
//const shh = instance.shh.newIdentity(); dispatch({
type: WEB3.CREATE,
instance,
});
// const latestBlockFilter = web3.eth.filter('latest'); // await dispatch( _onNewBlock(instance) );
const onBlockMined = async (error: ?Error, blockHash: ?string) => { resolve(instance);
if (error) { }
window.setTimeout(() => {
// try again
onBlockMined(new Error('manually_triggered_error'), undefined);
}, 30000);
}
if (blockHash) {
dispatch({
type: WEB3.BLOCK_UPDATED,
network,
blockHash,
});
}
// TODO: filter only current device const onEnd = async () => {
const accounts = getState().accounts.filter(a => a.network === network); web3.currentProvider.removeAllListeners('connect');
for (const account of accounts) { web3.currentProvider.removeAllListeners('end');
const nonce = await getNonceAsync(web3, account.address); web3.currentProvider.removeAllListeners('error');
if (nonce !== account.nonce) { web3.currentProvider.reset();
dispatch(AccountsActions.setNonce(account.address, account.network, account.deviceState, nonce)); // if (web3.eth)
// web3.eth.clearSubscriptions();
// dispatch( getBalance(account) );
// TODO: check if nonce was updated, const instance = getState().web3.find(w3 => w3.network === network);
// update tokens balance,
// update account balance, if (instance && instance.web3.currentProvider.connected) {
// update pending transactions // backend disconnects
// dispatch({
// type: 'WEB3.DISCONNECT',
// network
// });
} else {
// backend initialization error for given url, try next one
try {
const web3 = await dispatch( initWeb3(network, urlIndex + 1) );
resolve(web3);
} catch (error) {
reject(error);
} }
dispatch(getBalance(account));
// dispatch( getNonce(account) );
} }
}
const tokens = getState().tokens.filter(t => t.network === network); web3.currentProvider.on('connect', onConnect);
tokens.forEach(token => dispatch(getTokenBalance(token))); web3.currentProvider.on('end', onEnd);
web3.currentProvider.on('error', onEnd);
dispatch(getGasPrice(network)); });
}
const pending = getState().pending.filter(p => p.network === network);
pending.forEach(pendingTx => dispatch(getTransactionReceipt(pendingTx)));
};
// latestBlockFilter.watch(onBlockMined);
onBlockMined(new Error('manually_triggered_error'), undefined);
// init next coin const _onNewBlock = (instance: Web3Instance): PromiseAction<void> => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
dispatch(init(web3, coinIndex + 1));
// const latestBlock = await instance.web3.eth.getBlockNumber();
// let instance2 = new Web3( new Web3.providers.HttpProvider('https://pyrus2.ubiqscan.io') ); // dispatch({
// console.log("INIT WEB3", instance, instance2); // type: WEB3.BLOCK_UPDATED,
// instance2.eth.getGasPrice((error, gasPrice) => { // network: instance.network,
// console.log("---gasss price from UBQ", gasPrice) // blockHash: latestBlock,
// }); // });
};
}
export function getGasPrice(network: string): AsyncAction { // TODO: filter only current device
return async (dispatch: Dispatch, getState: GetState): Promise<void> => { const accounts = getState().accounts.filter(a => a.network === instance.network);
const index: number = getState().web3.findIndex(w3 => w3.network === network); for (const account of accounts) {
const nonce = await instance.web3.eth.getTransactionCount(account.address);
if (nonce !== account.nonce) {
dispatch(AccountsActions.setNonce(account.address, account.network, account.deviceState, nonce));
}
const web3instance = getState().web3[index]; const balance = await instance.web3.eth.getBalance(account.address);
const { web3 } = web3instance; const newBalance = EthereumjsUnits.convert(balance, 'wei', 'ether');
web3.eth.getGasPrice((error, gasPrice) => { if (newBalance !== account.balance) {
if (!error) { dispatch(AccountsActions.setBalance(
if (web3instance.gasPrice && web3instance.gasPrice.toString() !== gasPrice.toString()) { account.address,
dispatch({ account.network,
type: WEB3.GAS_PRICE_UPDATED, account.deviceState,
network, newBalance
gasPrice, ));
}); }
} }
}
}); const tokens = getState().tokens.filter(t => t.network === instance.network);
}; for (const token of tokens) {
} const balance = await dispatch( getTokenBalance(token) );
console.warn("TOK BALAC", balance)
// const newBalance: string = balance.dividedBy(Math.pow(10, token.decimals)).toString(10);
if (balance !== token.balance) {
dispatch(TokenActions.setBalance(
token.address,
token.ethAddress,
balance,
));
}
}
export function getBalance(account: Account): AsyncAction { // dispatch(getGasPrice(network));
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const web3instance = getState().web3.filter(w3 => w3.network === account.network)[0];
const { web3 } = web3instance;
web3.eth.getBalance(account.address, (error: Error, balance: BigNumber) => {
if (!error) {
const newBalance: string = web3.fromWei(balance.toString(), 'ether');
if (account.balance !== newBalance) {
dispatch(AccountsActions.setBalance(
account.address,
account.network,
account.deviceState,
newBalance,
));
// dispatch( loadHistory(addr) );
}
}
});
};
}
export function getTokenBalance(token: Token): AsyncAction {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const web3instance = getState().web3.filter(w3 => w3.network === token.network)[0];
const contract = web3instance.erc20.at(token.address);
contract.balanceOf(token.ethAddress, (error: Error, balance: BigNumber) => {
if (balance) {
const newBalance: string = balance.dividedBy(Math.pow(10, token.decimals)).toString(10);
if (newBalance !== token.balance) {
dispatch(TokenActions.setBalance(
token.address,
token.ethAddress,
newBalance,
));
}
}
});
};
} }
export function getNonce(account: Account): AsyncAction { export const discoverAccount = (address: string, network: string): PromiseAction<AccountDiscovery> => async (dispatch: Dispatch, getState: GetState): Promise<AccountDiscovery> => {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => { const instance: Web3Instance = await dispatch( initWeb3(network) );
const web3instance = getState().web3.filter(w3 => w3.network === account.network)[0]; const balance = await instance.web3.eth.getBalance(address);
const { web3 } = web3instance; const nonce = await instance.web3.eth.getTransactionCount(address);
return {
web3.eth.getTransactionCount(account.address, (error: Error, result: number) => { transactions: 0,
if (!error) { block: 0,
if (account.nonce !== result) { balance: EthereumjsUnits.convert(balance, 'wei', 'ether'),
dispatch(AccountsActions.setNonce(account.address, account.network, account.deviceState, result)); nonce
}
}
});
}; };
} }
export const getTransactionReceipt = (tx: PendingTx): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => { export const resolvePendingTransactions = (network: string): PromiseAction<void> => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const web3instance = getState().web3.filter(w3 => w3.network === tx.network)[0]; const instance: Web3Instance = await dispatch( initWeb3(network) );
const { web3 } = web3instance; const pending = getState().pending.filter(p => p.network === network);
for (const tx of pending) {
web3.eth.getTransaction(tx.id, (error: Error, status: TransactionStatus) => { const status = await instance.web3.eth.getTransaction(tx.id);
if (!error && !status) { if (!status) {
dispatch({ dispatch({
type: PENDING.TX_NOT_FOUND, type: PENDING.TX_NOT_FOUND,
tx, tx,
}); });
} else if (status && status.blockNumber) { } else {
web3.eth.getTransactionReceipt(tx.id, (error: Error, receipt: TransactionReceipt) => { const receipt = await instance.web3.eth.getTransactionReceipt(tx.id);
if (receipt) { if (receipt) {
if (status.gas !== receipt.gasUsed) { if (status.gas !== receipt.gasUsed) {
dispatch({
type: PENDING.TX_TOKEN_ERROR,
tx,
});
}
dispatch({ dispatch({
type: PENDING.TX_RESOLVED, type: PENDING.TX_TOKEN_ERROR,
tx, tx,
receipt,
}); });
} }
}); dispatch({
type: PENDING.TX_RESOLVED,
tx,
receipt,
});
}
} }
}); }
}; }
export const getTransaction = (web3: Web3, txid: string): Promise<any> => new Promise((resolve, reject) => { export const getPendingInfo = (network: string, txid: string): PromiseAction<void> => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
web3.eth.getTransaction(txid, (error, result) => { const instance: Web3Instance = await dispatch( initWeb3(network) );
if (error) { const tx = await instance.web3.eth.getTransaction(txid);
reject(error);
} else {
resolve(result);
}
});
});
export const getBalanceAsync = (web3: Web3, address: string): Promise<BigNumber> => new Promise((resolve, reject) => { /*
web3.eth.getBalance(address, (error: Error, result: BigNumber) => { if (tx.input !== "0x") {
if (error) { // find token:
reject(error); // tx.to <= smart contract address
} else {
resolve(result);
}
});
});
export const getTokenBalanceAsync = (erc20: ContractFactory, token: Token): Promise<string> => new Promise((resolve, reject) => { // smart contract data
const contract = erc20.at(token.address); const decoder = new InputDataDecoder(instance.erc20.options.jsonInterface);
contract.balanceOf(token.ethAddress, (error: Error, balance: BigNumber) => { const data = decoder.decodeData(tx.input);
if (error) { if (data.name === 'transfer') {
reject(error); console.warn("DATA!", data.inputs[0], data.inputs[1].toString(10));
} else {
const newBalance: string = balance.dividedBy(Math.pow(10, token.decimals)).toString(10);
resolve(newBalance);
} }
});
});
export const getNonceAsync = (web3: Web3, address: string): Promise<number> => new Promise((resolve, reject) => { }
web3.eth.getTransactionCount(address, (error: Error, result: number) => { */
if (error) { // return tx;
reject(error); }
} else {
resolve(result);
}
});
});
export const getTxInput = (): PromiseAction<void> => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const instance: Web3Instance = await dispatch( initWeb3("ropsten") );
console.warn("GETTX", instance.erc20.options.jsonInterface)
// const inputData = instance.web3.utils.hexToAscii("0xa9059cbb00000000000000000000000073d0385f4d8e00c5e6504c6030f47bf6212736a80000000000000000000000000000000000000000000000000000000000000001");
// console.warn("input data!", inputData);
}
export const getTokenInfoAsync = (erc20: ContractFactory, address: string): Promise<?NetworkToken> => new Promise((resolve) => {
const contract = erc20.at(address, (error/* , res */) => {
// console.warn("callback", error, res)
});
const info: NetworkToken = { export const updateAccount = (account: Account, newAccount: AccountDiscovery, network: string): PromiseAction<void> => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const instance: Web3Instance = await dispatch( initWeb3(network) );
const balance = await instance.web3.eth.getBalance(account.address);
const nonce = await instance.web3.eth.getTransactionCount(account.address);
dispatch( AccountsActions.update( { ...account, ...newAccount, balance: EthereumjsUnits.convert(balance, 'wei', 'ether'), nonce }) );
// TODO update tokens for this account
}
export const getTokenInfo = (address: string, network: string): PromiseAction<NetworkToken> => async (dispatch: Dispatch, getState: GetState): Promise<NetworkToken> => {
const instance: Web3Instance = await dispatch( initWeb3(network) );
const contract = instance.erc20.clone();
contract.options.address = address;
const name = await contract.methods.name().call();
const symbol = await contract.methods.symbol().call();
const decimals = await contract.methods.decimals().call();
return {
address, address,
name: '', name,
symbol: '', symbol,
decimals: 0, decimals,
}; };
};
contract.name.call((error: Error, name: string) => { export const getTokenBalance = (token: Token): PromiseAction<string> => async (dispatch: Dispatch, getState: GetState): Promise<string> => {
if (error) { const instance = await dispatch( initWeb3(token.network) );
resolve(null); const contract = instance.erc20.clone();
return; contract.options.address = token.address;
}
info.name = name;
const balance = await contract.methods.balanceOf(token.ethAddress).call();
return new BigNumber(balance).dividedBy(Math.pow(10, token.decimals)).toString(10);
};
contract.symbol.call((error: Error, symbol: string) => { export const getCurrentGasPrice = (network: string): PromiseAction<string> => async (dispatch: Dispatch, getState: GetState): Promise<string> => {
if (error) { const instance = getState().web3.find(w3 => w3.network === network);
resolve(null); if (instance) {
return; return EthereumjsUnits.convert(instance.gasPrice, 'wei', 'gwei');
} } else {
info.symbol = symbol; return "0";
}
// const index: number = getState().web3.findIndex(w3 => w3.network === network);
// const web3instance = getState().web3[index];
// const web3 = web3instance.web3;
// web3.eth.getGasPrice((error, gasPrice) => {
// if (!error) {
// if (web3instance.gasPrice && web3instance.gasPrice.toString() !== gasPrice.toString()) {
// dispatch({
// type: WEB3.GAS_PRICE_UPDATED,
// network,
// gasPrice,
// });
// }
// }
// });
}
contract.decimals.call((error: Error, decimals: BigNumber) => { export const estimateGasLimit = (network: string, options: EstimateGasOptions): PromiseAction<number> => async (dispatch: Dispatch, getState: GetState): Promise<number> => {
if (decimals) { const instance = await dispatch( initWeb3(network) );
info.decimals = decimals.toNumber(); // TODO: allow data starting with 0x ...
resolve(info); options.to = '0x0000000000000000000000000000000000000000';
} else { options.data = `0x${options.data.length % 2 === 0 ? options.data : `0${options.data}`}`;
resolve(null); options.value = instance.web3.utils.toHex( EthereumjsUnits.convert(options.value || '0', 'ether', 'wei') );
} options.gasPrice = instance.web3.utils.toHex( EthereumjsUnits.convert(options.gasPrice, 'gwei', 'wei') );
});
});
});
});
export const estimateGas = (web3: Web3, options: EstimateGasOptions): Promise<number> => new Promise((resolve, reject) => {
web3.eth.estimateGas(options, (error: ?Error, gas: ?number) => {
if (error) {
reject(error);
} else if (typeof gas === 'number') {
resolve(gas);
}
});
});
export const pushTx = (web3: Web3, tx: any): Promise<string> => new Promise((resolve, reject) => { const limit = await instance.web3.eth.estimateGas(options);
web3.eth.sendRawTransaction(tx, (error: Error, result: string) => { return limit;
if (error) { };
reject(error);
} else { // export const prepareTx = (tx: EthereumTxRequest): PromiseAction<EthereumPreparedTx> => async (dispatch: Dispatch, getState: GetState): Promise<EthereumPreparedTx> => {
resolve(result);
} // const instance = await dispatch( initWeb3(tx.network) );
}); // const token = tx.token;
}); // let data: string = `0x${tx.data}`; // TODO: check if already prefixed
// let value: string = instance.web3.utils.toHex( EthereumjsUnits.convert(tx.amount, 'ether', 'wei') );
// let to: string = tx.to;
// if (token) {
// // smart contract transaction
// const contract = instance.erc20.clone();
// contract.options.address = token.address;
// const tokenAmount: string = new BigNumber(tx.amount).times(Math.pow(10, token.decimals)).toString(10);
// data = instance.erc20.methods.transfer(to, tokenAmount).encodeABI();
// value = '0x00';
// to = token.address;
// }
// return {
// to,
// value,
// data,
// chainId: instance.chainId,
// nonce: instance.web3.utils.toHex(tx.nonce),
// gasLimit: instance.web3.utils.toHex(tx.gasLimit),
// gasPrice: instance.web3.utils.toHex( EthereumjsUnits.convert(tx.gasPrice, 'gwei', 'wei') ),
// r: '',
// s: '',
// v: '',
// }
// };
// export const pushTx = (network: string, tx: EthereumPreparedTx): PromiseAction<string> => async (dispatch: Dispatch, getState: GetState): Promise<string> => {
// const instance = await dispatch( initWeb3(network) );
// const ethTx = new EthereumjsTx(tx);
// const serializedTx = `0x${ethTx.serialize().toString('hex')}`;
// return new Promise((resolve, reject) => {
// instance.web3.eth.sendSignedTransaction(serializedTx)
// .on('error', error => reject(error))
// .once('transactionHash', (hash: string) => {
// resolve(hash);
// });
// });
// };

@ -6,6 +6,7 @@ export const DISPOSE: 'account__dispose' = 'account__dispose';
export const CREATE: 'account__create' = 'account__create'; export const CREATE: 'account__create' = 'account__create';
export const REMOVE: 'account__remove' = 'account__remove'; export const REMOVE: 'account__remove' = 'account__remove';
export const UPDATE: 'account__update' = 'account__update';
export const SET_BALANCE: 'account__set_balance' = 'account__set_balance'; export const SET_BALANCE: 'account__set_balance' = 'account__set_balance';
export const SET_NONCE: 'account__set_nonce' = 'account__set_nonce'; export const SET_NONCE: 'account__set_nonce' = 'account__set_nonce';
export const FROM_STORAGE: 'account__from_storage' = 'account__from_storage'; export const FROM_STORAGE: 'account__from_storage' = 'account__from_storage';

@ -2,6 +2,7 @@
export const FROM_STORAGE: 'pending__from_storage' = 'pending__from_storage'; export const FROM_STORAGE: 'pending__from_storage' = 'pending__from_storage';
export const ADD: 'pending__add' = 'pending__add';
export const TX_RESOLVED: 'pending__tx_resolved' = 'pending__tx_resolved'; export const TX_RESOLVED: 'pending__tx_resolved' = 'pending__tx_resolved';
export const TX_NOT_FOUND: 'pending__tx_not_found' = 'pending__tx_not_found'; export const TX_NOT_FOUND: 'pending__tx_not_found' = 'pending__tx_not_found';
export const TX_TOKEN_ERROR: 'pending__tx_token_error' = 'pending__tx_token_error'; export const TX_TOKEN_ERROR: 'pending__tx_token_error' = 'pending__tx_token_error';

@ -1,11 +1,12 @@
/* @flow */ /* @flow */
import { push } from 'react-router-redux'; import { push } from 'react-router-redux';
import { import TrezorConnect, {
TRANSPORT, DEVICE, TRANSPORT, DEVICE_EVENT, UI_EVENT, UI, DEVICE, BLOCKCHAIN
} from 'trezor-connect'; } from 'trezor-connect';
import * as TrezorConnectActions from 'actions/TrezorConnectActions'; import * as TrezorConnectActions from 'actions/TrezorConnectActions';
import * as DiscoveryActions from 'actions/DiscoveryActions'; import * as DiscoveryActions from 'actions/DiscoveryActions';
import * as BlockchainActions from 'actions/BlockchainActions';
import * as ModalActions from 'actions/ModalActions'; import * as ModalActions from 'actions/ModalActions';
import * as STORAGE from 'actions/constants/localStorage'; import * as STORAGE from 'actions/constants/localStorage';
import * as CONNECT from 'actions/constants/TrezorConnect'; import * as CONNECT from 'actions/constants/TrezorConnect';
@ -32,6 +33,9 @@ const TrezorConnectService: Middleware = (api: MiddlewareAPI) => (next: Middlewa
// api.dispatch( push('/') ); // api.dispatch( push('/') );
} else if (action.type === TRANSPORT.START) { } else if (action.type === TRANSPORT.START) {
api.dispatch(TrezorConnectActions.postInit()); api.dispatch(TrezorConnectActions.postInit());
api.dispatch( BlockchainActions.subscribe('ropsten') );
} else if (action.type === DEVICE.DISCONNECT) { } else if (action.type === DEVICE.DISCONNECT) {
api.dispatch(TrezorConnectActions.deviceDisconnect(action.device)); api.dispatch(TrezorConnectActions.deviceDisconnect(action.device));
} else if (action.type === CONNECT.REMEMBER_REQUEST) { } else if (action.type === CONNECT.REMEMBER_REQUEST) {
@ -59,6 +63,10 @@ const TrezorConnectService: Middleware = (api: MiddlewareAPI) => (next: Middlewa
api.dispatch(TrezorConnectActions.onSelectDevice(action.device)); api.dispatch(TrezorConnectActions.onSelectDevice(action.device));
} else if (action.type === CONNECT.COIN_CHANGED) { } else if (action.type === CONNECT.COIN_CHANGED) {
api.dispatch(TrezorConnectActions.coinChanged(action.payload.network)); api.dispatch(TrezorConnectActions.coinChanged(action.payload.network));
} else if (action.type === BLOCKCHAIN.BLOCK) {
// api.dispatch(BlockchainActions.onBlockMined(action.payload.coin));
} else if (action.type === BLOCKCHAIN.NOTIFICATION) {
// api.dispatch(BlockchainActions.onNotification(action.payload));
} }
return action; return action;

Loading…
Cancel
Save