diff --git a/app/scripts/lib/local-store.js b/app/scripts/lib/local-store.js index 707fb1122..b0a223561 100644 --- a/app/scripts/lib/local-store.js +++ b/app/scripts/lib/local-store.js @@ -1,7 +1,7 @@ import browser from 'webextension-polyfill'; import log from 'loglevel'; import { captureException } from '@sentry/browser'; -import { checkForError } from './util'; +import { checkForLastError } from '../../../shared/modules/browser-runtime.utils'; /** * A wrapper around the extension's storage local API @@ -81,7 +81,7 @@ export default class ExtensionStore { const { local } = browser.storage; return new Promise((resolve, reject) => { local.get(null).then((/** @type {any} */ result) => { - const err = checkForError(); + const err = checkForLastError(); if (err) { reject(err); } else { @@ -102,7 +102,7 @@ export default class ExtensionStore { const { local } = browser.storage; return new Promise((resolve, reject) => { local.set(obj).then(() => { - const err = checkForError(); + const err = checkForLastError(); if (err) { reject(err); } else { diff --git a/app/scripts/lib/util.js b/app/scripts/lib/util.ts similarity index 63% rename from app/scripts/lib/util.js rename to app/scripts/lib/util.ts index 2acbc3c99..dc21a9608 100644 --- a/app/scripts/lib/util.js +++ b/app/scripts/lib/util.ts @@ -1,6 +1,6 @@ -import browser from 'webextension-polyfill'; import BN from 'bn.js'; import { memoize } from 'lodash'; +import { AccessList } from '@ethereumjs/tx'; import { CHAIN_IDS, TEST_CHAINS } from '../../../shared/constants/network'; import { @@ -15,7 +15,10 @@ import { PLATFORM_BRAVE, } from '../../../shared/constants/app'; import { stripHexPrefix } from '../../../shared/modules/hexstring-utils'; -import { TransactionEnvelopeType } from '../../../shared/constants/transaction'; +import { + TransactionEnvelopeType, + TransactionMeta, +} from '../../../shared/constants/transaction'; /** * @see {@link getEnvironmentType} @@ -35,15 +38,15 @@ const getEnvironmentTypeMemo = memoize((url) => { /** * Returns the window type for the application * - * - `popup` refers to the extension opened through the browser app icon (in top right corner in chrome and firefox) - * - `fullscreen` refers to the main browser window - * - `notification` refers to the popup that appears in its own window when taking action outside of metamask - * - `background` refers to the background page + * - `popup` refers to the extension opened through the browser app icon (in top right corner in chrome and firefox) + * - `fullscreen` refers to the main browser window + * - `notification` refers to the popup that appears in its own window when taking action outside of metamask + * - `background` refers to the background page * * NOTE: This should only be called on internal URLs. * - * @param {string} [url] - the URL of the window - * @returns {string} the environment ENUM + * @param [url] - the URL of the window + * @returns the environment ENUM */ const getEnvironmentType = (url = window.location.href) => getEnvironmentTypeMemo(url); @@ -51,7 +54,7 @@ const getEnvironmentType = (url = window.location.href) => /** * Returns the platform (browser) where the extension is running. * - * @returns {string} the platform ENUM + * @returns the platform ENUM */ const getPlatform = () => { const { navigator } = window; @@ -72,54 +75,38 @@ const getPlatform = () => { /** * Converts a hex string to a BN object * - * @param {string} inputHex - A number represented as a hex string - * @returns {object} A BN object + * @param inputHex - A number represented as a hex string + * @returns A BN object */ -function hexToBn(inputHex) { +function hexToBn(inputHex: string) { return new BN(stripHexPrefix(inputHex), 16); } /** * Used to multiply a BN by a fraction * - * @param {BN} targetBN - The number to multiply by a fraction - * @param {number|string} numerator - The numerator of the fraction multiplier - * @param {number|string} denominator - The denominator of the fraction multiplier - * @returns {BN} The product of the multiplication + * @param targetBN - The number to multiply by a fraction + * @param numerator - The numerator of the fraction multiplier + * @param denominator - The denominator of the fraction multiplier + * @returns The product of the multiplication */ -function BnMultiplyByFraction(targetBN, numerator, denominator) { +function BnMultiplyByFraction( + targetBN: BN, + numerator: number, + denominator: number, +) { const numBN = new BN(numerator); const denomBN = new BN(denominator); return targetBN.mul(numBN).div(denomBN); } -/** - * Returns an Error if extension.runtime.lastError is present - * this is a workaround for the non-standard error object that's used - * - * @deprecated use checkForLastError in shared/modules/browser-runtime.utils.js - * @returns {Error|undefined} - */ -function checkForError() { - const { lastError } = browser.runtime; - if (!lastError) { - return undefined; - } - // if it quacks like an Error, its an Error - if (lastError.stack && lastError.message) { - return lastError; - } - // repair incomplete error object (eg chromium v77) - return new Error(lastError.message); -} - /** * Prefixes a hex string with '0x' or '-0x' and returns it. Idempotent. * - * @param {string} str - The string to prefix. - * @returns {string} The prefixed string. + * @param str - The string to prefix. + * @returns The prefixed string. */ -const addHexPrefix = (str) => { +const addHexPrefix = (str: string) => { if (typeof str !== 'string' || str.match(/^-?0x/u)) { return str; } @@ -135,10 +122,10 @@ const addHexPrefix = (str) => { return `0x${str}`; }; -function getChainType(chainId) { +function getChainType(chainId: string) { if (chainId === CHAIN_IDS.MAINNET) { return 'mainnet'; - } else if (TEST_CHAINS.includes(chainId)) { + } else if ((TEST_CHAINS as string[]).includes(chainId)) { return 'testnet'; } return 'custom'; @@ -147,11 +134,11 @@ function getChainType(chainId) { /** * Checks if the alarmname exists in the list * - * @param {Array} alarmList + * @param alarmList * @param alarmName * @returns */ -function checkAlarmExists(alarmList, alarmName) { +function checkAlarmExists(alarmList: { name: string }[], alarmName: string) { return alarmList.some((alarm) => alarm.name === alarmName); } @@ -160,7 +147,6 @@ export { getEnvironmentType, hexToBn, BnMultiplyByFraction, - checkForError, addHexPrefix, getChainType, checkAlarmExists, @@ -178,8 +164,8 @@ export const generateRandomId = () => { return result; }; -export const isValidDate = (d) => { - return d instanceof Date && !isNaN(d); +export const isValidDate = (d: Date | number) => { + return d instanceof Date; }; /** @@ -194,18 +180,26 @@ export const isValidDate = (d) => { * @property {() => void} reject - A function that rejects the Promise. */ +interface DeferredPromise { + promise: Promise; + resolve?: () => void; + reject?: () => void; +} + /** * Create a defered Promise. * - * @returns {DeferredPromise} A deferred Promise. + * @returns A deferred Promise. */ -export function deferredPromise() { - let resolve; - let reject; - const promise = new Promise((innerResolve, innerReject) => { - resolve = innerResolve; - reject = innerReject; - }); +export function deferredPromise(): DeferredPromise { + let resolve: DeferredPromise['resolve']; + let reject: DeferredPromise['reject']; + const promise = new Promise( + (innerResolve: () => void, innerReject: () => void) => { + resolve = innerResolve; + reject = innerReject; + }, + ); return { promise, resolve, reject }; } @@ -216,15 +210,18 @@ export function deferredPromise() { * in as the previous value on the first invocation of the returned method. * * @template A - The type of the compared value. - * @param {(prevValue: A, nextValue: A) => void} comparator - A method to compare + * @param comparator - A method to compare * the previous and next values. - * @param {A} [initialValue] - The initial value to supply to prevValue + * @param [initialValue] - The initial value to supply to prevValue * on first call of the method. */ -export function previousValueComparator(comparator, initialValue) { +export function previousValueComparator( + comparator: (previous: A, next: A) => boolean, + initialValue: A, +) { let first = true; - let cache; - return (value) => { + let cache: A; + return (value: A) => { try { if (first) { first = false; @@ -237,14 +234,37 @@ export function previousValueComparator(comparator, initialValue) { }; } -export function addUrlProtocolPrefix(urlString) { +export function addUrlProtocolPrefix(urlString: string) { if (!urlString.match(/(^http:\/\/)|(^https:\/\/)/u)) { return `https://${urlString}`; } return urlString; } -export function formatTxMetaForRpcResult(txMeta) { +interface FormattedTransactionMeta { + blockHash: string | null; + blockNumber: string | null; + from: string; + to: string; + hash: string; + nonce: string; + input: string; + v?: string; + r?: string; + s?: string; + value: string; + gas: string; + gasPrice?: string; + maxFeePerGas?: string; + maxPriorityFeePerGas?: string; + type: TransactionEnvelopeType; + accessList: AccessList | null; + transactionIndex: string | null; +} + +export function formatTxMetaForRpcResult( + txMeta: TransactionMeta, +): FormattedTransactionMeta { const { r, s, v, hash, txReceipt, txParams } = txMeta; const { to, @@ -259,7 +279,7 @@ export function formatTxMetaForRpcResult(txMeta) { maxPriorityFeePerGas, } = txParams; - const formattedTxMeta = { + const formattedTxMeta: FormattedTransactionMeta = { v, r, s, @@ -267,23 +287,25 @@ export function formatTxMetaForRpcResult(txMeta) { gas, from, hash, - nonce, + nonce: `${nonce}`, input: data || '0x', value: value || '0x0', accessList: accessList || null, blockHash: txReceipt?.blockHash || null, blockNumber: txReceipt?.blockNumber || null, transactionIndex: txReceipt?.transactionIndex || null, + type: + maxFeePerGas && maxPriorityFeePerGas + ? TransactionEnvelopeType.feeMarket + : TransactionEnvelopeType.legacy, }; if (maxFeePerGas && maxPriorityFeePerGas) { formattedTxMeta.gasPrice = maxFeePerGas; formattedTxMeta.maxFeePerGas = maxFeePerGas; formattedTxMeta.maxPriorityFeePerGas = maxPriorityFeePerGas; - formattedTxMeta.type = TransactionEnvelopeType.feeMarket; } else { formattedTxMeta.gasPrice = gasPrice; - formattedTxMeta.type = TransactionEnvelopeType.legacy; } return formattedTxMeta; diff --git a/shared/constants/transaction.ts b/shared/constants/transaction.ts index e766aba5e..93bdae540 100644 --- a/shared/constants/transaction.ts +++ b/shared/constants/transaction.ts @@ -1,3 +1,5 @@ +import { AccessList } from '@ethereumjs/tx'; + export enum TransactionType { /** * A transaction submitted with the same nonce as a previous transaction, a @@ -257,11 +259,25 @@ export interface TxParams { /** The transaction count for the current account/network */ nonce: number; /** The amount of gwei, in hexadecimal, per unit of gas */ - gasPrice: string; + gasPrice?: string; /** The max amount of gwei, in hexadecimal, the user is willing to pay */ gas: string; /** Hexadecimal encoded string representing calls to the EVM's ABI */ data?: string; + /** + * EIP-2930 https://eips.ethereum.org/EIPS/eip-2930 added the ability for + * transactions to specify which addresses they will interact with and allows + * for lower gas fees on specific opcodes. See the EIP for more details. + */ + accessList?: AccessList; + maxFeePerGas?: string; + maxPriorityFeePerGas?: string; +} + +export interface TxReceipt { + blockHash?: string; + blockNumber?: string; + transactionIndex?: string; } export interface TxError { @@ -318,6 +334,7 @@ export interface TransactionMeta { loadingDefaults: boolean; /** The transaction params as passed to the network provider. */ txParams: TxParams; + txReceipt: TxReceipt; /** A history of mutations to this TransactionMeta object. */ history: Record[]; /** A string representing the interface that suggested the transaction. */ @@ -344,6 +361,9 @@ export interface TransactionMeta { * on the network. */ hash: string; + v?: string; + r?: string; + s?: string; /** * The time the transaction was submitted to the network, in Unix epoch time * (ms). diff --git a/ui/components/app/confirm-page-container/confirm-page-container-header/confirm-page-container-header.component.test.js b/ui/components/app/confirm-page-container/confirm-page-container-header/confirm-page-container-header.component.test.js index 6f06f540c..776b2fe4a 100644 --- a/ui/components/app/confirm-page-container/confirm-page-container-header/confirm-page-container-header.component.test.js +++ b/ui/components/app/confirm-page-container/confirm-page-container-header/confirm-page-container-header.component.test.js @@ -4,8 +4,8 @@ import { renderWithProvider } from '../../../../../test/lib/render-helpers'; import { getEnvironmentType } from '../../../../../app/scripts/lib/util'; import ConfirmPageContainerHeader from '.'; -jest.mock('../../../../../app/scripts/lib/util.js', () => ({ - ...jest.requireActual('../../../../../app/scripts/lib/util.js'), +jest.mock('../../../../../app/scripts/lib/util', () => ({ + ...jest.requireActual('../../../../../app/scripts/lib/util'), getEnvironmentType: jest.fn(), }));