2018-12-03 18:04:57 +00:00
|
|
|
/* @flow */
|
|
|
|
|
|
|
|
import TrezorConnect from 'trezor-connect';
|
|
|
|
import BigNumber from 'bignumber.js';
|
2018-12-18 21:10:20 +00:00
|
|
|
import * as PENDING from 'actions/constants/pendingTx';
|
2018-12-03 18:04:57 +00:00
|
|
|
|
2019-03-04 12:33:02 +00:00
|
|
|
import type { TrezorDevice, Dispatch, GetState, PromiseAction } from 'flowtype';
|
2018-12-18 21:10:20 +00:00
|
|
|
import type { EthereumAccount, BlockchainNotification } from 'trezor-connect';
|
2018-12-03 18:04:57 +00:00
|
|
|
import type { Token } from 'reducers/TokensReducer';
|
|
|
|
import type { NetworkToken } from 'reducers/LocalStorageReducer';
|
|
|
|
import * as Web3Actions from 'actions/Web3Actions';
|
|
|
|
import * as AccountsActions from 'actions/AccountsActions';
|
|
|
|
|
2019-03-04 12:33:02 +00:00
|
|
|
export const discoverAccount = (
|
|
|
|
device: TrezorDevice,
|
|
|
|
descriptor: string,
|
|
|
|
network: string
|
|
|
|
): PromiseAction<EthereumAccount> => async (dispatch: Dispatch): Promise<EthereumAccount> => {
|
2018-12-03 18:04:57 +00:00
|
|
|
// get data from connect
|
|
|
|
const txs = await TrezorConnect.ethereumGetAccountInfo({
|
|
|
|
account: {
|
2019-01-04 13:32:14 +00:00
|
|
|
descriptor,
|
2018-12-03 18:04:57 +00:00
|
|
|
block: 0,
|
|
|
|
transactions: 0,
|
|
|
|
balance: '0',
|
2019-01-04 13:32:14 +00:00
|
|
|
availableBalance: '0',
|
2018-12-03 18:04:57 +00:00
|
|
|
nonce: 0,
|
|
|
|
},
|
|
|
|
coin: network,
|
|
|
|
});
|
|
|
|
|
|
|
|
if (!txs.success) {
|
|
|
|
throw new Error(txs.payload.error);
|
|
|
|
}
|
|
|
|
|
|
|
|
// blockbook web3 fallback
|
2019-01-04 13:32:14 +00:00
|
|
|
const web3account = await dispatch(Web3Actions.discoverAccount(descriptor, network));
|
2018-12-03 18:04:57 +00:00
|
|
|
return {
|
2019-01-04 13:32:14 +00:00
|
|
|
descriptor,
|
2018-12-03 18:04:57 +00:00
|
|
|
transactions: txs.payload.transactions,
|
|
|
|
block: txs.payload.block,
|
|
|
|
balance: web3account.balance,
|
2019-01-04 13:32:14 +00:00
|
|
|
availableBalance: web3account.balance,
|
2018-12-03 18:04:57 +00:00
|
|
|
nonce: web3account.nonce,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2019-03-04 12:33:02 +00:00
|
|
|
export const getTokenInfo = (input: string, network: string): PromiseAction<NetworkToken> => async (
|
|
|
|
dispatch: Dispatch
|
|
|
|
): Promise<NetworkToken> => dispatch(Web3Actions.getTokenInfo(input, network));
|
2018-12-03 18:04:57 +00:00
|
|
|
|
2019-03-04 12:33:02 +00:00
|
|
|
export const getTokenBalance = (token: Token): PromiseAction<string> => async (
|
|
|
|
dispatch: Dispatch
|
|
|
|
): Promise<string> => dispatch(Web3Actions.getTokenBalance(token));
|
2018-12-03 18:04:57 +00:00
|
|
|
|
2019-03-04 12:33:02 +00:00
|
|
|
export const getGasPrice = (
|
|
|
|
network: string,
|
|
|
|
defaultGasPrice: number
|
|
|
|
): PromiseAction<BigNumber> => async (dispatch: Dispatch): Promise<BigNumber> => {
|
2018-12-03 18:04:57 +00:00
|
|
|
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<Promise<string>> = [];
|
2019-03-04 12:33:02 +00:00
|
|
|
export const estimateGasLimit = (
|
|
|
|
network: string,
|
|
|
|
data: string,
|
|
|
|
value: string,
|
|
|
|
gasPrice: string
|
|
|
|
): PromiseAction<string> => async (dispatch: Dispatch): Promise<string> => {
|
2018-12-03 18:04:57 +00:00
|
|
|
// 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];
|
|
|
|
}
|
|
|
|
|
2019-03-04 12:33:02 +00:00
|
|
|
const call = dispatch(
|
|
|
|
Web3Actions.estimateGasLimit(network, {
|
|
|
|
to: '',
|
|
|
|
data,
|
|
|
|
value,
|
|
|
|
gasPrice,
|
|
|
|
})
|
|
|
|
);
|
2018-12-03 18:04:57 +00:00
|
|
|
// 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;
|
|
|
|
};
|
|
|
|
|
2019-03-04 12:33:02 +00:00
|
|
|
export const subscribe = (network: string): PromiseAction<void> => async (
|
|
|
|
dispatch: Dispatch,
|
|
|
|
getState: GetState
|
|
|
|
): Promise<void> => {
|
|
|
|
const accounts: Array<string> = getState()
|
|
|
|
.accounts.filter(a => a.network === network)
|
|
|
|
.map(a => a.descriptor); // eslint-disable-line no-unused-vars
|
2018-12-20 09:12:44 +00:00
|
|
|
const response = await TrezorConnect.blockchainSubscribe({
|
2018-12-03 18:04:57 +00:00
|
|
|
accounts,
|
|
|
|
coin: network,
|
|
|
|
});
|
2018-12-20 09:12:44 +00:00
|
|
|
if (!response.success) return;
|
2018-12-03 18:04:57 +00:00
|
|
|
// init web3 instance if not exists
|
|
|
|
await dispatch(Web3Actions.initWeb3(network));
|
|
|
|
};
|
|
|
|
|
2019-03-04 12:33:02 +00:00
|
|
|
export const onBlockMined = (network: string): PromiseAction<void> => async (
|
|
|
|
dispatch: Dispatch,
|
|
|
|
getState: GetState
|
|
|
|
): Promise<void> => {
|
2018-12-28 15:16:32 +00:00
|
|
|
// TODO: handle rollback,
|
|
|
|
// check latest saved transaction blockhash against blockhheight
|
|
|
|
|
2018-12-03 18:04:57 +00:00
|
|
|
// try to resolve pending transactions
|
|
|
|
await dispatch(Web3Actions.resolvePendingTransactions(network));
|
|
|
|
|
|
|
|
await dispatch(Web3Actions.updateGasPrice(network));
|
|
|
|
|
|
|
|
const accounts: Array<any> = 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]));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-03-04 12:33:02 +00:00
|
|
|
export const onNotification = (
|
|
|
|
payload: $ElementType<BlockchainNotification, 'payload'>
|
|
|
|
): PromiseAction<void> => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
2018-12-18 21:10:20 +00:00
|
|
|
const { notification } = payload;
|
2019-01-04 12:58:34 +00:00
|
|
|
const account = getState().accounts.find(a => a.descriptor === notification.descriptor);
|
2018-12-18 21:10:20 +00:00
|
|
|
if (!account) return;
|
|
|
|
|
2019-01-04 12:58:34 +00:00
|
|
|
if (!notification.blockHeight) {
|
2018-12-18 21:10:20 +00:00
|
|
|
dispatch({
|
|
|
|
type: PENDING.ADD,
|
|
|
|
payload: {
|
2018-12-19 16:46:07 +00:00
|
|
|
...notification,
|
2018-12-18 21:10:20 +00:00
|
|
|
deviceState: account.deviceState,
|
|
|
|
network: account.network,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
2018-12-03 18:04:57 +00:00
|
|
|
};
|
|
|
|
|
2019-03-04 12:33:02 +00:00
|
|
|
export const onError = (network: string): PromiseAction<void> => async (
|
|
|
|
dispatch: Dispatch
|
|
|
|
): Promise<void> => {
|
2018-12-03 18:04:57 +00:00
|
|
|
dispatch(Web3Actions.disconnect(network));
|
|
|
|
};
|