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 extension from 'extensionizer';
|
||||||
|
import { getBlockExplorerLink } from '@metamask/etherscan-link';
|
||||||
import { getEnvironmentType, checkForError } from '../lib/util';
|
import { getEnvironmentType, checkForError } from '../lib/util';
|
||||||
import { ENVIRONMENT_TYPE_BACKGROUND } from '../../../shared/constants/app';
|
import { ENVIRONMENT_TYPE_BACKGROUND } from '../../../shared/constants/app';
|
||||||
import { TRANSACTION_STATUSES } from '../../../shared/constants/transaction';
|
import { TRANSACTION_STATUSES } from '../../../shared/constants/transaction';
|
||||||
import { getBlockExplorerUrlForTx } from '../../../shared/modules/transaction.utils';
|
|
||||||
|
|
||||||
export default class ExtensionPlatform {
|
export default class ExtensionPlatform {
|
||||||
//
|
//
|
||||||
@ -192,7 +192,7 @@ export default class ExtensionPlatform {
|
|||||||
_showConfirmedTransaction(txMeta, rpcPrefs) {
|
_showConfirmedTransaction(txMeta, rpcPrefs) {
|
||||||
this._subscribeToNotificationClicked();
|
this._subscribeToNotificationClicked();
|
||||||
|
|
||||||
const url = getBlockExplorerUrlForTx(txMeta, rpcPrefs);
|
const url = getBlockExplorerLink(txMeta, rpcPrefs);
|
||||||
const nonce = parseInt(txMeta.txParams.nonce, 16);
|
const nonce = parseInt(txMeta.txParams.nonce, 16);
|
||||||
|
|
||||||
const title = 'Confirmed transaction';
|
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) {
|
export function transactionMatchesNetwork(transaction, chainId, networkId) {
|
||||||
if (typeof transaction.chainId !== 'undefined') {
|
if (typeof transaction.chainId !== 'undefined') {
|
||||||
return transaction.chainId === chainId;
|
return transaction.chainId === chainId;
|
||||||
}
|
}
|
||||||
return transaction.metamaskNetworkId === networkId;
|
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 PropTypes from 'prop-types';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import { getAccountLink } from '@metamask/etherscan-link';
|
||||||
|
|
||||||
import { showModal } from '../../../store/actions';
|
import { showModal } from '../../../store/actions';
|
||||||
import { CONNECTED_ROUTE } from '../../../helpers/constants/routes';
|
import { CONNECTED_ROUTE } from '../../../helpers/constants/routes';
|
||||||
import { Menu, MenuItem } from '../../ui/menu';
|
import { Menu, MenuItem } from '../../ui/menu';
|
||||||
import getAccountLink from '../../../helpers/utils/account-link';
|
|
||||||
import {
|
import {
|
||||||
getCurrentChainId,
|
getCurrentChainId,
|
||||||
getCurrentKeyring,
|
getCurrentKeyring,
|
||||||
@ -14,7 +14,10 @@ import {
|
|||||||
getSelectedIdentity,
|
getSelectedIdentity,
|
||||||
} from '../../../selectors';
|
} from '../../../selectors';
|
||||||
import { useI18nContext } from '../../../hooks/useI18nContext';
|
import { useI18nContext } from '../../../hooks/useI18nContext';
|
||||||
import { useMetricEvent } from '../../../hooks/useMetricEvent';
|
import {
|
||||||
|
useMetricEvent,
|
||||||
|
useNewMetricEvent,
|
||||||
|
} from '../../../hooks/useMetricEvent';
|
||||||
import { getEnvironmentType } from '../../../../app/scripts/lib/util';
|
import { getEnvironmentType } from '../../../../app/scripts/lib/util';
|
||||||
import { ENVIRONMENT_TYPE_FULLSCREEN } from '../../../../shared/constants/app';
|
import { ENVIRONMENT_TYPE_FULLSCREEN } from '../../../../shared/constants/app';
|
||||||
|
|
||||||
@ -22,6 +25,14 @@ export default function AccountOptionsMenu({ anchorElement, onClose }) {
|
|||||||
const t = useI18nContext();
|
const t = useI18nContext();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const history = useHistory();
|
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({
|
const openFullscreenEvent = useMetricEvent({
|
||||||
eventOpts: {
|
eventOpts: {
|
||||||
category: 'Navigation',
|
category: 'Navigation',
|
||||||
@ -36,13 +47,7 @@ export default function AccountOptionsMenu({ anchorElement, onClose }) {
|
|||||||
name: 'Viewed Account Details',
|
name: 'Viewed Account Details',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const viewOnEtherscanEvent = useMetricEvent({
|
|
||||||
eventOpts: {
|
|
||||||
category: 'Navigation',
|
|
||||||
action: 'Account Options',
|
|
||||||
name: 'Clicked View on Etherscan',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const openConnectedSitesEvent = useMetricEvent({
|
const openConnectedSitesEvent = useMetricEvent({
|
||||||
eventOpts: {
|
eventOpts: {
|
||||||
category: 'Navigation',
|
category: 'Navigation',
|
||||||
@ -51,12 +56,16 @@ export default function AccountOptionsMenu({ anchorElement, onClose }) {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const keyring = useSelector(getCurrentKeyring);
|
const blockExplorerLinkClickedEvent = useNewMetricEvent({
|
||||||
const chainId = useSelector(getCurrentChainId);
|
category: 'Navigation',
|
||||||
const rpcPrefs = useSelector(getRpcPrefsForCurrentProvider);
|
event: 'Clicked Block Explorer Link',
|
||||||
const selectedIdentity = useSelector(getSelectedIdentity);
|
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';
|
const isRemovable = keyring.type !== 'HD Key Tree';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -90,9 +99,9 @@ export default function AccountOptionsMenu({ anchorElement, onClose }) {
|
|||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
viewOnEtherscanEvent();
|
blockExplorerLinkClickedEvent();
|
||||||
global.platform.openTab({
|
global.platform.openTab({
|
||||||
url: getAccountLink(address, chainId, rpcPrefs),
|
url: addressLink,
|
||||||
});
|
});
|
||||||
onClose();
|
onClose();
|
||||||
}}
|
}}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import { getAccountLink } from '@metamask/etherscan-link';
|
||||||
|
|
||||||
import AccountModalContainer from '../account-modal-container';
|
import AccountModalContainer from '../account-modal-container';
|
||||||
import getAccountLink from '../../../../helpers/utils/account-link';
|
|
||||||
import QrView from '../../../ui/qr-code';
|
import QrView from '../../../ui/qr-code';
|
||||||
import EditableLabel from '../../../ui/editable-label';
|
import EditableLabel from '../../../ui/editable-label';
|
||||||
import Button from '../../../ui/button';
|
import Button from '../../../ui/button';
|
||||||
@ -18,6 +19,7 @@ export default class AccountDetailsModal extends Component {
|
|||||||
|
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
t: PropTypes.func,
|
t: PropTypes.func,
|
||||||
|
trackEvent: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -61,8 +63,20 @@ export default class AccountDetailsModal extends Component {
|
|||||||
type="secondary"
|
type="secondary"
|
||||||
className="account-details-modal__button"
|
className="account-details-modal__button"
|
||||||
onClick={() => {
|
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({
|
global.platform.openTab({
|
||||||
url: getAccountLink(address, chainId, rpcPrefs),
|
url: accountLink,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -36,6 +36,7 @@ describe('Account Details Modal', () => {
|
|||||||
wrapper = shallow(<AccountDetailsModal.WrappedComponent {...props} />, {
|
wrapper = shallow(<AccountDetailsModal.WrappedComponent {...props} />, {
|
||||||
context: {
|
context: {
|
||||||
t: (str) => str,
|
t: (str) => str,
|
||||||
|
trackEvent: (e) => e,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import { getAccountLink } from '@metamask/etherscan-link';
|
||||||
import Modal from '../../modal';
|
import Modal from '../../modal';
|
||||||
import { addressSummary } from '../../../../helpers/utils/util';
|
import { addressSummary } from '../../../../helpers/utils/util';
|
||||||
import Identicon from '../../../ui/identicon';
|
import Identicon from '../../../ui/identicon';
|
||||||
import getAccountLink from '../../../../helpers/utils/account-link';
|
|
||||||
|
|
||||||
export default class ConfirmRemoveAccount extends Component {
|
export default class ConfirmRemoveAccount extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
@ -16,6 +16,7 @@ export default class ConfirmRemoveAccount extends Component {
|
|||||||
|
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
t: PropTypes.func,
|
t: PropTypes.func,
|
||||||
|
trackEvent: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
handleRemove = () => {
|
handleRemove = () => {
|
||||||
@ -30,7 +31,7 @@ export default class ConfirmRemoveAccount extends Component {
|
|||||||
|
|
||||||
renderSelectedAccount() {
|
renderSelectedAccount() {
|
||||||
const { t } = this.context;
|
const { t } = this.context;
|
||||||
const { identity } = this.props;
|
const { identity, rpcPrefs, chainId } = this.props;
|
||||||
return (
|
return (
|
||||||
<div className="confirm-remove-account__account">
|
<div className="confirm-remove-account__account">
|
||||||
<div className="confirm-remove-account__account__identicon">
|
<div className="confirm-remove-account__account__identicon">
|
||||||
@ -53,11 +54,27 @@ export default class ConfirmRemoveAccount extends Component {
|
|||||||
<div className="confirm-remove-account__account__link">
|
<div className="confirm-remove-account__account__link">
|
||||||
<a
|
<a
|
||||||
className=""
|
className=""
|
||||||
href={getAccountLink(
|
onClick={() => {
|
||||||
identity.address,
|
const accountLink = getAccountLink(
|
||||||
this.props.chainId,
|
identity.address,
|
||||||
this.props.rpcPrefs,
|
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"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
title={t('etherscanView')}
|
title={t('etherscanView')}
|
||||||
|
@ -2,18 +2,19 @@ import React, { PureComponent } from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
|
||||||
|
import { getBlockExplorerLink } from '@metamask/etherscan-link';
|
||||||
import {
|
import {
|
||||||
getEthConversionFromWeiHex,
|
getEthConversionFromWeiHex,
|
||||||
getValueFromWeiHex,
|
getValueFromWeiHex,
|
||||||
} from '../../../helpers/utils/conversions.util';
|
} from '../../../helpers/utils/conversions.util';
|
||||||
import { formatDate } from '../../../helpers/utils/util';
|
import { formatDate } from '../../../helpers/utils/util';
|
||||||
import { getBlockExplorerUrlForTx } from '../../../../shared/modules/transaction.utils';
|
|
||||||
import TransactionActivityLogIcon from './transaction-activity-log-icon';
|
import TransactionActivityLogIcon from './transaction-activity-log-icon';
|
||||||
import { CONFIRMED_STATUS } from './transaction-activity-log.constants';
|
import { CONFIRMED_STATUS } from './transaction-activity-log.constants';
|
||||||
|
|
||||||
export default class TransactionActivityLog extends PureComponent {
|
export default class TransactionActivityLog extends PureComponent {
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
t: PropTypes.func,
|
t: PropTypes.func,
|
||||||
|
trackEvent: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
@ -31,10 +32,21 @@ export default class TransactionActivityLog extends PureComponent {
|
|||||||
};
|
};
|
||||||
|
|
||||||
handleActivityClick = (activity) => {
|
handleActivityClick = (activity) => {
|
||||||
const etherscanUrl = getBlockExplorerUrlForTx(
|
const { rpcPrefs } = this.props;
|
||||||
activity,
|
const etherscanUrl = getBlockExplorerLink(activity, rpcPrefs);
|
||||||
this.props.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 });
|
global.platform.openTab({ url: etherscanUrl });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import copyToClipboard from 'copy-to-clipboard';
|
import copyToClipboard from 'copy-to-clipboard';
|
||||||
|
import { getBlockExplorerLink } from '@metamask/etherscan-link';
|
||||||
import SenderToRecipient from '../../ui/sender-to-recipient';
|
import SenderToRecipient from '../../ui/sender-to-recipient';
|
||||||
import { FLAT_VARIANT } from '../../ui/sender-to-recipient/sender-to-recipient.constants';
|
import { FLAT_VARIANT } from '../../ui/sender-to-recipient/sender-to-recipient.constants';
|
||||||
import TransactionActivityLog from '../transaction-activity-log';
|
import TransactionActivityLog from '../transaction-activity-log';
|
||||||
@ -9,13 +10,13 @@ import Button from '../../ui/button';
|
|||||||
import Tooltip from '../../ui/tooltip';
|
import Tooltip from '../../ui/tooltip';
|
||||||
import Copy from '../../ui/icon/copy-icon.component';
|
import Copy from '../../ui/icon/copy-icon.component';
|
||||||
import Popover from '../../ui/popover';
|
import Popover from '../../ui/popover';
|
||||||
import { getBlockExplorerUrlForTx } from '../../../../shared/modules/transaction.utils';
|
|
||||||
import { TRANSACTION_TYPES } from '../../../../shared/constants/transaction';
|
import { TRANSACTION_TYPES } from '../../../../shared/constants/transaction';
|
||||||
|
|
||||||
export default class TransactionListItemDetails extends PureComponent {
|
export default class TransactionListItemDetails extends PureComponent {
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
t: PropTypes.func,
|
t: PropTypes.func,
|
||||||
metricsEvent: PropTypes.func,
|
metricsEvent: PropTypes.func,
|
||||||
|
trackEvent: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
@ -47,22 +48,30 @@ export default class TransactionListItemDetails extends PureComponent {
|
|||||||
justCopied: false,
|
justCopied: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
handleEtherscanClick = () => {
|
handleBlockExplorerClick = () => {
|
||||||
const {
|
const {
|
||||||
transactionGroup: { primaryTransaction },
|
transactionGroup: { primaryTransaction },
|
||||||
rpcPrefs,
|
rpcPrefs,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
const blockExplorerLink = getBlockExplorerLink(
|
||||||
|
primaryTransaction,
|
||||||
|
rpcPrefs,
|
||||||
|
);
|
||||||
|
|
||||||
this.context.metricsEvent({
|
this.context.trackEvent({
|
||||||
eventOpts: {
|
category: 'Transactions',
|
||||||
category: 'Navigation',
|
event: 'Clicked Block Explorer Link',
|
||||||
action: 'Activity Log',
|
properties: {
|
||||||
name: 'Clicked "View on Etherscan"',
|
link_type: 'Transaction Block Explorer',
|
||||||
|
action: 'Transaction Details',
|
||||||
|
block_explorer_domain: blockExplorerLink
|
||||||
|
? new URL(blockExplorerLink)?.hostname
|
||||||
|
: '',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
global.platform.openTab({
|
global.platform.openTab({
|
||||||
url: getBlockExplorerUrlForTx(primaryTransaction, rpcPrefs),
|
url: blockExplorerLink,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -203,7 +212,7 @@ export default class TransactionListItemDetails extends PureComponent {
|
|||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
type="raised"
|
type="raised"
|
||||||
onClick={this.handleEtherscanClick}
|
onClick={this.handleBlockExplorerClick}
|
||||||
disabled={!hash}
|
disabled={!hash}
|
||||||
>
|
>
|
||||||
<img src="/images/arrow-popout.svg" alt="" />
|
<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 = ({
|
const AssetOptions = ({
|
||||||
onRemove,
|
onRemove,
|
||||||
onViewEtherscan,
|
onClickBlockExplorer,
|
||||||
onViewAccountDetails,
|
onViewAccountDetails,
|
||||||
tokenSymbol,
|
tokenSymbol,
|
||||||
isNativeAsset,
|
isNativeAsset,
|
||||||
|
isEthNetwork,
|
||||||
}) => {
|
}) => {
|
||||||
const t = useContext(I18nContext);
|
const t = useContext(I18nContext);
|
||||||
const [assetOptionsButtonElement, setAssetOptionsButtonElement] = useState(
|
const [assetOptionsButtonElement, setAssetOptionsButtonElement] = useState(
|
||||||
@ -46,10 +47,10 @@ const AssetOptions = ({
|
|||||||
data-testid="asset-options__etherscan"
|
data-testid="asset-options__etherscan"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setAssetOptionsOpen(false);
|
setAssetOptionsOpen(false);
|
||||||
onViewEtherscan();
|
onClickBlockExplorer();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('viewOnEtherscan')}
|
{isEthNetwork ? t('viewOnEtherscan') : t('viewinExplorer')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
{isNativeAsset ? null : (
|
{isNativeAsset ? null : (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
@ -70,9 +71,10 @@ const AssetOptions = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
AssetOptions.propTypes = {
|
AssetOptions.propTypes = {
|
||||||
|
isEthNetwork: PropTypes.bool,
|
||||||
isNativeAsset: PropTypes.bool,
|
isNativeAsset: PropTypes.bool,
|
||||||
onRemove: PropTypes.func.isRequired,
|
onRemove: PropTypes.func.isRequired,
|
||||||
onViewEtherscan: PropTypes.func.isRequired,
|
onClickBlockExplorer: PropTypes.func.isRequired,
|
||||||
onViewAccountDetails: PropTypes.func.isRequired,
|
onViewAccountDetails: PropTypes.func.isRequired,
|
||||||
tokenSymbol: PropTypes.string,
|
tokenSymbol: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
@ -2,6 +2,7 @@ import React from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { useSelector, useDispatch } from 'react-redux';
|
import { useSelector, useDispatch } from 'react-redux';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
|
import { getAccountLink } from '@metamask/etherscan-link';
|
||||||
import TransactionList from '../../../components/app/transaction-list';
|
import TransactionList from '../../../components/app/transaction-list';
|
||||||
import { EthOverview } from '../../../components/app/wallet-overview';
|
import { EthOverview } from '../../../components/app/wallet-overview';
|
||||||
import {
|
import {
|
||||||
@ -11,8 +12,8 @@ import {
|
|||||||
getSelectedAddress,
|
getSelectedAddress,
|
||||||
} from '../../../selectors/selectors';
|
} from '../../../selectors/selectors';
|
||||||
import { showModal } from '../../../store/actions';
|
import { showModal } from '../../../store/actions';
|
||||||
import getAccountLink from '../../../helpers/utils/account-link';
|
|
||||||
import { DEFAULT_ROUTE } from '../../../helpers/constants/routes';
|
import { DEFAULT_ROUTE } from '../../../helpers/constants/routes';
|
||||||
|
import { useNewMetricEvent } from '../../../hooks/useMetricEvent';
|
||||||
import AssetNavigation from './asset-navigation';
|
import AssetNavigation from './asset-navigation';
|
||||||
import AssetOptions from './asset-options';
|
import AssetOptions from './asset-options';
|
||||||
|
|
||||||
@ -26,6 +27,17 @@ export default function NativeAsset({ nativeCurrency }) {
|
|||||||
const rpcPrefs = useSelector(getRpcPrefsForCurrentProvider);
|
const rpcPrefs = useSelector(getRpcPrefsForCurrentProvider);
|
||||||
const address = useSelector(getSelectedAddress);
|
const address = useSelector(getSelectedAddress);
|
||||||
const history = useHistory();
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -33,12 +45,14 @@ export default function NativeAsset({ nativeCurrency }) {
|
|||||||
accountName={selectedAccountName}
|
accountName={selectedAccountName}
|
||||||
assetName={nativeCurrency}
|
assetName={nativeCurrency}
|
||||||
onBack={() => history.push(DEFAULT_ROUTE)}
|
onBack={() => history.push(DEFAULT_ROUTE)}
|
||||||
|
isEthNetwork={!rpcPrefs.blockExplorerUrl}
|
||||||
optionsButton={
|
optionsButton={
|
||||||
<AssetOptions
|
<AssetOptions
|
||||||
isNativeAsset
|
isNativeAsset
|
||||||
onViewEtherscan={() => {
|
onClickBlockExplorer={() => {
|
||||||
|
blockExplorerLinkClickedEvent();
|
||||||
global.platform.openTab({
|
global.platform.openTab({
|
||||||
url: getAccountLink(address, chainId, rpcPrefs),
|
url: accountLink,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
onViewAccountDetails={() => {
|
onViewAccountDetails={() => {
|
||||||
|
@ -2,16 +2,17 @@ import React from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import { useHistory } from 'react-router-dom';
|
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 TransactionList from '../../../components/app/transaction-list';
|
||||||
import { TokenOverview } from '../../../components/app/wallet-overview';
|
import { TokenOverview } from '../../../components/app/wallet-overview';
|
||||||
import {
|
import {
|
||||||
getCurrentChainId,
|
getCurrentChainId,
|
||||||
getSelectedIdentity,
|
getSelectedIdentity,
|
||||||
|
getRpcPrefsForCurrentProvider,
|
||||||
} from '../../../selectors/selectors';
|
} from '../../../selectors/selectors';
|
||||||
import { DEFAULT_ROUTE } from '../../../helpers/constants/routes';
|
import { DEFAULT_ROUTE } from '../../../helpers/constants/routes';
|
||||||
import { showModal } from '../../../store/actions';
|
import { showModal } from '../../../store/actions';
|
||||||
|
import { useNewMetricEvent } from '../../../hooks/useMetricEvent';
|
||||||
|
|
||||||
import AssetNavigation from './asset-navigation';
|
import AssetNavigation from './asset-navigation';
|
||||||
import AssetOptions from './asset-options';
|
import AssetOptions from './asset-options';
|
||||||
@ -19,10 +20,30 @@ import AssetOptions from './asset-options';
|
|||||||
export default function TokenAsset({ token }) {
|
export default function TokenAsset({ token }) {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const chainId = useSelector(getCurrentChainId);
|
const chainId = useSelector(getCurrentChainId);
|
||||||
|
const rpcPrefs = useSelector(getRpcPrefsForCurrentProvider);
|
||||||
const selectedIdentity = useSelector(getSelectedIdentity);
|
const selectedIdentity = useSelector(getSelectedIdentity);
|
||||||
const selectedAccountName = selectedIdentity.name;
|
const selectedAccountName = selectedIdentity.name;
|
||||||
const selectedAddress = selectedIdentity.address;
|
const selectedAddress = selectedIdentity.address;
|
||||||
const history = useHistory();
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -35,13 +56,10 @@ export default function TokenAsset({ token }) {
|
|||||||
onRemove={() =>
|
onRemove={() =>
|
||||||
dispatch(showModal({ name: 'HIDE_TOKEN_CONFIRMATION', token }))
|
dispatch(showModal({ name: 'HIDE_TOKEN_CONFIRMATION', token }))
|
||||||
}
|
}
|
||||||
onViewEtherscan={() => {
|
isEthNetwork={!rpcPrefs.blockExplorerUrl}
|
||||||
const url = createTokenTrackerLinkForChain(
|
onClickBlockExplorer={() => {
|
||||||
token.address,
|
blockExplorerLinkClickedEvent();
|
||||||
chainId,
|
global.platform.openTab({ url: tokenTrackerLink });
|
||||||
selectedAddress,
|
|
||||||
);
|
|
||||||
global.platform.openTab({ url });
|
|
||||||
}}
|
}}
|
||||||
onViewAccountDetails={() => {
|
onViewAccountDetails={() => {
|
||||||
dispatch(showModal({ name: 'ACCOUNT_DETAILS' }));
|
dispatch(showModal({ name: 'ACCOUNT_DETAILS' }));
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
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 Button from '../../../components/ui/button';
|
||||||
import Checkbox from '../../../components/ui/check-box';
|
import Checkbox from '../../../components/ui/check-box';
|
||||||
import Dropdown from '../../../components/ui/dropdown';
|
import Dropdown from '../../../components/ui/dropdown';
|
||||||
@ -83,7 +84,7 @@ class AccountList extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderAccounts() {
|
renderAccounts() {
|
||||||
const { accounts, connectedAccounts } = this.props;
|
const { accounts, connectedAccounts, rpcPrefs, chainId } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="hw-account-list">
|
<div className="hw-account-list">
|
||||||
@ -130,11 +131,27 @@ class AccountList extends Component {
|
|||||||
</div>
|
</div>
|
||||||
<a
|
<a
|
||||||
className="hw-account-list__item__link"
|
className="hw-account-list__item__link"
|
||||||
href={getAccountLink(
|
onClick={() => {
|
||||||
account.address,
|
const accountLink = getAccountLink(
|
||||||
this.props.chainId,
|
account.address,
|
||||||
this.props.rpcPrefs,
|
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"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
title={this.context.t('etherscanView')}
|
title={this.context.t('etherscanView')}
|
||||||
@ -282,6 +299,7 @@ AccountList.propTypes = {
|
|||||||
|
|
||||||
AccountList.contextTypes = {
|
AccountList.contextTypes = {
|
||||||
t: PropTypes.func,
|
t: PropTypes.func,
|
||||||
|
trackEvent: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AccountList;
|
export default AccountList;
|
||||||
|
@ -3,7 +3,7 @@ import React, { useContext, useRef, useState } from 'react';
|
|||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { createCustomExplorerLink } from '@metamask/etherscan-link';
|
import { getBlockExplorerLink } from '@metamask/etherscan-link';
|
||||||
import { I18nContext } from '../../../contexts/i18n';
|
import { I18nContext } from '../../../contexts/i18n';
|
||||||
import { useNewMetricEvent } from '../../../hooks/useMetricEvent';
|
import { useNewMetricEvent } from '../../../hooks/useMetricEvent';
|
||||||
import { MetaMetricsContext } from '../../../contexts/metametrics.new';
|
import { MetaMetricsContext } from '../../../contexts/metametrics.new';
|
||||||
@ -37,7 +37,6 @@ import {
|
|||||||
OFFLINE_FOR_MAINTENANCE,
|
OFFLINE_FOR_MAINTENANCE,
|
||||||
SWAPS_CHAINID_DEFAULT_BLOCK_EXPLORER_URL_MAP,
|
SWAPS_CHAINID_DEFAULT_BLOCK_EXPLORER_URL_MAP,
|
||||||
} from '../../../../shared/constants/swaps';
|
} 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 { isSwapsDefaultTokenSymbol } from '../../../../shared/modules/swaps.utils';
|
||||||
import PulseLoader from '../../../components/ui/pulse-loader';
|
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 { getRenderableNetworkFeesForQuote } from '../swaps.util';
|
||||||
import SwapsFooter from '../swaps-footer';
|
import SwapsFooter from '../swaps-footer';
|
||||||
import { getBlockExplorerUrlForTx } from '../../../../shared/modules/transaction.utils';
|
|
||||||
|
|
||||||
import SwapFailureIcon from './swap-failure-icon';
|
import SwapFailureIcon from './swap-failure-icon';
|
||||||
import SwapSuccessIcon from './swap-success-icon';
|
import SwapSuccessIcon from './swap-success-icon';
|
||||||
@ -116,17 +114,14 @@ export default function AwaitingSwap({
|
|||||||
category: 'swaps',
|
category: 'swaps',
|
||||||
});
|
});
|
||||||
|
|
||||||
let blockExplorerUrl;
|
const baseNetworkUrl =
|
||||||
if (txHash && rpcPrefs.blockExplorerUrl) {
|
rpcPrefs.blockExplorerUrl ??
|
||||||
blockExplorerUrl = getBlockExplorerUrlForTx({ hash: txHash }, rpcPrefs);
|
SWAPS_CHAINID_DEFAULT_BLOCK_EXPLORER_URL_MAP[chainId] ??
|
||||||
} else if (txHash && SWAPS_CHAINID_DEFAULT_BLOCK_EXPLORER_URL_MAP[chainId]) {
|
null;
|
||||||
blockExplorerUrl = createCustomExplorerLink(
|
const blockExplorerUrl = getBlockExplorerLink(
|
||||||
txHash,
|
{ hash: txHash, chainId },
|
||||||
SWAPS_CHAINID_DEFAULT_BLOCK_EXPLORER_URL_MAP[chainId],
|
{ blockExplorerUrl: baseNetworkUrl },
|
||||||
);
|
);
|
||||||
} else if (txHash && VALID_INFURA_CHAIN_IDS[chainId]) {
|
|
||||||
blockExplorerUrl = getBlockExplorerUrlForTx({ chainId, hash: txHash });
|
|
||||||
}
|
|
||||||
|
|
||||||
const isCustomBlockExplorerUrl =
|
const isCustomBlockExplorerUrl =
|
||||||
SWAPS_CHAINID_DEFAULT_BLOCK_EXPLORER_URL_MAP[chainId] ||
|
SWAPS_CHAINID_DEFAULT_BLOCK_EXPLORER_URL_MAP[chainId] ||
|
||||||
|
@ -2,6 +2,7 @@ import React, { useContext } from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { I18nContext } from '../../../../contexts/i18n';
|
import { I18nContext } from '../../../../contexts/i18n';
|
||||||
|
import { useNewMetricEvent } from '../../../../hooks/useMetricEvent';
|
||||||
|
|
||||||
export default function ViewOnEtherScanLink({
|
export default function ViewOnEtherScanLink({
|
||||||
txHash,
|
txHash,
|
||||||
@ -9,13 +10,29 @@ export default function ViewOnEtherScanLink({
|
|||||||
isCustomBlockExplorerUrl,
|
isCustomBlockExplorerUrl,
|
||||||
}) {
|
}) {
|
||||||
const t = useContext(I18nContext);
|
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 (
|
return (
|
||||||
<div
|
<div
|
||||||
className={classnames('awaiting-swap__view-on-etherscan', {
|
className={classnames('awaiting-swap__view-on-etherscan', {
|
||||||
'awaiting-swap__view-on-etherscan--visible': txHash,
|
'awaiting-swap__view-on-etherscan--visible': txHash,
|
||||||
'awaiting-swap__view-on-etherscan--invisible': !txHash,
|
'awaiting-swap__view-on-etherscan--invisible': !txHash,
|
||||||
})}
|
})}
|
||||||
onClick={() => global.platform.openTab({ url: blockExplorerUrl })}
|
onClick={() => {
|
||||||
|
blockExplorerLinkClickedEvent();
|
||||||
|
global.platform.openTab({ url: blockExplorerUrl });
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{isCustomBlockExplorerUrl
|
{isCustomBlockExplorerUrl
|
||||||
? t('viewOnCustomBlockExplorer', [new URL(blockExplorerUrl).hostname])
|
? t('viewOnCustomBlockExplorer', [new URL(blockExplorerUrl).hostname])
|
||||||
|
@ -4,11 +4,9 @@ import { useDispatch, useSelector } from 'react-redux';
|
|||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { uniqBy, isEqual } from 'lodash';
|
import { uniqBy, isEqual } from 'lodash';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import {
|
import { getTokenTrackerLink } from '@metamask/etherscan-link';
|
||||||
createCustomTokenTrackerLink,
|
|
||||||
createTokenTrackerLinkForChain,
|
|
||||||
} from '@metamask/etherscan-link';
|
|
||||||
import { MetaMetricsContext } from '../../../contexts/metametrics.new';
|
import { MetaMetricsContext } from '../../../contexts/metametrics.new';
|
||||||
|
import { useNewMetricEvent } from '../../../hooks/useMetricEvent';
|
||||||
import {
|
import {
|
||||||
useTokensToSearch,
|
useTokensToSearch,
|
||||||
getRenderableTokenData,
|
getRenderableTokenData,
|
||||||
@ -223,29 +221,34 @@ export default function BuildQuote({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
let blockExplorerTokenLink;
|
const blockExplorerTokenLink = getTokenTrackerLink(
|
||||||
let blockExplorerLabel;
|
selectedToToken.address,
|
||||||
if (rpcPrefs.blockExplorerUrl) {
|
chainId,
|
||||||
blockExplorerTokenLink = createCustomTokenTrackerLink(
|
null, // no networkId
|
||||||
selectedToToken.address,
|
null, // no holderAddress
|
||||||
rpcPrefs.blockExplorerUrl,
|
{
|
||||||
);
|
blockExplorerUrl:
|
||||||
blockExplorerLabel = new URL(rpcPrefs.blockExplorerUrl).hostname;
|
rpcPrefs.blockExplorerUrl ??
|
||||||
} else if (SWAPS_CHAINID_DEFAULT_BLOCK_EXPLORER_URL_MAP[chainId]) {
|
SWAPS_CHAINID_DEFAULT_BLOCK_EXPLORER_URL_MAP[chainId] ??
|
||||||
blockExplorerTokenLink = createCustomTokenTrackerLink(
|
null,
|
||||||
selectedToToken.address,
|
},
|
||||||
SWAPS_CHAINID_DEFAULT_BLOCK_EXPLORER_URL_MAP[chainId],
|
);
|
||||||
);
|
|
||||||
blockExplorerLabel = new URL(
|
const blockExplorerLabel = rpcPrefs.blockExplorerUrl
|
||||||
SWAPS_CHAINID_DEFAULT_BLOCK_EXPLORER_URL_MAP[chainId],
|
? new URL(blockExplorerTokenLink).hostname
|
||||||
).hostname;
|
: t('etherscan');
|
||||||
} else {
|
|
||||||
blockExplorerTokenLink = createTokenTrackerLinkForChain(
|
const blockExplorerLinkClickedEvent = useNewMetricEvent({
|
||||||
selectedToToken.address,
|
category: 'Swaps',
|
||||||
chainId,
|
event: 'Clicked Block Explorer Link',
|
||||||
);
|
properties: {
|
||||||
blockExplorerLabel = t('etherscan');
|
link_type: 'Token Tracker',
|
||||||
}
|
action: 'Swaps Confirmation',
|
||||||
|
block_explorer_domain: blockExplorerTokenLink
|
||||||
|
? new URL(blockExplorerTokenLink)?.hostname
|
||||||
|
: '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const { destinationTokenAddedForSwap } = fetchParams || {};
|
const { destinationTokenAddedForSwap } = fetchParams || {};
|
||||||
const { address: toAddress } = toToken || {};
|
const { address: toAddress } = toToken || {};
|
||||||
@ -449,7 +452,12 @@ export default function BuildQuote({
|
|||||||
<a
|
<a
|
||||||
className="build-quote__token-etherscan-link build-quote__underline"
|
className="build-quote__token-etherscan-link build-quote__underline"
|
||||||
key="build-quote-etherscan-link"
|
key="build-quote-etherscan-link"
|
||||||
href={blockExplorerTokenLink}
|
onClick={() => {
|
||||||
|
blockExplorerLinkClickedEvent();
|
||||||
|
global.platform.openTab({
|
||||||
|
url: blockExplorerTokenLink,
|
||||||
|
});
|
||||||
|
}}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
@ -488,7 +496,12 @@ export default function BuildQuote({
|
|||||||
<a
|
<a
|
||||||
className="build-quote__token-etherscan-link"
|
className="build-quote__token-etherscan-link"
|
||||||
key="build-quote-etherscan-link"
|
key="build-quote-etherscan-link"
|
||||||
href={blockExplorerTokenLink}
|
onClick={() => {
|
||||||
|
blockExplorerLinkClickedEvent();
|
||||||
|
global.platform.openTab({
|
||||||
|
url: blockExplorerTokenLink,
|
||||||
|
});
|
||||||
|
}}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
|
Loading…
Reference in New Issue
Block a user