/* @flow */ import TrezorConnect from 'trezor-connect'; import BigNumber from 'bignumber.js'; import * as PENDING from 'actions/constants/pendingTx'; import type { TrezorDevice, Dispatch, GetState, PromiseAction } from 'flowtype'; import type { EthereumAccount, BlockchainNotification } from 'trezor-connect'; import type { Token } from 'reducers/TokensReducer'; import type { NetworkToken } from 'reducers/LocalStorageReducer'; import * as Web3Actions from 'actions/Web3Actions'; import * as AccountsActions from 'actions/AccountsActions'; export const discoverAccount = ( device: TrezorDevice, descriptor: string, network: string ): PromiseAction => async (dispatch: Dispatch): Promise => { // get data from connect const txs = await TrezorConnect.ethereumGetAccountInfo({ account: { descriptor, block: 0, transactions: 0, balance: '0', availableBalance: '0', nonce: 0, }, coin: network, }); if (!txs.success) { throw new Error(txs.payload.error); } // blockbook web3 fallback const web3account = await dispatch(Web3Actions.discoverAccount(descriptor, network)); return { descriptor, transactions: txs.payload.transactions, block: txs.payload.block, balance: web3account.balance, availableBalance: web3account.balance, nonce: web3account.nonce, }; }; export const getTokenInfo = (input: string, network: string): PromiseAction => async ( dispatch: Dispatch ): Promise => dispatch(Web3Actions.getTokenInfo(input, network)); export const getTokenBalance = (token: Token): PromiseAction => async ( dispatch: Dispatch ): Promise => dispatch(Web3Actions.getTokenBalance(token)); export const getGasPrice = ( network: string, defaultGasPrice: number ): PromiseAction => async (dispatch: Dispatch): Promise => { try { const gasPrice = await dispatch(Web3Actions.getCurrentGasPrice(network)); return gasPrice === '0' ? new BigNumber(defaultGasPrice) : new BigNumber(gasPrice); } catch (error) { return new BigNumber(defaultGasPrice); } }; const estimateProxy: Array> = []; export const estimateGasLimit = ( network: string, data: string, value: string, gasPrice: string ): PromiseAction => async (dispatch: Dispatch): Promise => { // Since this method could be called multiple times in short period of time // check for pending calls in proxy and if there more than two (first is current running and the second is waiting for result of first) // TODO: should reject second call immediately? if (estimateProxy.length > 0) { // wait for proxy result (but do not process it) await estimateProxy[0]; } const call = dispatch( Web3Actions.estimateGasLimit(network, { to: '', data, value, gasPrice, }) ); // add current call to proxy estimateProxy.push(call); // wait for result const result = await call; // remove current call from proxy estimateProxy.splice(0, 1); // return result return result; }; export const subscribe = (network: string): PromiseAction => async ( dispatch: Dispatch, getState: GetState ): Promise => { const accounts: Array = getState() .accounts.filter(a => a.network === network) .map(a => a.descriptor); // eslint-disable-line no-unused-vars const response = await TrezorConnect.blockchainSubscribe({ accounts, coin: network, }); if (!response.success) return; // init web3 instance if not exists await dispatch(Web3Actions.initWeb3(network)); }; export const onBlockMined = (network: string): PromiseAction => async ( dispatch: Dispatch, getState: GetState ): Promise => { // TODO: handle rollback, // check latest saved transaction blockhash against blockhheight // try to resolve pending transactions await dispatch(Web3Actions.resolvePendingTransactions(network)); await dispatch(Web3Actions.updateGasPrice(network)); const accounts: Array = getState().accounts.filter(a => a.network === network); if (accounts.length > 0) { // find out which account changed const response = await TrezorConnect.ethereumGetAccountInfo({ accounts, coin: network, }); if (response.success) { response.payload.forEach((a, i) => { if (a.transactions > 0) { // load additional data from Web3 (balance, nonce, tokens) dispatch(Web3Actions.updateAccount(accounts[i], a, network)); } else { // there are no new txs, just update block dispatch(AccountsActions.update({ ...accounts[i], block: a.block })); // HACK: since blockbook can't work with smart contracts for now // try to update tokens balances added to this account using Web3 dispatch(Web3Actions.updateAccountTokens(accounts[i])); } }); } } }; export const onNotification = ( payload: $ElementType ): PromiseAction => async (dispatch: Dispatch, getState: GetState): Promise => { const { notification } = payload; const account = getState().accounts.find(a => a.descriptor === notification.descriptor); if (!account) return; if (!notification.blockHeight) { dispatch({ type: PENDING.ADD, payload: { ...notification, deviceState: account.deviceState, network: account.network, }, }); } }; export const onError = (network: string): PromiseAction => async ( dispatch: Dispatch ): Promise => { dispatch(Web3Actions.disconnect(network)); };