From 035d3185a11bfa36847718f2bf693f442d4677f9 Mon Sep 17 00:00:00 2001 From: pbca26 Date: Mon, 14 Dec 2020 20:29:03 +0300 Subject: [PATCH 01/13] decouple asyncForEach helper into a separate import --- src/lib/account-discovery.js | 9 ++------- src/lib/async.js | 7 +++++++ 2 files changed, 9 insertions(+), 7 deletions(-) create mode 100644 src/lib/async.js diff --git a/src/lib/account-discovery.js b/src/lib/account-discovery.js index a58873ad..7d41cb82 100755 --- a/src/lib/account-discovery.js +++ b/src/lib/account-discovery.js @@ -3,19 +3,14 @@ import blockchain from './blockchain'; import getAddress from './get-address'; import bitcoin from 'bitcoinjs-lib'; import parseHistory from './history-parser'; +import asyncForEach from './async'; let pubKeysCache = {}; -async function asyncForEach(array, callback) { - for (let index = 0; index < array.length; index++) { - await callback(array[index], index, array); - } -} - const walkDerivationPath = async node => { + const addresses = []; const addressConcurrency = 10; const gapLimit = 20; - const addresses = []; let consecutiveUnusedAddresses = 0; let addressIndex = 0; diff --git a/src/lib/async.js b/src/lib/async.js new file mode 100644 index 00000000..e210be18 --- /dev/null +++ b/src/lib/async.js @@ -0,0 +1,7 @@ +async function asyncForEach(array, callback) { + for (let index = 0; index < array.length; index++) { + await callback(array[index], index, array); + } +} + +export default asyncForEach; \ No newline at end of file From da35421936c142a6055ae87c49fbf08ca383cf1e Mon Sep 17 00:00:00 2001 From: pbca26 Date: Mon, 14 Dec 2020 20:33:43 +0300 Subject: [PATCH 02/13] add browser url configurable discovery options --- src/lib/account-discovery.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/lib/account-discovery.js b/src/lib/account-discovery.js index 7d41cb82..c88aa4f6 100755 --- a/src/lib/account-discovery.js +++ b/src/lib/account-discovery.js @@ -9,11 +9,18 @@ let pubKeysCache = {}; const walkDerivationPath = async node => { const addresses = []; - const addressConcurrency = 10; - const gapLimit = 20; + let addressConcurrency = 10; + let gapLimit = 20; let consecutiveUnusedAddresses = 0; let addressIndex = 0; + if (window.location.href.indexOf('extgap=s') > -1 ) gapLimit = 30; + if (window.location.href.indexOf('extgap=m') > -1 ) gapLimit = 40; + if (window.location.href.indexOf('extgap=l') > -1 ) gapLimit = 50; + + if (window.location.href.indexOf('timeout=s') > -1 ) addressConcurrency = 2; + if (window.location.href.indexOf('timeout=m') > -1 ) addressConcurrency = 5; + while (consecutiveUnusedAddresses < gapLimit) { const addressApiRequests = []; From 548a78747ec25fa9cd9089b2e9318b9cb85e49f8 Mon Sep 17 00:00:00 2001 From: pbca26 Date: Fri, 18 Dec 2020 15:09:31 +0300 Subject: [PATCH 03/13] check all balances btn comp boilerplate --- src/CheckAllBalancesButton.js | 76 +++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 src/CheckAllBalancesButton.js diff --git a/src/CheckAllBalancesButton.js b/src/CheckAllBalancesButton.js new file mode 100644 index 00000000..c9fd91c5 --- /dev/null +++ b/src/CheckAllBalancesButton.js @@ -0,0 +1,76 @@ +import React from 'react'; +import getKomodoRewards from './lib/get-komodo-rewards'; +import hw from './lib/hw'; +import accountDiscovery from './lib/account-discovery'; +import blockchain, {setExplorerUrl, getInfo} from './lib/blockchain'; +import updateActionState from './lib/update-action-state'; +import {TX_FEE, VENDOR} from './constants'; +import ActionListModal from './ActionListModal'; +import asyncForEach from './lib/async'; +import humanReadableSatoshis from './lib/human-readable-satoshis'; + +class CheckAllBalancesButton extends React.Component { + state = this.initialState; + + get initialState() { + return { + isCheckingRewards: false, + error: false, + actions: { + connect: { + icon: 'fab fa-usb', + description: this.props.vendor === 'ledger' ?
Connect and unlock your Ledger, then open the Komodo app on your device.
:
Connect and unlock your Trezor.
, + state: null + }, + approve: { + icon: 'fas fa-microchip', + description:
Approve all public key export requests on your device. There will be multiple requests.
, + state: null + }, + } + }; + } + + resetState = () => { + this.setState(this.initialState); + } + + calculateRewardData = ({accounts, tiptime}) => accounts.map(account => { + account.balance = account.utxos.reduce((balance, utxo) => balance + utxo.satoshis, 0); + account.rewards = account.utxos.reduce((rewards, utxo) => rewards + getKomodoRewards({tiptime, ...utxo}), 0); + account.claimableAmount = account.rewards - TX_FEE * 2; + + return account; + }); + + render() { + const { + isCheckingRewards, + actions, + error, + } = this.state; + + return ( + + + +

+ Exporting public keys from your {VENDOR[this.props.vendor]} device, scanning the blockchain for funds, and calculating any claimable rewards. Please approve any public key export requests on your device. +

+
+
+ ); + } +} + +export default CheckAllBalancesButton; From ea5e1984c5b51eba4a5a7f9cd734887fcf97c10f Mon Sep 17 00:00:00 2001 From: pbca26 Date: Fri, 18 Dec 2020 15:28:33 +0300 Subject: [PATCH 04/13] account discovery reset pubkeys method --- src/CheckAllBalancesButton.js | 2 +- src/lib/account-discovery.js | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/CheckAllBalancesButton.js b/src/CheckAllBalancesButton.js index c9fd91c5..47907ca4 100644 --- a/src/CheckAllBalancesButton.js +++ b/src/CheckAllBalancesButton.js @@ -1,7 +1,7 @@ import React from 'react'; import getKomodoRewards from './lib/get-komodo-rewards'; import hw from './lib/hw'; -import accountDiscovery from './lib/account-discovery'; +import accountDiscovery, {clearPubkeysCache} from './lib/account-discovery'; import blockchain, {setExplorerUrl, getInfo} from './lib/blockchain'; import updateActionState from './lib/update-action-state'; import {TX_FEE, VENDOR} from './constants'; diff --git a/src/lib/account-discovery.js b/src/lib/account-discovery.js index c88aa4f6..89fb3e7e 100755 --- a/src/lib/account-discovery.js +++ b/src/lib/account-discovery.js @@ -213,4 +213,8 @@ const accountDiscovery = async vendor => { return accounts; }; +export const clearPubkeysCache = () => { + pubKeysCache = {}; +}; + export default accountDiscovery; From ee863e4eefd3a70dbd60c339114716f93a4c8964 Mon Sep 17 00:00:00 2001 From: pbca26 Date: Fri, 18 Dec 2020 20:51:21 +0300 Subject: [PATCH 05/13] check all balance button comp add sequential balance check --- src/CheckAllBalancesButton.js | 64 ++++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/src/CheckAllBalancesButton.js b/src/CheckAllBalancesButton.js index 47907ca4..7db5597f 100644 --- a/src/CheckAllBalancesButton.js +++ b/src/CheckAllBalancesButton.js @@ -7,6 +7,7 @@ import updateActionState from './lib/update-action-state'; import {TX_FEE, VENDOR} from './constants'; import ActionListModal from './ActionListModal'; import asyncForEach from './lib/async'; +import coins from './lib/coins'; import humanReadableSatoshis from './lib/human-readable-satoshis'; class CheckAllBalancesButton extends React.Component { @@ -16,6 +17,8 @@ class CheckAllBalancesButton extends React.Component { return { isCheckingRewards: false, error: false, + coin: '', + balances: [], actions: { connect: { icon: 'fab fa-usb', @@ -32,6 +35,7 @@ class CheckAllBalancesButton extends React.Component { } resetState = () => { + cancel = true; this.setState(this.initialState); } @@ -43,6 +47,64 @@ class CheckAllBalancesButton extends React.Component { return account; }); + scanAddresses = async () => { + const coinTickers = Object.keys(coins); + let balances = []; + cancel = false; + + await asyncForEach(coinTickers, async (coin, index) => { + setExplorerUrl(coins[coin].api[0]); + this.setState({ + ...this.initialState, + isCheckingRewards: true, + coin, + balances, + }); + + let currentAction; + try { + currentAction = 'connect'; + updateActionState(this, currentAction, 'loading'); + const hwIsAvailable = await hw[this.props.vendor].isAvailable(); + if (!hwIsAvailable) { + throw new Error(`${VENDOR[this.props.vendor]} device is unavailable!`); + } + updateActionState(this, currentAction, true); + + currentAction = 'approve'; + updateActionState(this, currentAction, 'loading'); + let [accounts, tiptime] = await Promise.all([ + accountDiscovery(this.props.vendor), + blockchain.getTipTime() + ]); + + tiptime = this.props.checkTipTime(tiptime); + + accounts = this.calculateRewardData({accounts, tiptime}); + updateActionState(this, currentAction, true); + + let balanceSum = 0; + + for (let i = 0; i < accounts.length; i++) { + balanceSum += accounts[i].balance; + } + + if (balanceSum) { + balances.push({ + coin, + balance: balanceSum, + }); + } + + //this.setState({...this.initialState}); + } catch (error) { + console.warn(error); + updateActionState(this, currentAction, false); + this.setState({error: error.message}); + } + }); + }; + render() { const { isCheckingRewards, @@ -58,7 +120,7 @@ class CheckAllBalancesButton extends React.Component { {this.props.children} Date: Mon, 21 Dec 2020 21:31:05 +0300 Subject: [PATCH 06/13] check all balance button comp render balances --- package.json | 2 +- src/CheckAllBalancesButton.js | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 917e3ff8..473b6ece 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hw-kmd-wallet", - "version": "0.2.0", + "version": "0.2.1", "author": "Luke Childs (http://lukechilds.co.uk), Komodo Platform (https://komodoplatform.com)", "repository": "pbca26/hw-kmd-wallet", "homepage": "https://pbca26.github.io/hw-kmd-wallet", diff --git a/src/CheckAllBalancesButton.js b/src/CheckAllBalancesButton.js index 7db5597f..484abc77 100644 --- a/src/CheckAllBalancesButton.js +++ b/src/CheckAllBalancesButton.js @@ -105,6 +105,35 @@ class CheckAllBalancesButton extends React.Component { }); }; + renderCoinBalances() { + const balances = this.state.balances; + + return ( + + + + {headings.map(heading => )} + + + {balances.length > 10 && + + + {headings.map(heading => )} + + + } + + {balances.map(item => ( + + + + + ))} + +
{heading}
{heading}
{item.coin}{humanReadableSatoshis(item.balance)}{item.rewards ? ` (${humanReadableSatoshis(item.rewards)})` : ''}
+ ); + } + render() { const { isCheckingRewards, @@ -129,10 +158,14 @@ class CheckAllBalancesButton extends React.Component {

Exporting public keys from your {VENDOR[this.props.vendor]} device, scanning the blockchain for funds, and calculating any claimable rewards. Please approve any public key export requests on your device.

+ {this.state.balances && + this.state.balances.length > 0 && + {this.renderCoinBalances()} + }
); } } -export default CheckAllBalancesButton; +export default CheckAllBalancesButton; \ No newline at end of file From d403660a44f358e5b13b46534cb41b8fda650f9f Mon Sep 17 00:00:00 2001 From: pbca26 Date: Mon, 21 Dec 2020 22:44:45 +0300 Subject: [PATCH 07/13] check all balance button comp reset modal on loop end --- src/CheckAllBalancesButton.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/CheckAllBalancesButton.js b/src/CheckAllBalancesButton.js index 484abc77..091534dd 100644 --- a/src/CheckAllBalancesButton.js +++ b/src/CheckAllBalancesButton.js @@ -103,6 +103,22 @@ class CheckAllBalancesButton extends React.Component { this.setState({error: error.message}); } }); + + if (!this.state.error || (this.state.error && this.state.error.indexOf('Failed to fetch') > -1)) { + updateActionState(this, 'approve', true); + updateActionState(this, 'finished', true); + } + + clearPubkeysCache(); + + this.setState({ + error: false, + progress: '', + coin: '', + isCheckingRewards: true, + }); + + setExplorerUrl(this.props.explorerEndpoint); }; renderCoinBalances() { From 88fb0a371adb4fd545184d293a8ad15f2af1dc6d Mon Sep 17 00:00:00 2001 From: pbca26 Date: Mon, 21 Dec 2020 23:35:24 +0300 Subject: [PATCH 08/13] check all balance button comp check if api endpoint is responding --- src/CheckAllBalancesButton.js | 132 +++++++++++++++++++++------------- 1 file changed, 81 insertions(+), 51 deletions(-) diff --git a/src/CheckAllBalancesButton.js b/src/CheckAllBalancesButton.js index 091534dd..b28f36eb 100644 --- a/src/CheckAllBalancesButton.js +++ b/src/CheckAllBalancesButton.js @@ -10,6 +10,11 @@ import asyncForEach from './lib/async'; import coins from './lib/coins'; import humanReadableSatoshis from './lib/human-readable-satoshis'; +const headings = [ + 'Coin', + 'Balance', +]; + class CheckAllBalancesButton extends React.Component { state = this.initialState; @@ -19,6 +24,7 @@ class CheckAllBalancesButton extends React.Component { error: false, coin: '', balances: [], + progress: '', actions: { connect: { icon: 'fab fa-usb', @@ -30,6 +36,11 @@ class CheckAllBalancesButton extends React.Component { description:
Approve all public key export requests on your device. There will be multiple requests.
, state: null }, + finished: { + icon: 'fas fa-check', + description:
All coin balances are checked.
, + state: null + }, } }; } @@ -53,57 +64,78 @@ class CheckAllBalancesButton extends React.Component { cancel = false; await asyncForEach(coinTickers, async (coin, index) => { - setExplorerUrl(coins[coin].api[0]); - this.setState({ - ...this.initialState, - isCheckingRewards: true, - coin, - balances, - }); - - let currentAction; - try { - currentAction = 'connect'; - updateActionState(this, currentAction, 'loading'); - const hwIsAvailable = await hw[this.props.vendor].isAvailable(); - if (!hwIsAvailable) { - throw new Error(`${VENDOR[this.props.vendor]} device is unavailable!`); + const getInfoRes = await Promise.all(coins[coin].api.map((value, index) => { + return getInfo(value); + })); + let isExplorerEndpointSet = false; + + console.warn('checkExplorerEndpoints', getInfoRes); + + for (let i = 0; i < coins[coin].api.length; i++) { + if (getInfoRes[i] && + getInfoRes[i].hasOwnProperty('info') && + getInfoRes[i].info.hasOwnProperty('version')) { + console.warn(`${coin} set api endpoint to ${coins[coin].api[i]}`); + setExplorerUrl(coins[coin].api[i]); + isExplorerEndpointSet = true; + + break; } - updateActionState(this, currentAction, true); + } - currentAction = 'approve'; - updateActionState(this, currentAction, 'loading'); - let [accounts, tiptime] = await Promise.all([ - accountDiscovery(this.props.vendor), - blockchain.getTipTime() - ]); + if (isExplorerEndpointSet) { + this.setState({ + ...this.initialState, + isCheckingRewards: true, + coin, + progress: ` (${index + 1}/${coinTickers.length})`, + balances, + }); + + let currentAction; + try { + currentAction = 'connect'; + updateActionState(this, currentAction, 'loading'); + const hwIsAvailable = await hw[this.props.vendor].isAvailable(); + if (!hwIsAvailable) { + throw new Error(`${VENDOR[this.props.vendor]} device is unavailable!`); + } + updateActionState(this, currentAction, true); - tiptime = this.props.checkTipTime(tiptime); + currentAction = 'approve'; + updateActionState(this, currentAction, 'loading'); + let [accounts, tiptime] = await Promise.all([ + accountDiscovery(this.props.vendor), + blockchain.getTipTime() + ]); - accounts = this.calculateRewardData({accounts, tiptime}); - updateActionState(this, currentAction, true); + tiptime = this.props.checkTipTime(tiptime); - let balanceSum = 0; + accounts = this.calculateRewardData({accounts, tiptime}); + updateActionState(this, currentAction, true); - for (let i = 0; i < accounts.length; i++) { - balanceSum += accounts[i].balance; - } + let balanceSum = 0; - if (balanceSum) { - balances.push({ - coin, - balance: balanceSum, - }); - } + for (let i = 0; i < accounts.length; i++) { + balanceSum += accounts[i].balance; + } - //this.setState({...this.initialState}); - } catch (error) { - console.warn(error); - updateActionState(this, currentAction, false); - this.setState({error: error.message}); + if (balanceSum) { + balances.push({ + coin, + balance: balanceSum, + }); + } + + //this.setState({...this.initialState}); + } catch (error) { + console.warn(error); + updateActionState(this, currentAction, false); + this.setState({error: error.message}); + } } }); - + if (!this.state.error || (this.state.error && this.state.error.indexOf('Failed to fetch') > -1)) { updateActionState(this, 'approve', true); updateActionState(this, 'finished', true); @@ -131,18 +163,16 @@ class CheckAllBalancesButton extends React.Component { {headings.map(heading => {heading})} - {balances.length > 10 && - - - {headings.map(heading => {heading})} - - - } + + + {headings.map(heading => {heading})} + + {balances.map(item => ( {item.coin} - {humanReadableSatoshis(item.balance)}{item.rewards ? ` (${humanReadableSatoshis(item.rewards)})` : ''} + {humanReadableSatoshis(item.balance)} ))} @@ -165,7 +195,7 @@ class CheckAllBalancesButton extends React.Component { {this.props.children} Date: Tue, 22 Dec 2020 00:06:27 +0300 Subject: [PATCH 09/13] check all balance button comp make modal cancellable --- src/CheckAllBalancesButton.js | 158 ++++++++++++++++++---------------- 1 file changed, 82 insertions(+), 76 deletions(-) diff --git a/src/CheckAllBalancesButton.js b/src/CheckAllBalancesButton.js index b28f36eb..f6e1c21a 100644 --- a/src/CheckAllBalancesButton.js +++ b/src/CheckAllBalancesButton.js @@ -15,6 +15,8 @@ const headings = [ 'Balance', ]; +let cancel = false; + class CheckAllBalancesButton extends React.Component { state = this.initialState; @@ -64,91 +66,95 @@ class CheckAllBalancesButton extends React.Component { cancel = false; await asyncForEach(coinTickers, async (coin, index) => { - const getInfoRes = await Promise.all(coins[coin].api.map((value, index) => { - return getInfo(value); - })); - let isExplorerEndpointSet = false; - - console.warn('checkExplorerEndpoints', getInfoRes); - - for (let i = 0; i < coins[coin].api.length; i++) { - if (getInfoRes[i] && - getInfoRes[i].hasOwnProperty('info') && - getInfoRes[i].info.hasOwnProperty('version')) { - console.warn(`${coin} set api endpoint to ${coins[coin].api[i]}`); - setExplorerUrl(coins[coin].api[i]); - isExplorerEndpointSet = true; - - break; - } - } - - if (isExplorerEndpointSet) { - this.setState({ - ...this.initialState, - isCheckingRewards: true, - coin, - progress: ` (${index + 1}/${coinTickers.length})`, - balances, - }); - - let currentAction; - try { - currentAction = 'connect'; - updateActionState(this, currentAction, 'loading'); - const hwIsAvailable = await hw[this.props.vendor].isAvailable(); - if (!hwIsAvailable) { - throw new Error(`${VENDOR[this.props.vendor]} device is unavailable!`); - } - updateActionState(this, currentAction, true); - - currentAction = 'approve'; - updateActionState(this, currentAction, 'loading'); - let [accounts, tiptime] = await Promise.all([ - accountDiscovery(this.props.vendor), - blockchain.getTipTime() - ]); - - tiptime = this.props.checkTipTime(tiptime); - - accounts = this.calculateRewardData({accounts, tiptime}); - updateActionState(this, currentAction, true); - - let balanceSum = 0; - - for (let i = 0; i < accounts.length; i++) { - balanceSum += accounts[i].balance; + if (!cancel) { + const getInfoRes = await Promise.all(coins[coin].api.map((value, index) => { + return getInfo(value); + })); + let isExplorerEndpointSet = false; + + console.warn('checkExplorerEndpoints', getInfoRes); + + for (let i = 0; i < coins[coin].api.length; i++) { + if (getInfoRes[i] && + getInfoRes[i].hasOwnProperty('info') && + getInfoRes[i].info.hasOwnProperty('version')) { + console.warn(`${coin} set api endpoint to ${coins[coin].api[i]}`); + setExplorerUrl(coins[coin].api[i]); + isExplorerEndpointSet = true; + + break; } + } - if (balanceSum) { - balances.push({ - coin, - balance: balanceSum, - }); + if (isExplorerEndpointSet) { + this.setState({ + ...this.initialState, + isCheckingRewards: true, + coin, + progress: ` (${index + 1}/${coinTickers.length})`, + balances, + }); + + let currentAction; + try { + currentAction = 'connect'; + updateActionState(this, currentAction, 'loading'); + const hwIsAvailable = await hw[this.props.vendor].isAvailable(); + if (!hwIsAvailable) { + throw new Error(`${VENDOR[this.props.vendor]} device is unavailable!`); + } + updateActionState(this, currentAction, true); + + currentAction = 'approve'; + updateActionState(this, currentAction, 'loading'); + let [accounts, tiptime] = await Promise.all([ + accountDiscovery(this.props.vendor), + blockchain.getTipTime() + ]); + + tiptime = this.props.checkTipTime(tiptime); + + accounts = this.calculateRewardData({accounts, tiptime}); + updateActionState(this, currentAction, true); + + let balanceSum = 0; + + for (let i = 0; i < accounts.length; i++) { + balanceSum += accounts[i].balance; + } + + if (balanceSum) { + balances.push({ + coin, + balance: balanceSum, + }); + } + + //this.setState({...this.initialState}); + } catch (error) { + console.warn(error); + updateActionState(this, currentAction, false); + this.setState({error: error.message}); } - - //this.setState({...this.initialState}); - } catch (error) { - console.warn(error); - updateActionState(this, currentAction, false); - this.setState({error: error.message}); } } }); - if (!this.state.error || (this.state.error && this.state.error.indexOf('Failed to fetch') > -1)) { - updateActionState(this, 'approve', true); - updateActionState(this, 'finished', true); - } + if (!cancel) { + if (!this.state.error || (this.state.error && this.state.error.indexOf('Failed to fetch') > -1)) { + updateActionState(this, 'approve', true); + updateActionState(this, 'finished', true); + } - clearPubkeysCache(); + clearPubkeysCache(); - this.setState({ - error: false, - progress: '', - coin: '', - isCheckingRewards: true, - }); + this.setState({ + error: false, + progress: '', + coin: '', + isCheckingRewards: true, + }); + } setExplorerUrl(this.props.explorerEndpoint); }; From bbf12787d55ce39cf40391b92683832af2d69c1e Mon Sep 17 00:00:00 2001 From: pbca26 Date: Tue, 22 Dec 2020 16:35:57 +0300 Subject: [PATCH 10/13] check all balance button comp display kmd rewards in balances list --- src/App.js | 22 ++++++++++++++++------ src/CheckAllBalancesButton.js | 29 ++++++++++++++++++++++------- 2 files changed, 38 insertions(+), 13 deletions(-) diff --git a/src/App.js b/src/App.js index 8f5c4fea..be0373f8 100755 --- a/src/App.js +++ b/src/App.js @@ -5,6 +5,7 @@ import {isEqual} from 'lodash'; import Header from './Header'; import BetaWarning from './BetaWarning'; import CheckBalanceButton from './CheckBalanceButton'; +import CheckAllBalancesButton from './CheckAllBalancesButton'; import Accounts from './Accounts'; import WarnU2fCompatibility from './WarnU2fCompatibility'; import WarnBrowser from './WarnBrowser'; @@ -367,12 +368,21 @@ class App extends React.Component { {(this.state.vendor === 'trezor' || (this.state.vendor === 'ledger' && this.state.ledgerDeviceType)) && this.state.explorerEndpoint && - - Check Balance - + + + Check Balance + + + Check All + + }