1
0
mirror of https://github.com/trezor/trezor-wallet synced 2024-11-14 04:19:09 +00:00

update Blockchain and Discovery using trezor-connect@8

This commit is contained in:
Szymon Lesisz 2020-03-30 20:44:17 +02:00
parent 486bfff397
commit 2231f42b37
10 changed files with 224 additions and 494 deletions

View File

@ -15,7 +15,7 @@
"name": "Ethereum",
"symbol": "ETH",
"shortcut": "eth",
"bip44": "m/44'/60'/0'/0",
"bip44": "m/44'/60'/0'/0/a",
"chainId": 1,
"defaultGasPrice": 64,
"defaultGasLimit": 21000,
@ -26,8 +26,8 @@
"wss://eth2.trezor.io/geth"
],
"explorer": {
"tx": "https://etherscan.io/tx/",
"address": "https://etherscan.io/address/"
"tx": "https://eth1.trezor.io/tx/",
"address": "https://eth1.trezor.io/tx/"
},
"hasSignVerify": true
},
@ -38,7 +38,7 @@
"symbol": "ETC",
"shortcut": "etc",
"chainId": 61,
"bip44": "m/44'/61'/0'/0",
"bip44": "m/44'/61'/0'/0/a",
"defaultGasPrice": 64,
"defaultGasLimit": 21000,
"defaultGasLimitTokens": 200000,
@ -61,7 +61,7 @@
"symbol": "tROP",
"shortcut": "trop",
"chainId": 3,
"bip44": "m/44'/60'/0'/0",
"bip44": "m/44'/60'/0'/0/a",
"defaultGasPrice": 64,
"defaultGasLimit": 21000,
"defaultGasLimitTokens": 200000,
@ -83,8 +83,8 @@
"wss://ropsten1.trezor.io/geth"
],
"explorer": {
"tx": "https://ropsten.etherscan.io/tx/",
"address": "https://ropsten.etherscan.io/address/"
"tx": "https://ropsten1.trezor.io/tx/",
"address": "https://ropsten1.trezor.io/address/"
},
"hasSignVerify": true
},

View File

@ -76,32 +76,31 @@ export const subscribe = (networkName: string): PromiseAction<void> => async (
}
};
export const onBlockMined = (
payload: $ElementType<BlockchainBlock, 'payload'>
): PromiseAction<void> => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
export const onBlockMined = (payload: BlockchainBlock): PromiseAction<void> => async (
dispatch: Dispatch,
getState: GetState
): Promise<void> => {
const shortcut = payload.coin.shortcut.toLowerCase();
const { block } = payload;
if (getState().router.location.state.network !== shortcut) return;
const { config } = getState().localStorage;
const network = config.networks.find(c => c.shortcut === shortcut);
if (!network) return;
switch (network.type) {
case 'ethereum':
await dispatch(EthereumBlockchainActions.onBlockMined(shortcut));
await dispatch(EthereumBlockchainActions.onBlockMined(network));
break;
case 'ripple':
await dispatch(RippleBlockchainActions.onBlockMined(shortcut, block));
await dispatch(RippleBlockchainActions.onBlockMined(network));
break;
default:
break;
}
};
export const onNotification = (
payload: $ElementType<BlockchainNotification, 'payload'>
): PromiseAction<void> => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
export const onNotification = (payload: BlockchainNotification): PromiseAction<void> => async (
dispatch: Dispatch,
getState: GetState
): Promise<void> => {
const shortcut = payload.coin.shortcut.toLowerCase();
const { config } = getState().localStorage;
const network = config.networks.find(c => c.shortcut === shortcut);
@ -109,11 +108,10 @@ export const onNotification = (
switch (network.type) {
case 'ethereum':
// this is not working until blockchain-link will start support blockbook backends
await dispatch(EthereumBlockchainActions.onNotification(payload));
await dispatch(EthereumBlockchainActions.onNotification(payload, network));
break;
case 'ripple':
await dispatch(RippleBlockchainActions.onNotification(payload));
await dispatch(RippleBlockchainActions.onNotification(payload, network));
break;
default:
break;
@ -122,9 +120,10 @@ export const onNotification = (
// Handle BLOCKCHAIN.ERROR event from TrezorConnect
// disconnect and remove Web3 websocket instance if exists
export const onError = (
payload: $ElementType<BlockchainError, 'payload'>
): PromiseAction<void> => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
export const onError = (payload: BlockchainError): PromiseAction<void> => async (
dispatch: Dispatch,
getState: GetState
): Promise<void> => {
const shortcut = payload.coin.shortcut.toLowerCase();
const { config } = getState().localStorage;
const network = config.networks.find(c => c.shortcut === shortcut);

View File

@ -4,10 +4,9 @@ import * as ACCOUNT from 'actions/constants/account';
import * as IMPORT from 'actions/constants/importAccount';
import * as NOTIFICATION from 'actions/constants/notification';
import type { AsyncAction, Account, TrezorDevice, Network, Dispatch, GetState } from 'flowtype';
import * as BlockchainActions from 'actions/ethereum/BlockchainActions';
import * as LocalStorageActions from 'actions/LocalStorageActions';
import TrezorConnect from 'trezor-connect';
import { toDecimalAmount } from 'utils/formatUtils';
import { enhanceAccount } from 'utils/accountUtils';
export type ImportAccountAction =
| {
@ -23,10 +22,7 @@ export type ImportAccountAction =
const findIndex = (accounts: Array<Account>, network: Network, device: TrezorDevice): number => {
return accounts.filter(
a =>
a.imported === true &&
a.network === network.shortcut &&
a.deviceID === (device.features || {}).device_id
a => a.imported === true && a.network === network.shortcut && a.deviceID === device.id
).length;
};
@ -35,113 +31,22 @@ export const importAddress = (
network: Network,
device: ?TrezorDevice
): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
if (!device) return;
if (!device || !device.features) return;
dispatch({
type: IMPORT.START,
});
let payload;
try {
if (network.type === 'ethereum') {
const account = await dispatch(
BlockchainActions.discoverAccount(device, address, network.shortcut)
);
const response = await TrezorConnect.getAccountInfo({
descriptor: address,
coin: network.shortcut,
});
const index = findIndex(getState().accounts, network, device);
const empty = account.nonce <= 0 && account.balance === '0';
payload = {
imported: true,
index,
network: network.shortcut,
deviceID: device.features ? device.features.device_id : '0',
deviceState: device.state || '0',
accountPath: account.path || [],
descriptor: account.descriptor,
balance: account.balance,
availableBalance: account.balance,
block: account.block,
transactions: account.transactions,
empty,
networkType: 'ethereum',
nonce: account.nonce,
};
dispatch({
type: ACCOUNT.CREATE,
payload,
});
dispatch({
type: IMPORT.SUCCESS,
});
dispatch(LocalStorageActions.setImportedAccount(payload));
dispatch({
type: NOTIFICATION.ADD,
payload: {
variant: 'success',
title: 'The account has been successfully imported',
cancelable: true,
},
});
} else if (network.type === 'ripple') {
const response = await TrezorConnect.rippleGetAccountInfo({
account: {
descriptor: address,
},
coin: network.shortcut,
});
// handle TREZOR response error
if (!response.success) {
throw new Error(response.payload.error);
}
const account = response.payload;
const empty = account.sequence <= 0 && account.balance === '0';
const index = findIndex(getState().accounts, network, device);
payload = {
imported: true,
index,
network: network.shortcut,
deviceID: device.features ? device.features.device_id : '0',
deviceState: device.state || '0',
accountPath: account.path || [],
descriptor: account.descriptor,
balance: toDecimalAmount(account.balance, network.decimals),
availableBalance: toDecimalAmount(account.availableBalance, network.decimals),
block: account.block,
transactions: account.transactions,
empty,
networkType: 'ripple',
sequence: account.sequence,
reserve: toDecimalAmount(account.reserve, network.decimals),
};
dispatch({
type: ACCOUNT.CREATE,
payload,
});
dispatch({
type: IMPORT.SUCCESS,
});
dispatch(LocalStorageActions.setImportedAccount(payload));
dispatch({
type: NOTIFICATION.ADD,
payload: {
variant: 'success',
title: 'The account has been successfully imported',
cancelable: true,
},
});
}
} catch (error) {
// handle TREZOR response error
if (!response.success) {
dispatch({
type: IMPORT.FAIL,
error: error.message,
error: response.payload.error,
});
dispatch({
@ -149,9 +54,36 @@ export const importAddress = (
payload: {
variant: 'error',
title: 'Import account error',
message: error.message,
message: response.payload.error,
cancelable: true,
},
});
return;
}
const index = findIndex(getState().accounts, network, device);
const account = enhanceAccount(response.payload, {
imported: true,
index,
network,
device,
});
dispatch({
type: ACCOUNT.CREATE,
payload: account,
});
dispatch({
type: IMPORT.SUCCESS,
});
dispatch(LocalStorageActions.setImportedAccount(account));
dispatch({
type: NOTIFICATION.ADD,
payload: {
variant: 'success',
title: 'The account has been successfully imported',
cancelable: true,
},
});
};

View File

@ -3,47 +3,14 @@
import TrezorConnect from 'trezor-connect';
import BigNumber from 'bignumber.js';
import * as PENDING from 'actions/constants/pendingTx';
import * as AccountsActions from 'actions/AccountsActions';
import * as Web3Actions from 'actions/Web3Actions';
import { mergeAccount, enhanceTransaction } from 'utils/accountUtils';
import type { TrezorDevice, Dispatch, GetState, PromiseAction } from 'flowtype';
import type { EthereumAccount, BlockchainNotification } from 'trezor-connect';
import type { Dispatch, GetState, PromiseAction, Network } from 'flowtype';
import type { 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<EthereumAccount> => async (dispatch: Dispatch): Promise<EthereumAccount> => {
// 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<NetworkToken> => async (
dispatch: Dispatch
@ -103,9 +70,9 @@ export const subscribe = (network: string): PromiseAction<void> => async (
dispatch: Dispatch,
getState: GetState
): Promise<void> => {
const accounts: Array<string> = getState()
const accounts = getState()
.accounts.filter(a => a.network === network)
.map(a => a.descriptor); // eslint-disable-line no-unused-vars
.map(a => ({ descriptor: a.descriptor }));
const response = await TrezorConnect.blockchainSubscribe({
accounts,
coin: network,
@ -115,7 +82,7 @@ export const subscribe = (network: string): PromiseAction<void> => async (
await dispatch(Web3Actions.initWeb3(network));
};
export const onBlockMined = (network: string): PromiseAction<void> => async (
export const onBlockMined = (network: Network): PromiseAction<void> => async (
dispatch: Dispatch,
getState: GetState
): Promise<void> => {
@ -123,56 +90,52 @@ export const onBlockMined = (network: string): PromiseAction<void> => async (
// check latest saved transaction blockhash against blockhheight
// try to resolve pending transactions
await dispatch(Web3Actions.resolvePendingTransactions(network));
await dispatch(Web3Actions.resolvePendingTransactions(network.shortcut));
await dispatch(Web3Actions.updateGasPrice(network));
await dispatch(Web3Actions.updateGasPrice(network.shortcut));
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,
});
const accounts = getState().accounts.filter(a => a.network === network.shortcut);
if (accounts.length === 0) return;
const blockchain = getState().blockchain.find(b => b.shortcut === network.shortcut);
if (!blockchain) return; // flowtype fallback
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
// TODO: There still could be internal transactions as a result of contract
// If that's the case, account balance won't be updated
// Currently waiting for deprecating web3 and utilising new blockbook
dispatch(AccountsActions.update({ ...accounts[i], block: a.block }));
// find out which account changed
const bundle = accounts.map(a => ({ descriptor: a.descriptor, coin: network.shortcut }));
const response = await TrezorConnect.getAccountInfo({ bundle });
// 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]));
}
});
}
}
if (!response.success) return;
response.payload.forEach((info, i) => {
dispatch(
AccountsActions.update(mergeAccount(info, accounts[i], network, blockchain.block))
);
dispatch(Web3Actions.updateAccountTokens(accounts[i]));
});
};
export const onNotification = (
payload: $ElementType<BlockchainNotification, 'payload'>
payload: BlockchainNotification,
network: Network
): PromiseAction<void> => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const { notification } = payload;
const account = getState().accounts.find(a => a.descriptor === notification.descriptor);
if (!account) return;
const { descriptor, tx } = payload.notification;
const account = getState().accounts.find(a => a.descriptor === descriptor);
const blockchain = getState().blockchain.find(b => b.shortcut === network.shortcut);
if (!account || !blockchain) return;
dispatch({
type: PENDING.ADD,
payload: enhanceTransaction(account, tx, network),
});
if (!notification.blockHeight) {
dispatch({
type: PENDING.ADD,
payload: {
...notification,
deviceState: account.deviceState,
network: account.network,
},
});
}
const response = await TrezorConnect.getAccountInfo({
descriptor: account.descriptor,
coin: account.network,
});
if (!response.success) return;
dispatch(
AccountsActions.update(mergeAccount(response.payload, account, network, blockchain.block))
);
};
export const onError = (network: string): PromiseAction<void> => async (

View File

@ -1,10 +1,8 @@
/* @flow */
import TrezorConnect from 'trezor-connect';
import EthereumjsUtil from 'ethereumjs-util';
import * as DISCOVERY from 'actions/constants/discovery';
import * as BlockchainActions from 'actions/ethereum/BlockchainActions';
import { enhanceAccount } from 'utils/accountUtils';
import type { PromiseAction, Dispatch, GetState, TrezorDevice, Network, Account } from 'flowtype';
import type { Discovery } from 'reducers/DiscoveryReducer';
@ -13,49 +11,17 @@ export type DiscoveryStartAction = {
networkType: 'ethereum',
network: Network,
device: TrezorDevice,
publicKey: string,
chainCode: string,
basePath: Array<number>,
};
// first iteration
// generate public key for this account
// start discovery process
export const begin = (
device: TrezorDevice,
network: Network
): PromiseAction<DiscoveryStartAction> => async (): Promise<DiscoveryStartAction> => {
// get xpub from TREZOR
const response = await TrezorConnect.getPublicKey({
device: {
path: device.path,
instance: device.instance,
state: device.state,
},
path: network.bip44,
keepSession: true, // acquire and hold session
//useEmptyPassphrase: !device.instance,
useEmptyPassphrase: device.useEmptyPassphrase,
network: network.name,
});
// handle TREZOR response error
if (!response.success) {
throw new Error(response.payload.error);
}
const basePath: Array<number> = response.payload.path;
return {
type: DISCOVERY.START,
networkType: 'ethereum',
network,
device,
publicKey: response.payload.publicKey,
chainCode: response.payload.chainCode,
basePath,
};
};
): PromiseAction<DiscoveryStartAction> => async (): Promise<DiscoveryStartAction> => ({
type: DISCOVERY.START,
networkType: 'ethereum',
network,
device,
});
export const discoverAccount = (
device: TrezorDevice,
@ -65,38 +31,31 @@ export const discoverAccount = (
const network = config.networks.find(c => c.shortcut === discoveryProcess.network);
if (!network) throw new Error('Discovery network not found');
const derivedKey = discoveryProcess.hdKey.derive(`m/${discoveryProcess.accountIndex}`);
const path = discoveryProcess.basePath.concat(discoveryProcess.accountIndex);
const publicAddress: string = EthereumjsUtil.publicToAddress(
derivedKey.publicKey,
true
).toString('hex');
const ethAddress: string = EthereumjsUtil.toChecksumAddress(publicAddress);
const { accountIndex } = discoveryProcess;
const path = network.bip44.slice(0).replace('a', accountIndex.toString());
// TODO: check if address was created before
const account = await dispatch(
BlockchainActions.discoverAccount(device, ethAddress, network.shortcut)
);
const response = await TrezorConnect.getAccountInfo({
device: {
path: device.path,
instance: device.instance,
state: device.state,
},
path,
// details: 'tokenBalances', TODO: load ERC20
pageSize: 1,
keepSession: true, // acquire and hold session
useEmptyPassphrase: device.useEmptyPassphrase,
coin: network.shortcut,
});
// const accountIsEmpty = account.transactions <= 0 && account.nonce <= 0 && account.balance === '0';
const empty = account.nonce <= 0 && account.balance === '0';
// handle TREZOR response error
if (!response.success) {
throw new Error(response.payload.error);
}
return {
imported: false,
return enhanceAccount(response.payload, {
index: discoveryProcess.accountIndex,
network: network.shortcut,
deviceID: device.features ? device.features.device_id : '0',
deviceState: device.state || '0',
accountPath: path,
descriptor: ethAddress,
balance: account.balance,
availableBalance: account.balance,
block: account.block,
transactions: account.transactions,
empty,
networkType: 'ethereum',
nonce: account.nonce,
};
network,
device,
});
};

View File

@ -4,7 +4,7 @@ import TrezorConnect from 'trezor-connect';
import * as BLOCKCHAIN from 'actions/constants/blockchain';
import * as PENDING from 'actions/constants/pendingTx';
import * as AccountsActions from 'actions/AccountsActions';
import { toDecimalAmount } from 'utils/formatUtils';
import { mergeAccount, enhanceTransaction } from 'utils/accountUtils';
import { observeChanges } from 'reducers/utils';
import type { BlockchainNotification } from 'trezor-connect';
@ -20,10 +20,10 @@ import type {
export const subscribe = (network: string): PromiseAction<void> => async (
dispatch: Dispatch,
getState: GetState
): Promise<void> => {
const accounts: Array<string> = getState()
) => {
const accounts = getState()
.accounts.filter(a => a.network === network)
.map(a => a.descriptor);
.map(a => ({ descriptor: a.descriptor }));
await TrezorConnect.blockchainSubscribe({
accounts,
coin: network,
@ -47,161 +47,81 @@ export const getFeeLevels = (network: Network): PayloadAction<Array<BlockchainFe
return blockchain.feeLevels;
};
export const onBlockMined = (networkShortcut: string, block: number): PromiseAction<void> => async (
export const onBlockMined = (network: Network): PromiseAction<void> => async (
dispatch: Dispatch,
getState: GetState
): Promise<void> => {
const blockchain = getState().blockchain.find(b => b.shortcut === networkShortcut);
const blockchain = getState().blockchain.find(b => b.shortcut === network.shortcut);
if (!blockchain) return; // flowtype fallback
// if last update was more than 5 minutes ago
const now = new Date().getTime();
if (blockchain.feeTimestamp < now - 300000) {
const feeRequest = await TrezorConnect.blockchainEstimateFee({
coin: networkShortcut,
request: {
feeLevels: 'smart',
},
coin: network.shortcut,
});
if (feeRequest.success && observeChanges(blockchain.feeLevels, feeRequest.payload)) {
// check if downloaded fee levels are different
dispatch({
type: BLOCKCHAIN.UPDATE_FEE,
shortcut: networkShortcut,
feeLevels: feeRequest.payload,
shortcut: network.shortcut,
feeLevels: feeRequest.payload.levels.map(l => ({
name: 'Normal',
value: l.feePerUnit,
})),
});
}
}
// TODO: check for blockchain rollbacks here!
const accounts: Array<any> = getState().accounts.filter(a => a.network === networkShortcut);
const accounts = getState().accounts.filter(a => a.network === network.shortcut);
if (accounts.length === 0) return;
const { networks } = getState().localStorage.config;
const network = networks.find(c => c.shortcut === networkShortcut);
if (!network) return;
// HACK: Since Connect always returns account.transactions as 0
// we don't have info about new transactions for the account since last update.
// Untill there is a better solution compare accounts block.
// If we missed some blocks (wallet was offline) we'll update the account
// If we are update to date with the last block that means wallet was online
// and we would get Blockchain notification about new transaction if needed
accounts.forEach(async account => {
const missingBlocks = account.block !== block - 1;
if (!missingBlocks) {
// account was last updated on account.block, current block is +1, we didn't miss single block
// if there was new tx, blockchain notification would let us know
// so just update the block for the account
dispatch(
AccountsActions.update({
...account,
block,
})
);
} else {
// we missed some blocks (wallet was offline). get updated account info from connect
const response = await TrezorConnect.rippleGetAccountInfo({
account: {
descriptor: account.descriptor,
},
level: 'transactions',
coin: networkShortcut,
});
const bundle = accounts.map(a => ({ descriptor: a.descriptor, coin: network.shortcut }));
const response = await TrezorConnect.getAccountInfo({ bundle });
if (!response.success) return;
if (!response.success) return;
const updatedAccount = response.payload;
// new txs
dispatch(
AccountsActions.update({
...account,
balance: toDecimalAmount(updatedAccount.balance, network.decimals),
availableBalance: toDecimalAmount(
updatedAccount.availableBalance,
network.decimals
),
block: updatedAccount.block,
sequence: updatedAccount.sequence,
})
);
}
response.payload.forEach((info, i) => {
dispatch(
AccountsActions.update(mergeAccount(info, accounts[i], network, blockchain.block))
);
});
};
export const onNotification = (
payload: $ElementType<BlockchainNotification, 'payload'>
payload: BlockchainNotification,
network: Network
): PromiseAction<void> => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const { notification } = payload;
const account = getState().accounts.find(a => a.descriptor === notification.descriptor);
if (!account) return;
const { network } = getState().selectedAccount;
if (!network) return; // flowtype fallback
const { descriptor, tx } = payload.notification;
const account = getState().accounts.find(a => a.descriptor === descriptor);
const blockchain = getState().blockchain.find(b => b.shortcut === network.shortcut);
if (!account || !blockchain) return;
if (!notification.blockHeight) {
if (!tx.blockHeight) {
dispatch({
type: PENDING.ADD,
payload: {
...notification,
deviceState: account.deviceState,
network: account.network,
amount: toDecimalAmount(notification.amount, network.decimals),
total:
notification.type === 'send'
? toDecimalAmount(notification.total, network.decimals)
: toDecimalAmount(notification.amount, network.decimals),
fee: toDecimalAmount(notification.fee, network.decimals),
},
payload: enhanceTransaction(account, tx, network),
});
// todo: replace "send success" notification with link to explorer
} else {
dispatch({
type: PENDING.TX_RESOLVED,
hash: notification.hash,
hash: tx.txid,
});
}
// In case of tx sent between two Trezor accounts there is a possibility that only 1 notification will be received
// therefore we need to find target account and update data for it as well
const accountsToUpdate = [account];
const targetAddress =
notification.type === 'send'
? notification.outputs[0].addresses[0]
: notification.inputs[0].addresses[0];
const targetAccount = getState().accounts.find(a => a.descriptor === targetAddress);
if (targetAccount) {
accountsToUpdate.push(targetAccount);
}
accountsToUpdate.forEach(async a => {
const response = await TrezorConnect.rippleGetAccountInfo({
account: {
descriptor: a.descriptor,
from: a.block,
history: false,
},
coin: a.network,
});
if (response.success) {
const updatedAccount = response.payload;
const empty = updatedAccount.sequence <= 0 && updatedAccount.balance === '0';
dispatch(
AccountsActions.update({
networkType: 'ripple',
...a,
balance: toDecimalAmount(updatedAccount.balance, network.decimals),
availableBalance: toDecimalAmount(
updatedAccount.availableBalance,
network.decimals
),
block: updatedAccount.block,
sequence: updatedAccount.sequence,
reserve: toDecimalAmount(updatedAccount.reserve, network.decimals),
empty,
})
);
}
const response = await TrezorConnect.getAccountInfo({
descriptor: account.descriptor,
coin: account.network,
});
if (!response.success) return;
dispatch(
AccountsActions.update(mergeAccount(response.payload, account, network, blockchain.block))
);
};

View File

@ -2,7 +2,7 @@
import TrezorConnect from 'trezor-connect';
import * as DISCOVERY from 'actions/constants/discovery';
import { toDecimalAmount } from 'utils/formatUtils';
import { enhanceAccount } from 'utils/accountUtils';
import type { PromiseAction, GetState, Dispatch, TrezorDevice, Network, Account } from 'flowtype';
import type { Discovery } from 'reducers/DiscoveryReducer';
@ -35,16 +35,13 @@ export const discoverAccount = (
const { accountIndex } = discoveryProcess;
const path = network.bip44.slice(0).replace('a', accountIndex.toString());
const response = await TrezorConnect.rippleGetAccountInfo({
const response = await TrezorConnect.getAccountInfo({
device: {
path: device.path,
instance: device.instance,
state: device.state,
},
account: {
path,
block: 0,
},
path,
keepSession: true, // acquire and hold session
useEmptyPassphrase: device.useEmptyPassphrase,
coin: network.shortcut,
@ -55,26 +52,9 @@ export const discoverAccount = (
throw new Error(response.payload.error);
}
const account = response.payload;
const empty = account.sequence <= 0 && account.balance === '0';
return {
imported: false,
return enhanceAccount(response.payload, {
index: discoveryProcess.accountIndex,
network: network.shortcut,
deviceID: device.features ? device.features.device_id : '0',
deviceState: device.state || '0',
accountPath: account.path || [],
descriptor: account.descriptor,
balance: toDecimalAmount(account.balance, network.decimals),
availableBalance: toDecimalAmount(account.availableBalance, network.decimals),
block: account.block,
transactions: account.transactions,
empty,
networkType: 'ripple',
sequence: account.sequence,
reserve: toDecimalAmount(account.reserve, network.decimals),
};
network,
device,
});
};

View File

@ -6,13 +6,13 @@ import * as ACCOUNT from 'actions/constants/account';
import type { Action, TrezorDevice } from 'flowtype';
type AccountCommon = {
type AccountCommon = {|
+imported: boolean,
+index: number,
+network: string, // network id (shortcut)
+deviceID: string, // empty for imported accounts
+deviceState: string, // empty for imported accounts
+accountPath: Array<number>, // empty for imported accounts
+accountPath: string, // empty for imported accounts
+descriptor: string, // address or xpub
balance: string,
@ -21,22 +21,31 @@ type AccountCommon = {
empty: boolean, // account without transactions
transactions: number, // deprecated
};
|};
export type Account =
| (AccountCommon & {
networkType: 'ethereum',
nonce: number,
})
| (AccountCommon & {
networkType: 'ripple',
sequence: number,
reserve: string,
})
| (AccountCommon & {
networkType: 'bitcoin',
addressIndex: number,
});
| {|
...AccountCommon,
...{|
networkType: 'ethereum',
nonce: string,
|},
|}
| {|
...AccountCommon,
...{|
networkType: 'ripple',
sequence: number,
reserve: string,
|},
|}
| {|
...AccountCommon,
...{|
networkType: 'bitcoin',
addressIndex: number,
|},
|};
export type State = Array<Account>;
@ -51,14 +60,12 @@ export const findDeviceAccounts = (
return state.filter(
addr =>
(addr.deviceState === device.state ||
(addr.imported && addr.deviceID === (device.features || {}).device_id)) &&
(addr.imported && addr.deviceID === device.id)) &&
addr.network === network
);
}
return state.filter(
addr =>
addr.deviceState === device.state ||
(addr.imported && addr.deviceID === (device.features || {}).device_id)
addr => addr.deviceState === device.state || (addr.imported && addr.deviceID === device.id)
);
};

View File

@ -4,7 +4,7 @@ import { BLOCKCHAIN as BLOCKCHAIN_EVENT } from 'trezor-connect';
import * as BLOCKCHAIN_ACTION from 'actions/constants/blockchain';
import type { Action } from 'flowtype';
import type { BlockchainConnect, BlockchainError, BlockchainBlock } from 'trezor-connect';
import type { BlockchainInfo, BlockchainError, BlockchainBlock } from 'trezor-connect';
export type BlockchainFeeLevel = {
name: string,
@ -50,16 +50,15 @@ const onStartSubscribe = (state: State, shortcut: string): State => {
]);
};
const onConnect = (state: State, action: BlockchainConnect): State => {
const shortcut = action.payload.coin.shortcut.toLowerCase();
const onConnect = (state: State, info: BlockchainInfo): State => {
const shortcut = info.coin.shortcut.toLowerCase();
const network = state.find(b => b.shortcut === shortcut);
const { info } = action.payload;
if (network) {
const others = state.filter(b => b !== network);
return others.concat([
{
...network,
block: info.block,
block: info.blockHeight,
connected: true,
connecting: false,
reconnectionAttempts: 0,
@ -73,15 +72,15 @@ const onConnect = (state: State, action: BlockchainConnect): State => {
connected: true,
connecting: false,
reconnectionAttempts: 0,
block: info.block,
block: info.blockHeight,
feeTimestamp: 0,
feeLevels: [],
},
]);
};
const onError = (state: State, action: BlockchainError): State => {
const shortcut = action.payload.coin.shortcut.toLowerCase();
const onError = (state: State, payload: BlockchainError): State => {
const shortcut = payload.coin.shortcut.toLowerCase();
const network = state.find(b => b.shortcut === shortcut);
if (network) {
const others = state.filter(b => b !== network);
@ -108,15 +107,15 @@ const onError = (state: State, action: BlockchainError): State => {
]);
};
const onBlock = (state: State, action: BlockchainBlock): State => {
const shortcut = action.payload.coin.shortcut.toLowerCase();
const onBlock = (state: State, payload: BlockchainBlock): State => {
const shortcut = payload.coin.shortcut.toLowerCase();
const network = state.find(b => b.shortcut === shortcut);
if (network) {
const others = state.filter(b => b !== network);
return others.concat([
{
...network,
block: action.payload.block,
block: payload.blockHeight,
},
]);
}
@ -143,11 +142,11 @@ export default (state: State = initialState, action: Action): State => {
case BLOCKCHAIN_ACTION.START_SUBSCRIBE:
return onStartSubscribe(state, action.shortcut);
case BLOCKCHAIN_EVENT.CONNECT:
return onConnect(state, action);
return onConnect(state, action.payload);
case BLOCKCHAIN_EVENT.ERROR:
return onError(state, action);
return onError(state, action.payload);
case BLOCKCHAIN_EVENT.BLOCK:
return onBlock(state, action);
return onBlock(state, action.payload);
case BLOCKCHAIN_ACTION.UPDATE_FEE:
return updateFee(state, action.shortcut, action.feeLevels);

View File

@ -1,7 +1,5 @@
/* @flow */
import HDKey from 'hdkey';
import * as DISCOVERY from 'actions/constants/discovery';
import * as ACCOUNT from 'actions/constants/account';
import * as CONNECT from 'actions/constants/TrezorConnect';
@ -27,10 +25,6 @@ export type Discovery = {
waitingForBlockchain: boolean,
fwNotSupported: boolean,
fwOutdated: boolean,
publicKey: string, // used in ethereum only
chainCode: string, // used in ethereum only
hdKey: HDKey, // used in ethereum only
};
export type State = Array<Discovery>;
@ -46,10 +40,6 @@ const defaultDiscovery: Discovery = {
waitingForBlockchain: false,
fwNotSupported: false,
fwOutdated: false,
publicKey: '',
chainCode: '',
hdKey: null,
};
const findIndex = (state: State, network: string, deviceState: string): number =>
@ -63,18 +53,6 @@ const start = (state: State, action: DiscoveryStartAction): State => {
deviceState,
};
if (action.networkType === 'ethereum') {
const hdKey = new HDKey();
hdKey.publicKey = Buffer.from(action.publicKey, 'hex');
hdKey.chainCode = Buffer.from(action.chainCode, 'hex');
instance.hdKey = hdKey;
instance.publicKey = action.publicKey;
instance.chainCode = action.chainCode;
instance.basePath = action.basePath;
}
const newState: State = [...state];
const index: number = findIndex(state, action.network.shortcut, deviceState);
if (index >= 0) {
@ -202,15 +180,8 @@ export default function discovery(state: State = initialState, action: Action):
return notSupported(state, action);
case DISCOVERY.FROM_STORAGE:
return action.payload.map(d => {
if (d.publicKey.length < 1) return d;
// recreate ethereum discovery HDKey
// deprecated: will be removed after switching to blockbook
const hdKey: HDKey = new HDKey();
hdKey.publicKey = Buffer.from(d.publicKey, 'hex');
hdKey.chainCode = Buffer.from(d.chainCode, 'hex');
return {
...d,
hdKey,
interrupted: false,
waitingForDevice: false,
waitingForBlockchain: false,