2021-02-04 19:15:23 +01:00
|
|
|
import BN from 'bn.js';
|
|
|
|
import { memoize } from 'lodash';
|
2023-01-23 17:18:22 +01:00
|
|
|
import { AccessList } from '@ethereumjs/tx';
|
2022-09-14 16:55:31 +02:00
|
|
|
import { CHAIN_IDS, TEST_CHAINS } from '../../../shared/constants/network';
|
2020-01-09 04:34:58 +01:00
|
|
|
|
|
|
|
import {
|
2018-04-17 08:03:47 +02:00
|
|
|
ENVIRONMENT_TYPE_POPUP,
|
|
|
|
ENVIRONMENT_TYPE_NOTIFICATION,
|
|
|
|
ENVIRONMENT_TYPE_FULLSCREEN,
|
Close window after opening fullscreen (#6966)
* Add background environment type
The `getEnvironmentType` method now checks for the background
environment as well, instead of returning 'notification' for that case.
Instead of adding another regex for the background path, the regexes
for each environment have been replaced with the URL constructor[0].
This is the standard method of parsing URLs, and is available in all
supported browsers.
[0]: https://developer.mozilla.org/en-US/docs/Web/API/URL
* Add note regarding a missing manifest permission
The `url` parameter to `tabs.query(...)` requires the `tabs` permission,
and will be ignored otherwise. We are missing this permission, so that
call does not work.
* Close window after opening full screen
The browser behaviour when opening a new tab differs between Chrome and
Firefox. In the case of a popup, Chrome will close the popup whereas
Firefox will leave it open. In the case of the notification window,
Chrome will move the new tab to the foreground, whereas Firefox will
leave the notification window in the foreground when opening a new tab.
We always want to close the current UI (popup or notification) when
switching to a full-screen view. The only exception to this is when the
switch is triggered from the background, which has no UI.
Closes #6513, #6685
2019-08-08 16:50:32 +02:00
|
|
|
ENVIRONMENT_TYPE_BACKGROUND,
|
2018-08-08 09:00:39 +02:00
|
|
|
PLATFORM_FIREFOX,
|
|
|
|
PLATFORM_OPERA,
|
|
|
|
PLATFORM_CHROME,
|
|
|
|
PLATFORM_EDGE,
|
|
|
|
PLATFORM_BRAVE,
|
2021-02-04 19:15:23 +01:00
|
|
|
} from '../../../shared/constants/app';
|
2022-09-22 17:04:24 +02:00
|
|
|
import { stripHexPrefix } from '../../../shared/modules/hexstring-utils';
|
2023-01-23 17:18:22 +01:00
|
|
|
import {
|
|
|
|
TransactionEnvelopeType,
|
|
|
|
TransactionMeta,
|
|
|
|
} from '../../../shared/constants/transaction';
|
2017-07-25 22:08:31 +02:00
|
|
|
|
2018-04-18 20:45:48 +02:00
|
|
|
/**
|
2020-05-20 15:06:18 +02:00
|
|
|
* @see {@link getEnvironmentType}
|
2018-04-18 20:45:48 +02:00
|
|
|
*/
|
2020-05-20 15:06:18 +02:00
|
|
|
const getEnvironmentTypeMemo = memoize((url) => {
|
2021-02-04 19:15:23 +01:00
|
|
|
const parsedUrl = new URL(url);
|
Close window after opening fullscreen (#6966)
* Add background environment type
The `getEnvironmentType` method now checks for the background
environment as well, instead of returning 'notification' for that case.
Instead of adding another regex for the background path, the regexes
for each environment have been replaced with the URL constructor[0].
This is the standard method of parsing URLs, and is available in all
supported browsers.
[0]: https://developer.mozilla.org/en-US/docs/Web/API/URL
* Add note regarding a missing manifest permission
The `url` parameter to `tabs.query(...)` requires the `tabs` permission,
and will be ignored otherwise. We are missing this permission, so that
call does not work.
* Close window after opening full screen
The browser behaviour when opening a new tab differs between Chrome and
Firefox. In the case of a popup, Chrome will close the popup whereas
Firefox will leave it open. In the case of the notification window,
Chrome will move the new tab to the foreground, whereas Firefox will
leave the notification window in the foreground when opening a new tab.
We always want to close the current UI (popup or notification) when
switching to a full-screen view. The only exception to this is when the
switch is triggered from the background, which has no UI.
Closes #6513, #6685
2019-08-08 16:50:32 +02:00
|
|
|
if (parsedUrl.pathname === '/popup.html') {
|
2021-02-04 19:15:23 +01:00
|
|
|
return ENVIRONMENT_TYPE_POPUP;
|
2022-05-06 00:28:48 +02:00
|
|
|
} else if (['/home.html'].includes(parsedUrl.pathname)) {
|
2021-02-04 19:15:23 +01:00
|
|
|
return ENVIRONMENT_TYPE_FULLSCREEN;
|
Close window after opening fullscreen (#6966)
* Add background environment type
The `getEnvironmentType` method now checks for the background
environment as well, instead of returning 'notification' for that case.
Instead of adding another regex for the background path, the regexes
for each environment have been replaced with the URL constructor[0].
This is the standard method of parsing URLs, and is available in all
supported browsers.
[0]: https://developer.mozilla.org/en-US/docs/Web/API/URL
* Add note regarding a missing manifest permission
The `url` parameter to `tabs.query(...)` requires the `tabs` permission,
and will be ignored otherwise. We are missing this permission, so that
call does not work.
* Close window after opening full screen
The browser behaviour when opening a new tab differs between Chrome and
Firefox. In the case of a popup, Chrome will close the popup whereas
Firefox will leave it open. In the case of the notification window,
Chrome will move the new tab to the foreground, whereas Firefox will
leave the notification window in the foreground when opening a new tab.
We always want to close the current UI (popup or notification) when
switching to a full-screen view. The only exception to this is when the
switch is triggered from the background, which has no UI.
Closes #6513, #6685
2019-08-08 16:50:32 +02:00
|
|
|
} else if (parsedUrl.pathname === '/notification.html') {
|
2021-02-04 19:15:23 +01:00
|
|
|
return ENVIRONMENT_TYPE_NOTIFICATION;
|
2018-04-17 08:03:47 +02:00
|
|
|
}
|
2021-02-04 19:15:23 +01:00
|
|
|
return ENVIRONMENT_TYPE_BACKGROUND;
|
|
|
|
});
|
2020-05-20 15:06:18 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the window type for the application
|
|
|
|
*
|
2023-01-23 17:18:22 +01:00
|
|
|
* - `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
|
2020-05-20 15:06:18 +02:00
|
|
|
*
|
|
|
|
* NOTE: This should only be called on internal URLs.
|
|
|
|
*
|
2023-01-23 17:18:22 +01:00
|
|
|
* @param [url] - the URL of the window
|
|
|
|
* @returns the environment ENUM
|
2020-05-20 15:06:18 +02:00
|
|
|
*/
|
2020-11-03 00:41:28 +01:00
|
|
|
const getEnvironmentType = (url = window.location.href) =>
|
2021-02-04 19:15:23 +01:00
|
|
|
getEnvironmentTypeMemo(url);
|
2018-04-17 08:03:47 +02:00
|
|
|
|
2018-08-08 09:00:39 +02:00
|
|
|
/**
|
|
|
|
* Returns the platform (browser) where the extension is running.
|
|
|
|
*
|
2023-01-23 17:18:22 +01:00
|
|
|
* @returns the platform ENUM
|
2018-08-08 09:00:39 +02:00
|
|
|
*/
|
2021-11-06 01:28:44 +01:00
|
|
|
const getPlatform = () => {
|
|
|
|
const { navigator } = window;
|
|
|
|
const { userAgent } = navigator;
|
|
|
|
|
|
|
|
if (userAgent.includes('Firefox')) {
|
|
|
|
return PLATFORM_FIREFOX;
|
|
|
|
} else if ('brave' in navigator) {
|
|
|
|
return PLATFORM_BRAVE;
|
|
|
|
} else if (userAgent.includes('Edg/')) {
|
|
|
|
return PLATFORM_EDGE;
|
|
|
|
} else if (userAgent.includes('OPR')) {
|
|
|
|
return PLATFORM_OPERA;
|
2018-08-08 09:00:39 +02:00
|
|
|
}
|
2021-11-06 01:28:44 +01:00
|
|
|
return PLATFORM_CHROME;
|
2021-02-04 19:15:23 +01:00
|
|
|
};
|
2018-08-08 09:00:39 +02:00
|
|
|
|
2018-04-16 19:08:04 +02:00
|
|
|
/**
|
|
|
|
* Converts a hex string to a BN object
|
|
|
|
*
|
2023-01-23 17:18:22 +01:00
|
|
|
* @param inputHex - A number represented as a hex string
|
|
|
|
* @returns A BN object
|
2018-04-16 19:08:04 +02:00
|
|
|
*/
|
2023-01-23 17:18:22 +01:00
|
|
|
function hexToBn(inputHex: string) {
|
2021-04-16 17:05:13 +02:00
|
|
|
return new BN(stripHexPrefix(inputHex), 16);
|
2017-08-04 19:55:00 +02:00
|
|
|
}
|
|
|
|
|
2018-04-16 19:08:04 +02:00
|
|
|
/**
|
|
|
|
* Used to multiply a BN by a fraction
|
|
|
|
*
|
2023-01-23 17:18:22 +01:00
|
|
|
* @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
|
2018-04-16 19:08:04 +02:00
|
|
|
*/
|
2023-01-23 17:18:22 +01:00
|
|
|
function BnMultiplyByFraction(
|
|
|
|
targetBN: BN,
|
|
|
|
numerator: number,
|
|
|
|
denominator: number,
|
|
|
|
) {
|
2021-02-04 19:15:23 +01:00
|
|
|
const numBN = new BN(numerator);
|
|
|
|
const denomBN = new BN(denominator);
|
|
|
|
return targetBN.mul(numBN).div(denomBN);
|
2017-08-04 19:55:00 +02:00
|
|
|
}
|
2018-04-17 08:03:47 +02:00
|
|
|
|
2020-11-06 22:18:00 +01:00
|
|
|
/**
|
|
|
|
* Prefixes a hex string with '0x' or '-0x' and returns it. Idempotent.
|
|
|
|
*
|
2023-01-23 17:18:22 +01:00
|
|
|
* @param str - The string to prefix.
|
|
|
|
* @returns The prefixed string.
|
2020-11-06 22:18:00 +01:00
|
|
|
*/
|
2023-01-23 17:18:22 +01:00
|
|
|
const addHexPrefix = (str: string) => {
|
2020-11-06 22:18:00 +01:00
|
|
|
if (typeof str !== 'string' || str.match(/^-?0x/u)) {
|
2021-02-04 19:15:23 +01:00
|
|
|
return str;
|
2020-11-06 22:18:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (str.match(/^-?0X/u)) {
|
2021-02-04 19:15:23 +01:00
|
|
|
return str.replace('0X', '0x');
|
2020-11-06 22:18:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (str.startsWith('-')) {
|
2021-02-04 19:15:23 +01:00
|
|
|
return str.replace('-', '-0x');
|
2020-11-06 22:18:00 +01:00
|
|
|
}
|
|
|
|
|
2021-02-04 19:15:23 +01:00
|
|
|
return `0x${str}`;
|
|
|
|
};
|
2020-11-06 22:18:00 +01:00
|
|
|
|
2023-01-23 17:18:22 +01:00
|
|
|
function getChainType(chainId: string) {
|
2022-09-14 16:55:31 +02:00
|
|
|
if (chainId === CHAIN_IDS.MAINNET) {
|
2021-07-02 17:31:27 +02:00
|
|
|
return 'mainnet';
|
2023-01-23 17:18:22 +01:00
|
|
|
} else if ((TEST_CHAINS as string[]).includes(chainId)) {
|
2021-07-02 17:31:27 +02:00
|
|
|
return 'testnet';
|
|
|
|
}
|
|
|
|
return 'custom';
|
|
|
|
}
|
|
|
|
|
2022-10-04 19:03:50 +02:00
|
|
|
/**
|
|
|
|
* Checks if the alarmname exists in the list
|
|
|
|
*
|
2023-01-23 17:18:22 +01:00
|
|
|
* @param alarmList
|
2022-10-04 19:03:50 +02:00
|
|
|
* @param alarmName
|
|
|
|
* @returns
|
|
|
|
*/
|
2023-01-23 17:18:22 +01:00
|
|
|
function checkAlarmExists(alarmList: { name: string }[], alarmName: string) {
|
2022-10-04 19:03:50 +02:00
|
|
|
return alarmList.some((alarm) => alarm.name === alarmName);
|
|
|
|
}
|
|
|
|
|
2020-01-09 04:34:58 +01:00
|
|
|
export {
|
2018-08-08 09:00:39 +02:00
|
|
|
getPlatform,
|
2018-04-17 08:03:47 +02:00
|
|
|
getEnvironmentType,
|
|
|
|
hexToBn,
|
|
|
|
BnMultiplyByFraction,
|
2020-11-06 22:18:00 +01:00
|
|
|
addHexPrefix,
|
2021-07-02 17:31:27 +02:00
|
|
|
getChainType,
|
2022-10-04 19:03:50 +02:00
|
|
|
checkAlarmExists,
|
2021-02-04 19:15:23 +01:00
|
|
|
};
|
2022-11-08 00:03:03 +01:00
|
|
|
|
|
|
|
// Taken from https://stackoverflow.com/a/1349426/3696652
|
|
|
|
const characters =
|
|
|
|
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
|
|
export const generateRandomId = () => {
|
|
|
|
let result = '';
|
|
|
|
const charactersLength = characters.length;
|
|
|
|
for (let i = 0; i < 20; i++) {
|
|
|
|
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
};
|
|
|
|
|
2023-01-23 17:18:22 +01:00
|
|
|
export const isValidDate = (d: Date | number) => {
|
|
|
|
return d instanceof Date;
|
2022-11-08 00:03:03 +01:00
|
|
|
};
|
2022-11-24 14:32:05 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* A deferred Promise.
|
|
|
|
*
|
|
|
|
* A deferred Promise is one that can be resolved or rejected independently of
|
|
|
|
* the Promise construction.
|
|
|
|
*
|
|
|
|
* @typedef {object} DeferredPromise
|
|
|
|
* @property {Promise} promise - The Promise that has been deferred.
|
|
|
|
* @property {() => void} resolve - A function that resolves the Promise.
|
|
|
|
* @property {() => void} reject - A function that rejects the Promise.
|
|
|
|
*/
|
|
|
|
|
2023-01-23 17:18:22 +01:00
|
|
|
interface DeferredPromise {
|
|
|
|
promise: Promise<any>;
|
|
|
|
resolve?: () => void;
|
|
|
|
reject?: () => void;
|
|
|
|
}
|
|
|
|
|
2022-11-24 14:32:05 +01:00
|
|
|
/**
|
|
|
|
* Create a defered Promise.
|
|
|
|
*
|
2023-01-23 17:18:22 +01:00
|
|
|
* @returns A deferred Promise.
|
2022-11-24 14:32:05 +01:00
|
|
|
*/
|
2023-01-23 17:18:22 +01:00
|
|
|
export function deferredPromise(): DeferredPromise {
|
|
|
|
let resolve: DeferredPromise['resolve'];
|
|
|
|
let reject: DeferredPromise['reject'];
|
|
|
|
const promise = new Promise<void>(
|
|
|
|
(innerResolve: () => void, innerReject: () => void) => {
|
|
|
|
resolve = innerResolve;
|
|
|
|
reject = innerReject;
|
|
|
|
},
|
|
|
|
);
|
2022-11-24 14:32:05 +01:00
|
|
|
return { promise, resolve, reject };
|
|
|
|
}
|
2022-12-02 18:59:03 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a function with arity 1 that caches the argument that the function
|
|
|
|
* is called with and invokes the comparator with both the cached, previous,
|
|
|
|
* value and the current value. If specified, the initialValue will be passed
|
|
|
|
* in as the previous value on the first invocation of the returned method.
|
|
|
|
*
|
|
|
|
* @template A - The type of the compared value.
|
2023-01-23 17:18:22 +01:00
|
|
|
* @param comparator - A method to compare
|
2022-12-02 18:59:03 +01:00
|
|
|
* the previous and next values.
|
2023-01-23 17:18:22 +01:00
|
|
|
* @param [initialValue] - The initial value to supply to prevValue
|
2022-12-02 18:59:03 +01:00
|
|
|
* on first call of the method.
|
|
|
|
*/
|
2023-01-23 17:18:22 +01:00
|
|
|
export function previousValueComparator<A>(
|
|
|
|
comparator: (previous: A, next: A) => boolean,
|
|
|
|
initialValue: A,
|
|
|
|
) {
|
2022-12-02 18:59:03 +01:00
|
|
|
let first = true;
|
2023-01-23 17:18:22 +01:00
|
|
|
let cache: A;
|
|
|
|
return (value: A) => {
|
2022-12-02 18:59:03 +01:00
|
|
|
try {
|
|
|
|
if (first) {
|
|
|
|
first = false;
|
|
|
|
return comparator(initialValue ?? value, value);
|
|
|
|
}
|
|
|
|
return comparator(cache, value);
|
|
|
|
} finally {
|
|
|
|
cache = value;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
2022-12-19 18:46:36 +01:00
|
|
|
|
2023-01-23 17:18:22 +01:00
|
|
|
export function addUrlProtocolPrefix(urlString: string) {
|
2022-12-19 18:46:36 +01:00
|
|
|
if (!urlString.match(/(^http:\/\/)|(^https:\/\/)/u)) {
|
|
|
|
return `https://${urlString}`;
|
|
|
|
}
|
|
|
|
return urlString;
|
|
|
|
}
|
2023-01-06 18:14:50 +01:00
|
|
|
|
2023-01-23 17:18:22 +01:00
|
|
|
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 {
|
2023-01-06 18:14:50 +01:00
|
|
|
const { r, s, v, hash, txReceipt, txParams } = txMeta;
|
|
|
|
const {
|
|
|
|
to,
|
|
|
|
data,
|
|
|
|
nonce,
|
|
|
|
gas,
|
|
|
|
from,
|
|
|
|
value,
|
|
|
|
gasPrice,
|
|
|
|
accessList,
|
|
|
|
maxFeePerGas,
|
|
|
|
maxPriorityFeePerGas,
|
|
|
|
} = txParams;
|
|
|
|
|
2023-01-23 17:18:22 +01:00
|
|
|
const formattedTxMeta: FormattedTransactionMeta = {
|
2023-01-06 18:14:50 +01:00
|
|
|
v,
|
|
|
|
r,
|
|
|
|
s,
|
|
|
|
to,
|
|
|
|
gas,
|
|
|
|
from,
|
|
|
|
hash,
|
2023-01-23 17:18:22 +01:00
|
|
|
nonce: `${nonce}`,
|
2023-01-06 18:14:50 +01:00
|
|
|
input: data || '0x',
|
|
|
|
value: value || '0x0',
|
|
|
|
accessList: accessList || null,
|
|
|
|
blockHash: txReceipt?.blockHash || null,
|
|
|
|
blockNumber: txReceipt?.blockNumber || null,
|
|
|
|
transactionIndex: txReceipt?.transactionIndex || null,
|
2023-01-23 17:18:22 +01:00
|
|
|
type:
|
|
|
|
maxFeePerGas && maxPriorityFeePerGas
|
|
|
|
? TransactionEnvelopeType.feeMarket
|
|
|
|
: TransactionEnvelopeType.legacy,
|
2023-01-06 18:14:50 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
if (maxFeePerGas && maxPriorityFeePerGas) {
|
|
|
|
formattedTxMeta.gasPrice = maxFeePerGas;
|
|
|
|
formattedTxMeta.maxFeePerGas = maxFeePerGas;
|
|
|
|
formattedTxMeta.maxPriorityFeePerGas = maxPriorityFeePerGas;
|
|
|
|
} else {
|
|
|
|
formattedTxMeta.gasPrice = gasPrice;
|
|
|
|
}
|
|
|
|
|
|
|
|
return formattedTxMeta;
|
|
|
|
}
|