mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
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
This commit is contained in:
parent
b7a1c8c302
commit
f19207ca87
@ -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…
Reference in New Issue
Block a user