use etherscan-link customBlockExplorer methods with customNetwork usage tracking (#11017)

* use etherscan-link customBlockExplorer methods with customNetwork usage tracking

* consolidate blockexplorer events, add domain to metametrics event

* lint fix
feature/default_network_editable
Alex Donesky 4 years ago committed by GitHub
parent b7a1c8c302
commit f19207ca87
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      app/scripts/platforms/extension.js
  2. 96
      shared/modules/tests/transaction.utils.test.js
  3. 31
      shared/modules/transaction.utils.js
  4. 41
      ui/components/app/menu-bar/account-options-menu.js
  5. 18
      ui/components/app/modals/account-details-modal/account-details-modal.component.js
  6. 1
      ui/components/app/modals/account-details-modal/account-details-modal.test.js
  7. 31
      ui/components/app/modals/confirm-remove-account/confirm-remove-account.component.js
  8. 22
      ui/components/app/transaction-activity-log/transaction-activity-log.component.js
  9. 27
      ui/components/app/transaction-list-item-details/transaction-list-item-details.component.js
  10. 12
      ui/helpers/utils/account-link.js
  11. 49
      ui/helpers/utils/account-link.test.js
  12. 10
      ui/pages/asset/components/asset-options.js
  13. 20
      ui/pages/asset/components/native-asset.js
  14. 36
      ui/pages/asset/components/token-asset.js
  15. 32
      ui/pages/create-account/connect-hardware/account-list.js
  16. 23
      ui/pages/swaps/awaiting-swap/awaiting-swap.js
  17. 19
      ui/pages/swaps/awaiting-swap/view-on-ether-scan-link/view-on-ether-scan-link.js
  18. 71
      ui/pages/swaps/build-quote/build-quote.js

@ -1,8 +1,8 @@
import extension from 'extensionizer';
import { getBlockExplorerLink } from '@metamask/etherscan-link';
import { getEnvironmentType, checkForError } from '../lib/util';
import { ENVIRONMENT_TYPE_BACKGROUND } from '../../../shared/constants/app';
import { TRANSACTION_STATUSES } from '../../../shared/constants/transaction';
import { getBlockExplorerUrlForTx } from '../../../shared/modules/transaction.utils';
export default class ExtensionPlatform {
//
@ -192,7 +192,7 @@ export default class ExtensionPlatform {
_showConfirmedTransaction(txMeta, rpcPrefs) {
this._subscribeToNotificationClicked();
const url = getBlockExplorerUrlForTx(txMeta, rpcPrefs);
const url = getBlockExplorerLink(txMeta, rpcPrefs);
const nonce = parseInt(txMeta.txParams.nonce, 16);
const title = 'Confirmed transaction';

@ -1,96 +0,0 @@
import { strict as assert } from 'assert';
import {
MAINNET_CHAIN_ID,
MAINNET_NETWORK_ID,
ROPSTEN_CHAIN_ID,
ROPSTEN_NETWORK_ID,
} from '../../constants/network';
import { getBlockExplorerUrlForTx } from '../transaction.utils';
const tests = [
{
expected: 'https://etherscan.io/tx/0xabcd',
transaction: {
metamaskNetworkId: MAINNET_NETWORK_ID,
hash: '0xabcd',
},
},
{
expected: 'https://ropsten.etherscan.io/tx/0xdef0',
transaction: {
metamaskNetworkId: ROPSTEN_NETWORK_ID,
hash: '0xdef0',
},
rpcPrefs: {},
},
{
// test handling of `blockExplorerUrl` for a custom RPC
expected: 'https://block.explorer/tx/0xabcd',
transaction: {
metamaskNetworkId: '31',
hash: '0xabcd',
},
rpcPrefs: {
blockExplorerUrl: 'https://block.explorer',
},
},
{
// test handling of trailing `/` in `blockExplorerUrl` for a custom RPC
expected: 'https://another.block.explorer/tx/0xdef0',
transaction: {
networkId: '33',
hash: '0xdef0',
},
rpcPrefs: {
blockExplorerUrl: 'https://another.block.explorer/',
},
},
{
expected: 'https://etherscan.io/tx/0xabcd',
transaction: {
chainId: MAINNET_CHAIN_ID,
hash: '0xabcd',
},
},
{
expected: 'https://ropsten.etherscan.io/tx/0xdef0',
transaction: {
chainId: ROPSTEN_CHAIN_ID,
hash: '0xdef0',
},
rpcPrefs: {},
},
{
// test handling of `blockExplorerUrl` for a custom RPC
expected: 'https://block.explorer/tx/0xabcd',
transaction: {
chainId: '0x1f',
hash: '0xabcd',
},
rpcPrefs: {
blockExplorerUrl: 'https://block.explorer',
},
},
{
// test handling of trailing `/` in `blockExplorerUrl` for a custom RPC
expected: 'https://another.block.explorer/tx/0xdef0',
transaction: {
chainId: '0x21',
hash: '0xdef0',
},
rpcPrefs: {
blockExplorerUrl: 'https://another.block.explorer/',
},
},
];
describe('getBlockExplorerUrlForTx', function () {
tests.forEach((test) => {
it(`should return '${test.expected}' for transaction with hash: '${test.transaction.hash}'`, function () {
assert.strictEqual(
getBlockExplorerUrlForTx(test.transaction, test.rpcPrefs),
test.expected,
);
});
});
});

@ -1,37 +1,6 @@
import {
createExplorerLink,
createExplorerLinkForChain,
} from '@metamask/etherscan-link';
export function transactionMatchesNetwork(transaction, chainId, networkId) {
if (typeof transaction.chainId !== 'undefined') {
return transaction.chainId === chainId;
}
return transaction.metamaskNetworkId === networkId;
}
/**
* build the etherscan link for a transaction by either chainId, if available
* or metamaskNetworkId as a fallback. If rpcPrefs is provided will build the
* url for the provided blockExplorerUrl.
*
* @param {Object} transaction - a transaction object from state
* @param {string} [transaction.metamaskNetworkId] - network id tx occurred on
* @param {string} [transaction.chainId] - chain id tx occurred on
* @param {string} [transaction.hash] - hash of the transaction
* @param {Object} [rpcPrefs] - the rpc preferences for the current RPC network
* @param {string} [rpcPrefs.blockExplorerUrl] - the block explorer url for RPC
* networks
* @returns {string}
*/
export function getBlockExplorerUrlForTx(transaction, rpcPrefs = {}) {
if (rpcPrefs.blockExplorerUrl) {
return `${rpcPrefs.blockExplorerUrl.replace(/\/+$/u, '')}/tx/${
transaction.hash
}`;
}
if (transaction.chainId) {
return createExplorerLinkForChain(transaction.hash, transaction.chainId);
}
return createExplorerLink(transaction.hash, transaction.metamaskNetworkId);
}

@ -2,11 +2,11 @@ import React from 'react';
import PropTypes from 'prop-types';
import { useHistory } from 'react-router-dom';
import { useDispatch, useSelector } from 'react-redux';
import { getAccountLink } from '@metamask/etherscan-link';
import { showModal } from '../../../store/actions';
import { CONNECTED_ROUTE } from '../../../helpers/constants/routes';
import { Menu, MenuItem } from '../../ui/menu';
import getAccountLink from '../../../helpers/utils/account-link';
import {
getCurrentChainId,
getCurrentKeyring,
@ -14,7 +14,10 @@ import {
getSelectedIdentity,
} from '../../../selectors';
import { useI18nContext } from '../../../hooks/useI18nContext';
import { useMetricEvent } from '../../../hooks/useMetricEvent';
import {
useMetricEvent,
useNewMetricEvent,
} from '../../../hooks/useMetricEvent';
import { getEnvironmentType } from '../../../../app/scripts/lib/util';
import { ENVIRONMENT_TYPE_FULLSCREEN } from '../../../../shared/constants/app';
@ -22,6 +25,14 @@ export default function AccountOptionsMenu({ anchorElement, onClose }) {
const t = useI18nContext();
const dispatch = useDispatch();
const history = useHistory();
const keyring = useSelector(getCurrentKeyring);
const chainId = useSelector(getCurrentChainId);
const rpcPrefs = useSelector(getRpcPrefsForCurrentProvider);
const selectedIdentity = useSelector(getSelectedIdentity);
const { address } = selectedIdentity;
const addressLink = getAccountLink(address, chainId, rpcPrefs);
const openFullscreenEvent = useMetricEvent({
eventOpts: {
category: 'Navigation',
@ -36,13 +47,7 @@ export default function AccountOptionsMenu({ anchorElement, onClose }) {
name: 'Viewed Account Details',
},
});
const viewOnEtherscanEvent = useMetricEvent({
eventOpts: {
category: 'Navigation',
action: 'Account Options',
name: 'Clicked View on Etherscan',
},
});
const openConnectedSitesEvent = useMetricEvent({
eventOpts: {
category: 'Navigation',
@ -51,12 +56,16 @@ export default function AccountOptionsMenu({ anchorElement, onClose }) {
},
});
const keyring = useSelector(getCurrentKeyring);
const chainId = useSelector(getCurrentChainId);
const rpcPrefs = useSelector(getRpcPrefsForCurrentProvider);
const selectedIdentity = useSelector(getSelectedIdentity);
const blockExplorerLinkClickedEvent = useNewMetricEvent({
category: 'Navigation',
event: 'Clicked Block Explorer Link',
properties: {
link_type: 'Account Tracker',
action: 'Account Options',
block_explorer_domain: addressLink ? new URL(addressLink)?.hostname : '',
},
});
const { address } = selectedIdentity;
const isRemovable = keyring.type !== 'HD Key Tree';
return (
@ -90,9 +99,9 @@ export default function AccountOptionsMenu({ anchorElement, onClose }) {
</MenuItem>
<MenuItem
onClick={() => {
viewOnEtherscanEvent();
blockExplorerLinkClickedEvent();
global.platform.openTab({
url: getAccountLink(address, chainId, rpcPrefs),
url: addressLink,
});
onClose();
}}

@ -1,7 +1,8 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { getAccountLink } from '@metamask/etherscan-link';
import AccountModalContainer from '../account-modal-container';
import getAccountLink from '../../../../helpers/utils/account-link';
import QrView from '../../../ui/qr-code';
import EditableLabel from '../../../ui/editable-label';
import Button from '../../../ui/button';
@ -18,6 +19,7 @@ export default class AccountDetailsModal extends Component {
static contextTypes = {
t: PropTypes.func,
trackEvent: PropTypes.func,
};
render() {
@ -61,8 +63,20 @@ export default class AccountDetailsModal extends Component {
type="secondary"
className="account-details-modal__button"
onClick={() => {
const accountLink = getAccountLink(address, chainId, rpcPrefs);
this.context.trackEvent({
category: 'Navigation',
event: 'Clicked Block Explorer Link',
properties: {
link_type: 'Account Tracker',
action: 'Account Details Modal',
block_explorer_domain: accountLink
? new URL(accountLink)?.hostname
: '',
},
});
global.platform.openTab({
url: getAccountLink(address, chainId, rpcPrefs),
url: accountLink,
});
}}
>

@ -36,6 +36,7 @@ describe('Account Details Modal', () => {
wrapper = shallow(<AccountDetailsModal.WrappedComponent {...props} />, {
context: {
t: (str) => str,
trackEvent: (e) => e,
},
});
});

@ -1,9 +1,9 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { getAccountLink } from '@metamask/etherscan-link';
import Modal from '../../modal';
import { addressSummary } from '../../../../helpers/utils/util';
import Identicon from '../../../ui/identicon';
import getAccountLink from '../../../../helpers/utils/account-link';
export default class ConfirmRemoveAccount extends Component {
static propTypes = {
@ -16,6 +16,7 @@ export default class ConfirmRemoveAccount extends Component {
static contextTypes = {
t: PropTypes.func,
trackEvent: PropTypes.func,
};
handleRemove = () => {
@ -30,7 +31,7 @@ export default class ConfirmRemoveAccount extends Component {
renderSelectedAccount() {
const { t } = this.context;
const { identity } = this.props;
const { identity, rpcPrefs, chainId } = this.props;
return (
<div className="confirm-remove-account__account">
<div className="confirm-remove-account__account__identicon">
@ -53,11 +54,27 @@ export default class ConfirmRemoveAccount extends Component {
<div className="confirm-remove-account__account__link">
<a
className=""
href={getAccountLink(
identity.address,
this.props.chainId,
this.props.rpcPrefs,
)}
onClick={() => {
const accountLink = getAccountLink(
identity.address,
chainId,
rpcPrefs,
);
this.context.trackEvent({
category: 'Accounts',
event: 'Clicked Block Explorer Link',
properties: {
link_type: 'Account Tracker',
action: 'Remove Account',
block_explorer_domain: accountLink
? new URL(accountLink)?.hostname
: '',
},
});
global.platform.openTab({
url: accountLink,
});
}}
target="_blank"
rel="noopener noreferrer"
title={t('etherscanView')}

@ -2,18 +2,19 @@ import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { getBlockExplorerLink } from '@metamask/etherscan-link';
import {
getEthConversionFromWeiHex,
getValueFromWeiHex,
} from '../../../helpers/utils/conversions.util';
import { formatDate } from '../../../helpers/utils/util';
import { getBlockExplorerUrlForTx } from '../../../../shared/modules/transaction.utils';
import TransactionActivityLogIcon from './transaction-activity-log-icon';
import { CONFIRMED_STATUS } from './transaction-activity-log.constants';
export default class TransactionActivityLog extends PureComponent {
static contextTypes = {
t: PropTypes.func,
trackEvent: PropTypes.func,
};
static propTypes = {
@ -31,10 +32,21 @@ export default class TransactionActivityLog extends PureComponent {
};
handleActivityClick = (activity) => {
const etherscanUrl = getBlockExplorerUrlForTx(
activity,
this.props.rpcPrefs,
);
const { rpcPrefs } = this.props;
const etherscanUrl = getBlockExplorerLink(activity, rpcPrefs);
this.context.trackEvent({
category: 'Transactions',
event: 'Clicked Block Explorer Link',
properties: {
link_type: 'Transaction Block Explorer',
action: 'Activity Details',
block_explorer_domain: etherscanUrl
? new URL(etherscanUrl)?.hostname
: '',
},
});
global.platform.openTab({ url: etherscanUrl });
};

@ -1,6 +1,7 @@
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import copyToClipboard from 'copy-to-clipboard';
import { getBlockExplorerLink } from '@metamask/etherscan-link';
import SenderToRecipient from '../../ui/sender-to-recipient';
import { FLAT_VARIANT } from '../../ui/sender-to-recipient/sender-to-recipient.constants';
import TransactionActivityLog from '../transaction-activity-log';
@ -9,13 +10,13 @@ import Button from '../../ui/button';
import Tooltip from '../../ui/tooltip';
import Copy from '../../ui/icon/copy-icon.component';
import Popover from '../../ui/popover';
import { getBlockExplorerUrlForTx } from '../../../../shared/modules/transaction.utils';
import { TRANSACTION_TYPES } from '../../../../shared/constants/transaction';
export default class TransactionListItemDetails extends PureComponent {
static contextTypes = {
t: PropTypes.func,
metricsEvent: PropTypes.func,
trackEvent: PropTypes.func,
};
static defaultProps = {
@ -47,22 +48,30 @@ export default class TransactionListItemDetails extends PureComponent {
justCopied: false,
};
handleEtherscanClick = () => {
handleBlockExplorerClick = () => {
const {
transactionGroup: { primaryTransaction },
rpcPrefs,
} = this.props;
const blockExplorerLink = getBlockExplorerLink(
primaryTransaction,
rpcPrefs,
);
this.context.metricsEvent({
eventOpts: {
category: 'Navigation',
action: 'Activity Log',
name: 'Clicked "View on Etherscan"',
this.context.trackEvent({
category: 'Transactions',
event: 'Clicked Block Explorer Link',
properties: {
link_type: 'Transaction Block Explorer',
action: 'Transaction Details',
block_explorer_domain: blockExplorerLink
? new URL(blockExplorerLink)?.hostname
: '',
},
});
global.platform.openTab({
url: getBlockExplorerUrlForTx(primaryTransaction, rpcPrefs),
url: blockExplorerLink,
});
};
@ -203,7 +212,7 @@ export default class TransactionListItemDetails extends PureComponent {
>
<Button
type="raised"
onClick={this.handleEtherscanClick}
onClick={this.handleBlockExplorerClick}
disabled={!hash}
>
<img src="/images/arrow-popout.svg" alt="" />

@ -1,12 +0,0 @@
import { createAccountLinkForChain } from '@metamask/etherscan-link';
export default function getAccountLink(address, chainId, rpcPrefs) {
if (rpcPrefs && rpcPrefs.blockExplorerUrl) {
return `${rpcPrefs.blockExplorerUrl.replace(
/\/+$/u,
'',
)}/address/${address}`;
}
return createAccountLinkForChain(address, chainId);
}

@ -1,49 +0,0 @@
import {
MAINNET_CHAIN_ID,
ROPSTEN_CHAIN_ID,
} from '../../../shared/constants/network';
import getAccountLink from './account-link';
describe('Account link', () => {
describe('getAccountLink', () => {
it('should return the correct block explorer url for an account', () => {
const tests = [
{
expected: 'https://etherscan.io/address/0xabcd',
chainId: MAINNET_CHAIN_ID,
address: '0xabcd',
},
{
expected: 'https://ropsten.etherscan.io/address/0xdef0',
chainId: ROPSTEN_CHAIN_ID,
address: '0xdef0',
rpcPrefs: {},
},
{
// test handling of `blockExplorerUrl` for a custom RPC
expected: 'https://block.explorer/address/0xabcd',
chainId: '0x21',
address: '0xabcd',
rpcPrefs: {
blockExplorerUrl: 'https://block.explorer',
},
},
{
// test handling of trailing `/` in `blockExplorerUrl` for a custom RPC
expected: 'https://another.block.explorer/address/0xdef0',
chainId: '0x1f',
address: '0xdef0',
rpcPrefs: {
blockExplorerUrl: 'https://another.block.explorer/',
},
},
];
tests.forEach(({ expected, address, chainId, rpcPrefs }) => {
expect(getAccountLink(address, chainId, rpcPrefs)).toStrictEqual(
expected,
);
});
});
});
});

@ -6,10 +6,11 @@ import { Menu, MenuItem } from '../../../components/ui/menu';
const AssetOptions = ({
onRemove,
onViewEtherscan,
onClickBlockExplorer,
onViewAccountDetails,
tokenSymbol,
isNativeAsset,
isEthNetwork,
}) => {
const t = useContext(I18nContext);
const [assetOptionsButtonElement, setAssetOptionsButtonElement] = useState(
@ -46,10 +47,10 @@ const AssetOptions = ({
data-testid="asset-options__etherscan"
onClick={() => {
setAssetOptionsOpen(false);
onViewEtherscan();
onClickBlockExplorer();
}}
>
{t('viewOnEtherscan')}
{isEthNetwork ? t('viewOnEtherscan') : t('viewinExplorer')}
</MenuItem>
{isNativeAsset ? null : (
<MenuItem
@ -70,9 +71,10 @@ const AssetOptions = ({
};
AssetOptions.propTypes = {
isEthNetwork: PropTypes.bool,
isNativeAsset: PropTypes.bool,
onRemove: PropTypes.func.isRequired,
onViewEtherscan: PropTypes.func.isRequired,
onClickBlockExplorer: PropTypes.func.isRequired,
onViewAccountDetails: PropTypes.func.isRequired,
tokenSymbol: PropTypes.string,
};

@ -2,6 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import { useSelector, useDispatch } from 'react-redux';
import { useHistory } from 'react-router-dom';
import { getAccountLink } from '@metamask/etherscan-link';
import TransactionList from '../../../components/app/transaction-list';
import { EthOverview } from '../../../components/app/wallet-overview';
import {
@ -11,8 +12,8 @@ import {
getSelectedAddress,
} from '../../../selectors/selectors';
import { showModal } from '../../../store/actions';
import getAccountLink from '../../../helpers/utils/account-link';
import { DEFAULT_ROUTE } from '../../../helpers/constants/routes';
import { useNewMetricEvent } from '../../../hooks/useMetricEvent';
import AssetNavigation from './asset-navigation';
import AssetOptions from './asset-options';
@ -26,6 +27,17 @@ export default function NativeAsset({ nativeCurrency }) {
const rpcPrefs = useSelector(getRpcPrefsForCurrentProvider);
const address = useSelector(getSelectedAddress);
const history = useHistory();
const accountLink = getAccountLink(address, chainId, rpcPrefs);
const blockExplorerLinkClickedEvent = useNewMetricEvent({
category: 'Navigation',
event: 'Clicked Block Explorer Link',
properties: {
link_type: 'Account Tracker',
action: 'Asset Options',
block_explorer_domain: accountLink ? new URL(accountLink)?.hostname : '',
},
});
return (
<>
@ -33,12 +45,14 @@ export default function NativeAsset({ nativeCurrency }) {
accountName={selectedAccountName}
assetName={nativeCurrency}
onBack={() => history.push(DEFAULT_ROUTE)}
isEthNetwork={!rpcPrefs.blockExplorerUrl}
optionsButton={
<AssetOptions
isNativeAsset
onViewEtherscan={() => {
onClickBlockExplorer={() => {
blockExplorerLinkClickedEvent();
global.platform.openTab({
url: getAccountLink(address, chainId, rpcPrefs),
url: accountLink,
});
}}
onViewAccountDetails={() => {

@ -2,16 +2,17 @@ import React from 'react';
import PropTypes from 'prop-types';
import { useDispatch, useSelector } from 'react-redux';
import { useHistory } from 'react-router-dom';
import { createTokenTrackerLinkForChain } from '@metamask/etherscan-link';
import { getTokenTrackerLink } from '@metamask/etherscan-link';
import TransactionList from '../../../components/app/transaction-list';
import { TokenOverview } from '../../../components/app/wallet-overview';
import {
getCurrentChainId,
getSelectedIdentity,
getRpcPrefsForCurrentProvider,
} from '../../../selectors/selectors';
import { DEFAULT_ROUTE } from '../../../helpers/constants/routes';
import { showModal } from '../../../store/actions';
import { useNewMetricEvent } from '../../../hooks/useMetricEvent';
import AssetNavigation from './asset-navigation';
import AssetOptions from './asset-options';
@ -19,10 +20,30 @@ import AssetOptions from './asset-options';
export default function TokenAsset({ token }) {
const dispatch = useDispatch();
const chainId = useSelector(getCurrentChainId);
const rpcPrefs = useSelector(getRpcPrefsForCurrentProvider);
const selectedIdentity = useSelector(getSelectedIdentity);
const selectedAccountName = selectedIdentity.name;
const selectedAddress = selectedIdentity.address;
const history = useHistory();
const tokenTrackerLink = getTokenTrackerLink(
token.address,
chainId,
null,
selectedAddress,
rpcPrefs,
);
const blockExplorerLinkClickedEvent = useNewMetricEvent({
category: 'Navigation',
event: 'Clicked Block Explorer Link',
properties: {
link_type: 'Token Tracker',
action: 'Token Options',
block_explorer_domain: tokenTrackerLink
? new URL(tokenTrackerLink)?.hostname
: '',
},
});
return (
<>
@ -35,13 +56,10 @@ export default function TokenAsset({ token }) {
onRemove={() =>
dispatch(showModal({ name: 'HIDE_TOKEN_CONFIRMATION', token }))
}
onViewEtherscan={() => {
const url = createTokenTrackerLinkForChain(
token.address,
chainId,
selectedAddress,
);
global.platform.openTab({ url });
isEthNetwork={!rpcPrefs.blockExplorerUrl}
onClickBlockExplorer={() => {
blockExplorerLinkClickedEvent();
global.platform.openTab({ url: tokenTrackerLink });
}}
onViewAccountDetails={() => {
dispatch(showModal({ name: 'ACCOUNT_DETAILS' }));

@ -1,6 +1,7 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import getAccountLink from '../../../helpers/utils/account-link';
import { getAccountLink } from '@metamask/etherscan-link';
import Button from '../../../components/ui/button';
import Checkbox from '../../../components/ui/check-box';
import Dropdown from '../../../components/ui/dropdown';
@ -83,7 +84,7 @@ class AccountList extends Component {
}
renderAccounts() {
const { accounts, connectedAccounts } = this.props;
const { accounts, connectedAccounts, rpcPrefs, chainId } = this.props;
return (
<div className="hw-account-list">
@ -130,11 +131,27 @@ class AccountList extends Component {
</div>
<a
className="hw-account-list__item__link"
href={getAccountLink(
account.address,
this.props.chainId,
this.props.rpcPrefs,
)}
onClick={() => {
const accountLink = getAccountLink(
account.address,
chainId,
rpcPrefs,
);
this.context.trackEvent({
category: 'Account',
event: 'Clicked Block Explorer Link',
properties: {
actions: 'Hardware Connect',
link_type: 'Account Tracker',
block_explorer_domain: accountLink
? new URL(accountLink)?.hostname
: '',
},
});
global.platform.openTab({
url: accountLink,
});
}}
target="_blank"
rel="noopener noreferrer"
title={this.context.t('etherscanView')}
@ -282,6 +299,7 @@ AccountList.propTypes = {
AccountList.contextTypes = {
t: PropTypes.func,
trackEvent: PropTypes.func,
};
export default AccountList;

@ -3,7 +3,7 @@ import React, { useContext, useRef, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import PropTypes from 'prop-types';
import { useHistory } from 'react-router-dom';
import { createCustomExplorerLink } from '@metamask/etherscan-link';
import { getBlockExplorerLink } from '@metamask/etherscan-link';
import { I18nContext } from '../../../contexts/i18n';
import { useNewMetricEvent } from '../../../hooks/useMetricEvent';
import { MetaMetricsContext } from '../../../contexts/metametrics.new';
@ -37,7 +37,6 @@ import {
OFFLINE_FOR_MAINTENANCE,
SWAPS_CHAINID_DEFAULT_BLOCK_EXPLORER_URL_MAP,
} from '../../../../shared/constants/swaps';
import { CHAIN_ID_TO_TYPE_MAP as VALID_INFURA_CHAIN_IDS } from '../../../../shared/constants/network';
import { isSwapsDefaultTokenSymbol } from '../../../../shared/modules/swaps.utils';
import PulseLoader from '../../../components/ui/pulse-loader';
@ -45,7 +44,6 @@ import { ASSET_ROUTE, DEFAULT_ROUTE } from '../../../helpers/constants/routes';
import { getRenderableNetworkFeesForQuote } from '../swaps.util';
import SwapsFooter from '../swaps-footer';
import { getBlockExplorerUrlForTx } from '../../../../shared/modules/transaction.utils';
import SwapFailureIcon from './swap-failure-icon';
import SwapSuccessIcon from './swap-success-icon';
@ -116,17 +114,14 @@ export default function AwaitingSwap({
category: 'swaps',
});
let blockExplorerUrl;
if (txHash && rpcPrefs.blockExplorerUrl) {
blockExplorerUrl = getBlockExplorerUrlForTx({ hash: txHash }, rpcPrefs);
} else if (txHash && SWAPS_CHAINID_DEFAULT_BLOCK_EXPLORER_URL_MAP[chainId]) {
blockExplorerUrl = createCustomExplorerLink(
txHash,
SWAPS_CHAINID_DEFAULT_BLOCK_EXPLORER_URL_MAP[chainId],
);
} else if (txHash && VALID_INFURA_CHAIN_IDS[chainId]) {
blockExplorerUrl = getBlockExplorerUrlForTx({ chainId, hash: txHash });
}
const baseNetworkUrl =
rpcPrefs.blockExplorerUrl ??
SWAPS_CHAINID_DEFAULT_BLOCK_EXPLORER_URL_MAP[chainId] ??
null;
const blockExplorerUrl = getBlockExplorerLink(
{ hash: txHash, chainId },
{ blockExplorerUrl: baseNetworkUrl },
);
const isCustomBlockExplorerUrl =
SWAPS_CHAINID_DEFAULT_BLOCK_EXPLORER_URL_MAP[chainId] ||

@ -2,6 +2,7 @@ import React, { useContext } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { I18nContext } from '../../../../contexts/i18n';
import { useNewMetricEvent } from '../../../../hooks/useMetricEvent';
export default function ViewOnEtherScanLink({
txHash,
@ -9,13 +10,29 @@ export default function ViewOnEtherScanLink({
isCustomBlockExplorerUrl,
}) {
const t = useContext(I18nContext);
const blockExplorerLinkClickedEvent = useNewMetricEvent({
category: 'Swaps',
event: 'Clicked Block Explorer Link',
properties: {
link_type: 'Transaction Block Explorer',
action: 'Swap Transaction',
block_explorer_domain: blockExplorerUrl
? new URL(blockExplorerUrl)?.hostname
: '',
},
});
return (
<div
className={classnames('awaiting-swap__view-on-etherscan', {
'awaiting-swap__view-on-etherscan--visible': txHash,
'awaiting-swap__view-on-etherscan--invisible': !txHash,
})}
onClick={() => global.platform.openTab({ url: blockExplorerUrl })}
onClick={() => {
blockExplorerLinkClickedEvent();
global.platform.openTab({ url: blockExplorerUrl });
}}
>
{isCustomBlockExplorerUrl
? t('viewOnCustomBlockExplorer', [new URL(blockExplorerUrl).hostname])

@ -4,11 +4,9 @@ import { useDispatch, useSelector } from 'react-redux';
import classnames from 'classnames';
import { uniqBy, isEqual } from 'lodash';
import { useHistory } from 'react-router-dom';
import {
createCustomTokenTrackerLink,
createTokenTrackerLinkForChain,
} from '@metamask/etherscan-link';
import { getTokenTrackerLink } from '@metamask/etherscan-link';
import { MetaMetricsContext } from '../../../contexts/metametrics.new';
import { useNewMetricEvent } from '../../../hooks/useMetricEvent';
import {
useTokensToSearch,
getRenderableTokenData,
@ -223,29 +221,34 @@ export default function BuildQuote({
);
};
let blockExplorerTokenLink;
let blockExplorerLabel;
if (rpcPrefs.blockExplorerUrl) {
blockExplorerTokenLink = createCustomTokenTrackerLink(
selectedToToken.address,
rpcPrefs.blockExplorerUrl,
);
blockExplorerLabel = new URL(rpcPrefs.blockExplorerUrl).hostname;
} else if (SWAPS_CHAINID_DEFAULT_BLOCK_EXPLORER_URL_MAP[chainId]) {
blockExplorerTokenLink = createCustomTokenTrackerLink(
selectedToToken.address,
SWAPS_CHAINID_DEFAULT_BLOCK_EXPLORER_URL_MAP[chainId],
);
blockExplorerLabel = new URL(
SWAPS_CHAINID_DEFAULT_BLOCK_EXPLORER_URL_MAP[chainId],
).hostname;
} else {
blockExplorerTokenLink = createTokenTrackerLinkForChain(
selectedToToken.address,
chainId,
);
blockExplorerLabel = t('etherscan');
}
const blockExplorerTokenLink = getTokenTrackerLink(
selectedToToken.address,
chainId,
null, // no networkId
null, // no holderAddress
{
blockExplorerUrl:
rpcPrefs.blockExplorerUrl ??
SWAPS_CHAINID_DEFAULT_BLOCK_EXPLORER_URL_MAP[chainId] ??
null,
},
);
const blockExplorerLabel = rpcPrefs.blockExplorerUrl
? new URL(blockExplorerTokenLink).hostname
: t('etherscan');
const blockExplorerLinkClickedEvent = useNewMetricEvent({
category: 'Swaps',
event: 'Clicked Block Explorer Link',
properties: {
link_type: 'Token Tracker',
action: 'Swaps Confirmation',
block_explorer_domain: blockExplorerTokenLink
? new URL(blockExplorerTokenLink)?.hostname
: '',
},
});
const { destinationTokenAddedForSwap } = fetchParams || {};
const { address: toAddress } = toToken || {};
@ -449,7 +452,12 @@ export default function BuildQuote({
<a
className="build-quote__token-etherscan-link build-quote__underline"
key="build-quote-etherscan-link"
href={blockExplorerTokenLink}
onClick={() => {
blockExplorerLinkClickedEvent();
global.platform.openTab({
url: blockExplorerTokenLink,
});
}}
target="_blank"
rel="noopener noreferrer"
>
@ -488,7 +496,12 @@ export default function BuildQuote({
<a
className="build-quote__token-etherscan-link"
key="build-quote-etherscan-link"
href={blockExplorerTokenLink}
onClick={() => {
blockExplorerLinkClickedEvent();
global.platform.openTab({
url: blockExplorerTokenLink,
});
}}
target="_blank"
rel="noopener noreferrer"
>

Loading…
Cancel
Save