diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 8d0d8af55..d0cf9c1a0 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -319,7 +319,15 @@ export default class MetamaskController extends EventEmitter { status === TRANSACTION_STATUSES.FAILED ) { const txMeta = this.txController.txStateManager.getTx(txId); - this.platform.showTransactionNotification(txMeta); + const frequentRpcListDetail = this.preferencesController.getFrequentRpcListDetail(); + let rpcPrefs = {}; + if (txMeta.chainId) { + const rpcSettings = frequentRpcListDetail.find( + (rpc) => txMeta.chainId === rpc.chainId, + ); + rpcPrefs = rpcSettings?.rpcPrefs ?? {}; + } + this.platform.showTransactionNotification(txMeta, rpcPrefs); const { txReceipt } = txMeta; if (txReceipt && txReceipt.status === '0x0') { diff --git a/app/scripts/platforms/extension.js b/app/scripts/platforms/extension.js index 98196c946..0eaeefaa8 100644 --- a/app/scripts/platforms/extension.js +++ b/app/scripts/platforms/extension.js @@ -1,8 +1,8 @@ import extension from 'extensionizer'; -import { createExplorerLink as explorerLink } 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 { // @@ -110,7 +110,7 @@ export default class ExtensionPlatform { } } - showTransactionNotification(txMeta) { + showTransactionNotification(txMeta, rpcPrefs) { const { status, txReceipt: { status: receiptStatus } = {} } = txMeta; if (status === TRANSACTION_STATUSES.CONFIRMED) { @@ -120,7 +120,7 @@ export default class ExtensionPlatform { txMeta, 'Transaction encountered an error.', ) - : this._showConfirmedTransaction(txMeta); + : this._showConfirmedTransaction(txMeta, rpcPrefs); } else if (status === TRANSACTION_STATUSES.FAILED) { this._showFailedTransaction(txMeta); } @@ -189,10 +189,10 @@ export default class ExtensionPlatform { }); } - _showConfirmedTransaction(txMeta) { + _showConfirmedTransaction(txMeta, rpcPrefs) { this._subscribeToNotificationClicked(); - const url = explorerLink(txMeta.hash, txMeta.metamaskNetworkId); + const url = getBlockExplorerUrlForTx(txMeta, rpcPrefs); const nonce = parseInt(txMeta.txParams.nonce, 16); const title = 'Confirmed transaction'; diff --git a/package.json b/package.json index 3d26e47d8..690fd6096 100644 --- a/package.json +++ b/package.json @@ -20,9 +20,9 @@ "forwarder": "node ./development/static-server.js ./node_modules/@metamask/forwarder/dist/ --port 9010", "dapp-forwarder": "concurrently -k -n forwarder,dapp -p '[{time}][{name}]' 'yarn forwarder' 'yarn dapp'", "sendwithprivatedapp": "node development/static-server.js test/e2e/send-eth-with-private-key-test --port 8080", - "test:unit": "mocha --exit --require test/env.js --require test/setup.js --recursive \"test/unit/**/*.test.js\" \"ui/app/**/*.test.js\"", + "test:unit": "mocha --exit --require test/env.js --require test/setup.js --recursive \"test/unit/**/*.test.js\" \"ui/app/**/*.test.js\" \"shared/**/*.test.js\"", "test:unit:global": "mocha --exit --require test/env.js --require test/setup.js --recursive test/unit-global/*.test.js", - "test:unit:lax": "mocha --exit --require test/env.js --require test/setup.js --recursive \"test/unit/{,**/!(permissions)}/*.test.js\" \"ui/app/**/*.test.js\"", + "test:unit:lax": "mocha --exit --require test/env.js --require test/setup.js --recursive \"test/unit/{,**/!(permissions)}/*.test.js\" \"ui/app/**/*.test.js\" \"shared/**/*.test.js\"", "test:unit:strict": "mocha --exit --require test/env.js --require test/setup.js --recursive \"test/unit/**/permissions/*.test.js\"", "test:unit:path": "mocha --exit --require test/env.js --require test/setup.js --recursive", "test:e2e:chrome": "SELENIUM_BROWSER=chrome test/e2e/run-all.sh", @@ -45,7 +45,7 @@ "verify-locales": "node ./development/verify-locale-strings.js", "verify-locales:fix": "node ./development/verify-locale-strings.js --fix", "mozilla-lint": "addons-linter dist/firefox", - "watch": "mocha --watch --require test/env.js --require test/setup.js --reporter min --recursive \"test/unit/**/*.js\" \"ui/app/**/*.test.js\"", + "watch": "mocha --watch --require test/env.js --require test/setup.js --reporter min --recursive \"test/unit/**/*.js\" \"ui/app/**/*.test.js\" \"shared/**/*.test.js\"", "devtools:react": "react-devtools", "devtools:redux": "remotedev --hostname=localhost --port=8000", "start:dev": "concurrently -k -n build,react,redux yarn:start yarn:devtools:react yarn:devtools:redux", diff --git a/shared/modules/tests/transaction.utils.test.js b/shared/modules/tests/transaction.utils.test.js new file mode 100644 index 000000000..2aac76828 --- /dev/null +++ b/shared/modules/tests/transaction.utils.test.js @@ -0,0 +1,96 @@ +import 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, + ); + }); + }); +}); diff --git a/shared/modules/transaction.utils.js b/shared/modules/transaction.utils.js index 9e89679f8..47a0a4334 100644 --- a/shared/modules/transaction.utils.js +++ b/shared/modules/transaction.utils.js @@ -1,6 +1,37 @@ +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); +} diff --git a/ui/app/components/app/transaction-activity-log/tests/transaction-activity-log.container.test.js b/ui/app/components/app/transaction-activity-log/tests/transaction-activity-log.container.test.js index b1608684c..612a0f046 100644 --- a/ui/app/components/app/transaction-activity-log/tests/transaction-activity-log.container.test.js +++ b/ui/app/components/app/transaction-activity-log/tests/transaction-activity-log.container.test.js @@ -19,12 +19,42 @@ describe('TransactionActivityLog container', function () { metamask: { conversionRate: 280.45, nativeCurrency: 'ETH', + frequentRpcListDetail: [], }, }; assert.deepStrictEqual(mapStateToProps(mockState), { conversionRate: 280.45, nativeCurrency: 'ETH', + rpcPrefs: {}, + }); + }); + + it('should return the correct props when on a custom network', function () { + const mockState = { + metamask: { + conversionRate: 280.45, + nativeCurrency: 'ETH', + frequentRpcListDetail: [ + { + rpcUrl: 'https://customnetwork.com/', + rpcPrefs: { + blockExplorerUrl: 'https://customblockexplorer.com/', + }, + }, + ], + provider: { + rpcUrl: 'https://customnetwork.com/', + }, + }, + }; + + assert.deepStrictEqual(mapStateToProps(mockState), { + conversionRate: 280.45, + nativeCurrency: 'ETH', + rpcPrefs: { + blockExplorerUrl: 'https://customblockexplorer.com/', + }, }); }); }); diff --git a/ui/app/components/app/transaction-activity-log/tests/transaction-activity-log.util.test.js b/ui/app/components/app/transaction-activity-log/tests/transaction-activity-log.util.test.js index e01a5a9de..8421dca96 100644 --- a/ui/app/components/app/transaction-activity-log/tests/transaction-activity-log.util.test.js +++ b/ui/app/components/app/transaction-activity-log/tests/transaction-activity-log.util.test.js @@ -1,4 +1,8 @@ import assert from 'assert'; +import { + ROPSTEN_CHAIN_ID, + ROPSTEN_NETWORK_ID, +} from '../../../../../../shared/constants/network'; import { TRANSACTION_STATUSES, TRANSACTION_TYPES, @@ -24,7 +28,8 @@ describe('TransactionActivityLog utils', function () { id: 6400627574331058, time: 1543958845581, status: TRANSACTION_STATUSES.UNAPPROVED, - metamaskNetworkId: '3', + metamaskNetworkId: ROPSTEN_NETWORK_ID, + chainId: ROPSTEN_CHAIN_ID, loadingDefaults: true, txParams: { from: '0x50a9d56c2b8ba9a5c7f2c08c3d26e0499f23a706', @@ -71,7 +76,8 @@ describe('TransactionActivityLog utils', function () { ], id: 6400627574331058, loadingDefaults: false, - metamaskNetworkId: '3', + metamaskNetworkId: ROPSTEN_NETWORK_ID, + chainId: ROPSTEN_CHAIN_ID, status: TRANSACTION_STATUSES.DROPPED, submittedTime: 1543958848135, time: 1543958845581, @@ -93,7 +99,8 @@ describe('TransactionActivityLog utils', function () { id: 6400627574331060, time: 1543958857697, status: TRANSACTION_STATUSES.UNAPPROVED, - metamaskNetworkId: '3', + metamaskNetworkId: ROPSTEN_NETWORK_ID, + chainId: ROPSTEN_CHAIN_ID, loadingDefaults: false, txParams: { from: '0x50a9d56c2b8ba9a5c7f2c08c3d26e0499f23a706', @@ -163,7 +170,8 @@ describe('TransactionActivityLog utils', function () { id: 6400627574331060, lastGasPrice: '0x4190ab00', loadingDefaults: false, - metamaskNetworkId: '3', + metamaskNetworkId: ROPSTEN_NETWORK_ID, + chainId: ROPSTEN_CHAIN_ID, status: TRANSACTION_STATUSES.CONFIRMED, submittedTime: 1543958860054, time: 1543958857697, @@ -185,6 +193,8 @@ describe('TransactionActivityLog utils', function () { const expected = [ { id: 6400627574331058, + metamaskNetworkId: ROPSTEN_NETWORK_ID, + chainId: ROPSTEN_CHAIN_ID, hash: '0xa14f13d36b3901e352ce3a7acb9b47b001e5a3370f06232a0953c6fc6fad91b3', eventKey: 'transactionCreated', @@ -193,6 +203,8 @@ describe('TransactionActivityLog utils', function () { }, { id: 6400627574331058, + metamaskNetworkId: ROPSTEN_NETWORK_ID, + chainId: ROPSTEN_CHAIN_ID, hash: '0xa14f13d36b3901e352ce3a7acb9b47b001e5a3370f06232a0953c6fc6fad91b3', eventKey: 'transactionSubmitted', @@ -201,6 +213,8 @@ describe('TransactionActivityLog utils', function () { }, { id: 6400627574331060, + metamaskNetworkId: ROPSTEN_NETWORK_ID, + chainId: ROPSTEN_CHAIN_ID, hash: '0xecbe181ee67c4291d04a7cb9ffbf1d5d831e4fbaa89994fd06bab5dd4cc79b33', eventKey: 'transactionResubmitted', @@ -209,6 +223,8 @@ describe('TransactionActivityLog utils', function () { }, { id: 6400627574331060, + metamaskNetworkId: ROPSTEN_NETWORK_ID, + chainId: ROPSTEN_CHAIN_ID, hash: '0xecbe181ee67c4291d04a7cb9ffbf1d5d831e4fbaa89994fd06bab5dd4cc79b33', eventKey: 'transactionConfirmed', @@ -249,7 +265,8 @@ describe('TransactionActivityLog utils', function () { { id: 5559712943815343, loadingDefaults: true, - metamaskNetworkId: '3', + metamaskNetworkId: ROPSTEN_NETWORK_ID, + chainId: ROPSTEN_CHAIN_ID, status: TRANSACTION_STATUSES.UNAPPROVED, time: 1535507561452, txParams: { @@ -389,6 +406,8 @@ describe('TransactionActivityLog utils', function () { value: '0x2386f26fc10000', }, hash: '0xabc', + chainId: ROPSTEN_CHAIN_ID, + metamaskNetworkId: ROPSTEN_NETWORK_ID, }; const expectedResult = [ @@ -398,6 +417,8 @@ describe('TransactionActivityLog utils', function () { value: '0x2386f26fc10000', id: 1, hash: '0xabc', + chainId: ROPSTEN_CHAIN_ID, + metamaskNetworkId: ROPSTEN_NETWORK_ID, }, { eventKey: 'transactionSubmitted', @@ -405,6 +426,8 @@ describe('TransactionActivityLog utils', function () { value: '0x2632e314a000', id: 1, hash: '0xabc', + chainId: ROPSTEN_CHAIN_ID, + metamaskNetworkId: ROPSTEN_NETWORK_ID, }, { eventKey: 'transactionConfirmed', @@ -412,6 +435,8 @@ describe('TransactionActivityLog utils', function () { value: '0x2632e314a000', id: 1, hash: '0xabc', + chainId: ROPSTEN_CHAIN_ID, + metamaskNetworkId: ROPSTEN_NETWORK_ID, }, ]; diff --git a/ui/app/components/app/transaction-activity-log/transaction-activity-log.component.js b/ui/app/components/app/transaction-activity-log/transaction-activity-log.component.js index 64fa9dc3e..215932e9f 100644 --- a/ui/app/components/app/transaction-activity-log/transaction-activity-log.component.js +++ b/ui/app/components/app/transaction-activity-log/transaction-activity-log.component.js @@ -1,13 +1,13 @@ import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; import classnames from 'classnames'; -import { createExplorerLink } 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'; @@ -28,14 +28,14 @@ export default class TransactionActivityLog extends PureComponent { onRetry: PropTypes.func, primaryTransaction: PropTypes.object, isEarliestNonce: PropTypes.bool, + rpcPrefs: PropTypes.object, }; - handleActivityClick = (hash) => { - const { primaryTransaction } = this.props; - const { metamaskNetworkId } = primaryTransaction; - - const etherscanUrl = createExplorerLink(hash, metamaskNetworkId); - + handleActivityClick = (activity) => { + const etherscanUrl = getBlockExplorerUrlForTx( + activity, + this.props.rpcPrefs, + ); global.platform.openTab({ url: etherscanUrl }); }; @@ -79,7 +79,7 @@ export default class TransactionActivityLog extends PureComponent { renderActivity(activity, index) { const { conversionRate, nativeCurrency } = this.props; - const { eventKey, value, timestamp, hash } = activity; + const { eventKey, value, timestamp } = activity; const ethValue = index === 0 ? `${getValueFromWeiHex({ @@ -111,7 +111,7 @@ export default class TransactionActivityLog extends PureComponent {
this.handleActivityClick(hash)} + onClick={() => this.handleActivityClick(activity)} > {activityText}
diff --git a/ui/app/components/app/transaction-activity-log/transaction-activity-log.container.js b/ui/app/components/app/transaction-activity-log/transaction-activity-log.container.js index bea5e6f7a..99f96baca 100644 --- a/ui/app/components/app/transaction-activity-log/transaction-activity-log.container.js +++ b/ui/app/components/app/transaction-activity-log/transaction-activity-log.container.js @@ -1,6 +1,10 @@ import { connect } from 'react-redux'; import { findLastIndex } from 'lodash'; -import { conversionRateSelector, getNativeCurrency } from '../../../selectors'; +import { + conversionRateSelector, + getNativeCurrency, + getRpcPrefsForCurrentProvider, +} from '../../../selectors'; import TransactionActivityLog from './transaction-activity-log.component'; import { combineTransactionHistories } from './transaction-activity-log.util'; import { @@ -15,6 +19,7 @@ const mapStateToProps = (state) => { return { conversionRate: conversionRateSelector(state), nativeCurrency: getNativeCurrency(state), + rpcPrefs: getRpcPrefsForCurrentProvider(state), }; }; diff --git a/ui/app/components/app/transaction-activity-log/transaction-activity-log.util.js b/ui/app/components/app/transaction-activity-log/transaction-activity-log.util.js index 0846b85bc..624e1027d 100644 --- a/ui/app/components/app/transaction-activity-log/transaction-activity-log.util.js +++ b/ui/app/components/app/transaction-activity-log/transaction-activity-log.util.js @@ -49,6 +49,8 @@ const statusHash = { export function getActivities(transaction, isFirstTransaction = false) { const { id, + chainId, + metamaskNetworkId, hash, history = [], txParams: { gas: paramsGasLimit, gasPrice: paramsGasPrice }, @@ -76,6 +78,8 @@ export function getActivities(transaction, isFirstTransaction = false) { return acc.concat({ id, hash, + chainId, + metamaskNetworkId, eventKey: TRANSACTION_CREATED_EVENT, timestamp, value, @@ -127,6 +131,8 @@ export function getActivities(transaction, isFirstTransaction = false) { hash, eventKey, timestamp, + chainId, + metamaskNetworkId, value: gasFee, }); } @@ -165,6 +171,8 @@ export function getActivities(transaction, isFirstTransaction = false) { events.push({ id, hash, + chainId, + metamaskNetworkId, eventKey: TRANSACTION_UPDATED_EVENT, timestamp, }); @@ -185,6 +193,8 @@ export function getActivities(transaction, isFirstTransaction = false) { ? historyActivities.concat({ id, hash, + chainId, + metamaskNetworkId, eventKey: TRANSACTION_ERRORED_EVENT, }) : historyActivities; diff --git a/ui/app/components/app/transaction-list-item-details/transaction-list-item-details.component.js b/ui/app/components/app/transaction-list-item-details/transaction-list-item-details.component.js index faa80846c..3c93ff921 100644 --- a/ui/app/components/app/transaction-list-item-details/transaction-list-item-details.component.js +++ b/ui/app/components/app/transaction-list-item-details/transaction-list-item-details.component.js @@ -1,7 +1,6 @@ import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; import copyToClipboard from 'copy-to-clipboard'; -import { getBlockExplorerUrlForTx } from '../../../helpers/utils/transactions.util'; 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'; @@ -10,6 +9,7 @@ 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'; export default class TransactionListItemDetails extends PureComponent { static contextTypes = { @@ -51,7 +51,6 @@ export default class TransactionListItemDetails extends PureComponent { transactionGroup: { primaryTransaction }, rpcPrefs, } = this.props; - const { hash, metamaskNetworkId } = primaryTransaction; this.context.metricsEvent({ eventOpts: { @@ -62,7 +61,7 @@ export default class TransactionListItemDetails extends PureComponent { }); global.platform.openTab({ - url: getBlockExplorerUrlForTx(metamaskNetworkId, hash, rpcPrefs), + url: getBlockExplorerUrlForTx(primaryTransaction, rpcPrefs), }); }; diff --git a/ui/app/helpers/utils/transactions.util.js b/ui/app/helpers/utils/transactions.util.js index cf90d31fe..ad2af9162 100644 --- a/ui/app/helpers/utils/transactions.util.js +++ b/ui/app/helpers/utils/transactions.util.js @@ -2,7 +2,6 @@ import { MethodRegistry } from 'eth-method-registry'; import abi from 'human-standard-token-abi'; import { ethers } from 'ethers'; import log from 'loglevel'; -import { createExplorerLink } from '@metamask/etherscan-link'; import { addHexPrefix } from '../../../../app/scripts/lib/util'; import { @@ -193,19 +192,6 @@ export function getStatusKey(transaction) { return transaction.status; } -/** - * Returns an external block explorer URL at which a transaction can be viewed. - * @param {number} networkId - * @param {string} hash - * @param {Object} rpcPrefs - */ -export function getBlockExplorerUrlForTx(networkId, hash, rpcPrefs = {}) { - if (rpcPrefs.blockExplorerUrl) { - return `${rpcPrefs.blockExplorerUrl.replace(/\/+$/u, '')}/tx/${hash}`; - } - return createExplorerLink(hash, networkId); -} - /** * Returns a title for the given transaction category. * diff --git a/ui/app/helpers/utils/transactions.util.test.js b/ui/app/helpers/utils/transactions.util.test.js index a725156bd..386edd6f8 100644 --- a/ui/app/helpers/utils/transactions.util.test.js +++ b/ui/app/helpers/utils/transactions.util.test.js @@ -60,47 +60,4 @@ describe('Transactions utils', function () { }); }); }); - - describe('getBlockExplorerUrlForTx', function () { - it('should return the correct block explorer url for a transaction', function () { - const tests = [ - { - expected: 'https://etherscan.io/tx/0xabcd', - networkId: '1', - hash: '0xabcd', - }, - { - expected: 'https://ropsten.etherscan.io/tx/0xdef0', - networkId: '3', - hash: '0xdef0', - rpcPrefs: {}, - }, - { - // test handling of `blockExplorerUrl` for a custom RPC - expected: 'https://block.explorer/tx/0xabcd', - networkId: '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', - networkId: '33', - hash: '0xdef0', - rpcPrefs: { - blockExplorerUrl: 'https://another.block.explorer/', - }, - }, - ]; - - tests.forEach(({ expected, networkId, hash, rpcPrefs }) => { - assert.strictEqual( - utils.getBlockExplorerUrlForTx(networkId, hash, rpcPrefs), - expected, - ); - }); - }); - }); }); diff --git a/ui/app/pages/asset/components/token-asset.js b/ui/app/pages/asset/components/token-asset.js index 4c6647ec4..19059343b 100644 --- a/ui/app/pages/asset/components/token-asset.js +++ b/ui/app/pages/asset/components/token-asset.js @@ -2,12 +2,12 @@ import React from 'react'; import PropTypes from 'prop-types'; import { useDispatch, useSelector } from 'react-redux'; import { useHistory } from 'react-router-dom'; -import { createTokenTrackerLink } from '@metamask/etherscan-link'; +import { createTokenTrackerLinkForChain } from '@metamask/etherscan-link'; import TransactionList from '../../../components/app/transaction-list'; import { TokenOverview } from '../../../components/app/wallet-overview'; import { - getCurrentNetworkId, + getCurrentChainId, getSelectedIdentity, } from '../../../selectors/selectors'; import { DEFAULT_ROUTE } from '../../../helpers/constants/routes'; @@ -18,7 +18,7 @@ import TokenOptions from './token-options'; export default function TokenAsset({ token }) { const dispatch = useDispatch(); - const network = useSelector(getCurrentNetworkId); + const chainId = useSelector(getCurrentChainId); const selectedIdentity = useSelector(getSelectedIdentity); const selectedAccountName = selectedIdentity.name; const selectedAddress = selectedIdentity.address; @@ -36,9 +36,9 @@ export default function TokenAsset({ token }) { dispatch(showModal({ name: 'HIDE_TOKEN_CONFIRMATION', token })) } onViewEtherscan={() => { - const url = createTokenTrackerLink( + const url = createTokenTrackerLinkForChain( token.address, - network, + chainId, selectedAddress, ); global.platform.openTab({ url }); diff --git a/ui/app/pages/swaps/awaiting-swap/awaiting-swap.js b/ui/app/pages/swaps/awaiting-swap/awaiting-swap.js index 3828dab6f..b02639b26 100644 --- a/ui/app/pages/swaps/awaiting-swap/awaiting-swap.js +++ b/ui/app/pages/swaps/awaiting-swap/awaiting-swap.js @@ -6,7 +6,12 @@ import { useHistory } from 'react-router-dom'; import { I18nContext } from '../../../contexts/i18n'; import { useNewMetricEvent } from '../../../hooks/useMetricEvent'; import { MetaMetricsContext } from '../../../contexts/metametrics.new'; -import { getCurrentCurrency, getUSDConversionRate } from '../../../selectors'; +import { + getCurrentChainId, + getCurrentCurrency, + getRpcPrefsForCurrentProvider, + getUSDConversionRate, +} from '../../../selectors'; import { getUsedQuote, getFetchParams, @@ -19,7 +24,6 @@ import { } from '../../../ducks/swaps/swaps'; import Mascot from '../../../components/ui/mascot'; import PulseLoader from '../../../components/ui/pulse-loader'; -import { getBlockExplorerUrlForTx } from '../../../helpers/utils/transactions.util'; import { QUOTES_EXPIRED_ERROR, SWAP_FAILED_ERROR, @@ -31,6 +35,7 @@ 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'; import QuotesTimeoutIcon from './quotes-timeout-icon'; @@ -40,9 +45,7 @@ export default function AwaitingSwap({ swapComplete, errorKey, txHash, - networkId, tokensReceived, - rpcPrefs, submittingSwap, inputValue, maxSlippage, @@ -60,6 +63,8 @@ export default function AwaitingSwap({ const swapsGasPrice = useSelector(getUsedSwapsGasPrice); const currentCurrency = useSelector(getCurrentCurrency); const usdConversionRate = useSelector(getUSDConversionRate); + const chainId = useSelector(getCurrentChainId); + const rpcPrefs = useSelector(getRpcPrefsForCurrentProvider); const [trackedQuotesExpiredEvent, setTrackedQuotesExpiredEvent] = useState( false, @@ -96,7 +101,7 @@ export default function AwaitingSwap({ }); const blockExplorerUrl = - txHash && getBlockExplorerUrlForTx(networkId, txHash, rpcPrefs); + txHash && getBlockExplorerUrlForTx({ chainId, hash: txHash }, rpcPrefs); let headerText; let statusImage; @@ -240,10 +245,8 @@ export default function AwaitingSwap({ AwaitingSwap.propTypes = { swapComplete: PropTypes.bool, - networkId: PropTypes.string.isRequired, txHash: PropTypes.string, tokensReceived: PropTypes.string, - rpcPrefs: PropTypes.object.isRequired, errorKey: PropTypes.oneOf([ QUOTES_EXPIRED_ERROR, SWAP_FAILED_ERROR, diff --git a/ui/app/pages/swaps/index.js b/ui/app/pages/swaps/index.js index f588206f8..42f7e7e9f 100644 --- a/ui/app/pages/swaps/index.js +++ b/ui/app/pages/swaps/index.js @@ -11,7 +11,6 @@ import BigNumber from 'bignumber.js'; import { I18nContext } from '../../contexts/i18n'; import { getSelectedAccount, - getCurrentNetworkId, getCurrentChainId, } from '../../selectors/selectors'; import { @@ -55,10 +54,7 @@ import { setBackgroundSwapRouteState, setSwapsErrorKey, } from '../../store/actions'; -import { - currentNetworkTxListSelector, - getRpcPrefsForCurrentProvider, -} from '../../selectors'; +import { currentNetworkTxListSelector } from '../../selectors'; import { useNewMetricEvent } from '../../hooks/useMetricEvent'; import FeatureToggledRoute from '../../helpers/higher-order-components/feature-toggled-route'; @@ -97,8 +93,6 @@ export default function Swap() { const tradeTxId = useSelector(getTradeTxId); const approveTxId = useSelector(getApproveTxId); const aggregatorMetadata = useSelector(getAggregatorMetadata); - const networkId = useSelector(getCurrentNetworkId); - const rpcPrefs = useSelector(getRpcPrefsForCurrentProvider); const fetchingQuotes = useSelector(getFetchingQuotes); let swapsErrorKey = useSelector(getSwapsErrorKey); const swapsEnabled = useSelector(getSwapsFeatureLiveness); @@ -317,8 +311,6 @@ export default function Swap() { swapComplete={false} errorKey={swapsErrorKey} txHash={tradeTxData?.hash} - networkId={networkId} - rpcPrefs={rpcPrefs} inputValue={inputValue} maxSlippage={maxSlippage} submittedTime={tradeTxData?.submittedTime} @@ -364,11 +356,7 @@ export default function Swap() { exact render={() => { return swapsEnabled === false ? ( - + ) : ( ); @@ -381,13 +369,11 @@ export default function Swap() { return routeState === 'awaiting' || tradeTxData ? (