diff --git a/.github/scripts/add-team-label.ts b/.github/scripts/add-team-label.ts deleted file mode 100644 index 40461c598bd6..000000000000 --- a/.github/scripts/add-team-label.ts +++ /dev/null @@ -1,89 +0,0 @@ -import * as core from '@actions/core'; -import { context, getOctokit } from '@actions/github'; -import { GitHub } from '@actions/github/lib/utils'; - -import { retrieveLabel } from './shared/label'; -import { Labelable, addLabelByIdToLabelable } from './shared/labelable'; -import { retrievePullRequest } from './shared/pull-request'; - -main().catch((error: Error): void => { - console.error(error); - process.exit(1); -}); - -async function main(): Promise { - // "GITHUB_TOKEN" is an automatically generated, repository-specific access token provided by GitHub Actions. - // We can't use "GITHUB_TOKEN" here, as its permissions are scoped to the repository where the action is running. - // "GITHUB_TOKEN" does not have access to other repositories, even when they belong to the same organization. - // As we want to get files which are not necessarily located in the same repository, - // we need to create our own "PERSONAL_ACCESS_TOKEN" with "repo" permissions. - // Such a token allows to access other repositories of the MetaMask organisation. - const personalAccessToken = process.env.PERSONAL_ACCESS_TOKEN; - if (!personalAccessToken) { - core.setFailed('PERSONAL_ACCESS_TOKEN not found'); - process.exit(1); - } - - // Initialise octokit, required to call Github GraphQL API - const octokit: InstanceType = getOctokit(personalAccessToken, { - previews: ['bane'], // The "bane" preview is required for adding, updating, creating and deleting labels. - }); - - // Retrieve pull request info from context - const pullRequestRepoOwner = context.repo.owner; - const pullRequestRepoName = context.repo.repo; - const pullRequestNumber = context.payload.pull_request?.number; - if (!pullRequestNumber) { - core.setFailed('Pull request number not found'); - process.exit(1); - } - - // Retrieve pull request - const pullRequest: Labelable = await retrievePullRequest( - octokit, - pullRequestRepoOwner, - pullRequestRepoName, - pullRequestNumber, - ); - - // Get the team label id based on the author of the pull request - const teamLabelId = await getTeamLabelIdByAuthor( - octokit, - pullRequestRepoOwner, - pullRequestRepoName, - pullRequest.author, - ); - - // Add the team label by id to the pull request - await addLabelByIdToLabelable(octokit, pullRequest, teamLabelId); -} - -// This helper function gets the team label id based on the author of the pull request -const getTeamLabelIdByAuthor = async ( - octokit: InstanceType, - repoOwner: string, - repoName: string, - author: string, -): Promise => { - // Retrieve the teams.json file from the repository - const { data } = (await octokit.request( - 'GET /repos/{owner}/{repo}/contents/{path}', - { owner: repoOwner, repo: 'MetaMask-planning', path: 'teams.json' }, - )) as { data: { content: string } }; - - // Parse the teams.json file content to json from base64 - const teamMembers: Record = JSON.parse(atob(data.content)); - - // Get the label name based on the author - const labelName = teamMembers[author]; - - if (!labelName) { - core.setFailed(`Team label not found for author: ${author}`); - process.exit(1); - } - - // Retrieve the label id based on the label name - const labelId = await retrieveLabel(octokit, repoOwner, repoName, labelName); - - return labelId; -}; diff --git a/.github/workflows/add-team-label.yml b/.github/workflows/add-team-label.yml index 02a75aefd4a2..2046456ef426 100644 --- a/.github/workflows/add-team-label.yml +++ b/.github/workflows/add-team-label.yml @@ -7,6 +7,8 @@ on: jobs: add-team-label: - uses: metamask/github-tools/.github/workflows/add-team-label.yml@main + uses: metamask/github-tools/.github/workflows/add-team-label.yml@058012b49ff2fbd9649c566ba43b29497f93b21d + permissions: + pull-requests: write secrets: PERSONAL_ACCESS_TOKEN: ${{ secrets.RELEASE_LABEL_TOKEN }} diff --git a/.gitignore b/.gitignore index 07c6ca599dae..eb8b930adf69 100644 --- a/.gitignore +++ b/.gitignore @@ -78,3 +78,5 @@ licenseInfos.json # API Spec tests html-report/ +/app/images/branding + diff --git a/.storybook/main.js b/.storybook/main.js index 554f6d955ee0..d63d924aa2e2 100644 --- a/.storybook/main.js +++ b/.storybook/main.js @@ -48,6 +48,10 @@ module.exports = { config.resolve.alias['../../../../../../store/actions'] = require.resolve( '../ui/__mocks__/actions.js', ); + // Import within controller-utils crashes storybook. + config.resolve.alias['@ethereumjs/util'] = require.resolve( + '../ui/__mocks__/ethereumjs-util.js', + ); config.resolve.fallback = { child_process: false, constants: false, diff --git a/.storybook/test-data.js b/.storybook/test-data.js index d251f3194877..274fdfdb3651 100644 --- a/.storybook/test-data.js +++ b/.storybook/test-data.js @@ -1,6 +1,5 @@ import { draftTransactionInitialState } from '../ui/ducks/send'; import { KeyringType } from '../shared/constants/keyring'; -import { NetworkType } from '@metamask/controller-utils'; import { NetworkStatus } from '@metamask/network-controller'; import { EthAccountType } from '@metamask/keyring-api'; import { CHAIN_IDS } from '../shared/constants/network'; @@ -8,6 +7,7 @@ import { copyable, divider, heading, panel, text } from '@metamask/snaps-sdk'; import { getJsxElementFromComponent } from '@metamask/snaps-utils'; import { FirstTimeFlowType } from '../shared/constants/onboarding'; import { ETH_EOA_METHODS } from '../shared/constants/eth-methods'; +import { mockNetworkState } from '../test/stub/networks'; const state = { invalidCustomNetwork: { @@ -181,15 +181,6 @@ const state = { 1559: true, }, }, - selectedNetworkClientId: NetworkType.mainnet, - networksMetadata: { - [NetworkType.mainnet]: { - EIPS: { - 1559: true, - }, - status: NetworkStatus.Available, - }, - }, gasFeeEstimates: '0x5208', swapsState: { quotes: {}, @@ -705,13 +696,6 @@ const state = { connectedStatusPopoverHasBeenShown: true, swapsWelcomeMessageHasBeenShown: true, defaultHomeActiveTabName: 'Tokens', - providerConfig: { - type: 'sepolia', - ticker: 'ETH', - nickname: 'Sepolia', - rpcUrl: '', - chainId: '0xaa36a7', - }, network: '5', accounts: { '0x64a845a5b02460acf8a3d84503b0d68d028b4bb4': { @@ -1234,21 +1218,23 @@ const state = { accounts: ['0x9d0ba4ddac06032527b140912ec808ab9451b788'], }, ], - networkConfigurations: { - 'test-networkConfigurationId-1': { + ...mockNetworkState({ + id: 'test-networkConfigurationId-1', rpcUrl: 'https://testrpc.com', chainId: '0x1', nickname: 'mainnet', - rpcPrefs: { blockExplorerUrl: 'https://etherscan.io' }, - }, - 'test-networkConfigurationId-2': { + blockExplorerUrl: 'https://etherscan.io', + metadata: { + EIPS: { 1559: true }, + status: NetworkStatus.Available, + } + }, { + id: 'test-networkConfigurationId-2', rpcUrl: 'http://localhost:8545', chainId: '0x539', ticker: 'ETH', nickname: 'Localhost 8545', - rpcPrefs: {}, - }, - }, + }), accountTokens: { '0x64a845a5b02460acf8a3d84503b0d68d028b4bb4': { '0x1': [ diff --git a/.yarn/patches/@metamask-assets-controllers-npm-33.0.0-3e7448c4cd.patch b/.yarn/patches/@metamask-assets-controllers-npm-33.0.0-3e7448c4cd.patch deleted file mode 100644 index 8961c8f629e6..000000000000 --- a/.yarn/patches/@metamask-assets-controllers-npm-33.0.0-3e7448c4cd.patch +++ /dev/null @@ -1,58 +0,0 @@ -diff --git a/dist/chunk-FGAZXVKS.js b/dist/chunk-FGAZXVKS.js -index 4a6aa7918599269951044b9f8c7c076a7fe6a9c7..4c1054a867ff6aef1e26f8d84e2831f234c4e44e 100644 ---- a/dist/chunk-FGAZXVKS.js -+++ b/dist/chunk-FGAZXVKS.js -@@ -503,6 +503,7 @@ fetchAndMapExchangeRatesForUnsupportedNativeCurrency_fn = async function({ - if (fallbackCurrencyToNativeCurrencyConversionRate === null) { - return {}; - } -+ const convertFallbackToNative = (value) => value !== void 0 && value !== null ? value * fallbackCurrencyToNativeCurrencyConversionRate : void 0; - const updatedContractExchangeRates = Object.entries( - contractExchangeInformations - ).reduce((acc, [tokenAddress, token]) => { -@@ -510,7 +511,15 @@ fetchAndMapExchangeRatesForUnsupportedNativeCurrency_fn = async function({ - ...acc, - [tokenAddress]: { - ...token, -- price: token.price ? token.price * fallbackCurrencyToNativeCurrencyConversionRate : void 0 -+ currency: nativeCurrency, -+ price: convertFallbackToNative(token.price), -+ marketCap: convertFallbackToNative(token.marketCap), -+ allTimeHigh: convertFallbackToNative(token.allTimeHigh), -+ allTimeLow: convertFallbackToNative(token.allTimeLow), -+ totalVolume: convertFallbackToNative(token.totalVolume), -+ high1d: convertFallbackToNative(token.high1d), -+ low1d: convertFallbackToNative(token.low1d), -+ dilutedMarketCap: convertFallbackToNative(token.dilutedMarketCap) - } - }; - return acc; -diff --git a/dist/chunk-P3O5CVAH.mjs b/dist/chunk-P3O5CVAH.mjs -index 32379704450baee58a37623b1d0f6b471e69de2e..069c3deec15ad85057c910ecf0db31d856fd6ae2 100644 ---- a/dist/chunk-P3O5CVAH.mjs -+++ b/dist/chunk-P3O5CVAH.mjs -@@ -503,6 +503,7 @@ fetchAndMapExchangeRatesForUnsupportedNativeCurrency_fn = async function({ - if (fallbackCurrencyToNativeCurrencyConversionRate === null) { - return {}; - } -+ const convertFallbackToNative = (value) => value !== void 0 && value !== null ? value * fallbackCurrencyToNativeCurrencyConversionRate : void 0; - const updatedContractExchangeRates = Object.entries( - contractExchangeInformations - ).reduce((acc, [tokenAddress, token]) => { -@@ -510,7 +511,15 @@ fetchAndMapExchangeRatesForUnsupportedNativeCurrency_fn = async function({ - ...acc, - [tokenAddress]: { - ...token, -- price: token.price ? token.price * fallbackCurrencyToNativeCurrencyConversionRate : void 0 -+ currency: nativeCurrency, -+ price: convertFallbackToNative(token.price), -+ marketCap: convertFallbackToNative(token.marketCap), -+ allTimeHigh: convertFallbackToNative(token.allTimeHigh), -+ allTimeLow: convertFallbackToNative(token.allTimeLow), -+ totalVolume: convertFallbackToNative(token.totalVolume), -+ high1d: convertFallbackToNative(token.high1d), -+ low1d: convertFallbackToNative(token.low1d), -+ dilutedMarketCap: convertFallbackToNative(token.dilutedMarketCap) - } - }; - return acc; diff --git a/.yarn/patches/@metamask-keyring-controller-npm-16.1.0-7043d2dc62.patch b/.yarn/patches/@metamask-keyring-controller-npm-16.1.0-7043d2dc62.patch deleted file mode 100644 index 79a007d20474..000000000000 --- a/.yarn/patches/@metamask-keyring-controller-npm-16.1.0-7043d2dc62.patch +++ /dev/null @@ -1,6158 +0,0 @@ -diff --git a/dist/KeyringController.js b/dist/KeyringController.js -index 03a6cecff820613ada02f40d3f88edb93c3fe6ed..a690f825be4d66eb48aec945c8e853a66c68ab94 100644 ---- a/dist/KeyringController.js -+++ b/dist/KeyringController.js -@@ -7,7 +7,7 @@ - - - --var _chunkBRS27QHFjs = require('./chunk-BRS27QHF.js'); -+var _chunkL4UUWIZAjs = require('./chunk-L4UUWIZA.js'); - require('./chunk-NOCGQCUM.js'); - - -@@ -18,5 +18,5 @@ require('./chunk-NOCGQCUM.js'); - - - --exports.AccountImportStrategy = _chunkBRS27QHFjs.AccountImportStrategy; exports.KeyringController = _chunkBRS27QHFjs.KeyringController; exports.KeyringTypes = _chunkBRS27QHFjs.KeyringTypes; exports.SignTypedDataVersion = _chunkBRS27QHFjs.SignTypedDataVersion; exports.default = _chunkBRS27QHFjs.KeyringController_default; exports.getDefaultKeyringState = _chunkBRS27QHFjs.getDefaultKeyringState; exports.isCustodyKeyring = _chunkBRS27QHFjs.isCustodyKeyring; exports.keyringBuilderFactory = _chunkBRS27QHFjs.keyringBuilderFactory; -+exports.AccountImportStrategy = _chunkL4UUWIZAjs.AccountImportStrategy; exports.KeyringController = _chunkL4UUWIZAjs.KeyringController; exports.KeyringTypes = _chunkL4UUWIZAjs.KeyringTypes; exports.SignTypedDataVersion = _chunkL4UUWIZAjs.SignTypedDataVersion; exports.default = _chunkL4UUWIZAjs.KeyringController_default; exports.getDefaultKeyringState = _chunkL4UUWIZAjs.getDefaultKeyringState; exports.isCustodyKeyring = _chunkL4UUWIZAjs.isCustodyKeyring; exports.keyringBuilderFactory = _chunkL4UUWIZAjs.keyringBuilderFactory; - //# sourceMappingURL=KeyringController.js.map -\ No newline at end of file -diff --git a/dist/KeyringController.mjs b/dist/KeyringController.mjs -index 58ef8b875e01d8b09ec3880af35f43acdc40f406..7d315f91917002b37edabe91ab7c710e202a4e4e 100644 ---- a/dist/KeyringController.mjs -+++ b/dist/KeyringController.mjs -@@ -7,7 +7,7 @@ import { - getDefaultKeyringState, - isCustodyKeyring, - keyringBuilderFactory --} from "./chunk-STFS4REY.mjs"; -+} from "./chunk-7A7D7THR.mjs"; - import "./chunk-F64I344Z.mjs"; - export { - AccountImportStrategy, -diff --git a/dist/chunk-7A7D7THR.mjs b/dist/chunk-7A7D7THR.mjs -new file mode 100644 -index 0000000000000000000000000000000000000000..42d34b71f245ca1a9e2e019a945df795a6a603c2 ---- /dev/null -+++ b/dist/chunk-7A7D7THR.mjs -@@ -0,0 +1,1506 @@ -+import { -+ __privateAdd, -+ __privateGet, -+ __privateMethod, -+ __privateSet -+} from "./chunk-F64I344Z.mjs"; -+ -+// src/KeyringController.ts -+import { isValidPrivate, toBuffer, getBinarySize } from "@ethereumjs/util"; -+import { BaseController } from "@metamask/base-controller"; -+import * as encryptorUtils from "@metamask/browser-passworder"; -+import HDKeyring from "@metamask/eth-hd-keyring"; -+import { normalize as ethNormalize } from "@metamask/eth-sig-util"; -+import SimpleKeyring from "@metamask/eth-simple-keyring"; -+import { -+ add0x, -+ assertIsStrictHexString, -+ bytesToHex, -+ hasProperty, -+ isObject, -+ isStrictHexString, -+ isValidHexAddress, -+ isValidJson, -+ remove0x -+} from "@metamask/utils"; -+import { Mutex } from "async-mutex"; -+import Wallet, { thirdparty as importers } from "ethereumjs-wallet"; -+var name = "KeyringController"; -+var KeyringTypes = /* @__PURE__ */ ((KeyringTypes2) => { -+ KeyringTypes2["simple"] = "Simple Key Pair"; -+ KeyringTypes2["hd"] = "HD Key Tree"; -+ KeyringTypes2["qr"] = "QR Hardware Wallet Device"; -+ KeyringTypes2["trezor"] = "Trezor Hardware"; -+ KeyringTypes2["ledger"] = "Ledger Hardware"; -+ KeyringTypes2["lattice"] = "Lattice Hardware"; -+ KeyringTypes2["snap"] = "Snap Keyring"; -+ return KeyringTypes2; -+})(KeyringTypes || {}); -+var isCustodyKeyring = (keyringType) => { -+ return keyringType.startsWith("Custody"); -+}; -+var AccountImportStrategy = /* @__PURE__ */ ((AccountImportStrategy2) => { -+ AccountImportStrategy2["privateKey"] = "privateKey"; -+ AccountImportStrategy2["json"] = "json"; -+ return AccountImportStrategy2; -+})(AccountImportStrategy || {}); -+var SignTypedDataVersion = /* @__PURE__ */ ((SignTypedDataVersion2) => { -+ SignTypedDataVersion2["V1"] = "V1"; -+ SignTypedDataVersion2["V3"] = "V3"; -+ SignTypedDataVersion2["V4"] = "V4"; -+ return SignTypedDataVersion2; -+})(SignTypedDataVersion || {}); -+function keyringBuilderFactory(KeyringConstructor) { -+ const builder = () => new KeyringConstructor(); -+ builder.type = KeyringConstructor.type; -+ return builder; -+} -+var defaultKeyringBuilders = [ -+ keyringBuilderFactory(SimpleKeyring), -+ keyringBuilderFactory(HDKeyring) -+]; -+var getDefaultKeyringState = () => { -+ return { -+ isUnlocked: false, -+ keyrings: [] -+ }; -+}; -+function assertHasUint8ArrayMnemonic(keyring) { -+ if (!(hasProperty(keyring, "mnemonic") && keyring.mnemonic instanceof Uint8Array)) { -+ throw new Error("Can't get mnemonic bytes from keyring"); -+ } -+} -+function assertIsExportableKeyEncryptor(encryptor) { -+ if (!("importKey" in encryptor && typeof encryptor.importKey === "function" && "decryptWithKey" in encryptor && typeof encryptor.decryptWithKey === "function" && "encryptWithKey" in encryptor && typeof encryptor.encryptWithKey === "function")) { -+ throw new Error("KeyringController - The encryptor does not support encryption key export." /* UnsupportedEncryptionKeyExport */); -+ } -+} -+function assertIsValidPassword(password) { -+ if (typeof password !== "string") { -+ throw new Error("KeyringController - Password must be of type string." /* WrongPasswordType */); -+ } -+ if (!password || !password.length) { -+ throw new Error("KeyringController - Password cannot be empty." /* InvalidEmptyPassword */); -+ } -+} -+function isSerializedKeyringsArray(array) { -+ return typeof array === "object" && Array.isArray(array) && array.every((value) => value.type && isValidJson(value.data)); -+} -+async function displayForKeyring(keyring) { -+ const accounts = await keyring.getAccounts(); -+ return { -+ type: keyring.type, -+ // Cast to `string[]` here is safe here because `accounts` has no nullish -+ // values, and `normalize` returns `string` unless given a nullish value -+ accounts: accounts.map(normalize) -+ }; -+} -+function isEthAddress(address) { -+ return ( -+ // NOTE: This function only checks for lowercased strings -+ isStrictHexString(address.toLowerCase()) && // This checks for lowercased addresses and checksum addresses too -+ isValidHexAddress(address) -+ ); -+} -+function normalize(address) { -+ return isEthAddress(address) ? ethNormalize(address) : address; -+} -+var _controllerOperationMutex, _vaultOperationMutex, _keyringBuilders, _keyrings, _unsupportedKeyrings, _password, _encryptor, _cacheEncryptionKey, _qrKeyringStateListener, _registerMessageHandlers, registerMessageHandlers_fn, _getKeyringBuilderForType, getKeyringBuilderForType_fn, _addQRKeyring, addQRKeyring_fn, _subscribeToQRKeyringEvents, subscribeToQRKeyringEvents_fn, _unsubscribeFromQRKeyringsEvents, unsubscribeFromQRKeyringsEvents_fn, _createNewVaultWithKeyring, createNewVaultWithKeyring_fn, _getUpdatedKeyrings, getUpdatedKeyrings_fn, _getSerializedKeyrings, getSerializedKeyrings_fn, _restoreSerializedKeyrings, restoreSerializedKeyrings_fn, _unlockKeyrings, unlockKeyrings_fn, _updateVault, updateVault_fn, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn, _createKeyringWithFirstAccount, createKeyringWithFirstAccount_fn, _newKeyring, newKeyring_fn, _clearKeyrings, clearKeyrings_fn, _restoreKeyring, restoreKeyring_fn, _destroyKeyring, destroyKeyring_fn, _removeEmptyKeyrings, removeEmptyKeyrings_fn, _checkForDuplicate, checkForDuplicate_fn, _setUnlocked, setUnlocked_fn, _persistOrRollback, persistOrRollback_fn, _withRollback, withRollback_fn, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn, _withControllerLock, withControllerLock_fn, _withVaultLock, withVaultLock_fn; -+var KeyringController = class extends BaseController { -+ /** -+ * Creates a KeyringController instance. -+ * -+ * @param options - Initial options used to configure this controller -+ * @param options.encryptor - An optional object for defining encryption schemes. -+ * @param options.keyringBuilders - Set a new name for account. -+ * @param options.cacheEncryptionKey - Whether to cache or not encryption key. -+ * @param options.messenger - A restricted controller messenger. -+ * @param options.state - Initial state to set on this controller. -+ */ -+ constructor(options) { -+ const { -+ encryptor = encryptorUtils, -+ keyringBuilders, -+ messenger, -+ state -+ } = options; -+ super({ -+ name, -+ metadata: { -+ vault: { persist: true, anonymous: false }, -+ isUnlocked: { persist: false, anonymous: true }, -+ keyrings: { persist: false, anonymous: false }, -+ encryptionKey: { persist: false, anonymous: false }, -+ encryptionSalt: { persist: false, anonymous: false } -+ }, -+ messenger, -+ state: { -+ ...getDefaultKeyringState(), -+ ...state -+ } -+ }); -+ /** -+ * Constructor helper for registering this controller's messaging system -+ * actions. -+ */ -+ __privateAdd(this, _registerMessageHandlers); -+ /** -+ * Get the keyring builder for the given `type`. -+ * -+ * @param type - The type of keyring to get the builder for. -+ * @returns The keyring builder, or undefined if none exists. -+ */ -+ __privateAdd(this, _getKeyringBuilderForType); -+ /** -+ * Add qr hardware keyring. -+ * -+ * @returns The added keyring -+ * @throws If a QRKeyring builder is not provided -+ * when initializing the controller -+ */ -+ __privateAdd(this, _addQRKeyring); -+ /** -+ * Subscribe to a QRKeyring state change events and -+ * forward them through the messaging system. -+ * -+ * @param qrKeyring - The QRKeyring instance to subscribe to -+ */ -+ __privateAdd(this, _subscribeToQRKeyringEvents); -+ __privateAdd(this, _unsubscribeFromQRKeyringsEvents); -+ /** -+ * Create new vault with an initial keyring -+ * -+ * Destroys any old encrypted storage, -+ * creates a new encrypted store with the given password, -+ * creates a new wallet with 1 account. -+ * -+ * @fires KeyringController:unlock -+ * @param password - The password to encrypt the vault with. -+ * @param keyring - A object containing the params to instantiate a new keyring. -+ * @param keyring.type - The keyring type. -+ * @param keyring.opts - Optional parameters required to instantiate the keyring. -+ * @returns A promise that resolves to the state. -+ */ -+ __privateAdd(this, _createNewVaultWithKeyring); -+ /** -+ * Get the updated array of each keyring's type and -+ * accounts list. -+ * -+ * @returns A promise resolving to the updated keyrings array. -+ */ -+ __privateAdd(this, _getUpdatedKeyrings); -+ /** -+ * Serialize the current array of keyring instances, -+ * including unsupported keyrings by default. -+ * -+ * @param options - Method options. -+ * @param options.includeUnsupported - Whether to include unsupported keyrings. -+ * @returns The serialized keyrings. -+ */ -+ __privateAdd(this, _getSerializedKeyrings); -+ /** -+ * Restore a serialized keyrings array. -+ * -+ * @param serializedKeyrings - The serialized keyrings array. -+ */ -+ __privateAdd(this, _restoreSerializedKeyrings); -+ /** -+ * Unlock Keyrings, decrypting the vault and deserializing all -+ * keyrings contained in it, using a password or an encryption key with salt. -+ * -+ * @param password - The keyring controller password. -+ * @param encryptionKey - An exported key string to unlock keyrings with. -+ * @param encryptionSalt - The salt used to encrypt the vault. -+ * @returns A promise resolving to the deserialized keyrings array. -+ */ -+ __privateAdd(this, _unlockKeyrings); -+ /** -+ * Update the vault with the current keyrings. -+ * -+ * @returns A promise resolving to `true` if the operation is successful. -+ */ -+ __privateAdd(this, _updateVault); -+ /** -+ * Retrieves all the accounts from keyrings instances -+ * that are currently in memory. -+ * -+ * @returns A promise resolving to an array of accounts. -+ */ -+ __privateAdd(this, _getAccountsFromKeyrings); -+ /** -+ * Create a new keyring, ensuring that the first account is -+ * also created. -+ * -+ * @param type - Keyring type to instantiate. -+ * @param opts - Optional parameters required to instantiate the keyring. -+ * @returns A promise that resolves if the operation is successful. -+ */ -+ __privateAdd(this, _createKeyringWithFirstAccount); -+ /** -+ * Instantiate, initialize and return a new keyring of the given `type`, -+ * using the given `opts`. The keyring is built using the keyring builder -+ * registered for the given `type`. -+ * -+ * -+ * @param type - The type of keyring to add. -+ * @param data - The data to restore a previously serialized keyring. -+ * @returns The new keyring. -+ * @throws If the keyring includes duplicated accounts. -+ */ -+ __privateAdd(this, _newKeyring); -+ /** -+ * Remove all managed keyrings, destroying all their -+ * instances in memory. -+ */ -+ __privateAdd(this, _clearKeyrings); -+ /** -+ * Restore a Keyring from a provided serialized payload. -+ * On success, returns the resulting keyring instance. -+ * -+ * @param serialized - The serialized keyring. -+ * @returns The deserialized keyring or undefined if the keyring type is unsupported. -+ */ -+ __privateAdd(this, _restoreKeyring); -+ /** -+ * Destroy Keyring -+ * -+ * Some keyrings support a method called `destroy`, that destroys the -+ * keyring along with removing all its event listeners and, in some cases, -+ * clears the keyring bridge iframe from the DOM. -+ * -+ * @param keyring - The keyring to destroy. -+ */ -+ __privateAdd(this, _destroyKeyring); -+ /** -+ * Remove empty keyrings. -+ * -+ * Loops through the keyrings and removes the ones with empty accounts -+ * (usually after removing the last / only account) from a keyring. -+ */ -+ __privateAdd(this, _removeEmptyKeyrings); -+ /** -+ * Checks for duplicate keypairs, using the the first account in the given -+ * array. Rejects if a duplicate is found. -+ * -+ * Only supports 'Simple Key Pair'. -+ * -+ * @param type - The key pair type to check for. -+ * @param newAccountArray - Array of new accounts. -+ * @returns The account, if no duplicate is found. -+ */ -+ __privateAdd(this, _checkForDuplicate); -+ /** -+ * Set the `isUnlocked` to true and notify listeners -+ * through the messenger. -+ * -+ * @fires KeyringController:unlock -+ */ -+ __privateAdd(this, _setUnlocked); -+ /** -+ * Execute the given function after acquiring the controller lock -+ * and save the keyrings to state after it, or rollback to their -+ * previous state in case of error. -+ * -+ * @param fn - The function to execute. -+ * @returns The result of the function. -+ */ -+ __privateAdd(this, _persistOrRollback); -+ /** -+ * Execute the given function after acquiring the controller lock -+ * and rollback keyrings and password states in case of error. -+ * -+ * @param fn - The function to execute atomically. -+ * @returns The result of the function. -+ */ -+ __privateAdd(this, _withRollback); -+ /** -+ * Assert that the controller mutex is locked. -+ * -+ * @throws If the controller mutex is not locked. -+ */ -+ __privateAdd(this, _assertControllerMutexIsLocked); -+ /** -+ * Lock the controller mutex before executing the given function, -+ * and release it after the function is resolved or after an -+ * error is thrown. -+ * -+ * This wrapper ensures that each mutable operation that interacts with the -+ * controller and that changes its state is executed in a mutually exclusive way, -+ * preventing unsafe concurrent access that could lead to unpredictable behavior. -+ * -+ * @param fn - The function to execute while the controller mutex is locked. -+ * @returns The result of the function. -+ */ -+ __privateAdd(this, _withControllerLock); -+ /** -+ * Lock the vault mutex before executing the given function, -+ * and release it after the function is resolved or after an -+ * error is thrown. -+ * -+ * This ensures that each operation that interacts with the vault -+ * is executed in a mutually exclusive way. -+ * -+ * @param fn - The function to execute while the vault mutex is locked. -+ * @returns The result of the function. -+ */ -+ __privateAdd(this, _withVaultLock); -+ __privateAdd(this, _controllerOperationMutex, new Mutex()); -+ __privateAdd(this, _vaultOperationMutex, new Mutex()); -+ __privateAdd(this, _keyringBuilders, void 0); -+ __privateAdd(this, _keyrings, void 0); -+ __privateAdd(this, _unsupportedKeyrings, void 0); -+ __privateAdd(this, _password, void 0); -+ __privateAdd(this, _encryptor, void 0); -+ __privateAdd(this, _cacheEncryptionKey, void 0); -+ __privateAdd(this, _qrKeyringStateListener, void 0); -+ __privateSet(this, _keyringBuilders, keyringBuilders ? defaultKeyringBuilders.concat(keyringBuilders) : defaultKeyringBuilders); -+ __privateSet(this, _encryptor, encryptor); -+ __privateSet(this, _keyrings, []); -+ __privateSet(this, _unsupportedKeyrings, []); -+ __privateSet(this, _cacheEncryptionKey, Boolean(options.cacheEncryptionKey)); -+ if (__privateGet(this, _cacheEncryptionKey)) { -+ assertIsExportableKeyEncryptor(encryptor); -+ } -+ __privateMethod(this, _registerMessageHandlers, registerMessageHandlers_fn).call(this); -+ } -+ /** -+ * Adds a new account to the default (first) HD seed phrase keyring. -+ * -+ * @param accountCount - Number of accounts before adding a new one, used to -+ * make the method idempotent. -+ * @returns Promise resolving to the added account address. -+ */ -+ async addNewAccount(accountCount) { -+ return __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -+ const primaryKeyring = this.getKeyringsByType("HD Key Tree")[0]; -+ if (!primaryKeyring) { -+ throw new Error("No HD keyring found"); -+ } -+ const oldAccounts = await primaryKeyring.getAccounts(); -+ if (accountCount && oldAccounts.length !== accountCount) { -+ if (accountCount > oldAccounts.length) { -+ throw new Error("Account out of sequence"); -+ } -+ const existingAccount = oldAccounts[accountCount]; -+ if (!existingAccount) { -+ throw new Error(`Can't find account at index ${accountCount}`); -+ } -+ return existingAccount; -+ } -+ const [addedAccountAddress] = await primaryKeyring.addAccounts(1); -+ await this.verifySeedPhrase(); -+ return addedAccountAddress; -+ }); -+ } -+ /** -+ * Adds a new account to the specified keyring. -+ * -+ * @param keyring - Keyring to add the account to. -+ * @param accountCount - Number of accounts before adding a new one, used to make the method idempotent. -+ * @returns Promise resolving to the added account address -+ */ -+ async addNewAccountForKeyring(keyring, accountCount) { -+ return __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -+ const oldAccounts = await __privateMethod(this, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn).call(this); -+ if (accountCount && oldAccounts.length !== accountCount) { -+ if (accountCount > oldAccounts.length) { -+ throw new Error("Account out of sequence"); -+ } -+ const existingAccount = oldAccounts[accountCount]; -+ assertIsStrictHexString(existingAccount); -+ return existingAccount; -+ } -+ await keyring.addAccounts(1); -+ const addedAccountAddress = (await __privateMethod(this, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn).call(this)).find( -+ (selectedAddress) => !oldAccounts.includes(selectedAddress) -+ ); -+ assertIsStrictHexString(addedAccountAddress); -+ return addedAccountAddress; -+ }); -+ } -+ /** -+ * Adds a new account to the default (first) HD seed phrase keyring without updating identities in preferences. -+ * -+ * @returns Promise resolving to the added account address. -+ */ -+ async addNewAccountWithoutUpdate() { -+ return __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -+ const primaryKeyring = this.getKeyringsByType("HD Key Tree")[0]; -+ if (!primaryKeyring) { -+ throw new Error("No HD keyring found"); -+ } -+ const [addedAccountAddress] = await primaryKeyring.addAccounts(1); -+ await this.verifySeedPhrase(); -+ return addedAccountAddress; -+ }); -+ } -+ /** -+ * Effectively the same as creating a new keychain then populating it -+ * using the given seed phrase. -+ * -+ * @param password - Password to unlock keychain. -+ * @param seed - A BIP39-compliant seed phrase as Uint8Array, -+ * either as a string or an array of UTF-8 bytes that represent the string. -+ * @returns Promise resolving when the operation ends successfully. -+ */ -+ async createNewVaultAndRestore(password, seed) { -+ return __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -+ assertIsValidPassword(password); -+ await __privateMethod(this, _createNewVaultWithKeyring, createNewVaultWithKeyring_fn).call(this, password, { -+ type: "HD Key Tree" /* hd */, -+ opts: { -+ mnemonic: seed, -+ numberOfAccounts: 1 -+ } -+ }); -+ }); -+ } -+ /** -+ * Create a new primary keychain and wipe any previous keychains. -+ * -+ * @param password - Password to unlock the new vault. -+ * @returns Promise resolving when the operation ends successfully. -+ */ -+ async createNewVaultAndKeychain(password) { -+ return __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -+ const accounts = await __privateMethod(this, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn).call(this); -+ if (!accounts.length) { -+ await __privateMethod(this, _createNewVaultWithKeyring, createNewVaultWithKeyring_fn).call(this, password, { -+ type: "HD Key Tree" /* hd */ -+ }); -+ } -+ }); -+ } -+ /** -+ * Adds a new keyring of the given `type`. -+ * -+ * @param type - Keyring type name. -+ * @param opts - Keyring options. -+ * @throws If a builder for the given `type` does not exist. -+ * @returns Promise resolving to the added keyring. -+ */ -+ async addNewKeyring(type, opts) { -+ if (type === "QR Hardware Wallet Device" /* qr */) { -+ return this.getOrAddQRKeyring(); -+ } -+ return __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => __privateMethod(this, _newKeyring, newKeyring_fn).call(this, type, opts)); -+ } -+ /** -+ * Method to verify a given password validity. Throws an -+ * error if the password is invalid. -+ * -+ * @param password - Password of the keyring. -+ */ -+ async verifyPassword(password) { -+ if (!this.state.vault) { -+ throw new Error("KeyringController - Cannot unlock without a previous vault." /* VaultError */); -+ } -+ await __privateGet(this, _encryptor).decrypt(password, this.state.vault); -+ } -+ /** -+ * Returns the status of the vault. -+ * -+ * @returns Boolean returning true if the vault is unlocked. -+ */ -+ isUnlocked() { -+ return this.state.isUnlocked; -+ } -+ /** -+ * Gets the seed phrase of the HD keyring. -+ * -+ * @param password - Password of the keyring. -+ * @returns Promise resolving to the seed phrase. -+ */ -+ async exportSeedPhrase(password) { -+ await this.verifyPassword(password); -+ assertHasUint8ArrayMnemonic(__privateGet(this, _keyrings)[0]); -+ return __privateGet(this, _keyrings)[0].mnemonic; -+ } -+ /** -+ * Gets the private key from the keyring controlling an address. -+ * -+ * @param password - Password of the keyring. -+ * @param address - Address to export. -+ * @returns Promise resolving to the private key for an address. -+ */ -+ async exportAccount(password, address) { -+ await this.verifyPassword(password); -+ const keyring = await this.getKeyringForAccount( -+ address -+ ); -+ if (!keyring.exportAccount) { -+ throw new Error("`KeyringController - The keyring for the current address does not support the method exportAccount" /* UnsupportedExportAccount */); -+ } -+ return await keyring.exportAccount(normalize(address)); -+ } -+ /** -+ * Returns the public addresses of all accounts from every keyring. -+ * -+ * @returns A promise resolving to an array of addresses. -+ */ -+ async getAccounts() { -+ return this.state.keyrings.reduce( -+ (accounts, keyring) => accounts.concat(keyring.accounts), -+ [] -+ ); -+ } -+ /** -+ * Get encryption public key. -+ * -+ * @param account - An account address. -+ * @param opts - Additional encryption options. -+ * @throws If the `account` does not exist or does not support the `getEncryptionPublicKey` method -+ * @returns Promise resolving to encyption public key of the `account` if one exists. -+ */ -+ async getEncryptionPublicKey(account, opts) { -+ const address = ethNormalize(account); -+ const keyring = await this.getKeyringForAccount( -+ account -+ ); -+ if (!keyring.getEncryptionPublicKey) { -+ throw new Error("KeyringController - The keyring for the current address does not support the method getEncryptionPublicKey." /* UnsupportedGetEncryptionPublicKey */); -+ } -+ return await keyring.getEncryptionPublicKey(address, opts); -+ } -+ /** -+ * Attempts to decrypt the provided message parameters. -+ * -+ * @param messageParams - The decryption message parameters. -+ * @param messageParams.from - The address of the account you want to use to decrypt the message. -+ * @param messageParams.data - The encrypted data that you want to decrypt. -+ * @returns The raw decryption result. -+ */ -+ async decryptMessage(messageParams) { -+ const address = ethNormalize(messageParams.from); -+ const keyring = await this.getKeyringForAccount( -+ address -+ ); -+ if (!keyring.decryptMessage) { -+ throw new Error("KeyringController - The keyring for the current address does not support the method decryptMessage." /* UnsupportedDecryptMessage */); -+ } -+ return keyring.decryptMessage(address, messageParams.data); -+ } -+ /** -+ * Returns the currently initialized keyring that manages -+ * the specified `address` if one exists. -+ * -+ * @deprecated Use of this method is discouraged as actions executed directly on -+ * keyrings are not being reflected in the KeyringController state and not -+ * persisted in the vault. Use `withKeyring` instead. -+ * @param account - An account address. -+ * @returns Promise resolving to keyring of the `account` if one exists. -+ */ -+ async getKeyringForAccount(account) { -+ const address = normalize(account); -+ const candidates = await Promise.all( -+ __privateGet(this, _keyrings).map(async (keyring) => { -+ return Promise.all([keyring, keyring.getAccounts()]); -+ }) -+ ); -+ const winners = candidates.filter((candidate) => { -+ const accounts = candidate[1].map(normalize); -+ return accounts.includes(address); -+ }); -+ if (winners.length && winners[0]?.length) { -+ return winners[0][0]; -+ } -+ let errorInfo = ""; -+ if (!candidates.length) { -+ errorInfo = "There are no keyrings"; -+ } else if (!winners.length) { -+ errorInfo = "There are keyrings, but none match the address"; -+ } -+ throw new Error( -+ `${"KeyringController - No keyring found" /* NoKeyring */}. Error info: ${errorInfo}` -+ ); -+ } -+ /** -+ * Returns all keyrings of the given type. -+ * -+ * @deprecated Use of this method is discouraged as actions executed directly on -+ * keyrings are not being reflected in the KeyringController state and not -+ * persisted in the vault. Use `withKeyring` instead. -+ * @param type - Keyring type name. -+ * @returns An array of keyrings of the given type. -+ */ -+ getKeyringsByType(type) { -+ return __privateGet(this, _keyrings).filter((keyring) => keyring.type === type); -+ } -+ /** -+ * Persist all serialized keyrings in the vault. -+ * -+ * @deprecated This method is being phased out in favor of `withKeyring`. -+ * @returns Promise resolving with `true` value when the -+ * operation completes. -+ */ -+ async persistAllKeyrings() { -+ return __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => true); -+ } -+ /** -+ * Imports an account with the specified import strategy. -+ * -+ * @param strategy - Import strategy name. -+ * @param args - Array of arguments to pass to the underlying stategy. -+ * @throws Will throw when passed an unrecognized strategy. -+ * @returns Promise resolving to the imported account address. -+ */ -+ async importAccountWithStrategy(strategy, args) { -+ return __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -+ let privateKey; -+ switch (strategy) { -+ case "privateKey": -+ const [importedKey] = args; -+ if (!importedKey) { -+ throw new Error("Cannot import an empty key."); -+ } -+ const prefixed = add0x(importedKey); -+ let bufferedPrivateKey; -+ try { -+ bufferedPrivateKey = toBuffer(prefixed); -+ } catch { -+ throw new Error("Cannot import invalid private key."); -+ } -+ if (!isValidPrivate(bufferedPrivateKey) || // ensures that the key is 64 bytes long -+ getBinarySize(prefixed) !== 64 + "0x".length) { -+ throw new Error("Cannot import invalid private key."); -+ } -+ privateKey = remove0x(prefixed); -+ break; -+ case "json": -+ let wallet; -+ const [input, password] = args; -+ try { -+ wallet = importers.fromEtherWallet(input, password); -+ } catch (e) { -+ wallet = wallet || await Wallet.fromV3(input, password, true); -+ } -+ privateKey = bytesToHex(wallet.getPrivateKey()); -+ break; -+ default: -+ throw new Error(`Unexpected import strategy: '${strategy}'`); -+ } -+ const newKeyring = await __privateMethod(this, _newKeyring, newKeyring_fn).call(this, "Simple Key Pair" /* simple */, [ -+ privateKey -+ ]); -+ const accounts = await newKeyring.getAccounts(); -+ return accounts[0]; -+ }); -+ } -+ /** -+ * Removes an account from keyring state. -+ * -+ * @param address - Address of the account to remove. -+ * @fires KeyringController:accountRemoved -+ * @returns Promise resolving when the account is removed. -+ */ -+ async removeAccount(address) { -+ await __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -+ const keyring = await this.getKeyringForAccount( -+ address -+ ); -+ if (!keyring.removeAccount) { -+ throw new Error("`KeyringController - The keyring for the current address does not support the method removeAccount" /* UnsupportedRemoveAccount */); -+ } -+ await keyring.removeAccount(address); -+ const accounts = await keyring.getAccounts(); -+ if (accounts.length === 0) { -+ await __privateMethod(this, _removeEmptyKeyrings, removeEmptyKeyrings_fn).call(this); -+ } -+ }); -+ this.messagingSystem.publish(`${name}:accountRemoved`, address); -+ } -+ /** -+ * Deallocates all secrets and locks the wallet. -+ * -+ * @returns Promise resolving when the operation completes. -+ */ -+ async setLocked() { -+ return __privateMethod(this, _withRollback, withRollback_fn).call(this, async () => { -+ __privateMethod(this, _unsubscribeFromQRKeyringsEvents, unsubscribeFromQRKeyringsEvents_fn).call(this); -+ __privateSet(this, _password, void 0); -+ await __privateMethod(this, _clearKeyrings, clearKeyrings_fn).call(this); -+ this.update((state) => { -+ state.isUnlocked = false; -+ state.keyrings = []; -+ delete state.encryptionKey; -+ delete state.encryptionSalt; -+ }); -+ this.messagingSystem.publish(`${name}:lock`); -+ }); -+ } -+ /** -+ * Signs message by calling down into a specific keyring. -+ * -+ * @param messageParams - PersonalMessageParams object to sign. -+ * @returns Promise resolving to a signed message string. -+ */ -+ async signMessage(messageParams) { -+ if (!messageParams.data) { -+ throw new Error("Can't sign an empty message"); -+ } -+ const address = ethNormalize(messageParams.from); -+ const keyring = await this.getKeyringForAccount( -+ address -+ ); -+ if (!keyring.signMessage) { -+ throw new Error("KeyringController - The keyring for the current address does not support the method signMessage." /* UnsupportedSignMessage */); -+ } -+ return await keyring.signMessage(address, messageParams.data); -+ } -+ /** -+ * Signs personal message by calling down into a specific keyring. -+ * -+ * @param messageParams - PersonalMessageParams object to sign. -+ * @returns Promise resolving to a signed message string. -+ */ -+ async signPersonalMessage(messageParams) { -+ const address = ethNormalize(messageParams.from); -+ const keyring = await this.getKeyringForAccount( -+ address -+ ); -+ if (!keyring.signPersonalMessage) { -+ throw new Error("KeyringController - The keyring for the current address does not support the method signPersonalMessage." /* UnsupportedSignPersonalMessage */); -+ } -+ const normalizedData = normalize(messageParams.data); -+ return await keyring.signPersonalMessage(address, normalizedData); -+ } -+ /** -+ * Signs typed message by calling down into a specific keyring. -+ * -+ * @param messageParams - TypedMessageParams object to sign. -+ * @param version - Compatibility version EIP712. -+ * @throws Will throw when passed an unrecognized version. -+ * @returns Promise resolving to a signed message string or an error if any. -+ */ -+ async signTypedMessage(messageParams, version) { -+ try { -+ if (![ -+ "V1" /* V1 */, -+ "V3" /* V3 */, -+ "V4" /* V4 */ -+ ].includes(version)) { -+ throw new Error(`Unexpected signTypedMessage version: '${version}'`); -+ } -+ const address = ethNormalize(messageParams.from); -+ const keyring = await this.getKeyringForAccount( -+ address -+ ); -+ if (!keyring.signTypedData) { -+ throw new Error("KeyringController - The keyring for the current address does not support the method signTypedMessage." /* UnsupportedSignTypedMessage */); -+ } -+ return await keyring.signTypedData( -+ address, -+ version !== "V1" /* V1 */ && typeof messageParams.data === "string" ? JSON.parse(messageParams.data) : messageParams.data, -+ { version } -+ ); -+ } catch (error) { -+ throw new Error(`Keyring Controller signTypedMessage: ${error}`); -+ } -+ } -+ /** -+ * Signs a transaction by calling down into a specific keyring. -+ * -+ * @param transaction - Transaction object to sign. Must be a `ethereumjs-tx` transaction instance. -+ * @param from - Address to sign from, should be in keychain. -+ * @param opts - An optional options object. -+ * @returns Promise resolving to a signed transaction string. -+ */ -+ async signTransaction(transaction, from, opts) { -+ const address = ethNormalize(from); -+ const keyring = await this.getKeyringForAccount( -+ address -+ ); -+ if (!keyring.signTransaction) { -+ throw new Error("KeyringController - The keyring for the current address does not support the method signTransaction." /* UnsupportedSignTransaction */); -+ } -+ return await keyring.signTransaction(address, transaction, opts); -+ } -+ /** -+ * Convert a base transaction to a base UserOperation. -+ * -+ * @param from - Address of the sender. -+ * @param transactions - Base transactions to include in the UserOperation. -+ * @param executionContext - The execution context to use for the UserOperation. -+ * @returns A pseudo-UserOperation that can be used to construct a real. -+ */ -+ async prepareUserOperation(from, transactions, executionContext) { -+ const address = ethNormalize(from); -+ const keyring = await this.getKeyringForAccount( -+ address -+ ); -+ if (!keyring.prepareUserOperation) { -+ throw new Error("KeyringController - The keyring for the current address does not support the method prepareUserOperation." /* UnsupportedPrepareUserOperation */); -+ } -+ return await keyring.prepareUserOperation( -+ address, -+ transactions, -+ executionContext -+ ); -+ } -+ /** -+ * Patches properties of a UserOperation. Currently, only the -+ * `paymasterAndData` can be patched. -+ * -+ * @param from - Address of the sender. -+ * @param userOp - UserOperation to patch. -+ * @param executionContext - The execution context to use for the UserOperation. -+ * @returns A patch to apply to the UserOperation. -+ */ -+ async patchUserOperation(from, userOp, executionContext) { -+ const address = ethNormalize(from); -+ const keyring = await this.getKeyringForAccount( -+ address -+ ); -+ if (!keyring.patchUserOperation) { -+ throw new Error("KeyringController - The keyring for the current address does not support the method patchUserOperation." /* UnsupportedPatchUserOperation */); -+ } -+ return await keyring.patchUserOperation(address, userOp, executionContext); -+ } -+ /** -+ * Signs an UserOperation. -+ * -+ * @param from - Address of the sender. -+ * @param userOp - UserOperation to sign. -+ * @param executionContext - The execution context to use for the UserOperation. -+ * @returns The signature of the UserOperation. -+ */ -+ async signUserOperation(from, userOp, executionContext) { -+ const address = ethNormalize(from); -+ const keyring = await this.getKeyringForAccount( -+ address -+ ); -+ if (!keyring.signUserOperation) { -+ throw new Error("KeyringController - The keyring for the current address does not support the method signUserOperation." /* UnsupportedSignUserOperation */); -+ } -+ return await keyring.signUserOperation(address, userOp, executionContext); -+ } -+ /** -+ * Changes the password used to encrypt the vault. -+ * -+ * @param password - The new password. -+ * @returns Promise resolving when the operation completes. -+ */ -+ changePassword(password) { -+ return __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -+ if (!this.state.isUnlocked) { -+ throw new Error("KeyringController - Cannot persist vault without password and encryption key" /* MissingCredentials */); -+ } -+ assertIsValidPassword(password); -+ __privateSet(this, _password, password); -+ if (__privateGet(this, _cacheEncryptionKey)) { -+ this.update((state) => { -+ delete state.encryptionKey; -+ delete state.encryptionSalt; -+ }); -+ } -+ }); -+ } -+ /** -+ * Attempts to decrypt the current vault and load its keyrings, -+ * using the given encryption key and salt. -+ * -+ * @param encryptionKey - Key to unlock the keychain. -+ * @param encryptionSalt - Salt to unlock the keychain. -+ * @returns Promise resolving when the operation completes. -+ */ -+ async submitEncryptionKey(encryptionKey, encryptionSalt) { -+ return __privateMethod(this, _withRollback, withRollback_fn).call(this, async () => { -+ __privateSet(this, _keyrings, await __privateMethod(this, _unlockKeyrings, unlockKeyrings_fn).call(this, void 0, encryptionKey, encryptionSalt)); -+ __privateMethod(this, _setUnlocked, setUnlocked_fn).call(this); -+ }); -+ } -+ /** -+ * Attempts to decrypt the current vault and load its keyrings, -+ * using the given password. -+ * -+ * @param password - Password to unlock the keychain. -+ * @returns Promise resolving when the operation completes. -+ */ -+ async submitPassword(password) { -+ return __privateMethod(this, _withRollback, withRollback_fn).call(this, async () => { -+ __privateSet(this, _keyrings, await __privateMethod(this, _unlockKeyrings, unlockKeyrings_fn).call(this, password)); -+ __privateMethod(this, _setUnlocked, setUnlocked_fn).call(this); -+ }); -+ } -+ /** -+ * Verifies the that the seed phrase restores the current keychain's accounts. -+ * -+ * @returns Promise resolving to the seed phrase as Uint8Array. -+ */ -+ async verifySeedPhrase() { -+ const primaryKeyring = this.getKeyringsByType("HD Key Tree" /* hd */)[0]; -+ if (!primaryKeyring) { -+ throw new Error("No HD keyring found."); -+ } -+ assertHasUint8ArrayMnemonic(primaryKeyring); -+ const seedWords = primaryKeyring.mnemonic; -+ const accounts = await primaryKeyring.getAccounts(); -+ if (accounts.length === 0) { -+ throw new Error("Cannot verify an empty keyring."); -+ } -+ const hdKeyringBuilder = __privateMethod(this, _getKeyringBuilderForType, getKeyringBuilderForType_fn).call(this, "HD Key Tree" /* hd */); -+ const hdKeyring = hdKeyringBuilder(); -+ await hdKeyring.deserialize({ -+ mnemonic: seedWords, -+ numberOfAccounts: accounts.length -+ }); -+ const testAccounts = await hdKeyring.getAccounts(); -+ if (testAccounts.length !== accounts.length) { -+ throw new Error("Seed phrase imported incorrect number of accounts."); -+ } -+ testAccounts.forEach((account, i) => { -+ if (account.toLowerCase() !== accounts[i].toLowerCase()) { -+ throw new Error("Seed phrase imported different accounts."); -+ } -+ }); -+ return seedWords; -+ } -+ async withKeyring(selector, operation, options = { -+ createIfMissing: false -+ }) { -+ return __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -+ let keyring; -+ if ("address" in selector) { -+ keyring = await this.getKeyringForAccount(selector.address); -+ } else { -+ keyring = this.getKeyringsByType(selector.type)[selector.index || 0]; -+ if (!keyring && options.createIfMissing) { -+ keyring = await __privateMethod(this, _newKeyring, newKeyring_fn).call(this, selector.type, options.createWithData); -+ } -+ } -+ if (!keyring) { -+ throw new Error("KeyringController - Keyring not found." /* KeyringNotFound */); -+ } -+ const result = await operation(keyring); -+ if (Object.is(result, keyring)) { -+ throw new Error("KeyringController - Returning keyring instances is unsafe" /* UnsafeDirectKeyringAccess */); -+ } -+ return result; -+ }); -+ } -+ // QR Hardware related methods -+ /** -+ * Get QR Hardware keyring. -+ * -+ * @returns The QR Keyring if defined, otherwise undefined -+ */ -+ getQRKeyring() { -+ return this.getKeyringsByType("QR Hardware Wallet Device" /* qr */)[0]; -+ } -+ /** -+ * Get QR hardware keyring. If it doesn't exist, add it. -+ * -+ * @returns The added keyring -+ */ -+ async getOrAddQRKeyring() { -+ return this.getQRKeyring() || await __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => __privateMethod(this, _addQRKeyring, addQRKeyring_fn).call(this)); -+ } -+ // TODO: Replace `any` with type -+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -+ async restoreQRKeyring(serialized) { -+ return __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -+ const keyring = this.getQRKeyring() || await __privateMethod(this, _addQRKeyring, addQRKeyring_fn).call(this); -+ keyring.deserialize(serialized); -+ }); -+ } -+ async resetQRKeyringState() { -+ (await this.getOrAddQRKeyring()).resetStore(); -+ } -+ async getQRKeyringState() { -+ return (await this.getOrAddQRKeyring()).getMemStore(); -+ } -+ async submitQRCryptoHDKey(cryptoHDKey) { -+ (await this.getOrAddQRKeyring()).submitCryptoHDKey(cryptoHDKey); -+ } -+ async submitQRCryptoAccount(cryptoAccount) { -+ (await this.getOrAddQRKeyring()).submitCryptoAccount(cryptoAccount); -+ } -+ async submitQRSignature(requestId, ethSignature) { -+ (await this.getOrAddQRKeyring()).submitSignature(requestId, ethSignature); -+ } -+ async cancelQRSignRequest() { -+ (await this.getOrAddQRKeyring()).cancelSignRequest(); -+ } -+ /** -+ * Cancels qr keyring sync. -+ */ -+ async cancelQRSynchronization() { -+ (await this.getOrAddQRKeyring()).cancelSync(); -+ } -+ async connectQRHardware(page) { -+ return __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -+ try { -+ const keyring = this.getQRKeyring() || await __privateMethod(this, _addQRKeyring, addQRKeyring_fn).call(this); -+ let accounts; -+ switch (page) { -+ case -1: -+ accounts = await keyring.getPreviousPage(); -+ break; -+ case 1: -+ accounts = await keyring.getNextPage(); -+ break; -+ default: -+ accounts = await keyring.getFirstPage(); -+ } -+ return accounts.map((account) => { -+ return { -+ ...account, -+ balance: "0x0" -+ }; -+ }); -+ } catch (e) { -+ throw new Error(`Unspecified error when connect QR Hardware, ${e}`); -+ } -+ }); -+ } -+ async unlockQRHardwareWalletAccount(index) { -+ return __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -+ const keyring = this.getQRKeyring() || await __privateMethod(this, _addQRKeyring, addQRKeyring_fn).call(this); -+ keyring.setAccountToUnlock(index); -+ await keyring.addAccounts(1); -+ }); -+ } -+ async getAccountKeyringType(account) { -+ const keyring = await this.getKeyringForAccount( -+ account -+ ); -+ return keyring.type; -+ } -+ async forgetQRDevice() { -+ return __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -+ const keyring = this.getQRKeyring(); -+ if (!keyring) { -+ return { removedAccounts: [], remainingAccounts: [] }; -+ } -+ const allAccounts = await __privateMethod(this, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn).call(this); -+ keyring.forgetDevice(); -+ const remainingAccounts = await __privateMethod(this, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn).call(this); -+ const removedAccounts = allAccounts.filter( -+ (address) => !remainingAccounts.includes(address) -+ ); -+ return { removedAccounts, remainingAccounts }; -+ }); -+ } -+}; -+_controllerOperationMutex = new WeakMap(); -+_vaultOperationMutex = new WeakMap(); -+_keyringBuilders = new WeakMap(); -+_keyrings = new WeakMap(); -+_unsupportedKeyrings = new WeakMap(); -+_password = new WeakMap(); -+_encryptor = new WeakMap(); -+_cacheEncryptionKey = new WeakMap(); -+_qrKeyringStateListener = new WeakMap(); -+_registerMessageHandlers = new WeakSet(); -+registerMessageHandlers_fn = function() { -+ this.messagingSystem.registerActionHandler( -+ `${name}:signMessage`, -+ this.signMessage.bind(this) -+ ); -+ this.messagingSystem.registerActionHandler( -+ `${name}:signPersonalMessage`, -+ this.signPersonalMessage.bind(this) -+ ); -+ this.messagingSystem.registerActionHandler( -+ `${name}:signTypedMessage`, -+ this.signTypedMessage.bind(this) -+ ); -+ this.messagingSystem.registerActionHandler( -+ `${name}:decryptMessage`, -+ this.decryptMessage.bind(this) -+ ); -+ this.messagingSystem.registerActionHandler( -+ `${name}:getEncryptionPublicKey`, -+ this.getEncryptionPublicKey.bind(this) -+ ); -+ this.messagingSystem.registerActionHandler( -+ `${name}:getAccounts`, -+ this.getAccounts.bind(this) -+ ); -+ this.messagingSystem.registerActionHandler( -+ `${name}:getKeyringsByType`, -+ this.getKeyringsByType.bind(this) -+ ); -+ this.messagingSystem.registerActionHandler( -+ `${name}:getKeyringForAccount`, -+ this.getKeyringForAccount.bind(this) -+ ); -+ this.messagingSystem.registerActionHandler( -+ `${name}:persistAllKeyrings`, -+ this.persistAllKeyrings.bind(this) -+ ); -+ this.messagingSystem.registerActionHandler( -+ `${name}:prepareUserOperation`, -+ this.prepareUserOperation.bind(this) -+ ); -+ this.messagingSystem.registerActionHandler( -+ `${name}:patchUserOperation`, -+ this.patchUserOperation.bind(this) -+ ); -+ this.messagingSystem.registerActionHandler( -+ `${name}:signUserOperation`, -+ this.signUserOperation.bind(this) -+ ); -+}; -+_getKeyringBuilderForType = new WeakSet(); -+getKeyringBuilderForType_fn = function(type) { -+ return __privateGet(this, _keyringBuilders).find( -+ (keyringBuilder) => keyringBuilder.type === type -+ ); -+}; -+_addQRKeyring = new WeakSet(); -+addQRKeyring_fn = async function() { -+ __privateMethod(this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this); -+ return await __privateMethod(this, _newKeyring, newKeyring_fn).call(this, "QR Hardware Wallet Device" /* qr */); -+}; -+_subscribeToQRKeyringEvents = new WeakSet(); -+subscribeToQRKeyringEvents_fn = function(qrKeyring) { -+ __privateSet(this, _qrKeyringStateListener, (state) => { -+ this.messagingSystem.publish(`${name}:qrKeyringStateChange`, state); -+ }); -+ qrKeyring.getMemStore().subscribe(__privateGet(this, _qrKeyringStateListener)); -+}; -+_unsubscribeFromQRKeyringsEvents = new WeakSet(); -+unsubscribeFromQRKeyringsEvents_fn = function() { -+ const qrKeyrings = this.getKeyringsByType( -+ "QR Hardware Wallet Device" /* qr */ -+ ); -+ qrKeyrings.forEach((qrKeyring) => { -+ if (__privateGet(this, _qrKeyringStateListener)) { -+ qrKeyring.getMemStore().unsubscribe(__privateGet(this, _qrKeyringStateListener)); -+ } -+ }); -+}; -+_createNewVaultWithKeyring = new WeakSet(); -+createNewVaultWithKeyring_fn = async function(password, keyring) { -+ __privateMethod(this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this); -+ if (typeof password !== "string") { -+ throw new TypeError("KeyringController - Password must be of type string." /* WrongPasswordType */); -+ } -+ this.update((state) => { -+ delete state.encryptionKey; -+ delete state.encryptionSalt; -+ }); -+ __privateSet(this, _password, password); -+ await __privateMethod(this, _clearKeyrings, clearKeyrings_fn).call(this); -+ await __privateMethod(this, _createKeyringWithFirstAccount, createKeyringWithFirstAccount_fn).call(this, keyring.type, keyring.opts); -+ __privateMethod(this, _setUnlocked, setUnlocked_fn).call(this); -+}; -+_getUpdatedKeyrings = new WeakSet(); -+getUpdatedKeyrings_fn = async function() { -+ return Promise.all(__privateGet(this, _keyrings).map(displayForKeyring)); -+}; -+_getSerializedKeyrings = new WeakSet(); -+getSerializedKeyrings_fn = async function({ includeUnsupported } = { -+ includeUnsupported: true -+}) { -+ const serializedKeyrings = await Promise.all( -+ __privateGet(this, _keyrings).map(async (keyring) => { -+ const [type, data] = await Promise.all([ -+ keyring.type, -+ keyring.serialize() -+ ]); -+ return { type, data }; -+ }) -+ ); -+ if (includeUnsupported) { -+ serializedKeyrings.push(...__privateGet(this, _unsupportedKeyrings)); -+ } -+ return serializedKeyrings; -+}; -+_restoreSerializedKeyrings = new WeakSet(); -+restoreSerializedKeyrings_fn = async function(serializedKeyrings) { -+ await __privateMethod(this, _clearKeyrings, clearKeyrings_fn).call(this); -+ for (const serializedKeyring of serializedKeyrings) { -+ await __privateMethod(this, _restoreKeyring, restoreKeyring_fn).call(this, serializedKeyring); -+ } -+}; -+_unlockKeyrings = new WeakSet(); -+unlockKeyrings_fn = async function(password, encryptionKey, encryptionSalt) { -+ return __privateMethod(this, _withVaultLock, withVaultLock_fn).call(this, async ({ releaseLock }) => { -+ const encryptedVault = this.state.vault; -+ if (!encryptedVault) { -+ throw new Error("KeyringController - Cannot unlock without a previous vault." /* VaultError */); -+ } -+ let vault; -+ const updatedState = {}; -+ if (__privateGet(this, _cacheEncryptionKey)) { -+ assertIsExportableKeyEncryptor(__privateGet(this, _encryptor)); -+ if (password) { -+ const result = await __privateGet(this, _encryptor).decryptWithDetail( -+ password, -+ encryptedVault -+ ); -+ vault = result.vault; -+ __privateSet(this, _password, password); -+ updatedState.encryptionKey = result.exportedKeyString; -+ updatedState.encryptionSalt = result.salt; -+ } else { -+ const parsedEncryptedVault = JSON.parse(encryptedVault); -+ if (encryptionSalt !== parsedEncryptedVault.salt) { -+ throw new Error("KeyringController - Encryption key and salt provided are expired" /* ExpiredCredentials */); -+ } -+ if (typeof encryptionKey !== "string") { -+ throw new TypeError("KeyringController - Password must be of type string." /* WrongPasswordType */); -+ } -+ const key = await __privateGet(this, _encryptor).importKey(encryptionKey); -+ vault = await __privateGet(this, _encryptor).decryptWithKey( -+ key, -+ parsedEncryptedVault -+ ); -+ updatedState.encryptionKey = encryptionKey; -+ updatedState.encryptionSalt = encryptionSalt; -+ } -+ } else { -+ if (typeof password !== "string") { -+ throw new TypeError("KeyringController - Password must be of type string." /* WrongPasswordType */); -+ } -+ vault = await __privateGet(this, _encryptor).decrypt(password, encryptedVault); -+ __privateSet(this, _password, password); -+ } -+ if (!isSerializedKeyringsArray(vault)) { -+ throw new Error("KeyringController - The decrypted vault has an unexpected shape." /* VaultDataError */); -+ } -+ await __privateMethod(this, _restoreSerializedKeyrings, restoreSerializedKeyrings_fn).call(this, vault); -+ const updatedKeyrings = await __privateMethod(this, _getUpdatedKeyrings, getUpdatedKeyrings_fn).call(this); -+ this.update((state) => { -+ state.keyrings = updatedKeyrings; -+ if (updatedState.encryptionKey || updatedState.encryptionSalt) { -+ state.encryptionKey = updatedState.encryptionKey; -+ state.encryptionSalt = updatedState.encryptionSalt; -+ } -+ }); -+ if (__privateGet(this, _password) && (!__privateGet(this, _cacheEncryptionKey) || !encryptionKey) && __privateGet(this, _encryptor).isVaultUpdated && !__privateGet(this, _encryptor).isVaultUpdated(encryptedVault)) { -+ releaseLock(); -+ await __privateMethod(this, _updateVault, updateVault_fn).call(this); -+ } -+ return __privateGet(this, _keyrings); -+ }); -+}; -+_updateVault = new WeakSet(); -+updateVault_fn = function() { -+ return __privateMethod(this, _withVaultLock, withVaultLock_fn).call(this, async () => { -+ const { encryptionKey, encryptionSalt } = this.state; -+ if (!__privateGet(this, _password) && !encryptionKey) { -+ throw new Error("KeyringController - Cannot persist vault without password and encryption key" /* MissingCredentials */); -+ } -+ const serializedKeyrings = await __privateMethod(this, _getSerializedKeyrings, getSerializedKeyrings_fn).call(this); -+ if (!serializedKeyrings.some((keyring) => keyring.type === "HD Key Tree" /* hd */)) { -+ throw new Error("KeyringController - No HD Keyring found" /* NoHdKeyring */); -+ } -+ const updatedState = {}; -+ if (__privateGet(this, _cacheEncryptionKey)) { -+ assertIsExportableKeyEncryptor(__privateGet(this, _encryptor)); -+ if (encryptionKey) { -+ const key = await __privateGet(this, _encryptor).importKey(encryptionKey); -+ const vaultJSON = await __privateGet(this, _encryptor).encryptWithKey( -+ key, -+ serializedKeyrings -+ ); -+ vaultJSON.salt = encryptionSalt; -+ updatedState.vault = JSON.stringify(vaultJSON); -+ } else if (__privateGet(this, _password)) { -+ const { vault: newVault, exportedKeyString } = await __privateGet(this, _encryptor).encryptWithDetail( -+ __privateGet(this, _password), -+ serializedKeyrings -+ ); -+ updatedState.vault = newVault; -+ updatedState.encryptionKey = exportedKeyString; -+ } -+ } else { -+ assertIsValidPassword(__privateGet(this, _password)); -+ updatedState.vault = await __privateGet(this, _encryptor).encrypt( -+ __privateGet(this, _password), -+ serializedKeyrings -+ ); -+ } -+ if (!updatedState.vault) { -+ throw new Error("KeyringController - Cannot persist vault without vault information" /* MissingVaultData */); -+ } -+ const updatedKeyrings = await __privateMethod(this, _getUpdatedKeyrings, getUpdatedKeyrings_fn).call(this); -+ this.update((state) => { -+ state.vault = updatedState.vault; -+ state.keyrings = updatedKeyrings; -+ if (updatedState.encryptionKey) { -+ state.encryptionKey = updatedState.encryptionKey; -+ state.encryptionSalt = JSON.parse(updatedState.vault).salt; -+ } -+ }); -+ return true; -+ }); -+}; -+_getAccountsFromKeyrings = new WeakSet(); -+getAccountsFromKeyrings_fn = async function() { -+ const keyrings = __privateGet(this, _keyrings); -+ const keyringArrays = await Promise.all( -+ keyrings.map(async (keyring) => keyring.getAccounts()) -+ ); -+ const addresses = keyringArrays.reduce((res, arr) => { -+ return res.concat(arr); -+ }, []); -+ return addresses.map(normalize); -+}; -+_createKeyringWithFirstAccount = new WeakSet(); -+createKeyringWithFirstAccount_fn = async function(type, opts) { -+ __privateMethod(this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this); -+ const keyring = await __privateMethod(this, _newKeyring, newKeyring_fn).call(this, type, opts); -+ const [firstAccount] = await keyring.getAccounts(); -+ if (!firstAccount) { -+ throw new Error("KeyringController - First Account not found." /* NoFirstAccount */); -+ } -+}; -+_newKeyring = new WeakSet(); -+newKeyring_fn = async function(type, data) { -+ __privateMethod(this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this); -+ const keyringBuilder = __privateMethod(this, _getKeyringBuilderForType, getKeyringBuilderForType_fn).call(this, type); -+ if (!keyringBuilder) { -+ throw new Error( -+ `${"KeyringController - No keyringBuilder found for keyring" /* NoKeyringBuilder */}. Keyring type: ${type}` -+ ); -+ } -+ const keyring = keyringBuilder(); -+ await keyring.deserialize(data); -+ if (keyring.init) { -+ await keyring.init(); -+ } -+ if (type === "HD Key Tree" /* hd */ && (!isObject(data) || !data.mnemonic)) { -+ if (!keyring.generateRandomMnemonic) { -+ throw new Error( -+ "KeyringController - The current keyring does not support the method generateRandomMnemonic." /* UnsupportedGenerateRandomMnemonic */ -+ ); -+ } -+ keyring.generateRandomMnemonic(); -+ await keyring.addAccounts(1); -+ } -+ await __privateMethod(this, _checkForDuplicate, checkForDuplicate_fn).call(this, type, await keyring.getAccounts()); -+ if (type === "QR Hardware Wallet Device" /* qr */) { -+ __privateMethod(this, _subscribeToQRKeyringEvents, subscribeToQRKeyringEvents_fn).call(this, keyring); -+ } -+ __privateGet(this, _keyrings).push(keyring); -+ return keyring; -+}; -+_clearKeyrings = new WeakSet(); -+clearKeyrings_fn = async function() { -+ __privateMethod(this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this); -+ for (const keyring of __privateGet(this, _keyrings)) { -+ await __privateMethod(this, _destroyKeyring, destroyKeyring_fn).call(this, keyring); -+ } -+ __privateSet(this, _keyrings, []); -+}; -+_restoreKeyring = new WeakSet(); -+restoreKeyring_fn = async function(serialized) { -+ __privateMethod(this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this); -+ try { -+ const { type, data } = serialized; -+ return await __privateMethod(this, _newKeyring, newKeyring_fn).call(this, type, data); -+ } catch (_) { -+ __privateGet(this, _unsupportedKeyrings).push(serialized); -+ return void 0; -+ } -+}; -+_destroyKeyring = new WeakSet(); -+destroyKeyring_fn = async function(keyring) { -+ await keyring.destroy?.(); -+}; -+_removeEmptyKeyrings = new WeakSet(); -+removeEmptyKeyrings_fn = async function() { -+ __privateMethod(this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this); -+ const validKeyrings = []; -+ await Promise.all( -+ __privateGet(this, _keyrings).map(async (keyring) => { -+ const accounts = await keyring.getAccounts(); -+ if (accounts.length > 0) { -+ validKeyrings.push(keyring); -+ } else { -+ await __privateMethod(this, _destroyKeyring, destroyKeyring_fn).call(this, keyring); -+ } -+ }) -+ ); -+ __privateSet(this, _keyrings, validKeyrings); -+}; -+_checkForDuplicate = new WeakSet(); -+checkForDuplicate_fn = async function(type, newAccountArray) { -+ const accounts = await __privateMethod(this, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn).call(this); -+ switch (type) { -+ case "Simple Key Pair" /* simple */: { -+ const isIncluded = Boolean( -+ accounts.find( -+ (key) => newAccountArray[0] && (key === newAccountArray[0] || key === remove0x(newAccountArray[0])) -+ ) -+ ); -+ if (isIncluded) { -+ throw new Error("KeyringController - The account you are trying to import is a duplicate" /* DuplicatedAccount */); -+ } -+ return newAccountArray; -+ } -+ default: { -+ return newAccountArray; -+ } -+ } -+}; -+_setUnlocked = new WeakSet(); -+setUnlocked_fn = function() { -+ __privateMethod(this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this); -+ this.update((state) => { -+ state.isUnlocked = true; -+ }); -+ this.messagingSystem.publish(`${name}:unlock`); -+}; -+_persistOrRollback = new WeakSet(); -+persistOrRollback_fn = async function(fn) { -+ return __privateMethod(this, _withRollback, withRollback_fn).call(this, async ({ releaseLock }) => { -+ const callbackResult = await fn({ releaseLock }); -+ await __privateMethod(this, _updateVault, updateVault_fn).call(this); -+ return callbackResult; -+ }); -+}; -+_withRollback = new WeakSet(); -+withRollback_fn = async function(fn) { -+ return __privateMethod(this, _withControllerLock, withControllerLock_fn).call(this, async ({ releaseLock }) => { -+ const currentSerializedKeyrings = await __privateMethod(this, _getSerializedKeyrings, getSerializedKeyrings_fn).call(this); -+ const currentPassword = __privateGet(this, _password); -+ try { -+ return await fn({ releaseLock }); -+ } catch (e) { -+ await __privateMethod(this, _restoreSerializedKeyrings, restoreSerializedKeyrings_fn).call(this, currentSerializedKeyrings); -+ __privateSet(this, _password, currentPassword); -+ throw e; -+ } -+ }); -+}; -+_assertControllerMutexIsLocked = new WeakSet(); -+assertControllerMutexIsLocked_fn = function() { -+ if (!__privateGet(this, _controllerOperationMutex).isLocked()) { -+ throw new Error("KeyringController - attempt to update vault during a non mutually exclusive operation" /* ControllerLockRequired */); -+ } -+}; -+_withControllerLock = new WeakSet(); -+withControllerLock_fn = async function(fn) { -+ return withLock(__privateGet(this, _controllerOperationMutex), fn); -+}; -+_withVaultLock = new WeakSet(); -+withVaultLock_fn = async function(fn) { -+ __privateMethod(this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this); -+ return withLock(__privateGet(this, _vaultOperationMutex), fn); -+}; -+async function withLock(mutex, fn) { -+ const releaseLock = await mutex.acquire(); -+ try { -+ return await fn({ releaseLock }); -+ } finally { -+ releaseLock(); -+ } -+} -+var KeyringController_default = KeyringController; -+ -+export { -+ KeyringTypes, -+ isCustodyKeyring, -+ AccountImportStrategy, -+ SignTypedDataVersion, -+ keyringBuilderFactory, -+ getDefaultKeyringState, -+ KeyringController, -+ KeyringController_default -+}; -+//# sourceMappingURL=chunk-7A7D7THR.mjs.map -\ No newline at end of file -diff --git a/dist/chunk-7A7D7THR.mjs.map b/dist/chunk-7A7D7THR.mjs.map -new file mode 100644 -index 0000000000000000000000000000000000000000..1e66368615fd8c5550c36f68586a25339b072f2f ---- /dev/null -+++ b/dist/chunk-7A7D7THR.mjs.map -@@ -0,0 +1 @@ -+{"version":3,"sources":["../src/KeyringController.ts"],"sourcesContent":["import type { TxData, TypedTransaction } from '@ethereumjs/tx';\nimport { isValidPrivate, toBuffer, getBinarySize } from '@ethereumjs/util';\nimport type {\n MetaMaskKeyring as QRKeyring,\n IKeyringState as IQRKeyringState,\n} from '@keystonehq/metamask-airgapped-keyring';\nimport type { RestrictedControllerMessenger } from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport * as encryptorUtils from '@metamask/browser-passworder';\nimport HDKeyring from '@metamask/eth-hd-keyring';\nimport { normalize as ethNormalize } from '@metamask/eth-sig-util';\nimport SimpleKeyring from '@metamask/eth-simple-keyring';\nimport type {\n EthBaseTransaction,\n EthBaseUserOperation,\n EthKeyring,\n EthUserOperation,\n EthUserOperationPatch,\n KeyringExecutionContext,\n} from '@metamask/keyring-api';\nimport type {\n PersonalMessageParams,\n TypedMessageParams,\n} from '@metamask/message-manager';\nimport type {\n Eip1024EncryptedData,\n Hex,\n Json,\n KeyringClass,\n} from '@metamask/utils';\nimport {\n add0x,\n assertIsStrictHexString,\n bytesToHex,\n hasProperty,\n isObject,\n isStrictHexString,\n isValidHexAddress,\n isValidJson,\n remove0x,\n} from '@metamask/utils';\nimport { Mutex } from 'async-mutex';\nimport type { MutexInterface } from 'async-mutex';\nimport Wallet, { thirdparty as importers } from 'ethereumjs-wallet';\nimport type { Patch } from 'immer';\n\nimport { KeyringControllerError } from './constants';\n\nconst name = 'KeyringController';\n\n/**\n * Available keyring types\n */\nexport enum KeyringTypes {\n simple = 'Simple Key Pair',\n hd = 'HD Key Tree',\n qr = 'QR Hardware Wallet Device',\n trezor = 'Trezor Hardware',\n ledger = 'Ledger Hardware',\n lattice = 'Lattice Hardware',\n snap = 'Snap Keyring',\n}\n\n/**\n * Custody keyring types are a special case, as they are not a single type\n * but they all start with the prefix \"Custody\".\n * @param keyringType - The type of the keyring.\n * @returns Whether the keyring type is a custody keyring.\n */\nexport const isCustodyKeyring = (keyringType: string): boolean => {\n return keyringType.startsWith('Custody');\n};\n\n/**\n * @type KeyringControllerState\n *\n * Keyring controller state\n * @property vault - Encrypted string representing keyring data\n * @property isUnlocked - Whether vault is unlocked\n * @property keyringTypes - Account types\n * @property keyrings - Group of accounts\n * @property encryptionKey - Keyring encryption key\n * @property encryptionSalt - Keyring encryption salt\n */\nexport type KeyringControllerState = {\n vault?: string;\n isUnlocked: boolean;\n keyrings: KeyringObject[];\n encryptionKey?: string;\n encryptionSalt?: string;\n};\n\nexport type KeyringControllerMemState = Omit<\n KeyringControllerState,\n 'vault' | 'encryptionKey' | 'encryptionSalt'\n>;\n\nexport type KeyringControllerGetStateAction = {\n type: `${typeof name}:getState`;\n handler: () => KeyringControllerState;\n};\n\nexport type KeyringControllerSignMessageAction = {\n type: `${typeof name}:signMessage`;\n handler: KeyringController['signMessage'];\n};\n\nexport type KeyringControllerSignPersonalMessageAction = {\n type: `${typeof name}:signPersonalMessage`;\n handler: KeyringController['signPersonalMessage'];\n};\n\nexport type KeyringControllerSignTypedMessageAction = {\n type: `${typeof name}:signTypedMessage`;\n handler: KeyringController['signTypedMessage'];\n};\n\nexport type KeyringControllerDecryptMessageAction = {\n type: `${typeof name}:decryptMessage`;\n handler: KeyringController['decryptMessage'];\n};\n\nexport type KeyringControllerGetEncryptionPublicKeyAction = {\n type: `${typeof name}:getEncryptionPublicKey`;\n handler: KeyringController['getEncryptionPublicKey'];\n};\n\nexport type KeyringControllerGetKeyringsByTypeAction = {\n type: `${typeof name}:getKeyringsByType`;\n handler: KeyringController['getKeyringsByType'];\n};\n\nexport type KeyringControllerGetKeyringForAccountAction = {\n type: `${typeof name}:getKeyringForAccount`;\n handler: KeyringController['getKeyringForAccount'];\n};\n\nexport type KeyringControllerGetAccountsAction = {\n type: `${typeof name}:getAccounts`;\n handler: KeyringController['getAccounts'];\n};\n\nexport type KeyringControllerPersistAllKeyringsAction = {\n type: `${typeof name}:persistAllKeyrings`;\n handler: KeyringController['persistAllKeyrings'];\n};\n\nexport type KeyringControllerPrepareUserOperationAction = {\n type: `${typeof name}:prepareUserOperation`;\n handler: KeyringController['prepareUserOperation'];\n};\n\nexport type KeyringControllerPatchUserOperationAction = {\n type: `${typeof name}:patchUserOperation`;\n handler: KeyringController['patchUserOperation'];\n};\n\nexport type KeyringControllerSignUserOperationAction = {\n type: `${typeof name}:signUserOperation`;\n handler: KeyringController['signUserOperation'];\n};\n\nexport type KeyringControllerStateChangeEvent = {\n type: `${typeof name}:stateChange`;\n payload: [KeyringControllerState, Patch[]];\n};\n\nexport type KeyringControllerAccountRemovedEvent = {\n type: `${typeof name}:accountRemoved`;\n payload: [string];\n};\n\nexport type KeyringControllerLockEvent = {\n type: `${typeof name}:lock`;\n payload: [];\n};\n\nexport type KeyringControllerUnlockEvent = {\n type: `${typeof name}:unlock`;\n payload: [];\n};\n\nexport type KeyringControllerQRKeyringStateChangeEvent = {\n type: `${typeof name}:qrKeyringStateChange`;\n payload: [ReturnType];\n};\n\nexport type KeyringControllerActions =\n | KeyringControllerGetStateAction\n | KeyringControllerSignMessageAction\n | KeyringControllerSignPersonalMessageAction\n | KeyringControllerSignTypedMessageAction\n | KeyringControllerDecryptMessageAction\n | KeyringControllerGetEncryptionPublicKeyAction\n | KeyringControllerGetAccountsAction\n | KeyringControllerGetKeyringsByTypeAction\n | KeyringControllerGetKeyringForAccountAction\n | KeyringControllerPersistAllKeyringsAction\n | KeyringControllerPrepareUserOperationAction\n | KeyringControllerPatchUserOperationAction\n | KeyringControllerSignUserOperationAction;\n\nexport type KeyringControllerEvents =\n | KeyringControllerStateChangeEvent\n | KeyringControllerLockEvent\n | KeyringControllerUnlockEvent\n | KeyringControllerAccountRemovedEvent\n | KeyringControllerQRKeyringStateChangeEvent;\n\nexport type KeyringControllerMessenger = RestrictedControllerMessenger<\n typeof name,\n KeyringControllerActions,\n KeyringControllerEvents,\n never,\n never\n>;\n\nexport type KeyringControllerOptions = {\n keyringBuilders?: { (): EthKeyring; type: string }[];\n messenger: KeyringControllerMessenger;\n state?: { vault?: string };\n} & (\n | {\n cacheEncryptionKey: true;\n encryptor?: ExportableKeyEncryptor;\n }\n | {\n cacheEncryptionKey?: false;\n encryptor?: GenericEncryptor | ExportableKeyEncryptor;\n }\n);\n\n/**\n * @type KeyringObject\n *\n * Keyring object to return in fullUpdate\n * @property type - Keyring type\n * @property accounts - Associated accounts\n */\nexport type KeyringObject = {\n accounts: string[];\n type: string;\n};\n\n/**\n * A strategy for importing an account\n */\nexport enum AccountImportStrategy {\n privateKey = 'privateKey',\n json = 'json',\n}\n\n/**\n * The `signTypedMessage` version\n *\n * @see https://docs.metamask.io/guide/signing-data.html\n */\nexport enum SignTypedDataVersion {\n V1 = 'V1',\n V3 = 'V3',\n V4 = 'V4',\n}\n\n/**\n * A serialized keyring object.\n */\nexport type SerializedKeyring = {\n type: string;\n data: Json;\n};\n\n/**\n * A generic encryptor interface that supports encrypting and decrypting\n * serializable data with a password.\n */\nexport type GenericEncryptor = {\n /**\n * Encrypts the given object with the given password.\n *\n * @param password - The password to encrypt with.\n * @param object - The object to encrypt.\n * @returns The encrypted string.\n */\n encrypt: (password: string, object: Json) => Promise;\n /**\n * Decrypts the given encrypted string with the given password.\n *\n * @param password - The password to decrypt with.\n * @param encryptedString - The encrypted string to decrypt.\n * @returns The decrypted object.\n */\n decrypt: (password: string, encryptedString: string) => Promise;\n /**\n * Optional vault migration helper. Checks if the provided vault is up to date\n * with the desired encryption algorithm.\n *\n * @param vault - The encrypted string to check.\n * @param targetDerivationParams - The desired target derivation params.\n * @returns The updated encrypted string.\n */\n isVaultUpdated?: (\n vault: string,\n targetDerivationParams?: encryptorUtils.KeyDerivationOptions,\n ) => boolean;\n};\n\n/**\n * An encryptor interface that supports encrypting and decrypting\n * serializable data with a password, and exporting and importing keys.\n */\nexport type ExportableKeyEncryptor = GenericEncryptor & {\n /**\n * Encrypts the given object with the given encryption key.\n *\n * @param key - The encryption key to encrypt with.\n * @param object - The object to encrypt.\n * @returns The encryption result.\n */\n encryptWithKey: (\n key: unknown,\n object: Json,\n ) => Promise;\n /**\n * Encrypts the given object with the given password, and returns the\n * encryption result and the exported key string.\n *\n * @param password - The password to encrypt with.\n * @param object - The object to encrypt.\n * @param salt - The optional salt to use for encryption.\n * @returns The encrypted string and the exported key string.\n */\n encryptWithDetail: (\n password: string,\n object: Json,\n salt?: string,\n ) => Promise;\n /**\n * Decrypts the given encrypted string with the given encryption key.\n *\n * @param key - The encryption key to decrypt with.\n * @param encryptedString - The encrypted string to decrypt.\n * @returns The decrypted object.\n */\n decryptWithKey: (key: unknown, encryptedString: string) => Promise;\n /**\n * Decrypts the given encrypted string with the given password, and returns\n * the decrypted object and the salt and exported key string used for\n * encryption.\n *\n * @param password - The password to decrypt with.\n * @param encryptedString - The encrypted string to decrypt.\n * @returns The decrypted object and the salt and exported key string used for\n * encryption.\n */\n decryptWithDetail: (\n password: string,\n encryptedString: string,\n ) => Promise;\n /**\n * Generates an encryption key from exported key string.\n *\n * @param key - The exported key string.\n * @returns The encryption key.\n */\n importKey: (key: string) => Promise;\n};\n\nexport type KeyringSelector =\n | {\n type: string;\n index?: number;\n }\n | {\n address: Hex;\n };\n\n/**\n * A function executed within a mutually exclusive lock, with\n * a mutex releaser in its option bag.\n *\n * @param releaseLock - A function to release the lock.\n */\ntype MutuallyExclusiveCallback = ({\n releaseLock,\n}: {\n releaseLock: MutexInterface.Releaser;\n}) => Promise;\n\n/**\n * Get builder function for `Keyring`\n *\n * Returns a builder function for `Keyring` with a `type` property.\n *\n * @param KeyringConstructor - The Keyring class for the builder.\n * @returns A builder function for the given Keyring.\n */\nexport function keyringBuilderFactory(KeyringConstructor: KeyringClass) {\n const builder = () => new KeyringConstructor();\n\n builder.type = KeyringConstructor.type;\n\n return builder;\n}\n\nconst defaultKeyringBuilders = [\n keyringBuilderFactory(SimpleKeyring),\n keyringBuilderFactory(HDKeyring),\n];\n\nexport const getDefaultKeyringState = (): KeyringControllerState => {\n return {\n isUnlocked: false,\n keyrings: [],\n };\n};\n\n/**\n * Assert that the given keyring has an exportable\n * mnemonic.\n *\n * @param keyring - The keyring to check\n * @throws When the keyring does not have a mnemonic\n */\nfunction assertHasUint8ArrayMnemonic(\n keyring: EthKeyring,\n): asserts keyring is EthKeyring & { mnemonic: Uint8Array } {\n if (\n !(\n hasProperty(keyring, 'mnemonic') && keyring.mnemonic instanceof Uint8Array\n )\n ) {\n throw new Error(\"Can't get mnemonic bytes from keyring\");\n }\n}\n\n/**\n * Assert that the provided encryptor supports\n * encryption and encryption key export.\n *\n * @param encryptor - The encryptor to check.\n * @throws If the encryptor does not support key encryption.\n */\nfunction assertIsExportableKeyEncryptor(\n encryptor: GenericEncryptor | ExportableKeyEncryptor,\n): asserts encryptor is ExportableKeyEncryptor {\n if (\n !(\n 'importKey' in encryptor &&\n typeof encryptor.importKey === 'function' &&\n 'decryptWithKey' in encryptor &&\n typeof encryptor.decryptWithKey === 'function' &&\n 'encryptWithKey' in encryptor &&\n typeof encryptor.encryptWithKey === 'function'\n )\n ) {\n throw new Error(KeyringControllerError.UnsupportedEncryptionKeyExport);\n }\n}\n\n/**\n * Assert that the provided password is a valid non-empty string.\n *\n * @param password - The password to check.\n * @throws If the password is not a valid string.\n */\nfunction assertIsValidPassword(password: unknown): asserts password is string {\n if (typeof password !== 'string') {\n throw new Error(KeyringControllerError.WrongPasswordType);\n }\n\n if (!password || !password.length) {\n throw new Error(KeyringControllerError.InvalidEmptyPassword);\n }\n}\n\n/**\n * Checks if the provided value is a serialized keyrings array.\n *\n * @param array - The value to check.\n * @returns True if the value is a serialized keyrings array.\n */\nfunction isSerializedKeyringsArray(\n array: unknown,\n): array is SerializedKeyring[] {\n return (\n typeof array === 'object' &&\n Array.isArray(array) &&\n array.every((value) => value.type && isValidJson(value.data))\n );\n}\n\n/**\n * Display For Keyring\n *\n * Is used for adding the current keyrings to the state object.\n *\n * @param keyring - The keyring to display.\n * @returns A keyring display object, with type and accounts properties.\n */\nasync function displayForKeyring(\n keyring: EthKeyring,\n): Promise<{ type: string; accounts: string[] }> {\n const accounts = await keyring.getAccounts();\n\n return {\n type: keyring.type,\n // Cast to `string[]` here is safe here because `accounts` has no nullish\n // values, and `normalize` returns `string` unless given a nullish value\n accounts: accounts.map(normalize) as string[],\n };\n}\n\n/**\n * Check if address is an ethereum address\n *\n * @param address - An address.\n * @returns Returns true if the address is an ethereum one, false otherwise.\n */\nfunction isEthAddress(address: string): boolean {\n // We first check if it's a matching `Hex` string, so that is narrows down\n // `address` as an `Hex` type, allowing us to use `isValidHexAddress`\n return (\n // NOTE: This function only checks for lowercased strings\n isStrictHexString(address.toLowerCase()) &&\n // This checks for lowercased addresses and checksum addresses too\n isValidHexAddress(address as Hex)\n );\n}\n\n/**\n * Normalize ethereum or non-EVM address.\n *\n * @param address - Ethereum or non-EVM address.\n * @returns The normalized address.\n */\nfunction normalize(address: string): string | undefined {\n // Since the `KeyringController` is only dealing with address, we have\n // no other way to get the associated account type with this address. So we\n // are down to check the actual address format for now\n // TODO: Find a better way to not have those runtime checks based on the\n // address value!\n return isEthAddress(address) ? ethNormalize(address) : address;\n}\n\n/**\n * Controller responsible for establishing and managing user identity.\n *\n * This class is a wrapper around the `eth-keyring-controller` package. The\n * `eth-keyring-controller` manages the \"vault\", which is an encrypted store of private keys, and\n * it manages the wallet \"lock\" state. This wrapper class has convenience methods for interacting\n * with the internal keyring controller and handling certain complex operations that involve the\n * keyrings.\n */\nexport class KeyringController extends BaseController<\n typeof name,\n KeyringControllerState,\n KeyringControllerMessenger\n> {\n readonly #controllerOperationMutex = new Mutex();\n\n readonly #vaultOperationMutex = new Mutex();\n\n #keyringBuilders: { (): EthKeyring; type: string }[];\n\n #keyrings: EthKeyring[];\n\n #unsupportedKeyrings: SerializedKeyring[];\n\n #password?: string;\n\n #encryptor: GenericEncryptor | ExportableKeyEncryptor;\n\n #cacheEncryptionKey: boolean;\n\n #qrKeyringStateListener?: (\n state: ReturnType,\n ) => void;\n\n /**\n * Creates a KeyringController instance.\n *\n * @param options - Initial options used to configure this controller\n * @param options.encryptor - An optional object for defining encryption schemes.\n * @param options.keyringBuilders - Set a new name for account.\n * @param options.cacheEncryptionKey - Whether to cache or not encryption key.\n * @param options.messenger - A restricted controller messenger.\n * @param options.state - Initial state to set on this controller.\n */\n constructor(options: KeyringControllerOptions) {\n const {\n encryptor = encryptorUtils,\n keyringBuilders,\n messenger,\n state,\n } = options;\n\n super({\n name,\n metadata: {\n vault: { persist: true, anonymous: false },\n isUnlocked: { persist: false, anonymous: true },\n keyrings: { persist: false, anonymous: false },\n encryptionKey: { persist: false, anonymous: false },\n encryptionSalt: { persist: false, anonymous: false },\n },\n messenger,\n state: {\n ...getDefaultKeyringState(),\n ...state,\n },\n });\n\n this.#keyringBuilders = keyringBuilders\n ? defaultKeyringBuilders.concat(keyringBuilders)\n : defaultKeyringBuilders;\n\n this.#encryptor = encryptor;\n this.#keyrings = [];\n this.#unsupportedKeyrings = [];\n\n // This option allows the controller to cache an exported key\n // for use in decrypting and encrypting data without password\n this.#cacheEncryptionKey = Boolean(options.cacheEncryptionKey);\n if (this.#cacheEncryptionKey) {\n assertIsExportableKeyEncryptor(encryptor);\n }\n\n this.#registerMessageHandlers();\n }\n\n /**\n * Adds a new account to the default (first) HD seed phrase keyring.\n *\n * @param accountCount - Number of accounts before adding a new one, used to\n * make the method idempotent.\n * @returns Promise resolving to the added account address.\n */\n async addNewAccount(accountCount?: number): Promise {\n return this.#persistOrRollback(async () => {\n const primaryKeyring = this.getKeyringsByType('HD Key Tree')[0] as\n | EthKeyring\n | undefined;\n if (!primaryKeyring) {\n throw new Error('No HD keyring found');\n }\n const oldAccounts = await primaryKeyring.getAccounts();\n\n if (accountCount && oldAccounts.length !== accountCount) {\n if (accountCount > oldAccounts.length) {\n throw new Error('Account out of sequence');\n }\n // we return the account already existing at index `accountCount`\n const existingAccount = oldAccounts[accountCount];\n\n if (!existingAccount) {\n throw new Error(`Can't find account at index ${accountCount}`);\n }\n\n return existingAccount;\n }\n\n const [addedAccountAddress] = await primaryKeyring.addAccounts(1);\n await this.verifySeedPhrase();\n\n return addedAccountAddress;\n });\n }\n\n /**\n * Adds a new account to the specified keyring.\n *\n * @param keyring - Keyring to add the account to.\n * @param accountCount - Number of accounts before adding a new one, used to make the method idempotent.\n * @returns Promise resolving to the added account address\n */\n async addNewAccountForKeyring(\n keyring: EthKeyring,\n accountCount?: number,\n ): Promise {\n // READ THIS CAREFULLY:\n // We still uses `Hex` here, since we are not using this method when creating\n // and account using a \"Snap Keyring\". This function assume the `keyring` is\n // ethereum compatible, but \"Snap Keyring\" might not be.\n return this.#persistOrRollback(async () => {\n const oldAccounts = await this.#getAccountsFromKeyrings();\n\n if (accountCount && oldAccounts.length !== accountCount) {\n if (accountCount > oldAccounts.length) {\n throw new Error('Account out of sequence');\n }\n\n const existingAccount = oldAccounts[accountCount];\n assertIsStrictHexString(existingAccount);\n\n return existingAccount;\n }\n\n await keyring.addAccounts(1);\n\n const addedAccountAddress = (await this.#getAccountsFromKeyrings()).find(\n (selectedAddress) => !oldAccounts.includes(selectedAddress),\n );\n assertIsStrictHexString(addedAccountAddress);\n\n return addedAccountAddress;\n });\n }\n\n /**\n * Adds a new account to the default (first) HD seed phrase keyring without updating identities in preferences.\n *\n * @returns Promise resolving to the added account address.\n */\n async addNewAccountWithoutUpdate(): Promise {\n return this.#persistOrRollback(async () => {\n const primaryKeyring = this.getKeyringsByType('HD Key Tree')[0] as\n | EthKeyring\n | undefined;\n if (!primaryKeyring) {\n throw new Error('No HD keyring found');\n }\n const [addedAccountAddress] = await primaryKeyring.addAccounts(1);\n await this.verifySeedPhrase();\n return addedAccountAddress;\n });\n }\n\n /**\n * Effectively the same as creating a new keychain then populating it\n * using the given seed phrase.\n *\n * @param password - Password to unlock keychain.\n * @param seed - A BIP39-compliant seed phrase as Uint8Array,\n * either as a string or an array of UTF-8 bytes that represent the string.\n * @returns Promise resolving when the operation ends successfully.\n */\n async createNewVaultAndRestore(\n password: string,\n seed: Uint8Array,\n ): Promise {\n return this.#persistOrRollback(async () => {\n assertIsValidPassword(password);\n\n await this.#createNewVaultWithKeyring(password, {\n type: KeyringTypes.hd,\n opts: {\n mnemonic: seed,\n numberOfAccounts: 1,\n },\n });\n });\n }\n\n /**\n * Create a new primary keychain and wipe any previous keychains.\n *\n * @param password - Password to unlock the new vault.\n * @returns Promise resolving when the operation ends successfully.\n */\n async createNewVaultAndKeychain(password: string) {\n return this.#persistOrRollback(async () => {\n const accounts = await this.#getAccountsFromKeyrings();\n if (!accounts.length) {\n await this.#createNewVaultWithKeyring(password, {\n type: KeyringTypes.hd,\n });\n }\n });\n }\n\n /**\n * Adds a new keyring of the given `type`.\n *\n * @param type - Keyring type name.\n * @param opts - Keyring options.\n * @throws If a builder for the given `type` does not exist.\n * @returns Promise resolving to the added keyring.\n */\n async addNewKeyring(\n type: KeyringTypes | string,\n opts?: unknown,\n ): Promise {\n if (type === KeyringTypes.qr) {\n return this.getOrAddQRKeyring();\n }\n\n return this.#persistOrRollback(async () => this.#newKeyring(type, opts));\n }\n\n /**\n * Method to verify a given password validity. Throws an\n * error if the password is invalid.\n *\n * @param password - Password of the keyring.\n */\n async verifyPassword(password: string) {\n if (!this.state.vault) {\n throw new Error(KeyringControllerError.VaultError);\n }\n await this.#encryptor.decrypt(password, this.state.vault);\n }\n\n /**\n * Returns the status of the vault.\n *\n * @returns Boolean returning true if the vault is unlocked.\n */\n isUnlocked(): boolean {\n return this.state.isUnlocked;\n }\n\n /**\n * Gets the seed phrase of the HD keyring.\n *\n * @param password - Password of the keyring.\n * @returns Promise resolving to the seed phrase.\n */\n async exportSeedPhrase(password: string): Promise {\n await this.verifyPassword(password);\n assertHasUint8ArrayMnemonic(this.#keyrings[0]);\n return this.#keyrings[0].mnemonic;\n }\n\n /**\n * Gets the private key from the keyring controlling an address.\n *\n * @param password - Password of the keyring.\n * @param address - Address to export.\n * @returns Promise resolving to the private key for an address.\n */\n async exportAccount(password: string, address: string): Promise {\n await this.verifyPassword(password);\n\n const keyring = (await this.getKeyringForAccount(\n address,\n )) as EthKeyring;\n if (!keyring.exportAccount) {\n throw new Error(KeyringControllerError.UnsupportedExportAccount);\n }\n\n return await keyring.exportAccount(normalize(address) as Hex);\n }\n\n /**\n * Returns the public addresses of all accounts from every keyring.\n *\n * @returns A promise resolving to an array of addresses.\n */\n async getAccounts(): Promise {\n return this.state.keyrings.reduce(\n (accounts, keyring) => accounts.concat(keyring.accounts),\n [],\n );\n }\n\n /**\n * Get encryption public key.\n *\n * @param account - An account address.\n * @param opts - Additional encryption options.\n * @throws If the `account` does not exist or does not support the `getEncryptionPublicKey` method\n * @returns Promise resolving to encyption public key of the `account` if one exists.\n */\n async getEncryptionPublicKey(\n account: string,\n opts?: Record,\n ): Promise {\n const address = ethNormalize(account) as Hex;\n const keyring = (await this.getKeyringForAccount(\n account,\n )) as EthKeyring;\n if (!keyring.getEncryptionPublicKey) {\n throw new Error(KeyringControllerError.UnsupportedGetEncryptionPublicKey);\n }\n\n return await keyring.getEncryptionPublicKey(address, opts);\n }\n\n /**\n * Attempts to decrypt the provided message parameters.\n *\n * @param messageParams - The decryption message parameters.\n * @param messageParams.from - The address of the account you want to use to decrypt the message.\n * @param messageParams.data - The encrypted data that you want to decrypt.\n * @returns The raw decryption result.\n */\n async decryptMessage(messageParams: {\n from: string;\n data: Eip1024EncryptedData;\n }): Promise {\n const address = ethNormalize(messageParams.from) as Hex;\n const keyring = (await this.getKeyringForAccount(\n address,\n )) as EthKeyring;\n if (!keyring.decryptMessage) {\n throw new Error(KeyringControllerError.UnsupportedDecryptMessage);\n }\n\n return keyring.decryptMessage(address, messageParams.data);\n }\n\n /**\n * Returns the currently initialized keyring that manages\n * the specified `address` if one exists.\n *\n * @deprecated Use of this method is discouraged as actions executed directly on\n * keyrings are not being reflected in the KeyringController state and not\n * persisted in the vault. Use `withKeyring` instead.\n * @param account - An account address.\n * @returns Promise resolving to keyring of the `account` if one exists.\n */\n async getKeyringForAccount(account: string): Promise {\n const address = normalize(account);\n\n const candidates = await Promise.all(\n this.#keyrings.map(async (keyring) => {\n return Promise.all([keyring, keyring.getAccounts()]);\n }),\n );\n\n const winners = candidates.filter((candidate) => {\n const accounts = candidate[1].map(normalize);\n return accounts.includes(address);\n });\n\n if (winners.length && winners[0]?.length) {\n return winners[0][0];\n }\n\n // Adding more info to the error\n let errorInfo = '';\n if (!candidates.length) {\n errorInfo = 'There are no keyrings';\n } else if (!winners.length) {\n errorInfo = 'There are keyrings, but none match the address';\n }\n throw new Error(\n `${KeyringControllerError.NoKeyring}. Error info: ${errorInfo}`,\n );\n }\n\n /**\n * Returns all keyrings of the given type.\n *\n * @deprecated Use of this method is discouraged as actions executed directly on\n * keyrings are not being reflected in the KeyringController state and not\n * persisted in the vault. Use `withKeyring` instead.\n * @param type - Keyring type name.\n * @returns An array of keyrings of the given type.\n */\n getKeyringsByType(type: KeyringTypes | string): unknown[] {\n return this.#keyrings.filter((keyring) => keyring.type === type);\n }\n\n /**\n * Persist all serialized keyrings in the vault.\n *\n * @deprecated This method is being phased out in favor of `withKeyring`.\n * @returns Promise resolving with `true` value when the\n * operation completes.\n */\n async persistAllKeyrings(): Promise {\n return this.#persistOrRollback(async () => true);\n }\n\n /**\n * Imports an account with the specified import strategy.\n *\n * @param strategy - Import strategy name.\n * @param args - Array of arguments to pass to the underlying stategy.\n * @throws Will throw when passed an unrecognized strategy.\n * @returns Promise resolving to the imported account address.\n */\n async importAccountWithStrategy(\n strategy: AccountImportStrategy,\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n args: any[],\n ): Promise {\n return this.#persistOrRollback(async () => {\n let privateKey;\n switch (strategy) {\n case 'privateKey':\n const [importedKey] = args;\n if (!importedKey) {\n throw new Error('Cannot import an empty key.');\n }\n const prefixed = add0x(importedKey);\n\n let bufferedPrivateKey;\n try {\n bufferedPrivateKey = toBuffer(prefixed);\n } catch {\n throw new Error('Cannot import invalid private key.');\n }\n\n if (\n !isValidPrivate(bufferedPrivateKey) ||\n // ensures that the key is 64 bytes long\n getBinarySize(prefixed) !== 64 + '0x'.length\n ) {\n throw new Error('Cannot import invalid private key.');\n }\n\n privateKey = remove0x(prefixed);\n break;\n case 'json':\n let wallet;\n const [input, password] = args;\n try {\n wallet = importers.fromEtherWallet(input, password);\n } catch (e) {\n wallet = wallet || (await Wallet.fromV3(input, password, true));\n }\n privateKey = bytesToHex(wallet.getPrivateKey());\n break;\n default:\n throw new Error(`Unexpected import strategy: '${strategy}'`);\n }\n const newKeyring = (await this.#newKeyring(KeyringTypes.simple, [\n privateKey,\n ])) as EthKeyring;\n const accounts = await newKeyring.getAccounts();\n return accounts[0];\n });\n }\n\n /**\n * Removes an account from keyring state.\n *\n * @param address - Address of the account to remove.\n * @fires KeyringController:accountRemoved\n * @returns Promise resolving when the account is removed.\n */\n async removeAccount(address: string): Promise {\n await this.#persistOrRollback(async () => {\n const keyring = (await this.getKeyringForAccount(\n address,\n )) as EthKeyring;\n\n // Not all the keyrings support this, so we have to check\n if (!keyring.removeAccount) {\n throw new Error(KeyringControllerError.UnsupportedRemoveAccount);\n }\n\n // The `removeAccount` method of snaps keyring is async. We have to update\n // the interface of the other keyrings to be async as well.\n // eslint-disable-next-line @typescript-eslint/await-thenable\n // FIXME: We do cast to `Hex` to makes the type checker happy here, and\n // because `Keyring.removeAccount` requires address to be `Hex`. Those\n // type would need to be updated for a full non-EVM support.\n await keyring.removeAccount(address as Hex);\n\n const accounts = await keyring.getAccounts();\n // Check if this was the last/only account\n if (accounts.length === 0) {\n await this.#removeEmptyKeyrings();\n }\n });\n\n this.messagingSystem.publish(`${name}:accountRemoved`, address);\n }\n\n /**\n * Deallocates all secrets and locks the wallet.\n *\n * @returns Promise resolving when the operation completes.\n */\n async setLocked(): Promise {\n return this.#withRollback(async () => {\n this.#unsubscribeFromQRKeyringsEvents();\n\n this.#password = undefined;\n await this.#clearKeyrings();\n\n this.update((state) => {\n state.isUnlocked = false;\n state.keyrings = [];\n delete state.encryptionKey;\n delete state.encryptionSalt;\n });\n\n this.messagingSystem.publish(`${name}:lock`);\n });\n }\n\n /**\n * Signs message by calling down into a specific keyring.\n *\n * @param messageParams - PersonalMessageParams object to sign.\n * @returns Promise resolving to a signed message string.\n */\n async signMessage(messageParams: PersonalMessageParams): Promise {\n if (!messageParams.data) {\n throw new Error(\"Can't sign an empty message\");\n }\n\n const address = ethNormalize(messageParams.from) as Hex;\n const keyring = (await this.getKeyringForAccount(\n address,\n )) as EthKeyring;\n if (!keyring.signMessage) {\n throw new Error(KeyringControllerError.UnsupportedSignMessage);\n }\n\n return await keyring.signMessage(address, messageParams.data);\n }\n\n /**\n * Signs personal message by calling down into a specific keyring.\n *\n * @param messageParams - PersonalMessageParams object to sign.\n * @returns Promise resolving to a signed message string.\n */\n async signPersonalMessage(messageParams: PersonalMessageParams) {\n const address = ethNormalize(messageParams.from) as Hex;\n const keyring = (await this.getKeyringForAccount(\n address,\n )) as EthKeyring;\n if (!keyring.signPersonalMessage) {\n throw new Error(KeyringControllerError.UnsupportedSignPersonalMessage);\n }\n\n const normalizedData = normalize(messageParams.data) as Hex;\n\n return await keyring.signPersonalMessage(address, normalizedData);\n }\n\n /**\n * Signs typed message by calling down into a specific keyring.\n *\n * @param messageParams - TypedMessageParams object to sign.\n * @param version - Compatibility version EIP712.\n * @throws Will throw when passed an unrecognized version.\n * @returns Promise resolving to a signed message string or an error if any.\n */\n async signTypedMessage(\n messageParams: TypedMessageParams,\n version: SignTypedDataVersion,\n ): Promise {\n try {\n if (\n ![\n SignTypedDataVersion.V1,\n SignTypedDataVersion.V3,\n SignTypedDataVersion.V4,\n ].includes(version)\n ) {\n throw new Error(`Unexpected signTypedMessage version: '${version}'`);\n }\n\n // Cast to `Hex` here is safe here because `messageParams.from` is not nullish.\n // `normalize` returns `Hex` unless given a nullish value.\n const address = ethNormalize(messageParams.from) as Hex;\n const keyring = (await this.getKeyringForAccount(\n address,\n )) as EthKeyring;\n if (!keyring.signTypedData) {\n throw new Error(KeyringControllerError.UnsupportedSignTypedMessage);\n }\n\n return await keyring.signTypedData(\n address,\n version !== SignTypedDataVersion.V1 &&\n typeof messageParams.data === 'string'\n ? JSON.parse(messageParams.data)\n : messageParams.data,\n { version },\n );\n } catch (error) {\n throw new Error(`Keyring Controller signTypedMessage: ${error}`);\n }\n }\n\n /**\n * Signs a transaction by calling down into a specific keyring.\n *\n * @param transaction - Transaction object to sign. Must be a `ethereumjs-tx` transaction instance.\n * @param from - Address to sign from, should be in keychain.\n * @param opts - An optional options object.\n * @returns Promise resolving to a signed transaction string.\n */\n async signTransaction(\n transaction: TypedTransaction,\n from: string,\n opts?: Record,\n ): Promise {\n const address = ethNormalize(from) as Hex;\n const keyring = (await this.getKeyringForAccount(\n address,\n )) as EthKeyring;\n if (!keyring.signTransaction) {\n throw new Error(KeyringControllerError.UnsupportedSignTransaction);\n }\n\n return await keyring.signTransaction(address, transaction, opts);\n }\n\n /**\n * Convert a base transaction to a base UserOperation.\n *\n * @param from - Address of the sender.\n * @param transactions - Base transactions to include in the UserOperation.\n * @param executionContext - The execution context to use for the UserOperation.\n * @returns A pseudo-UserOperation that can be used to construct a real.\n */\n async prepareUserOperation(\n from: string,\n transactions: EthBaseTransaction[],\n executionContext: KeyringExecutionContext,\n ): Promise {\n const address = ethNormalize(from) as Hex;\n const keyring = (await this.getKeyringForAccount(\n address,\n )) as EthKeyring;\n\n if (!keyring.prepareUserOperation) {\n throw new Error(KeyringControllerError.UnsupportedPrepareUserOperation);\n }\n\n return await keyring.prepareUserOperation(\n address,\n transactions,\n executionContext,\n );\n }\n\n /**\n * Patches properties of a UserOperation. Currently, only the\n * `paymasterAndData` can be patched.\n *\n * @param from - Address of the sender.\n * @param userOp - UserOperation to patch.\n * @param executionContext - The execution context to use for the UserOperation.\n * @returns A patch to apply to the UserOperation.\n */\n async patchUserOperation(\n from: string,\n userOp: EthUserOperation,\n executionContext: KeyringExecutionContext,\n ): Promise {\n const address = ethNormalize(from) as Hex;\n const keyring = (await this.getKeyringForAccount(\n address,\n )) as EthKeyring;\n\n if (!keyring.patchUserOperation) {\n throw new Error(KeyringControllerError.UnsupportedPatchUserOperation);\n }\n\n return await keyring.patchUserOperation(address, userOp, executionContext);\n }\n\n /**\n * Signs an UserOperation.\n *\n * @param from - Address of the sender.\n * @param userOp - UserOperation to sign.\n * @param executionContext - The execution context to use for the UserOperation.\n * @returns The signature of the UserOperation.\n */\n async signUserOperation(\n from: string,\n userOp: EthUserOperation,\n executionContext: KeyringExecutionContext,\n ): Promise {\n const address = ethNormalize(from) as Hex;\n const keyring = (await this.getKeyringForAccount(\n address,\n )) as EthKeyring;\n\n if (!keyring.signUserOperation) {\n throw new Error(KeyringControllerError.UnsupportedSignUserOperation);\n }\n\n return await keyring.signUserOperation(address, userOp, executionContext);\n }\n\n /**\n * Changes the password used to encrypt the vault.\n *\n * @param password - The new password.\n * @returns Promise resolving when the operation completes.\n */\n changePassword(password: string): Promise {\n return this.#persistOrRollback(async () => {\n if (!this.state.isUnlocked) {\n throw new Error(KeyringControllerError.MissingCredentials);\n }\n\n assertIsValidPassword(password);\n\n this.#password = password;\n // We need to clear encryption key and salt from state\n // to force the controller to re-encrypt the vault using\n // the new password.\n if (this.#cacheEncryptionKey) {\n this.update((state) => {\n delete state.encryptionKey;\n delete state.encryptionSalt;\n });\n }\n });\n }\n\n /**\n * Attempts to decrypt the current vault and load its keyrings,\n * using the given encryption key and salt.\n *\n * @param encryptionKey - Key to unlock the keychain.\n * @param encryptionSalt - Salt to unlock the keychain.\n * @returns Promise resolving when the operation completes.\n */\n async submitEncryptionKey(\n encryptionKey: string,\n encryptionSalt: string,\n ): Promise {\n return this.#withRollback(async () => {\n this.#keyrings = await this.#unlockKeyrings(\n undefined,\n encryptionKey,\n encryptionSalt,\n );\n this.#setUnlocked();\n });\n }\n\n /**\n * Attempts to decrypt the current vault and load its keyrings,\n * using the given password.\n *\n * @param password - Password to unlock the keychain.\n * @returns Promise resolving when the operation completes.\n */\n async submitPassword(password: string): Promise {\n return this.#withRollback(async () => {\n this.#keyrings = await this.#unlockKeyrings(password);\n this.#setUnlocked();\n });\n }\n\n /**\n * Verifies the that the seed phrase restores the current keychain's accounts.\n *\n * @returns Promise resolving to the seed phrase as Uint8Array.\n */\n async verifySeedPhrase(): Promise {\n const primaryKeyring = this.getKeyringsByType(KeyringTypes.hd)[0] as\n | EthKeyring\n | undefined;\n if (!primaryKeyring) {\n throw new Error('No HD keyring found.');\n }\n\n assertHasUint8ArrayMnemonic(primaryKeyring);\n\n const seedWords = primaryKeyring.mnemonic;\n const accounts = await primaryKeyring.getAccounts();\n /* istanbul ignore if */\n if (accounts.length === 0) {\n throw new Error('Cannot verify an empty keyring.');\n }\n\n // The HD Keyring Builder is a default keyring builder\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const hdKeyringBuilder = this.#getKeyringBuilderForType(KeyringTypes.hd)!;\n\n const hdKeyring = hdKeyringBuilder();\n // @ts-expect-error @metamask/eth-hd-keyring correctly handles\n // Uint8Array seed phrases in the `deserialize` method.\n await hdKeyring.deserialize({\n mnemonic: seedWords,\n numberOfAccounts: accounts.length,\n });\n const testAccounts = await hdKeyring.getAccounts();\n /* istanbul ignore if */\n if (testAccounts.length !== accounts.length) {\n throw new Error('Seed phrase imported incorrect number of accounts.');\n }\n\n testAccounts.forEach((account: string, i: number) => {\n /* istanbul ignore if */\n if (account.toLowerCase() !== accounts[i].toLowerCase()) {\n throw new Error('Seed phrase imported different accounts.');\n }\n });\n\n return seedWords;\n }\n\n /**\n * Select a keyring and execute the given operation with\n * the selected keyring, as a mutually exclusive atomic\n * operation.\n *\n * The method automatically persists changes at the end of the\n * function execution, or rolls back the changes if an error\n * is thrown.\n *\n * @param selector - Keyring selector object.\n * @param operation - Function to execute with the selected keyring.\n * @param options - Additional options.\n * @param options.createIfMissing - Whether to create a new keyring if the selected one is missing.\n * @param options.createWithData - Optional data to use when creating a new keyring.\n * @returns Promise resolving to the result of the function execution.\n * @template SelectedKeyring - The type of the selected keyring.\n * @template CallbackResult - The type of the value resolved by the callback function.\n * @deprecated This method overload is deprecated. Use `withKeyring` without options instead.\n */\n async withKeyring<\n SelectedKeyring extends EthKeyring = EthKeyring,\n CallbackResult = void,\n >(\n selector: KeyringSelector,\n operation: (keyring: SelectedKeyring) => Promise,\n // eslint-disable-next-line @typescript-eslint/unified-signatures\n options:\n | { createIfMissing?: false }\n | { createIfMissing: true; createWithData?: unknown },\n ): Promise;\n\n /**\n * Select a keyring and execute the given operation with\n * the selected keyring, as a mutually exclusive atomic\n * operation.\n *\n * The method automatically persists changes at the end of the\n * function execution, or rolls back the changes if an error\n * is thrown.\n *\n * @param selector - Keyring selector object.\n * @param operation - Function to execute with the selected keyring.\n * @returns Promise resolving to the result of the function execution.\n * @template SelectedKeyring - The type of the selected keyring.\n * @template CallbackResult - The type of the value resolved by the callback function.\n */\n async withKeyring<\n SelectedKeyring extends EthKeyring = EthKeyring,\n CallbackResult = void,\n >(\n selector: KeyringSelector,\n operation: (keyring: SelectedKeyring) => Promise,\n ): Promise;\n\n async withKeyring<\n SelectedKeyring extends EthKeyring = EthKeyring,\n CallbackResult = void,\n >(\n selector: KeyringSelector,\n operation: (keyring: SelectedKeyring) => Promise,\n options:\n | { createIfMissing?: false }\n | { createIfMissing: true; createWithData?: unknown } = {\n createIfMissing: false,\n },\n ): Promise {\n return this.#persistOrRollback(async () => {\n let keyring: SelectedKeyring | undefined;\n\n if ('address' in selector) {\n keyring = (await this.getKeyringForAccount(selector.address)) as\n | SelectedKeyring\n | undefined;\n } else {\n keyring = this.getKeyringsByType(selector.type)[selector.index || 0] as\n | SelectedKeyring\n | undefined;\n\n if (!keyring && options.createIfMissing) {\n keyring = (await this.#newKeyring(\n selector.type,\n options.createWithData,\n )) as SelectedKeyring;\n }\n }\n\n if (!keyring) {\n throw new Error(KeyringControllerError.KeyringNotFound);\n }\n\n const result = await operation(keyring);\n\n if (Object.is(result, keyring)) {\n // Access to a keyring instance outside of controller safeguards\n // should be discouraged, as it can lead to unexpected behavior.\n // This error is thrown to prevent consumers using `withKeyring`\n // as a way to get a reference to a keyring instance.\n throw new Error(KeyringControllerError.UnsafeDirectKeyringAccess);\n }\n\n return result;\n });\n }\n\n // QR Hardware related methods\n\n /**\n * Get QR Hardware keyring.\n *\n * @returns The QR Keyring if defined, otherwise undefined\n */\n getQRKeyring(): QRKeyring | undefined {\n // QRKeyring is not yet compatible with Keyring type from @metamask/utils\n return this.getKeyringsByType(KeyringTypes.qr)[0] as unknown as QRKeyring;\n }\n\n /**\n * Get QR hardware keyring. If it doesn't exist, add it.\n *\n * @returns The added keyring\n */\n async getOrAddQRKeyring(): Promise {\n return (\n this.getQRKeyring() ||\n (await this.#persistOrRollback(async () => this.#addQRKeyring()))\n );\n }\n\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n async restoreQRKeyring(serialized: any): Promise {\n return this.#persistOrRollback(async () => {\n const keyring = this.getQRKeyring() || (await this.#addQRKeyring());\n keyring.deserialize(serialized);\n });\n }\n\n async resetQRKeyringState(): Promise {\n (await this.getOrAddQRKeyring()).resetStore();\n }\n\n async getQRKeyringState(): Promise {\n return (await this.getOrAddQRKeyring()).getMemStore();\n }\n\n async submitQRCryptoHDKey(cryptoHDKey: string): Promise {\n (await this.getOrAddQRKeyring()).submitCryptoHDKey(cryptoHDKey);\n }\n\n async submitQRCryptoAccount(cryptoAccount: string): Promise {\n (await this.getOrAddQRKeyring()).submitCryptoAccount(cryptoAccount);\n }\n\n async submitQRSignature(\n requestId: string,\n ethSignature: string,\n ): Promise {\n (await this.getOrAddQRKeyring()).submitSignature(requestId, ethSignature);\n }\n\n async cancelQRSignRequest(): Promise {\n (await this.getOrAddQRKeyring()).cancelSignRequest();\n }\n\n /**\n * Cancels qr keyring sync.\n */\n async cancelQRSynchronization(): Promise {\n // eslint-disable-next-line n/no-sync\n (await this.getOrAddQRKeyring()).cancelSync();\n }\n\n async connectQRHardware(\n page: number,\n ): Promise<{ balance: string; address: string; index: number }[]> {\n return this.#persistOrRollback(async () => {\n try {\n const keyring = this.getQRKeyring() || (await this.#addQRKeyring());\n let accounts;\n switch (page) {\n case -1:\n accounts = await keyring.getPreviousPage();\n break;\n case 1:\n accounts = await keyring.getNextPage();\n break;\n default:\n accounts = await keyring.getFirstPage();\n }\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return accounts.map((account: any) => {\n return {\n ...account,\n balance: '0x0',\n };\n });\n } catch (e) {\n // TODO: Add test case for when keyring throws\n /* istanbul ignore next */\n throw new Error(`Unspecified error when connect QR Hardware, ${e}`);\n }\n });\n }\n\n async unlockQRHardwareWalletAccount(index: number): Promise {\n return this.#persistOrRollback(async () => {\n const keyring = this.getQRKeyring() || (await this.#addQRKeyring());\n\n keyring.setAccountToUnlock(index);\n await keyring.addAccounts(1);\n });\n }\n\n async getAccountKeyringType(account: string): Promise {\n const keyring = (await this.getKeyringForAccount(\n account,\n )) as EthKeyring;\n return keyring.type;\n }\n\n async forgetQRDevice(): Promise<{\n removedAccounts: string[];\n remainingAccounts: string[];\n }> {\n return this.#persistOrRollback(async () => {\n const keyring = this.getQRKeyring();\n\n if (!keyring) {\n return { removedAccounts: [], remainingAccounts: [] };\n }\n\n const allAccounts = (await this.#getAccountsFromKeyrings()) as string[];\n keyring.forgetDevice();\n const remainingAccounts =\n (await this.#getAccountsFromKeyrings()) as string[];\n const removedAccounts = allAccounts.filter(\n (address: string) => !remainingAccounts.includes(address),\n );\n return { removedAccounts, remainingAccounts };\n });\n }\n\n /**\n * Constructor helper for registering this controller's messaging system\n * actions.\n */\n #registerMessageHandlers() {\n this.messagingSystem.registerActionHandler(\n `${name}:signMessage`,\n this.signMessage.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:signPersonalMessage`,\n this.signPersonalMessage.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:signTypedMessage`,\n this.signTypedMessage.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:decryptMessage`,\n this.decryptMessage.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:getEncryptionPublicKey`,\n this.getEncryptionPublicKey.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:getAccounts`,\n this.getAccounts.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:getKeyringsByType`,\n this.getKeyringsByType.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:getKeyringForAccount`,\n this.getKeyringForAccount.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:persistAllKeyrings`,\n this.persistAllKeyrings.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:prepareUserOperation`,\n this.prepareUserOperation.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:patchUserOperation`,\n this.patchUserOperation.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:signUserOperation`,\n this.signUserOperation.bind(this),\n );\n }\n\n /**\n * Get the keyring builder for the given `type`.\n *\n * @param type - The type of keyring to get the builder for.\n * @returns The keyring builder, or undefined if none exists.\n */\n #getKeyringBuilderForType(\n type: string,\n ): { (): EthKeyring; type: string } | undefined {\n return this.#keyringBuilders.find(\n (keyringBuilder) => keyringBuilder.type === type,\n );\n }\n\n /**\n * Add qr hardware keyring.\n *\n * @returns The added keyring\n * @throws If a QRKeyring builder is not provided\n * when initializing the controller\n */\n async #addQRKeyring(): Promise {\n this.#assertControllerMutexIsLocked();\n\n // QRKeyring is not yet compatible with Keyring type from @metamask/utils\n return (await this.#newKeyring(KeyringTypes.qr)) as unknown as QRKeyring;\n }\n\n /**\n * Subscribe to a QRKeyring state change events and\n * forward them through the messaging system.\n *\n * @param qrKeyring - The QRKeyring instance to subscribe to\n */\n #subscribeToQRKeyringEvents(qrKeyring: QRKeyring) {\n this.#qrKeyringStateListener = (state) => {\n this.messagingSystem.publish(`${name}:qrKeyringStateChange`, state);\n };\n\n qrKeyring.getMemStore().subscribe(this.#qrKeyringStateListener);\n }\n\n #unsubscribeFromQRKeyringsEvents() {\n const qrKeyrings = this.getKeyringsByType(\n KeyringTypes.qr,\n ) as unknown as QRKeyring[];\n\n qrKeyrings.forEach((qrKeyring) => {\n if (this.#qrKeyringStateListener) {\n qrKeyring.getMemStore().unsubscribe(this.#qrKeyringStateListener);\n }\n });\n }\n\n /**\n * Create new vault with an initial keyring\n *\n * Destroys any old encrypted storage,\n * creates a new encrypted store with the given password,\n * creates a new wallet with 1 account.\n *\n * @fires KeyringController:unlock\n * @param password - The password to encrypt the vault with.\n * @param keyring - A object containing the params to instantiate a new keyring.\n * @param keyring.type - The keyring type.\n * @param keyring.opts - Optional parameters required to instantiate the keyring.\n * @returns A promise that resolves to the state.\n */\n async #createNewVaultWithKeyring(\n password: string,\n keyring: {\n type: string;\n opts?: unknown;\n },\n ): Promise {\n this.#assertControllerMutexIsLocked();\n\n if (typeof password !== 'string') {\n throw new TypeError(KeyringControllerError.WrongPasswordType);\n }\n\n this.update((state) => {\n delete state.encryptionKey;\n delete state.encryptionSalt;\n });\n\n this.#password = password;\n\n await this.#clearKeyrings();\n await this.#createKeyringWithFirstAccount(keyring.type, keyring.opts);\n this.#setUnlocked();\n }\n\n /**\n * Get the updated array of each keyring's type and\n * accounts list.\n *\n * @returns A promise resolving to the updated keyrings array.\n */\n async #getUpdatedKeyrings(): Promise {\n return Promise.all(this.#keyrings.map(displayForKeyring));\n }\n\n /**\n * Serialize the current array of keyring instances,\n * including unsupported keyrings by default.\n *\n * @param options - Method options.\n * @param options.includeUnsupported - Whether to include unsupported keyrings.\n * @returns The serialized keyrings.\n */\n async #getSerializedKeyrings(\n { includeUnsupported }: { includeUnsupported: boolean } = {\n includeUnsupported: true,\n },\n ): Promise {\n const serializedKeyrings = await Promise.all(\n this.#keyrings.map(async (keyring) => {\n const [type, data] = await Promise.all([\n keyring.type,\n keyring.serialize(),\n ]);\n return { type, data };\n }),\n );\n\n if (includeUnsupported) {\n serializedKeyrings.push(...this.#unsupportedKeyrings);\n }\n\n return serializedKeyrings;\n }\n\n /**\n * Restore a serialized keyrings array.\n *\n * @param serializedKeyrings - The serialized keyrings array.\n */\n async #restoreSerializedKeyrings(\n serializedKeyrings: SerializedKeyring[],\n ): Promise {\n await this.#clearKeyrings();\n\n for (const serializedKeyring of serializedKeyrings) {\n await this.#restoreKeyring(serializedKeyring);\n }\n }\n\n /**\n * Unlock Keyrings, decrypting the vault and deserializing all\n * keyrings contained in it, using a password or an encryption key with salt.\n *\n * @param password - The keyring controller password.\n * @param encryptionKey - An exported key string to unlock keyrings with.\n * @param encryptionSalt - The salt used to encrypt the vault.\n * @returns A promise resolving to the deserialized keyrings array.\n */\n async #unlockKeyrings(\n password: string | undefined,\n encryptionKey?: string,\n encryptionSalt?: string,\n ): Promise[]> {\n return this.#withVaultLock(async ({ releaseLock }) => {\n const encryptedVault = this.state.vault;\n if (!encryptedVault) {\n throw new Error(KeyringControllerError.VaultError);\n }\n\n let vault;\n const updatedState: Partial = {};\n\n if (this.#cacheEncryptionKey) {\n assertIsExportableKeyEncryptor(this.#encryptor);\n\n if (password) {\n const result = await this.#encryptor.decryptWithDetail(\n password,\n encryptedVault,\n );\n vault = result.vault;\n this.#password = password;\n\n updatedState.encryptionKey = result.exportedKeyString;\n updatedState.encryptionSalt = result.salt;\n } else {\n const parsedEncryptedVault = JSON.parse(encryptedVault);\n\n if (encryptionSalt !== parsedEncryptedVault.salt) {\n throw new Error(KeyringControllerError.ExpiredCredentials);\n }\n\n if (typeof encryptionKey !== 'string') {\n throw new TypeError(KeyringControllerError.WrongPasswordType);\n }\n\n const key = await this.#encryptor.importKey(encryptionKey);\n vault = await this.#encryptor.decryptWithKey(\n key,\n parsedEncryptedVault,\n );\n\n // This call is required on the first call because encryptionKey\n // is not yet inside the memStore\n updatedState.encryptionKey = encryptionKey;\n // we can safely assume that encryptionSalt is defined here\n // because we compare it with the salt from the vault\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n updatedState.encryptionSalt = encryptionSalt!;\n }\n } else {\n if (typeof password !== 'string') {\n throw new TypeError(KeyringControllerError.WrongPasswordType);\n }\n\n vault = await this.#encryptor.decrypt(password, encryptedVault);\n this.#password = password;\n }\n\n if (!isSerializedKeyringsArray(vault)) {\n throw new Error(KeyringControllerError.VaultDataError);\n }\n\n await this.#restoreSerializedKeyrings(vault);\n const updatedKeyrings = await this.#getUpdatedKeyrings();\n\n this.update((state) => {\n state.keyrings = updatedKeyrings;\n if (updatedState.encryptionKey || updatedState.encryptionSalt) {\n state.encryptionKey = updatedState.encryptionKey;\n state.encryptionSalt = updatedState.encryptionSalt;\n }\n });\n\n if (\n this.#password &&\n (!this.#cacheEncryptionKey || !encryptionKey) &&\n this.#encryptor.isVaultUpdated &&\n !this.#encryptor.isVaultUpdated(encryptedVault)\n ) {\n // The lock needs to be released before persisting the keyrings\n // to avoid deadlock\n releaseLock();\n // Re-encrypt the vault with safer method if one is available\n await this.#updateVault();\n }\n\n return this.#keyrings;\n });\n }\n\n /**\n * Update the vault with the current keyrings.\n *\n * @returns A promise resolving to `true` if the operation is successful.\n */\n #updateVault(): Promise {\n return this.#withVaultLock(async () => {\n const { encryptionKey, encryptionSalt } = this.state;\n\n if (!this.#password && !encryptionKey) {\n throw new Error(KeyringControllerError.MissingCredentials);\n }\n\n const serializedKeyrings = await this.#getSerializedKeyrings();\n\n if (\n !serializedKeyrings.some((keyring) => keyring.type === KeyringTypes.hd)\n ) {\n throw new Error(KeyringControllerError.NoHdKeyring);\n }\n\n const updatedState: Partial = {};\n\n if (this.#cacheEncryptionKey) {\n assertIsExportableKeyEncryptor(this.#encryptor);\n\n if (encryptionKey) {\n const key = await this.#encryptor.importKey(encryptionKey);\n const vaultJSON = await this.#encryptor.encryptWithKey(\n key,\n serializedKeyrings,\n );\n vaultJSON.salt = encryptionSalt;\n updatedState.vault = JSON.stringify(vaultJSON);\n } else if (this.#password) {\n const { vault: newVault, exportedKeyString } =\n await this.#encryptor.encryptWithDetail(\n this.#password,\n serializedKeyrings,\n );\n\n updatedState.vault = newVault;\n updatedState.encryptionKey = exportedKeyString;\n }\n } else {\n assertIsValidPassword(this.#password);\n updatedState.vault = await this.#encryptor.encrypt(\n this.#password,\n serializedKeyrings,\n );\n }\n\n if (!updatedState.vault) {\n throw new Error(KeyringControllerError.MissingVaultData);\n }\n\n const updatedKeyrings = await this.#getUpdatedKeyrings();\n this.update((state) => {\n state.vault = updatedState.vault;\n state.keyrings = updatedKeyrings;\n if (updatedState.encryptionKey) {\n state.encryptionKey = updatedState.encryptionKey;\n state.encryptionSalt = JSON.parse(updatedState.vault as string).salt;\n }\n });\n\n return true;\n });\n }\n\n /**\n * Retrieves all the accounts from keyrings instances\n * that are currently in memory.\n *\n * @returns A promise resolving to an array of accounts.\n */\n async #getAccountsFromKeyrings(): Promise {\n const keyrings = this.#keyrings;\n\n const keyringArrays = await Promise.all(\n keyrings.map(async (keyring) => keyring.getAccounts()),\n );\n const addresses = keyringArrays.reduce((res, arr) => {\n return res.concat(arr);\n }, []);\n\n // Cast to `string[]` here is safe here because `addresses` has no nullish\n // values, and `normalize` returns `string` unless given a nullish value\n return addresses.map(normalize) as string[];\n }\n\n /**\n * Create a new keyring, ensuring that the first account is\n * also created.\n *\n * @param type - Keyring type to instantiate.\n * @param opts - Optional parameters required to instantiate the keyring.\n * @returns A promise that resolves if the operation is successful.\n */\n async #createKeyringWithFirstAccount(type: string, opts?: unknown) {\n this.#assertControllerMutexIsLocked();\n\n const keyring = (await this.#newKeyring(type, opts)) as EthKeyring;\n\n const [firstAccount] = await keyring.getAccounts();\n if (!firstAccount) {\n throw new Error(KeyringControllerError.NoFirstAccount);\n }\n }\n\n /**\n * Instantiate, initialize and return a new keyring of the given `type`,\n * using the given `opts`. The keyring is built using the keyring builder\n * registered for the given `type`.\n *\n *\n * @param type - The type of keyring to add.\n * @param data - The data to restore a previously serialized keyring.\n * @returns The new keyring.\n * @throws If the keyring includes duplicated accounts.\n */\n async #newKeyring(type: string, data?: unknown): Promise> {\n this.#assertControllerMutexIsLocked();\n\n const keyringBuilder = this.#getKeyringBuilderForType(type);\n\n if (!keyringBuilder) {\n throw new Error(\n `${KeyringControllerError.NoKeyringBuilder}. Keyring type: ${type}`,\n );\n }\n\n const keyring = keyringBuilder();\n\n // @ts-expect-error Enforce data type after updating clients\n await keyring.deserialize(data);\n\n if (keyring.init) {\n await keyring.init();\n }\n\n if (type === KeyringTypes.hd && (!isObject(data) || !data.mnemonic)) {\n if (!keyring.generateRandomMnemonic) {\n throw new Error(\n KeyringControllerError.UnsupportedGenerateRandomMnemonic,\n );\n }\n\n keyring.generateRandomMnemonic();\n await keyring.addAccounts(1);\n }\n\n await this.#checkForDuplicate(type, await keyring.getAccounts());\n\n if (type === KeyringTypes.qr) {\n // In case of a QR keyring type, we need to subscribe\n // to its events after creating it\n this.#subscribeToQRKeyringEvents(keyring as unknown as QRKeyring);\n }\n\n this.#keyrings.push(keyring);\n\n return keyring;\n }\n\n /**\n * Remove all managed keyrings, destroying all their\n * instances in memory.\n */\n async #clearKeyrings() {\n this.#assertControllerMutexIsLocked();\n for (const keyring of this.#keyrings) {\n await this.#destroyKeyring(keyring);\n }\n this.#keyrings = [];\n }\n\n /**\n * Restore a Keyring from a provided serialized payload.\n * On success, returns the resulting keyring instance.\n *\n * @param serialized - The serialized keyring.\n * @returns The deserialized keyring or undefined if the keyring type is unsupported.\n */\n async #restoreKeyring(\n serialized: SerializedKeyring,\n ): Promise | undefined> {\n this.#assertControllerMutexIsLocked();\n\n try {\n const { type, data } = serialized;\n return await this.#newKeyring(type, data);\n } catch (_) {\n this.#unsupportedKeyrings.push(serialized);\n return undefined;\n }\n }\n\n /**\n * Destroy Keyring\n *\n * Some keyrings support a method called `destroy`, that destroys the\n * keyring along with removing all its event listeners and, in some cases,\n * clears the keyring bridge iframe from the DOM.\n *\n * @param keyring - The keyring to destroy.\n */\n async #destroyKeyring(keyring: EthKeyring) {\n await keyring.destroy?.();\n }\n\n /**\n * Remove empty keyrings.\n *\n * Loops through the keyrings and removes the ones with empty accounts\n * (usually after removing the last / only account) from a keyring.\n */\n async #removeEmptyKeyrings(): Promise {\n this.#assertControllerMutexIsLocked();\n const validKeyrings: EthKeyring[] = [];\n\n // Since getAccounts returns a Promise\n // We need to wait to hear back form each keyring\n // in order to decide which ones are now valid (accounts.length > 0)\n\n await Promise.all(\n this.#keyrings.map(async (keyring: EthKeyring) => {\n const accounts = await keyring.getAccounts();\n if (accounts.length > 0) {\n validKeyrings.push(keyring);\n } else {\n await this.#destroyKeyring(keyring);\n }\n }),\n );\n this.#keyrings = validKeyrings;\n }\n\n /**\n * Checks for duplicate keypairs, using the the first account in the given\n * array. Rejects if a duplicate is found.\n *\n * Only supports 'Simple Key Pair'.\n *\n * @param type - The key pair type to check for.\n * @param newAccountArray - Array of new accounts.\n * @returns The account, if no duplicate is found.\n */\n async #checkForDuplicate(\n type: string,\n newAccountArray: string[],\n ): Promise {\n const accounts = await this.#getAccountsFromKeyrings();\n\n switch (type) {\n case KeyringTypes.simple: {\n const isIncluded = Boolean(\n accounts.find(\n (key) =>\n newAccountArray[0] &&\n (key === newAccountArray[0] ||\n key === remove0x(newAccountArray[0])),\n ),\n );\n\n if (isIncluded) {\n throw new Error(KeyringControllerError.DuplicatedAccount);\n }\n return newAccountArray;\n }\n\n default: {\n return newAccountArray;\n }\n }\n }\n\n /**\n * Set the `isUnlocked` to true and notify listeners\n * through the messenger.\n *\n * @fires KeyringController:unlock\n */\n #setUnlocked(): void {\n this.#assertControllerMutexIsLocked();\n\n this.update((state) => {\n state.isUnlocked = true;\n });\n this.messagingSystem.publish(`${name}:unlock`);\n }\n\n /**\n * Execute the given function after acquiring the controller lock\n * and save the keyrings to state after it, or rollback to their\n * previous state in case of error.\n *\n * @param fn - The function to execute.\n * @returns The result of the function.\n */\n async #persistOrRollback(fn: MutuallyExclusiveCallback): Promise {\n return this.#withRollback(async ({ releaseLock }) => {\n const callbackResult = await fn({ releaseLock });\n // State is committed only if the operation is successful\n await this.#updateVault();\n\n return callbackResult;\n });\n }\n\n /**\n * Execute the given function after acquiring the controller lock\n * and rollback keyrings and password states in case of error.\n *\n * @param fn - The function to execute atomically.\n * @returns The result of the function.\n */\n async #withRollback(fn: MutuallyExclusiveCallback): Promise {\n return this.#withControllerLock(async ({ releaseLock }) => {\n const currentSerializedKeyrings = await this.#getSerializedKeyrings();\n const currentPassword = this.#password;\n\n try {\n return await fn({ releaseLock });\n } catch (e) {\n // Keyrings and password are restored to their previous state\n await this.#restoreSerializedKeyrings(currentSerializedKeyrings);\n this.#password = currentPassword;\n\n throw e;\n }\n });\n }\n\n /**\n * Assert that the controller mutex is locked.\n *\n * @throws If the controller mutex is not locked.\n */\n #assertControllerMutexIsLocked() {\n if (!this.#controllerOperationMutex.isLocked()) {\n throw new Error(KeyringControllerError.ControllerLockRequired);\n }\n }\n\n /**\n * Lock the controller mutex before executing the given function,\n * and release it after the function is resolved or after an\n * error is thrown.\n *\n * This wrapper ensures that each mutable operation that interacts with the\n * controller and that changes its state is executed in a mutually exclusive way,\n * preventing unsafe concurrent access that could lead to unpredictable behavior.\n *\n * @param fn - The function to execute while the controller mutex is locked.\n * @returns The result of the function.\n */\n async #withControllerLock(fn: MutuallyExclusiveCallback): Promise {\n return withLock(this.#controllerOperationMutex, fn);\n }\n\n /**\n * Lock the vault mutex before executing the given function,\n * and release it after the function is resolved or after an\n * error is thrown.\n *\n * This ensures that each operation that interacts with the vault\n * is executed in a mutually exclusive way.\n *\n * @param fn - The function to execute while the vault mutex is locked.\n * @returns The result of the function.\n */\n async #withVaultLock(fn: MutuallyExclusiveCallback): Promise {\n this.#assertControllerMutexIsLocked();\n\n return withLock(this.#vaultOperationMutex, fn);\n }\n}\n\n/**\n * Lock the given mutex before executing the given function,\n * and release it after the function is resolved or after an\n * error is thrown.\n *\n * @param mutex - The mutex to lock.\n * @param fn - The function to execute while the mutex is locked.\n * @returns The result of the function.\n */\nasync function withLock(\n mutex: Mutex,\n fn: MutuallyExclusiveCallback,\n): Promise {\n const releaseLock = await mutex.acquire();\n\n try {\n return await fn({ releaseLock });\n } finally {\n releaseLock();\n }\n}\n\nexport default KeyringController;\n"],"mappings":"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","names":["KeyringTypes","AccountImportStrategy","SignTypedDataVersion"]} -\ No newline at end of file -diff --git a/dist/chunk-BRS27QHF.js b/dist/chunk-BRS27QHF.js -deleted file mode 100644 -index 93bf7c6e9b238aed269aabc193ede499d7efb76a..0000000000000000000000000000000000000000 ---- a/dist/chunk-BRS27QHF.js -+++ /dev/null -@@ -1,1500 +0,0 @@ --"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } -- -- -- -- --var _chunkNOCGQCUMjs = require('./chunk-NOCGQCUM.js'); -- --// src/KeyringController.ts --var _util = require('@ethereumjs/util'); --var _basecontroller = require('@metamask/base-controller'); --var _browserpassworder = require('@metamask/browser-passworder'); var encryptorUtils = _interopRequireWildcard(_browserpassworder); --var _ethhdkeyring = require('@metamask/eth-hd-keyring'); var _ethhdkeyring2 = _interopRequireDefault(_ethhdkeyring); --var _ethsigutil = require('@metamask/eth-sig-util'); --var _ethsimplekeyring = require('@metamask/eth-simple-keyring'); var _ethsimplekeyring2 = _interopRequireDefault(_ethsimplekeyring); -- -- -- -- -- -- -- -- -- -- --var _utils = require('@metamask/utils'); --var _asyncmutex = require('async-mutex'); --var _ethereumjswallet = require('ethereumjs-wallet'); var _ethereumjswallet2 = _interopRequireDefault(_ethereumjswallet); --var name = "KeyringController"; --var KeyringTypes = /* @__PURE__ */ ((KeyringTypes2) => { -- KeyringTypes2["simple"] = "Simple Key Pair"; -- KeyringTypes2["hd"] = "HD Key Tree"; -- KeyringTypes2["qr"] = "QR Hardware Wallet Device"; -- KeyringTypes2["trezor"] = "Trezor Hardware"; -- KeyringTypes2["ledger"] = "Ledger Hardware"; -- KeyringTypes2["lattice"] = "Lattice Hardware"; -- KeyringTypes2["snap"] = "Snap Keyring"; -- return KeyringTypes2; --})(KeyringTypes || {}); --var isCustodyKeyring = (keyringType) => { -- return keyringType.startsWith("Custody"); --}; --var AccountImportStrategy = /* @__PURE__ */ ((AccountImportStrategy2) => { -- AccountImportStrategy2["privateKey"] = "privateKey"; -- AccountImportStrategy2["json"] = "json"; -- return AccountImportStrategy2; --})(AccountImportStrategy || {}); --var SignTypedDataVersion = /* @__PURE__ */ ((SignTypedDataVersion2) => { -- SignTypedDataVersion2["V1"] = "V1"; -- SignTypedDataVersion2["V3"] = "V3"; -- SignTypedDataVersion2["V4"] = "V4"; -- return SignTypedDataVersion2; --})(SignTypedDataVersion || {}); --function keyringBuilderFactory(KeyringConstructor) { -- const builder = () => new KeyringConstructor(); -- builder.type = KeyringConstructor.type; -- return builder; --} --var defaultKeyringBuilders = [ -- keyringBuilderFactory(_ethsimplekeyring2.default), -- keyringBuilderFactory(_ethhdkeyring2.default) --]; --var getDefaultKeyringState = () => { -- return { -- isUnlocked: false, -- keyrings: [] -- }; --}; --function assertHasUint8ArrayMnemonic(keyring) { -- if (!(_utils.hasProperty.call(void 0, keyring, "mnemonic") && keyring.mnemonic instanceof Uint8Array)) { -- throw new Error("Can't get mnemonic bytes from keyring"); -- } --} --function assertIsExportableKeyEncryptor(encryptor) { -- if (!("importKey" in encryptor && typeof encryptor.importKey === "function" && "decryptWithKey" in encryptor && typeof encryptor.decryptWithKey === "function" && "encryptWithKey" in encryptor && typeof encryptor.encryptWithKey === "function")) { -- throw new Error("KeyringController - The encryptor does not support encryption key export." /* UnsupportedEncryptionKeyExport */); -- } --} --function assertIsValidPassword(password) { -- if (typeof password !== "string") { -- throw new Error("KeyringController - Password must be of type string." /* WrongPasswordType */); -- } -- if (!password || !password.length) { -- throw new Error("KeyringController - Password cannot be empty." /* InvalidEmptyPassword */); -- } --} --function isSerializedKeyringsArray(array) { -- return typeof array === "object" && Array.isArray(array) && array.every((value) => value.type && _utils.isValidJson.call(void 0, value.data)); --} --async function displayForKeyring(keyring) { -- const accounts = await keyring.getAccounts(); -- return { -- type: keyring.type, -- // Cast to `string[]` here is safe here because `accounts` has no nullish -- // values, and `normalize` returns `string` unless given a nullish value -- accounts: accounts.map(normalize) -- }; --} --function isEthAddress(address) { -- return ( -- // NOTE: This function only checks for lowercased strings -- _utils.isStrictHexString.call(void 0, address.toLowerCase()) && // This checks for lowercased addresses and checksum addresses too -- _utils.isValidHexAddress.call(void 0, address) -- ); --} --function normalize(address) { -- return isEthAddress(address) ? _ethsigutil.normalize.call(void 0, address) : address; --} --var _controllerOperationMutex, _vaultOperationMutex, _keyringBuilders, _keyrings, _unsupportedKeyrings, _password, _encryptor, _cacheEncryptionKey, _qrKeyringStateListener, _registerMessageHandlers, registerMessageHandlers_fn, _getKeyringBuilderForType, getKeyringBuilderForType_fn, _addQRKeyring, addQRKeyring_fn, _subscribeToQRKeyringEvents, subscribeToQRKeyringEvents_fn, _unsubscribeFromQRKeyringsEvents, unsubscribeFromQRKeyringsEvents_fn, _createNewVaultWithKeyring, createNewVaultWithKeyring_fn, _getUpdatedKeyrings, getUpdatedKeyrings_fn, _getSerializedKeyrings, getSerializedKeyrings_fn, _restoreSerializedKeyrings, restoreSerializedKeyrings_fn, _unlockKeyrings, unlockKeyrings_fn, _updateVault, updateVault_fn, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn, _createKeyringWithFirstAccount, createKeyringWithFirstAccount_fn, _newKeyring, newKeyring_fn, _clearKeyrings, clearKeyrings_fn, _restoreKeyring, restoreKeyring_fn, _destroyKeyring, destroyKeyring_fn, _removeEmptyKeyrings, removeEmptyKeyrings_fn, _checkForDuplicate, checkForDuplicate_fn, _setUnlocked, setUnlocked_fn, _persistOrRollback, persistOrRollback_fn, _withRollback, withRollback_fn, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn, _withControllerLock, withControllerLock_fn, _withVaultLock, withVaultLock_fn; --var KeyringController = class extends _basecontroller.BaseController { -- /** -- * Creates a KeyringController instance. -- * -- * @param options - Initial options used to configure this controller -- * @param options.encryptor - An optional object for defining encryption schemes. -- * @param options.keyringBuilders - Set a new name for account. -- * @param options.cacheEncryptionKey - Whether to cache or not encryption key. -- * @param options.messenger - A restricted controller messenger. -- * @param options.state - Initial state to set on this controller. -- */ -- constructor(options) { -- const { -- encryptor = encryptorUtils, -- keyringBuilders, -- messenger, -- state -- } = options; -- super({ -- name, -- metadata: { -- vault: { persist: true, anonymous: false }, -- isUnlocked: { persist: false, anonymous: true }, -- keyrings: { persist: false, anonymous: false }, -- encryptionKey: { persist: false, anonymous: false }, -- encryptionSalt: { persist: false, anonymous: false } -- }, -- messenger, -- state: { -- ...getDefaultKeyringState(), -- ...state -- } -- }); -- /** -- * Constructor helper for registering this controller's messaging system -- * actions. -- */ -- _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _registerMessageHandlers); -- /** -- * Get the keyring builder for the given `type`. -- * -- * @param type - The type of keyring to get the builder for. -- * @returns The keyring builder, or undefined if none exists. -- */ -- _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _getKeyringBuilderForType); -- /** -- * Add qr hardware keyring. -- * -- * @returns The added keyring -- * @throws If a QRKeyring builder is not provided -- * when initializing the controller -- */ -- _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _addQRKeyring); -- /** -- * Subscribe to a QRKeyring state change events and -- * forward them through the messaging system. -- * -- * @param qrKeyring - The QRKeyring instance to subscribe to -- */ -- _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _subscribeToQRKeyringEvents); -- _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _unsubscribeFromQRKeyringsEvents); -- /** -- * Create new vault with an initial keyring -- * -- * Destroys any old encrypted storage, -- * creates a new encrypted store with the given password, -- * creates a new wallet with 1 account. -- * -- * @fires KeyringController:unlock -- * @param password - The password to encrypt the vault with. -- * @param keyring - A object containing the params to instantiate a new keyring. -- * @param keyring.type - The keyring type. -- * @param keyring.opts - Optional parameters required to instantiate the keyring. -- * @returns A promise that resolves to the state. -- */ -- _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _createNewVaultWithKeyring); -- /** -- * Get the updated array of each keyring's type and -- * accounts list. -- * -- * @returns A promise resolving to the updated keyrings array. -- */ -- _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _getUpdatedKeyrings); -- /** -- * Serialize the current array of keyring instances, -- * including unsupported keyrings by default. -- * -- * @param options - Method options. -- * @param options.includeUnsupported - Whether to include unsupported keyrings. -- * @returns The serialized keyrings. -- */ -- _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _getSerializedKeyrings); -- /** -- * Restore a serialized keyrings array. -- * -- * @param serializedKeyrings - The serialized keyrings array. -- */ -- _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _restoreSerializedKeyrings); -- /** -- * Unlock Keyrings, decrypting the vault and deserializing all -- * keyrings contained in it, using a password or an encryption key with salt. -- * -- * @param password - The keyring controller password. -- * @param encryptionKey - An exported key string to unlock keyrings with. -- * @param encryptionSalt - The salt used to encrypt the vault. -- * @returns A promise resolving to the deserialized keyrings array. -- */ -- _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _unlockKeyrings); -- /** -- * Update the vault with the current keyrings. -- * -- * @returns A promise resolving to `true` if the operation is successful. -- */ -- _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _updateVault); -- /** -- * Retrieves all the accounts from keyrings instances -- * that are currently in memory. -- * -- * @returns A promise resolving to an array of accounts. -- */ -- _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _getAccountsFromKeyrings); -- /** -- * Create a new keyring, ensuring that the first account is -- * also created. -- * -- * @param type - Keyring type to instantiate. -- * @param opts - Optional parameters required to instantiate the keyring. -- * @returns A promise that resolves if the operation is successful. -- */ -- _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _createKeyringWithFirstAccount); -- /** -- * Instantiate, initialize and return a new keyring of the given `type`, -- * using the given `opts`. The keyring is built using the keyring builder -- * registered for the given `type`. -- * -- * -- * @param type - The type of keyring to add. -- * @param data - The data to restore a previously serialized keyring. -- * @returns The new keyring. -- * @throws If the keyring includes duplicated accounts. -- */ -- _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _newKeyring); -- /** -- * Remove all managed keyrings, destroying all their -- * instances in memory. -- */ -- _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _clearKeyrings); -- /** -- * Restore a Keyring from a provided serialized payload. -- * On success, returns the resulting keyring instance. -- * -- * @param serialized - The serialized keyring. -- * @returns The deserialized keyring or undefined if the keyring type is unsupported. -- */ -- _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _restoreKeyring); -- /** -- * Destroy Keyring -- * -- * Some keyrings support a method called `destroy`, that destroys the -- * keyring along with removing all its event listeners and, in some cases, -- * clears the keyring bridge iframe from the DOM. -- * -- * @param keyring - The keyring to destroy. -- */ -- _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _destroyKeyring); -- /** -- * Remove empty keyrings. -- * -- * Loops through the keyrings and removes the ones with empty accounts -- * (usually after removing the last / only account) from a keyring. -- */ -- _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _removeEmptyKeyrings); -- /** -- * Checks for duplicate keypairs, using the the first account in the given -- * array. Rejects if a duplicate is found. -- * -- * Only supports 'Simple Key Pair'. -- * -- * @param type - The key pair type to check for. -- * @param newAccountArray - Array of new accounts. -- * @returns The account, if no duplicate is found. -- */ -- _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _checkForDuplicate); -- /** -- * Set the `isUnlocked` to true and notify listeners -- * through the messenger. -- * -- * @fires KeyringController:unlock -- */ -- _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _setUnlocked); -- /** -- * Execute the given function after acquiring the controller lock -- * and save the keyrings to state after it, or rollback to their -- * previous state in case of error. -- * -- * @param fn - The function to execute. -- * @returns The result of the function. -- */ -- _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _persistOrRollback); -- /** -- * Execute the given function after acquiring the controller lock -- * and rollback keyrings and password states in case of error. -- * -- * @param fn - The function to execute atomically. -- * @returns The result of the function. -- */ -- _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _withRollback); -- /** -- * Assert that the controller mutex is locked. -- * -- * @throws If the controller mutex is not locked. -- */ -- _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _assertControllerMutexIsLocked); -- /** -- * Lock the controller mutex before executing the given function, -- * and release it after the function is resolved or after an -- * error is thrown. -- * -- * This wrapper ensures that each mutable operation that interacts with the -- * controller and that changes its state is executed in a mutually exclusive way, -- * preventing unsafe concurrent access that could lead to unpredictable behavior. -- * -- * @param fn - The function to execute while the controller mutex is locked. -- * @returns The result of the function. -- */ -- _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _withControllerLock); -- /** -- * Lock the vault mutex before executing the given function, -- * and release it after the function is resolved or after an -- * error is thrown. -- * -- * This ensures that each operation that interacts with the vault -- * is executed in a mutually exclusive way. -- * -- * @param fn - The function to execute while the vault mutex is locked. -- * @returns The result of the function. -- */ -- _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _withVaultLock); -- _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _controllerOperationMutex, new (0, _asyncmutex.Mutex)()); -- _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _vaultOperationMutex, new (0, _asyncmutex.Mutex)()); -- _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _keyringBuilders, void 0); -- _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _keyrings, void 0); -- _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _unsupportedKeyrings, void 0); -- _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _password, void 0); -- _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _encryptor, void 0); -- _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _cacheEncryptionKey, void 0); -- _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _qrKeyringStateListener, void 0); -- _chunkNOCGQCUMjs.__privateSet.call(void 0, this, _keyringBuilders, keyringBuilders ? defaultKeyringBuilders.concat(keyringBuilders) : defaultKeyringBuilders); -- _chunkNOCGQCUMjs.__privateSet.call(void 0, this, _encryptor, encryptor); -- _chunkNOCGQCUMjs.__privateSet.call(void 0, this, _keyrings, []); -- _chunkNOCGQCUMjs.__privateSet.call(void 0, this, _unsupportedKeyrings, []); -- _chunkNOCGQCUMjs.__privateSet.call(void 0, this, _cacheEncryptionKey, Boolean(options.cacheEncryptionKey)); -- if (_chunkNOCGQCUMjs.__privateGet.call(void 0, this, _cacheEncryptionKey)) { -- assertIsExportableKeyEncryptor(encryptor); -- } -- _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _registerMessageHandlers, registerMessageHandlers_fn).call(this); -- } -- /** -- * Adds a new account to the default (first) HD seed phrase keyring. -- * -- * @param accountCount - Number of accounts before adding a new one, used to -- * make the method idempotent. -- * @returns Promise resolving to the added account address. -- */ -- async addNewAccount(accountCount) { -- return _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -- const primaryKeyring = this.getKeyringsByType("HD Key Tree")[0]; -- if (!primaryKeyring) { -- throw new Error("No HD keyring found"); -- } -- const oldAccounts = await primaryKeyring.getAccounts(); -- if (accountCount && oldAccounts.length !== accountCount) { -- if (accountCount > oldAccounts.length) { -- throw new Error("Account out of sequence"); -- } -- const existingAccount = oldAccounts[accountCount]; -- if (!existingAccount) { -- throw new Error(`Can't find account at index ${accountCount}`); -- } -- return existingAccount; -- } -- const [addedAccountAddress] = await primaryKeyring.addAccounts(1); -- await this.verifySeedPhrase(); -- return addedAccountAddress; -- }); -- } -- /** -- * Adds a new account to the specified keyring. -- * -- * @param keyring - Keyring to add the account to. -- * @param accountCount - Number of accounts before adding a new one, used to make the method idempotent. -- * @returns Promise resolving to the added account address -- */ -- async addNewAccountForKeyring(keyring, accountCount) { -- return _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -- const oldAccounts = await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn).call(this); -- if (accountCount && oldAccounts.length !== accountCount) { -- if (accountCount > oldAccounts.length) { -- throw new Error("Account out of sequence"); -- } -- const existingAccount = oldAccounts[accountCount]; -- _utils.assertIsStrictHexString.call(void 0, existingAccount); -- return existingAccount; -- } -- await keyring.addAccounts(1); -- const addedAccountAddress = (await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn).call(this)).find( -- (selectedAddress) => !oldAccounts.includes(selectedAddress) -- ); -- _utils.assertIsStrictHexString.call(void 0, addedAccountAddress); -- return addedAccountAddress; -- }); -- } -- /** -- * Adds a new account to the default (first) HD seed phrase keyring without updating identities in preferences. -- * -- * @returns Promise resolving to the added account address. -- */ -- async addNewAccountWithoutUpdate() { -- return _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -- const primaryKeyring = this.getKeyringsByType("HD Key Tree")[0]; -- if (!primaryKeyring) { -- throw new Error("No HD keyring found"); -- } -- const [addedAccountAddress] = await primaryKeyring.addAccounts(1); -- await this.verifySeedPhrase(); -- return addedAccountAddress; -- }); -- } -- /** -- * Effectively the same as creating a new keychain then populating it -- * using the given seed phrase. -- * -- * @param password - Password to unlock keychain. -- * @param seed - A BIP39-compliant seed phrase as Uint8Array, -- * either as a string or an array of UTF-8 bytes that represent the string. -- * @returns Promise resolving when the operation ends successfully. -- */ -- async createNewVaultAndRestore(password, seed) { -- return _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -- assertIsValidPassword(password); -- await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _createNewVaultWithKeyring, createNewVaultWithKeyring_fn).call(this, password, { -- type: "HD Key Tree" /* hd */, -- opts: { -- mnemonic: seed, -- numberOfAccounts: 1 -- } -- }); -- }); -- } -- /** -- * Create a new primary keychain and wipe any previous keychains. -- * -- * @param password - Password to unlock the new vault. -- * @returns Promise resolving when the operation ends successfully. -- */ -- async createNewVaultAndKeychain(password) { -- return _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -- const accounts = await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn).call(this); -- if (!accounts.length) { -- await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _createNewVaultWithKeyring, createNewVaultWithKeyring_fn).call(this, password, { -- type: "HD Key Tree" /* hd */ -- }); -- } -- }); -- } -- /** -- * Adds a new keyring of the given `type`. -- * -- * @param type - Keyring type name. -- * @param opts - Keyring options. -- * @throws If a builder for the given `type` does not exist. -- * @returns Promise resolving to the added keyring. -- */ -- async addNewKeyring(type, opts) { -- if (type === "QR Hardware Wallet Device" /* qr */) { -- return this.getOrAddQRKeyring(); -- } -- return _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _persistOrRollback, persistOrRollback_fn).call(this, async () => _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _newKeyring, newKeyring_fn).call(this, type, opts)); -- } -- /** -- * Method to verify a given password validity. Throws an -- * error if the password is invalid. -- * -- * @param password - Password of the keyring. -- */ -- async verifyPassword(password) { -- if (!this.state.vault) { -- throw new Error("KeyringController - Cannot unlock without a previous vault." /* VaultError */); -- } -- await _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _encryptor).decrypt(password, this.state.vault); -- } -- /** -- * Returns the status of the vault. -- * -- * @returns Boolean returning true if the vault is unlocked. -- */ -- isUnlocked() { -- return this.state.isUnlocked; -- } -- /** -- * Gets the seed phrase of the HD keyring. -- * -- * @param password - Password of the keyring. -- * @returns Promise resolving to the seed phrase. -- */ -- async exportSeedPhrase(password) { -- await this.verifyPassword(password); -- assertHasUint8ArrayMnemonic(_chunkNOCGQCUMjs.__privateGet.call(void 0, this, _keyrings)[0]); -- return _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _keyrings)[0].mnemonic; -- } -- /** -- * Gets the private key from the keyring controlling an address. -- * -- * @param password - Password of the keyring. -- * @param address - Address to export. -- * @returns Promise resolving to the private key for an address. -- */ -- async exportAccount(password, address) { -- await this.verifyPassword(password); -- const keyring = await this.getKeyringForAccount( -- address -- ); -- if (!keyring.exportAccount) { -- throw new Error("`KeyringController - The keyring for the current address does not support the method exportAccount" /* UnsupportedExportAccount */); -- } -- return await keyring.exportAccount(normalize(address)); -- } -- /** -- * Returns the public addresses of all accounts from every keyring. -- * -- * @returns A promise resolving to an array of addresses. -- */ -- async getAccounts() { -- return this.state.keyrings.reduce( -- (accounts, keyring) => accounts.concat(keyring.accounts), -- [] -- ); -- } -- /** -- * Get encryption public key. -- * -- * @param account - An account address. -- * @param opts - Additional encryption options. -- * @throws If the `account` does not exist or does not support the `getEncryptionPublicKey` method -- * @returns Promise resolving to encyption public key of the `account` if one exists. -- */ -- async getEncryptionPublicKey(account, opts) { -- const address = _ethsigutil.normalize.call(void 0, account); -- const keyring = await this.getKeyringForAccount( -- account -- ); -- if (!keyring.getEncryptionPublicKey) { -- throw new Error("KeyringController - The keyring for the current address does not support the method getEncryptionPublicKey." /* UnsupportedGetEncryptionPublicKey */); -- } -- return await keyring.getEncryptionPublicKey(address, opts); -- } -- /** -- * Attempts to decrypt the provided message parameters. -- * -- * @param messageParams - The decryption message parameters. -- * @param messageParams.from - The address of the account you want to use to decrypt the message. -- * @param messageParams.data - The encrypted data that you want to decrypt. -- * @returns The raw decryption result. -- */ -- async decryptMessage(messageParams) { -- const address = _ethsigutil.normalize.call(void 0, messageParams.from); -- const keyring = await this.getKeyringForAccount( -- address -- ); -- if (!keyring.decryptMessage) { -- throw new Error("KeyringController - The keyring for the current address does not support the method decryptMessage." /* UnsupportedDecryptMessage */); -- } -- return keyring.decryptMessage(address, messageParams.data); -- } -- /** -- * Returns the currently initialized keyring that manages -- * the specified `address` if one exists. -- * -- * @deprecated Use of this method is discouraged as actions executed directly on -- * keyrings are not being reflected in the KeyringController state and not -- * persisted in the vault. Use `withKeyring` instead. -- * @param account - An account address. -- * @returns Promise resolving to keyring of the `account` if one exists. -- */ -- async getKeyringForAccount(account) { -- const address = normalize(account); -- const candidates = await Promise.all( -- _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _keyrings).map(async (keyring) => { -- return Promise.all([keyring, keyring.getAccounts()]); -- }) -- ); -- const winners = candidates.filter((candidate) => { -- const accounts = candidate[1].map(normalize); -- return accounts.includes(address); -- }); -- if (winners.length && winners[0]?.length) { -- return winners[0][0]; -- } -- let errorInfo = ""; -- if (!candidates.length) { -- errorInfo = "There are no keyrings"; -- } else if (!winners.length) { -- errorInfo = "There are keyrings, but none match the address"; -- } -- throw new Error( -- `${"KeyringController - No keyring found" /* NoKeyring */}. Error info: ${errorInfo}` -- ); -- } -- /** -- * Returns all keyrings of the given type. -- * -- * @deprecated Use of this method is discouraged as actions executed directly on -- * keyrings are not being reflected in the KeyringController state and not -- * persisted in the vault. Use `withKeyring` instead. -- * @param type - Keyring type name. -- * @returns An array of keyrings of the given type. -- */ -- getKeyringsByType(type) { -- return _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _keyrings).filter((keyring) => keyring.type === type); -- } -- /** -- * Persist all serialized keyrings in the vault. -- * -- * @deprecated This method is being phased out in favor of `withKeyring`. -- * @returns Promise resolving with `true` value when the -- * operation completes. -- */ -- async persistAllKeyrings() { -- return _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _persistOrRollback, persistOrRollback_fn).call(this, async () => true); -- } -- /** -- * Imports an account with the specified import strategy. -- * -- * @param strategy - Import strategy name. -- * @param args - Array of arguments to pass to the underlying stategy. -- * @throws Will throw when passed an unrecognized strategy. -- * @returns Promise resolving to the imported account address. -- */ -- async importAccountWithStrategy(strategy, args) { -- return _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -- let privateKey; -- switch (strategy) { -- case "privateKey": -- const [importedKey] = args; -- if (!importedKey) { -- throw new Error("Cannot import an empty key."); -- } -- const prefixed = _utils.add0x.call(void 0, importedKey); -- let bufferedPrivateKey; -- try { -- bufferedPrivateKey = _util.toBuffer.call(void 0, prefixed); -- } catch { -- throw new Error("Cannot import invalid private key."); -- } -- if (!_util.isValidPrivate.call(void 0, bufferedPrivateKey) || // ensures that the key is 64 bytes long -- _util.getBinarySize.call(void 0, prefixed) !== 64 + "0x".length) { -- throw new Error("Cannot import invalid private key."); -- } -- privateKey = _utils.remove0x.call(void 0, prefixed); -- break; -- case "json": -- let wallet; -- const [input, password] = args; -- try { -- wallet = _ethereumjswallet.thirdparty.fromEtherWallet(input, password); -- } catch (e) { -- wallet = wallet || await _ethereumjswallet2.default.fromV3(input, password, true); -- } -- privateKey = _utils.bytesToHex.call(void 0, wallet.getPrivateKey()); -- break; -- default: -- throw new Error(`Unexpected import strategy: '${strategy}'`); -- } -- const newKeyring = await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _newKeyring, newKeyring_fn).call(this, "Simple Key Pair" /* simple */, [ -- privateKey -- ]); -- const accounts = await newKeyring.getAccounts(); -- return accounts[0]; -- }); -- } -- /** -- * Removes an account from keyring state. -- * -- * @param address - Address of the account to remove. -- * @fires KeyringController:accountRemoved -- * @returns Promise resolving when the account is removed. -- */ -- async removeAccount(address) { -- await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -- const keyring = await this.getKeyringForAccount( -- address -- ); -- if (!keyring.removeAccount) { -- throw new Error("`KeyringController - The keyring for the current address does not support the method removeAccount" /* UnsupportedRemoveAccount */); -- } -- await keyring.removeAccount(address); -- const accounts = await keyring.getAccounts(); -- if (accounts.length === 0) { -- await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _removeEmptyKeyrings, removeEmptyKeyrings_fn).call(this); -- } -- }); -- this.messagingSystem.publish(`${name}:accountRemoved`, address); -- } -- /** -- * Deallocates all secrets and locks the wallet. -- * -- * @returns Promise resolving when the operation completes. -- */ -- async setLocked() { -- return _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _withRollback, withRollback_fn).call(this, async () => { -- _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _unsubscribeFromQRKeyringsEvents, unsubscribeFromQRKeyringsEvents_fn).call(this); -- _chunkNOCGQCUMjs.__privateSet.call(void 0, this, _password, void 0); -- await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _clearKeyrings, clearKeyrings_fn).call(this); -- this.update((state) => { -- state.isUnlocked = false; -- state.keyrings = []; -- }); -- this.messagingSystem.publish(`${name}:lock`); -- }); -- } -- /** -- * Signs message by calling down into a specific keyring. -- * -- * @param messageParams - PersonalMessageParams object to sign. -- * @returns Promise resolving to a signed message string. -- */ -- async signMessage(messageParams) { -- if (!messageParams.data) { -- throw new Error("Can't sign an empty message"); -- } -- const address = _ethsigutil.normalize.call(void 0, messageParams.from); -- const keyring = await this.getKeyringForAccount( -- address -- ); -- if (!keyring.signMessage) { -- throw new Error("KeyringController - The keyring for the current address does not support the method signMessage." /* UnsupportedSignMessage */); -- } -- return await keyring.signMessage(address, messageParams.data); -- } -- /** -- * Signs personal message by calling down into a specific keyring. -- * -- * @param messageParams - PersonalMessageParams object to sign. -- * @returns Promise resolving to a signed message string. -- */ -- async signPersonalMessage(messageParams) { -- const address = _ethsigutil.normalize.call(void 0, messageParams.from); -- const keyring = await this.getKeyringForAccount( -- address -- ); -- if (!keyring.signPersonalMessage) { -- throw new Error("KeyringController - The keyring for the current address does not support the method signPersonalMessage." /* UnsupportedSignPersonalMessage */); -- } -- const normalizedData = normalize(messageParams.data); -- return await keyring.signPersonalMessage(address, normalizedData); -- } -- /** -- * Signs typed message by calling down into a specific keyring. -- * -- * @param messageParams - TypedMessageParams object to sign. -- * @param version - Compatibility version EIP712. -- * @throws Will throw when passed an unrecognized version. -- * @returns Promise resolving to a signed message string or an error if any. -- */ -- async signTypedMessage(messageParams, version) { -- try { -- if (![ -- "V1" /* V1 */, -- "V3" /* V3 */, -- "V4" /* V4 */ -- ].includes(version)) { -- throw new Error(`Unexpected signTypedMessage version: '${version}'`); -- } -- const address = _ethsigutil.normalize.call(void 0, messageParams.from); -- const keyring = await this.getKeyringForAccount( -- address -- ); -- if (!keyring.signTypedData) { -- throw new Error("KeyringController - The keyring for the current address does not support the method signTypedMessage." /* UnsupportedSignTypedMessage */); -- } -- return await keyring.signTypedData( -- address, -- version !== "V1" /* V1 */ && typeof messageParams.data === "string" ? JSON.parse(messageParams.data) : messageParams.data, -- { version } -- ); -- } catch (error) { -- throw new Error(`Keyring Controller signTypedMessage: ${error}`); -- } -- } -- /** -- * Signs a transaction by calling down into a specific keyring. -- * -- * @param transaction - Transaction object to sign. Must be a `ethereumjs-tx` transaction instance. -- * @param from - Address to sign from, should be in keychain. -- * @param opts - An optional options object. -- * @returns Promise resolving to a signed transaction string. -- */ -- async signTransaction(transaction, from, opts) { -- const address = _ethsigutil.normalize.call(void 0, from); -- const keyring = await this.getKeyringForAccount( -- address -- ); -- if (!keyring.signTransaction) { -- throw new Error("KeyringController - The keyring for the current address does not support the method signTransaction." /* UnsupportedSignTransaction */); -- } -- return await keyring.signTransaction(address, transaction, opts); -- } -- /** -- * Convert a base transaction to a base UserOperation. -- * -- * @param from - Address of the sender. -- * @param transactions - Base transactions to include in the UserOperation. -- * @param executionContext - The execution context to use for the UserOperation. -- * @returns A pseudo-UserOperation that can be used to construct a real. -- */ -- async prepareUserOperation(from, transactions, executionContext) { -- const address = _ethsigutil.normalize.call(void 0, from); -- const keyring = await this.getKeyringForAccount( -- address -- ); -- if (!keyring.prepareUserOperation) { -- throw new Error("KeyringController - The keyring for the current address does not support the method prepareUserOperation." /* UnsupportedPrepareUserOperation */); -- } -- return await keyring.prepareUserOperation( -- address, -- transactions, -- executionContext -- ); -- } -- /** -- * Patches properties of a UserOperation. Currently, only the -- * `paymasterAndData` can be patched. -- * -- * @param from - Address of the sender. -- * @param userOp - UserOperation to patch. -- * @param executionContext - The execution context to use for the UserOperation. -- * @returns A patch to apply to the UserOperation. -- */ -- async patchUserOperation(from, userOp, executionContext) { -- const address = _ethsigutil.normalize.call(void 0, from); -- const keyring = await this.getKeyringForAccount( -- address -- ); -- if (!keyring.patchUserOperation) { -- throw new Error("KeyringController - The keyring for the current address does not support the method patchUserOperation." /* UnsupportedPatchUserOperation */); -- } -- return await keyring.patchUserOperation(address, userOp, executionContext); -- } -- /** -- * Signs an UserOperation. -- * -- * @param from - Address of the sender. -- * @param userOp - UserOperation to sign. -- * @param executionContext - The execution context to use for the UserOperation. -- * @returns The signature of the UserOperation. -- */ -- async signUserOperation(from, userOp, executionContext) { -- const address = _ethsigutil.normalize.call(void 0, from); -- const keyring = await this.getKeyringForAccount( -- address -- ); -- if (!keyring.signUserOperation) { -- throw new Error("KeyringController - The keyring for the current address does not support the method signUserOperation." /* UnsupportedSignUserOperation */); -- } -- return await keyring.signUserOperation(address, userOp, executionContext); -- } -- /** -- * Changes the password used to encrypt the vault. -- * -- * @param password - The new password. -- * @returns Promise resolving when the operation completes. -- */ -- changePassword(password) { -- return _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -- if (!this.state.isUnlocked) { -- throw new Error("KeyringController - Cannot persist vault without password and encryption key" /* MissingCredentials */); -- } -- assertIsValidPassword(password); -- _chunkNOCGQCUMjs.__privateSet.call(void 0, this, _password, password); -- if (_chunkNOCGQCUMjs.__privateGet.call(void 0, this, _cacheEncryptionKey)) { -- this.update((state) => { -- delete state.encryptionKey; -- delete state.encryptionSalt; -- }); -- } -- }); -- } -- /** -- * Attempts to decrypt the current vault and load its keyrings, -- * using the given encryption key and salt. -- * -- * @param encryptionKey - Key to unlock the keychain. -- * @param encryptionSalt - Salt to unlock the keychain. -- * @returns Promise resolving when the operation completes. -- */ -- async submitEncryptionKey(encryptionKey, encryptionSalt) { -- return _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _withRollback, withRollback_fn).call(this, async () => { -- _chunkNOCGQCUMjs.__privateSet.call(void 0, this, _keyrings, await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _unlockKeyrings, unlockKeyrings_fn).call(this, void 0, encryptionKey, encryptionSalt)); -- _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _setUnlocked, setUnlocked_fn).call(this); -- }); -- } -- /** -- * Attempts to decrypt the current vault and load its keyrings, -- * using the given password. -- * -- * @param password - Password to unlock the keychain. -- * @returns Promise resolving when the operation completes. -- */ -- async submitPassword(password) { -- return _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _withRollback, withRollback_fn).call(this, async () => { -- _chunkNOCGQCUMjs.__privateSet.call(void 0, this, _keyrings, await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _unlockKeyrings, unlockKeyrings_fn).call(this, password)); -- _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _setUnlocked, setUnlocked_fn).call(this); -- }); -- } -- /** -- * Verifies the that the seed phrase restores the current keychain's accounts. -- * -- * @returns Promise resolving to the seed phrase as Uint8Array. -- */ -- async verifySeedPhrase() { -- const primaryKeyring = this.getKeyringsByType("HD Key Tree" /* hd */)[0]; -- if (!primaryKeyring) { -- throw new Error("No HD keyring found."); -- } -- assertHasUint8ArrayMnemonic(primaryKeyring); -- const seedWords = primaryKeyring.mnemonic; -- const accounts = await primaryKeyring.getAccounts(); -- if (accounts.length === 0) { -- throw new Error("Cannot verify an empty keyring."); -- } -- const hdKeyringBuilder = _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _getKeyringBuilderForType, getKeyringBuilderForType_fn).call(this, "HD Key Tree" /* hd */); -- const hdKeyring = hdKeyringBuilder(); -- await hdKeyring.deserialize({ -- mnemonic: seedWords, -- numberOfAccounts: accounts.length -- }); -- const testAccounts = await hdKeyring.getAccounts(); -- if (testAccounts.length !== accounts.length) { -- throw new Error("Seed phrase imported incorrect number of accounts."); -- } -- testAccounts.forEach((account, i) => { -- if (account.toLowerCase() !== accounts[i].toLowerCase()) { -- throw new Error("Seed phrase imported different accounts."); -- } -- }); -- return seedWords; -- } -- async withKeyring(selector, operation, options = { -- createIfMissing: false -- }) { -- return _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -- let keyring; -- if ("address" in selector) { -- keyring = await this.getKeyringForAccount(selector.address); -- } else { -- keyring = this.getKeyringsByType(selector.type)[selector.index || 0]; -- if (!keyring && options.createIfMissing) { -- keyring = await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _newKeyring, newKeyring_fn).call(this, selector.type, options.createWithData); -- } -- } -- if (!keyring) { -- throw new Error("KeyringController - Keyring not found." /* KeyringNotFound */); -- } -- const result = await operation(keyring); -- if (Object.is(result, keyring)) { -- throw new Error("KeyringController - Returning keyring instances is unsafe" /* UnsafeDirectKeyringAccess */); -- } -- return result; -- }); -- } -- // QR Hardware related methods -- /** -- * Get QR Hardware keyring. -- * -- * @returns The QR Keyring if defined, otherwise undefined -- */ -- getQRKeyring() { -- return this.getKeyringsByType("QR Hardware Wallet Device" /* qr */)[0]; -- } -- /** -- * Get QR hardware keyring. If it doesn't exist, add it. -- * -- * @returns The added keyring -- */ -- async getOrAddQRKeyring() { -- return this.getQRKeyring() || await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _persistOrRollback, persistOrRollback_fn).call(this, async () => _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _addQRKeyring, addQRKeyring_fn).call(this)); -- } -- // TODO: Replace `any` with type -- // eslint-disable-next-line @typescript-eslint/no-explicit-any -- async restoreQRKeyring(serialized) { -- return _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -- const keyring = this.getQRKeyring() || await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _addQRKeyring, addQRKeyring_fn).call(this); -- keyring.deserialize(serialized); -- }); -- } -- async resetQRKeyringState() { -- (await this.getOrAddQRKeyring()).resetStore(); -- } -- async getQRKeyringState() { -- return (await this.getOrAddQRKeyring()).getMemStore(); -- } -- async submitQRCryptoHDKey(cryptoHDKey) { -- (await this.getOrAddQRKeyring()).submitCryptoHDKey(cryptoHDKey); -- } -- async submitQRCryptoAccount(cryptoAccount) { -- (await this.getOrAddQRKeyring()).submitCryptoAccount(cryptoAccount); -- } -- async submitQRSignature(requestId, ethSignature) { -- (await this.getOrAddQRKeyring()).submitSignature(requestId, ethSignature); -- } -- async cancelQRSignRequest() { -- (await this.getOrAddQRKeyring()).cancelSignRequest(); -- } -- /** -- * Cancels qr keyring sync. -- */ -- async cancelQRSynchronization() { -- (await this.getOrAddQRKeyring()).cancelSync(); -- } -- async connectQRHardware(page) { -- return _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -- try { -- const keyring = this.getQRKeyring() || await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _addQRKeyring, addQRKeyring_fn).call(this); -- let accounts; -- switch (page) { -- case -1: -- accounts = await keyring.getPreviousPage(); -- break; -- case 1: -- accounts = await keyring.getNextPage(); -- break; -- default: -- accounts = await keyring.getFirstPage(); -- } -- return accounts.map((account) => { -- return { -- ...account, -- balance: "0x0" -- }; -- }); -- } catch (e) { -- throw new Error(`Unspecified error when connect QR Hardware, ${e}`); -- } -- }); -- } -- async unlockQRHardwareWalletAccount(index) { -- return _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -- const keyring = this.getQRKeyring() || await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _addQRKeyring, addQRKeyring_fn).call(this); -- keyring.setAccountToUnlock(index); -- await keyring.addAccounts(1); -- }); -- } -- async getAccountKeyringType(account) { -- const keyring = await this.getKeyringForAccount( -- account -- ); -- return keyring.type; -- } -- async forgetQRDevice() { -- return _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -- const keyring = this.getQRKeyring(); -- if (!keyring) { -- return { removedAccounts: [], remainingAccounts: [] }; -- } -- const allAccounts = await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn).call(this); -- keyring.forgetDevice(); -- const remainingAccounts = await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn).call(this); -- const removedAccounts = allAccounts.filter( -- (address) => !remainingAccounts.includes(address) -- ); -- return { removedAccounts, remainingAccounts }; -- }); -- } --}; --_controllerOperationMutex = new WeakMap(); --_vaultOperationMutex = new WeakMap(); --_keyringBuilders = new WeakMap(); --_keyrings = new WeakMap(); --_unsupportedKeyrings = new WeakMap(); --_password = new WeakMap(); --_encryptor = new WeakMap(); --_cacheEncryptionKey = new WeakMap(); --_qrKeyringStateListener = new WeakMap(); --_registerMessageHandlers = new WeakSet(); --registerMessageHandlers_fn = function() { -- this.messagingSystem.registerActionHandler( -- `${name}:signMessage`, -- this.signMessage.bind(this) -- ); -- this.messagingSystem.registerActionHandler( -- `${name}:signPersonalMessage`, -- this.signPersonalMessage.bind(this) -- ); -- this.messagingSystem.registerActionHandler( -- `${name}:signTypedMessage`, -- this.signTypedMessage.bind(this) -- ); -- this.messagingSystem.registerActionHandler( -- `${name}:decryptMessage`, -- this.decryptMessage.bind(this) -- ); -- this.messagingSystem.registerActionHandler( -- `${name}:getEncryptionPublicKey`, -- this.getEncryptionPublicKey.bind(this) -- ); -- this.messagingSystem.registerActionHandler( -- `${name}:getAccounts`, -- this.getAccounts.bind(this) -- ); -- this.messagingSystem.registerActionHandler( -- `${name}:getKeyringsByType`, -- this.getKeyringsByType.bind(this) -- ); -- this.messagingSystem.registerActionHandler( -- `${name}:getKeyringForAccount`, -- this.getKeyringForAccount.bind(this) -- ); -- this.messagingSystem.registerActionHandler( -- `${name}:persistAllKeyrings`, -- this.persistAllKeyrings.bind(this) -- ); -- this.messagingSystem.registerActionHandler( -- `${name}:prepareUserOperation`, -- this.prepareUserOperation.bind(this) -- ); -- this.messagingSystem.registerActionHandler( -- `${name}:patchUserOperation`, -- this.patchUserOperation.bind(this) -- ); -- this.messagingSystem.registerActionHandler( -- `${name}:signUserOperation`, -- this.signUserOperation.bind(this) -- ); --}; --_getKeyringBuilderForType = new WeakSet(); --getKeyringBuilderForType_fn = function(type) { -- return _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _keyringBuilders).find( -- (keyringBuilder) => keyringBuilder.type === type -- ); --}; --_addQRKeyring = new WeakSet(); --addQRKeyring_fn = async function() { -- _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this); -- return await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _newKeyring, newKeyring_fn).call(this, "QR Hardware Wallet Device" /* qr */); --}; --_subscribeToQRKeyringEvents = new WeakSet(); --subscribeToQRKeyringEvents_fn = function(qrKeyring) { -- _chunkNOCGQCUMjs.__privateSet.call(void 0, this, _qrKeyringStateListener, (state) => { -- this.messagingSystem.publish(`${name}:qrKeyringStateChange`, state); -- }); -- qrKeyring.getMemStore().subscribe(_chunkNOCGQCUMjs.__privateGet.call(void 0, this, _qrKeyringStateListener)); --}; --_unsubscribeFromQRKeyringsEvents = new WeakSet(); --unsubscribeFromQRKeyringsEvents_fn = function() { -- const qrKeyrings = this.getKeyringsByType( -- "QR Hardware Wallet Device" /* qr */ -- ); -- qrKeyrings.forEach((qrKeyring) => { -- if (_chunkNOCGQCUMjs.__privateGet.call(void 0, this, _qrKeyringStateListener)) { -- qrKeyring.getMemStore().unsubscribe(_chunkNOCGQCUMjs.__privateGet.call(void 0, this, _qrKeyringStateListener)); -- } -- }); --}; --_createNewVaultWithKeyring = new WeakSet(); --createNewVaultWithKeyring_fn = async function(password, keyring) { -- _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this); -- if (typeof password !== "string") { -- throw new TypeError("KeyringController - Password must be of type string." /* WrongPasswordType */); -- } -- _chunkNOCGQCUMjs.__privateSet.call(void 0, this, _password, password); -- await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _clearKeyrings, clearKeyrings_fn).call(this); -- await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _createKeyringWithFirstAccount, createKeyringWithFirstAccount_fn).call(this, keyring.type, keyring.opts); -- _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _setUnlocked, setUnlocked_fn).call(this); --}; --_getUpdatedKeyrings = new WeakSet(); --getUpdatedKeyrings_fn = async function() { -- return Promise.all(_chunkNOCGQCUMjs.__privateGet.call(void 0, this, _keyrings).map(displayForKeyring)); --}; --_getSerializedKeyrings = new WeakSet(); --getSerializedKeyrings_fn = async function({ includeUnsupported } = { -- includeUnsupported: true --}) { -- const serializedKeyrings = await Promise.all( -- _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _keyrings).map(async (keyring) => { -- const [type, data] = await Promise.all([ -- keyring.type, -- keyring.serialize() -- ]); -- return { type, data }; -- }) -- ); -- if (includeUnsupported) { -- serializedKeyrings.push(..._chunkNOCGQCUMjs.__privateGet.call(void 0, this, _unsupportedKeyrings)); -- } -- return serializedKeyrings; --}; --_restoreSerializedKeyrings = new WeakSet(); --restoreSerializedKeyrings_fn = async function(serializedKeyrings) { -- await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _clearKeyrings, clearKeyrings_fn).call(this); -- for (const serializedKeyring of serializedKeyrings) { -- await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _restoreKeyring, restoreKeyring_fn).call(this, serializedKeyring); -- } --}; --_unlockKeyrings = new WeakSet(); --unlockKeyrings_fn = async function(password, encryptionKey, encryptionSalt) { -- return _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _withVaultLock, withVaultLock_fn).call(this, async ({ releaseLock }) => { -- const encryptedVault = this.state.vault; -- if (!encryptedVault) { -- throw new Error("KeyringController - Cannot unlock without a previous vault." /* VaultError */); -- } -- let vault; -- const updatedState = {}; -- if (_chunkNOCGQCUMjs.__privateGet.call(void 0, this, _cacheEncryptionKey)) { -- assertIsExportableKeyEncryptor(_chunkNOCGQCUMjs.__privateGet.call(void 0, this, _encryptor)); -- if (password) { -- const result = await _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _encryptor).decryptWithDetail( -- password, -- encryptedVault -- ); -- vault = result.vault; -- _chunkNOCGQCUMjs.__privateSet.call(void 0, this, _password, password); -- updatedState.encryptionKey = result.exportedKeyString; -- updatedState.encryptionSalt = result.salt; -- } else { -- const parsedEncryptedVault = JSON.parse(encryptedVault); -- if (encryptionSalt !== parsedEncryptedVault.salt) { -- throw new Error("KeyringController - Encryption key and salt provided are expired" /* ExpiredCredentials */); -- } -- if (typeof encryptionKey !== "string") { -- throw new TypeError("KeyringController - Password must be of type string." /* WrongPasswordType */); -- } -- const key = await _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _encryptor).importKey(encryptionKey); -- vault = await _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _encryptor).decryptWithKey( -- key, -- parsedEncryptedVault -- ); -- updatedState.encryptionKey = encryptionKey; -- updatedState.encryptionSalt = encryptionSalt; -- } -- } else { -- if (typeof password !== "string") { -- throw new TypeError("KeyringController - Password must be of type string." /* WrongPasswordType */); -- } -- vault = await _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _encryptor).decrypt(password, encryptedVault); -- _chunkNOCGQCUMjs.__privateSet.call(void 0, this, _password, password); -- } -- if (!isSerializedKeyringsArray(vault)) { -- throw new Error("KeyringController - The decrypted vault has an unexpected shape." /* VaultDataError */); -- } -- await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _restoreSerializedKeyrings, restoreSerializedKeyrings_fn).call(this, vault); -- const updatedKeyrings = await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _getUpdatedKeyrings, getUpdatedKeyrings_fn).call(this); -- this.update((state) => { -- state.keyrings = updatedKeyrings; -- if (updatedState.encryptionKey || updatedState.encryptionSalt) { -- state.encryptionKey = updatedState.encryptionKey; -- state.encryptionSalt = updatedState.encryptionSalt; -- } -- }); -- if (_chunkNOCGQCUMjs.__privateGet.call(void 0, this, _password) && (!_chunkNOCGQCUMjs.__privateGet.call(void 0, this, _cacheEncryptionKey) || !encryptionKey) && _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _encryptor).isVaultUpdated && !_chunkNOCGQCUMjs.__privateGet.call(void 0, this, _encryptor).isVaultUpdated(encryptedVault)) { -- releaseLock(); -- await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _updateVault, updateVault_fn).call(this); -- } -- return _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _keyrings); -- }); --}; --_updateVault = new WeakSet(); --updateVault_fn = function() { -- return _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _withVaultLock, withVaultLock_fn).call(this, async () => { -- const { encryptionKey, encryptionSalt } = this.state; -- if (!_chunkNOCGQCUMjs.__privateGet.call(void 0, this, _password) && !encryptionKey) { -- throw new Error("KeyringController - Cannot persist vault without password and encryption key" /* MissingCredentials */); -- } -- const serializedKeyrings = await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _getSerializedKeyrings, getSerializedKeyrings_fn).call(this); -- if (!serializedKeyrings.some((keyring) => keyring.type === "HD Key Tree" /* hd */)) { -- throw new Error("KeyringController - No HD Keyring found" /* NoHdKeyring */); -- } -- const updatedState = {}; -- if (_chunkNOCGQCUMjs.__privateGet.call(void 0, this, _cacheEncryptionKey)) { -- assertIsExportableKeyEncryptor(_chunkNOCGQCUMjs.__privateGet.call(void 0, this, _encryptor)); -- if (encryptionKey) { -- const key = await _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _encryptor).importKey(encryptionKey); -- const vaultJSON = await _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _encryptor).encryptWithKey( -- key, -- serializedKeyrings -- ); -- vaultJSON.salt = encryptionSalt; -- updatedState.vault = JSON.stringify(vaultJSON); -- } else if (_chunkNOCGQCUMjs.__privateGet.call(void 0, this, _password)) { -- const { vault: newVault, exportedKeyString } = await _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _encryptor).encryptWithDetail( -- _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _password), -- serializedKeyrings -- ); -- updatedState.vault = newVault; -- updatedState.encryptionKey = exportedKeyString; -- } -- } else { -- assertIsValidPassword(_chunkNOCGQCUMjs.__privateGet.call(void 0, this, _password)); -- updatedState.vault = await _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _encryptor).encrypt( -- _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _password), -- serializedKeyrings -- ); -- } -- if (!updatedState.vault) { -- throw new Error("KeyringController - Cannot persist vault without vault information" /* MissingVaultData */); -- } -- const updatedKeyrings = await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _getUpdatedKeyrings, getUpdatedKeyrings_fn).call(this); -- this.update((state) => { -- state.vault = updatedState.vault; -- state.keyrings = updatedKeyrings; -- if (updatedState.encryptionKey) { -- state.encryptionKey = updatedState.encryptionKey; -- state.encryptionSalt = JSON.parse(updatedState.vault).salt; -- } -- }); -- return true; -- }); --}; --_getAccountsFromKeyrings = new WeakSet(); --getAccountsFromKeyrings_fn = async function() { -- const keyrings = _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _keyrings); -- const keyringArrays = await Promise.all( -- keyrings.map(async (keyring) => keyring.getAccounts()) -- ); -- const addresses = keyringArrays.reduce((res, arr) => { -- return res.concat(arr); -- }, []); -- return addresses.map(normalize); --}; --_createKeyringWithFirstAccount = new WeakSet(); --createKeyringWithFirstAccount_fn = async function(type, opts) { -- _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this); -- const keyring = await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _newKeyring, newKeyring_fn).call(this, type, opts); -- const [firstAccount] = await keyring.getAccounts(); -- if (!firstAccount) { -- throw new Error("KeyringController - First Account not found." /* NoFirstAccount */); -- } --}; --_newKeyring = new WeakSet(); --newKeyring_fn = async function(type, data) { -- _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this); -- const keyringBuilder = _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _getKeyringBuilderForType, getKeyringBuilderForType_fn).call(this, type); -- if (!keyringBuilder) { -- throw new Error( -- `${"KeyringController - No keyringBuilder found for keyring" /* NoKeyringBuilder */}. Keyring type: ${type}` -- ); -- } -- const keyring = keyringBuilder(); -- await keyring.deserialize(data); -- if (keyring.init) { -- await keyring.init(); -- } -- if (type === "HD Key Tree" /* hd */ && (!_utils.isObject.call(void 0, data) || !data.mnemonic)) { -- if (!keyring.generateRandomMnemonic) { -- throw new Error( -- "KeyringController - The current keyring does not support the method generateRandomMnemonic." /* UnsupportedGenerateRandomMnemonic */ -- ); -- } -- keyring.generateRandomMnemonic(); -- await keyring.addAccounts(1); -- } -- await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _checkForDuplicate, checkForDuplicate_fn).call(this, type, await keyring.getAccounts()); -- if (type === "QR Hardware Wallet Device" /* qr */) { -- _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _subscribeToQRKeyringEvents, subscribeToQRKeyringEvents_fn).call(this, keyring); -- } -- _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _keyrings).push(keyring); -- return keyring; --}; --_clearKeyrings = new WeakSet(); --clearKeyrings_fn = async function() { -- _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this); -- for (const keyring of _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _keyrings)) { -- await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _destroyKeyring, destroyKeyring_fn).call(this, keyring); -- } -- _chunkNOCGQCUMjs.__privateSet.call(void 0, this, _keyrings, []); --}; --_restoreKeyring = new WeakSet(); --restoreKeyring_fn = async function(serialized) { -- _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this); -- try { -- const { type, data } = serialized; -- return await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _newKeyring, newKeyring_fn).call(this, type, data); -- } catch (_) { -- _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _unsupportedKeyrings).push(serialized); -- return void 0; -- } --}; --_destroyKeyring = new WeakSet(); --destroyKeyring_fn = async function(keyring) { -- await keyring.destroy?.(); --}; --_removeEmptyKeyrings = new WeakSet(); --removeEmptyKeyrings_fn = async function() { -- _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this); -- const validKeyrings = []; -- await Promise.all( -- _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _keyrings).map(async (keyring) => { -- const accounts = await keyring.getAccounts(); -- if (accounts.length > 0) { -- validKeyrings.push(keyring); -- } else { -- await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _destroyKeyring, destroyKeyring_fn).call(this, keyring); -- } -- }) -- ); -- _chunkNOCGQCUMjs.__privateSet.call(void 0, this, _keyrings, validKeyrings); --}; --_checkForDuplicate = new WeakSet(); --checkForDuplicate_fn = async function(type, newAccountArray) { -- const accounts = await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn).call(this); -- switch (type) { -- case "Simple Key Pair" /* simple */: { -- const isIncluded = Boolean( -- accounts.find( -- (key) => newAccountArray[0] && (key === newAccountArray[0] || key === _utils.remove0x.call(void 0, newAccountArray[0])) -- ) -- ); -- if (isIncluded) { -- throw new Error("KeyringController - The account you are trying to import is a duplicate" /* DuplicatedAccount */); -- } -- return newAccountArray; -- } -- default: { -- return newAccountArray; -- } -- } --}; --_setUnlocked = new WeakSet(); --setUnlocked_fn = function() { -- _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this); -- this.update((state) => { -- state.isUnlocked = true; -- }); -- this.messagingSystem.publish(`${name}:unlock`); --}; --_persistOrRollback = new WeakSet(); --persistOrRollback_fn = async function(fn) { -- return _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _withRollback, withRollback_fn).call(this, async ({ releaseLock }) => { -- const callbackResult = await fn({ releaseLock }); -- await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _updateVault, updateVault_fn).call(this); -- return callbackResult; -- }); --}; --_withRollback = new WeakSet(); --withRollback_fn = async function(fn) { -- return _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _withControllerLock, withControllerLock_fn).call(this, async ({ releaseLock }) => { -- const currentSerializedKeyrings = await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _getSerializedKeyrings, getSerializedKeyrings_fn).call(this); -- const currentPassword = _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _password); -- try { -- return await fn({ releaseLock }); -- } catch (e) { -- await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _restoreSerializedKeyrings, restoreSerializedKeyrings_fn).call(this, currentSerializedKeyrings); -- _chunkNOCGQCUMjs.__privateSet.call(void 0, this, _password, currentPassword); -- throw e; -- } -- }); --}; --_assertControllerMutexIsLocked = new WeakSet(); --assertControllerMutexIsLocked_fn = function() { -- if (!_chunkNOCGQCUMjs.__privateGet.call(void 0, this, _controllerOperationMutex).isLocked()) { -- throw new Error("KeyringController - attempt to update vault during a non mutually exclusive operation" /* ControllerLockRequired */); -- } --}; --_withControllerLock = new WeakSet(); --withControllerLock_fn = async function(fn) { -- return withLock(_chunkNOCGQCUMjs.__privateGet.call(void 0, this, _controllerOperationMutex), fn); --}; --_withVaultLock = new WeakSet(); --withVaultLock_fn = async function(fn) { -- _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this); -- return withLock(_chunkNOCGQCUMjs.__privateGet.call(void 0, this, _vaultOperationMutex), fn); --}; --async function withLock(mutex, fn) { -- const releaseLock = await mutex.acquire(); -- try { -- return await fn({ releaseLock }); -- } finally { -- releaseLock(); -- } --} --var KeyringController_default = KeyringController; -- -- -- -- -- -- -- -- -- -- --exports.KeyringTypes = KeyringTypes; exports.isCustodyKeyring = isCustodyKeyring; exports.AccountImportStrategy = AccountImportStrategy; exports.SignTypedDataVersion = SignTypedDataVersion; exports.keyringBuilderFactory = keyringBuilderFactory; exports.getDefaultKeyringState = getDefaultKeyringState; exports.KeyringController = KeyringController; exports.KeyringController_default = KeyringController_default; --//# sourceMappingURL=chunk-BRS27QHF.js.map -\ No newline at end of file -diff --git a/dist/chunk-BRS27QHF.js.map b/dist/chunk-BRS27QHF.js.map -deleted file mode 100644 -index e425cd5a63571b3647494e83eb458829f9c6c941..0000000000000000000000000000000000000000 ---- a/dist/chunk-BRS27QHF.js.map -+++ /dev/null -@@ -1 +0,0 @@ --{"version":3,"sources":["../src/KeyringController.ts"],"names":["KeyringTypes","AccountImportStrategy","SignTypedDataVersion"],"mappings":"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","sourcesContent":["import type { TxData, TypedTransaction } from '@ethereumjs/tx';\nimport { isValidPrivate, toBuffer, getBinarySize } from '@ethereumjs/util';\nimport type {\n MetaMaskKeyring as QRKeyring,\n IKeyringState as IQRKeyringState,\n} from '@keystonehq/metamask-airgapped-keyring';\nimport type { RestrictedControllerMessenger } from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport * as encryptorUtils from '@metamask/browser-passworder';\nimport HDKeyring from '@metamask/eth-hd-keyring';\nimport { normalize as ethNormalize } from '@metamask/eth-sig-util';\nimport SimpleKeyring from '@metamask/eth-simple-keyring';\nimport type {\n EthBaseTransaction,\n EthBaseUserOperation,\n EthKeyring,\n EthUserOperation,\n EthUserOperationPatch,\n KeyringExecutionContext,\n} from '@metamask/keyring-api';\nimport type {\n PersonalMessageParams,\n TypedMessageParams,\n} from '@metamask/message-manager';\nimport type {\n Eip1024EncryptedData,\n Hex,\n Json,\n KeyringClass,\n} from '@metamask/utils';\nimport {\n add0x,\n assertIsStrictHexString,\n bytesToHex,\n hasProperty,\n isObject,\n isStrictHexString,\n isValidHexAddress,\n isValidJson,\n remove0x,\n} from '@metamask/utils';\nimport { Mutex } from 'async-mutex';\nimport type { MutexInterface } from 'async-mutex';\nimport Wallet, { thirdparty as importers } from 'ethereumjs-wallet';\nimport type { Patch } from 'immer';\n\nimport { KeyringControllerError } from './constants';\n\nconst name = 'KeyringController';\n\n/**\n * Available keyring types\n */\nexport enum KeyringTypes {\n simple = 'Simple Key Pair',\n hd = 'HD Key Tree',\n qr = 'QR Hardware Wallet Device',\n trezor = 'Trezor Hardware',\n ledger = 'Ledger Hardware',\n lattice = 'Lattice Hardware',\n snap = 'Snap Keyring',\n}\n\n/**\n * Custody keyring types are a special case, as they are not a single type\n * but they all start with the prefix \"Custody\".\n * @param keyringType - The type of the keyring.\n * @returns Whether the keyring type is a custody keyring.\n */\nexport const isCustodyKeyring = (keyringType: string): boolean => {\n return keyringType.startsWith('Custody');\n};\n\n/**\n * @type KeyringControllerState\n *\n * Keyring controller state\n * @property vault - Encrypted string representing keyring data\n * @property isUnlocked - Whether vault is unlocked\n * @property keyringTypes - Account types\n * @property keyrings - Group of accounts\n * @property encryptionKey - Keyring encryption key\n * @property encryptionSalt - Keyring encryption salt\n */\nexport type KeyringControllerState = {\n vault?: string;\n isUnlocked: boolean;\n keyrings: KeyringObject[];\n encryptionKey?: string;\n encryptionSalt?: string;\n};\n\nexport type KeyringControllerMemState = Omit<\n KeyringControllerState,\n 'vault' | 'encryptionKey' | 'encryptionSalt'\n>;\n\nexport type KeyringControllerGetStateAction = {\n type: `${typeof name}:getState`;\n handler: () => KeyringControllerState;\n};\n\nexport type KeyringControllerSignMessageAction = {\n type: `${typeof name}:signMessage`;\n handler: KeyringController['signMessage'];\n};\n\nexport type KeyringControllerSignPersonalMessageAction = {\n type: `${typeof name}:signPersonalMessage`;\n handler: KeyringController['signPersonalMessage'];\n};\n\nexport type KeyringControllerSignTypedMessageAction = {\n type: `${typeof name}:signTypedMessage`;\n handler: KeyringController['signTypedMessage'];\n};\n\nexport type KeyringControllerDecryptMessageAction = {\n type: `${typeof name}:decryptMessage`;\n handler: KeyringController['decryptMessage'];\n};\n\nexport type KeyringControllerGetEncryptionPublicKeyAction = {\n type: `${typeof name}:getEncryptionPublicKey`;\n handler: KeyringController['getEncryptionPublicKey'];\n};\n\nexport type KeyringControllerGetKeyringsByTypeAction = {\n type: `${typeof name}:getKeyringsByType`;\n handler: KeyringController['getKeyringsByType'];\n};\n\nexport type KeyringControllerGetKeyringForAccountAction = {\n type: `${typeof name}:getKeyringForAccount`;\n handler: KeyringController['getKeyringForAccount'];\n};\n\nexport type KeyringControllerGetAccountsAction = {\n type: `${typeof name}:getAccounts`;\n handler: KeyringController['getAccounts'];\n};\n\nexport type KeyringControllerPersistAllKeyringsAction = {\n type: `${typeof name}:persistAllKeyrings`;\n handler: KeyringController['persistAllKeyrings'];\n};\n\nexport type KeyringControllerPrepareUserOperationAction = {\n type: `${typeof name}:prepareUserOperation`;\n handler: KeyringController['prepareUserOperation'];\n};\n\nexport type KeyringControllerPatchUserOperationAction = {\n type: `${typeof name}:patchUserOperation`;\n handler: KeyringController['patchUserOperation'];\n};\n\nexport type KeyringControllerSignUserOperationAction = {\n type: `${typeof name}:signUserOperation`;\n handler: KeyringController['signUserOperation'];\n};\n\nexport type KeyringControllerStateChangeEvent = {\n type: `${typeof name}:stateChange`;\n payload: [KeyringControllerState, Patch[]];\n};\n\nexport type KeyringControllerAccountRemovedEvent = {\n type: `${typeof name}:accountRemoved`;\n payload: [string];\n};\n\nexport type KeyringControllerLockEvent = {\n type: `${typeof name}:lock`;\n payload: [];\n};\n\nexport type KeyringControllerUnlockEvent = {\n type: `${typeof name}:unlock`;\n payload: [];\n};\n\nexport type KeyringControllerQRKeyringStateChangeEvent = {\n type: `${typeof name}:qrKeyringStateChange`;\n payload: [ReturnType];\n};\n\nexport type KeyringControllerActions =\n | KeyringControllerGetStateAction\n | KeyringControllerSignMessageAction\n | KeyringControllerSignPersonalMessageAction\n | KeyringControllerSignTypedMessageAction\n | KeyringControllerDecryptMessageAction\n | KeyringControllerGetEncryptionPublicKeyAction\n | KeyringControllerGetAccountsAction\n | KeyringControllerGetKeyringsByTypeAction\n | KeyringControllerGetKeyringForAccountAction\n | KeyringControllerPersistAllKeyringsAction\n | KeyringControllerPrepareUserOperationAction\n | KeyringControllerPatchUserOperationAction\n | KeyringControllerSignUserOperationAction;\n\nexport type KeyringControllerEvents =\n | KeyringControllerStateChangeEvent\n | KeyringControllerLockEvent\n | KeyringControllerUnlockEvent\n | KeyringControllerAccountRemovedEvent\n | KeyringControllerQRKeyringStateChangeEvent;\n\nexport type KeyringControllerMessenger = RestrictedControllerMessenger<\n typeof name,\n KeyringControllerActions,\n KeyringControllerEvents,\n never,\n never\n>;\n\nexport type KeyringControllerOptions = {\n keyringBuilders?: { (): EthKeyring; type: string }[];\n messenger: KeyringControllerMessenger;\n state?: { vault?: string };\n} & (\n | {\n cacheEncryptionKey: true;\n encryptor?: ExportableKeyEncryptor;\n }\n | {\n cacheEncryptionKey?: false;\n encryptor?: GenericEncryptor | ExportableKeyEncryptor;\n }\n);\n\n/**\n * @type KeyringObject\n *\n * Keyring object to return in fullUpdate\n * @property type - Keyring type\n * @property accounts - Associated accounts\n */\nexport type KeyringObject = {\n accounts: string[];\n type: string;\n};\n\n/**\n * A strategy for importing an account\n */\nexport enum AccountImportStrategy {\n privateKey = 'privateKey',\n json = 'json',\n}\n\n/**\n * The `signTypedMessage` version\n *\n * @see https://docs.metamask.io/guide/signing-data.html\n */\nexport enum SignTypedDataVersion {\n V1 = 'V1',\n V3 = 'V3',\n V4 = 'V4',\n}\n\n/**\n * A serialized keyring object.\n */\nexport type SerializedKeyring = {\n type: string;\n data: Json;\n};\n\n/**\n * A generic encryptor interface that supports encrypting and decrypting\n * serializable data with a password.\n */\nexport type GenericEncryptor = {\n /**\n * Encrypts the given object with the given password.\n *\n * @param password - The password to encrypt with.\n * @param object - The object to encrypt.\n * @returns The encrypted string.\n */\n encrypt: (password: string, object: Json) => Promise;\n /**\n * Decrypts the given encrypted string with the given password.\n *\n * @param password - The password to decrypt with.\n * @param encryptedString - The encrypted string to decrypt.\n * @returns The decrypted object.\n */\n decrypt: (password: string, encryptedString: string) => Promise;\n /**\n * Optional vault migration helper. Checks if the provided vault is up to date\n * with the desired encryption algorithm.\n *\n * @param vault - The encrypted string to check.\n * @param targetDerivationParams - The desired target derivation params.\n * @returns The updated encrypted string.\n */\n isVaultUpdated?: (\n vault: string,\n targetDerivationParams?: encryptorUtils.KeyDerivationOptions,\n ) => boolean;\n};\n\n/**\n * An encryptor interface that supports encrypting and decrypting\n * serializable data with a password, and exporting and importing keys.\n */\nexport type ExportableKeyEncryptor = GenericEncryptor & {\n /**\n * Encrypts the given object with the given encryption key.\n *\n * @param key - The encryption key to encrypt with.\n * @param object - The object to encrypt.\n * @returns The encryption result.\n */\n encryptWithKey: (\n key: unknown,\n object: Json,\n ) => Promise;\n /**\n * Encrypts the given object with the given password, and returns the\n * encryption result and the exported key string.\n *\n * @param password - The password to encrypt with.\n * @param object - The object to encrypt.\n * @param salt - The optional salt to use for encryption.\n * @returns The encrypted string and the exported key string.\n */\n encryptWithDetail: (\n password: string,\n object: Json,\n salt?: string,\n ) => Promise;\n /**\n * Decrypts the given encrypted string with the given encryption key.\n *\n * @param key - The encryption key to decrypt with.\n * @param encryptedString - The encrypted string to decrypt.\n * @returns The decrypted object.\n */\n decryptWithKey: (key: unknown, encryptedString: string) => Promise;\n /**\n * Decrypts the given encrypted string with the given password, and returns\n * the decrypted object and the salt and exported key string used for\n * encryption.\n *\n * @param password - The password to decrypt with.\n * @param encryptedString - The encrypted string to decrypt.\n * @returns The decrypted object and the salt and exported key string used for\n * encryption.\n */\n decryptWithDetail: (\n password: string,\n encryptedString: string,\n ) => Promise;\n /**\n * Generates an encryption key from exported key string.\n *\n * @param key - The exported key string.\n * @returns The encryption key.\n */\n importKey: (key: string) => Promise;\n};\n\nexport type KeyringSelector =\n | {\n type: string;\n index?: number;\n }\n | {\n address: Hex;\n };\n\n/**\n * A function executed within a mutually exclusive lock, with\n * a mutex releaser in its option bag.\n *\n * @param releaseLock - A function to release the lock.\n */\ntype MutuallyExclusiveCallback = ({\n releaseLock,\n}: {\n releaseLock: MutexInterface.Releaser;\n}) => Promise;\n\n/**\n * Get builder function for `Keyring`\n *\n * Returns a builder function for `Keyring` with a `type` property.\n *\n * @param KeyringConstructor - The Keyring class for the builder.\n * @returns A builder function for the given Keyring.\n */\nexport function keyringBuilderFactory(KeyringConstructor: KeyringClass) {\n const builder = () => new KeyringConstructor();\n\n builder.type = KeyringConstructor.type;\n\n return builder;\n}\n\nconst defaultKeyringBuilders = [\n keyringBuilderFactory(SimpleKeyring),\n keyringBuilderFactory(HDKeyring),\n];\n\nexport const getDefaultKeyringState = (): KeyringControllerState => {\n return {\n isUnlocked: false,\n keyrings: [],\n };\n};\n\n/**\n * Assert that the given keyring has an exportable\n * mnemonic.\n *\n * @param keyring - The keyring to check\n * @throws When the keyring does not have a mnemonic\n */\nfunction assertHasUint8ArrayMnemonic(\n keyring: EthKeyring,\n): asserts keyring is EthKeyring & { mnemonic: Uint8Array } {\n if (\n !(\n hasProperty(keyring, 'mnemonic') && keyring.mnemonic instanceof Uint8Array\n )\n ) {\n throw new Error(\"Can't get mnemonic bytes from keyring\");\n }\n}\n\n/**\n * Assert that the provided encryptor supports\n * encryption and encryption key export.\n *\n * @param encryptor - The encryptor to check.\n * @throws If the encryptor does not support key encryption.\n */\nfunction assertIsExportableKeyEncryptor(\n encryptor: GenericEncryptor | ExportableKeyEncryptor,\n): asserts encryptor is ExportableKeyEncryptor {\n if (\n !(\n 'importKey' in encryptor &&\n typeof encryptor.importKey === 'function' &&\n 'decryptWithKey' in encryptor &&\n typeof encryptor.decryptWithKey === 'function' &&\n 'encryptWithKey' in encryptor &&\n typeof encryptor.encryptWithKey === 'function'\n )\n ) {\n throw new Error(KeyringControllerError.UnsupportedEncryptionKeyExport);\n }\n}\n\n/**\n * Assert that the provided password is a valid non-empty string.\n *\n * @param password - The password to check.\n * @throws If the password is not a valid string.\n */\nfunction assertIsValidPassword(password: unknown): asserts password is string {\n if (typeof password !== 'string') {\n throw new Error(KeyringControllerError.WrongPasswordType);\n }\n\n if (!password || !password.length) {\n throw new Error(KeyringControllerError.InvalidEmptyPassword);\n }\n}\n\n/**\n * Checks if the provided value is a serialized keyrings array.\n *\n * @param array - The value to check.\n * @returns True if the value is a serialized keyrings array.\n */\nfunction isSerializedKeyringsArray(\n array: unknown,\n): array is SerializedKeyring[] {\n return (\n typeof array === 'object' &&\n Array.isArray(array) &&\n array.every((value) => value.type && isValidJson(value.data))\n );\n}\n\n/**\n * Display For Keyring\n *\n * Is used for adding the current keyrings to the state object.\n *\n * @param keyring - The keyring to display.\n * @returns A keyring display object, with type and accounts properties.\n */\nasync function displayForKeyring(\n keyring: EthKeyring,\n): Promise<{ type: string; accounts: string[] }> {\n const accounts = await keyring.getAccounts();\n\n return {\n type: keyring.type,\n // Cast to `string[]` here is safe here because `accounts` has no nullish\n // values, and `normalize` returns `string` unless given a nullish value\n accounts: accounts.map(normalize) as string[],\n };\n}\n\n/**\n * Check if address is an ethereum address\n *\n * @param address - An address.\n * @returns Returns true if the address is an ethereum one, false otherwise.\n */\nfunction isEthAddress(address: string): boolean {\n // We first check if it's a matching `Hex` string, so that is narrows down\n // `address` as an `Hex` type, allowing us to use `isValidHexAddress`\n return (\n // NOTE: This function only checks for lowercased strings\n isStrictHexString(address.toLowerCase()) &&\n // This checks for lowercased addresses and checksum addresses too\n isValidHexAddress(address as Hex)\n );\n}\n\n/**\n * Normalize ethereum or non-EVM address.\n *\n * @param address - Ethereum or non-EVM address.\n * @returns The normalized address.\n */\nfunction normalize(address: string): string | undefined {\n // Since the `KeyringController` is only dealing with address, we have\n // no other way to get the associated account type with this address. So we\n // are down to check the actual address format for now\n // TODO: Find a better way to not have those runtime checks based on the\n // address value!\n return isEthAddress(address) ? ethNormalize(address) : address;\n}\n\n/**\n * Controller responsible for establishing and managing user identity.\n *\n * This class is a wrapper around the `eth-keyring-controller` package. The\n * `eth-keyring-controller` manages the \"vault\", which is an encrypted store of private keys, and\n * it manages the wallet \"lock\" state. This wrapper class has convenience methods for interacting\n * with the internal keyring controller and handling certain complex operations that involve the\n * keyrings.\n */\nexport class KeyringController extends BaseController<\n typeof name,\n KeyringControllerState,\n KeyringControllerMessenger\n> {\n readonly #controllerOperationMutex = new Mutex();\n\n readonly #vaultOperationMutex = new Mutex();\n\n #keyringBuilders: { (): EthKeyring; type: string }[];\n\n #keyrings: EthKeyring[];\n\n #unsupportedKeyrings: SerializedKeyring[];\n\n #password?: string;\n\n #encryptor: GenericEncryptor | ExportableKeyEncryptor;\n\n #cacheEncryptionKey: boolean;\n\n #qrKeyringStateListener?: (\n state: ReturnType,\n ) => void;\n\n /**\n * Creates a KeyringController instance.\n *\n * @param options - Initial options used to configure this controller\n * @param options.encryptor - An optional object for defining encryption schemes.\n * @param options.keyringBuilders - Set a new name for account.\n * @param options.cacheEncryptionKey - Whether to cache or not encryption key.\n * @param options.messenger - A restricted controller messenger.\n * @param options.state - Initial state to set on this controller.\n */\n constructor(options: KeyringControllerOptions) {\n const {\n encryptor = encryptorUtils,\n keyringBuilders,\n messenger,\n state,\n } = options;\n\n super({\n name,\n metadata: {\n vault: { persist: true, anonymous: false },\n isUnlocked: { persist: false, anonymous: true },\n keyrings: { persist: false, anonymous: false },\n encryptionKey: { persist: false, anonymous: false },\n encryptionSalt: { persist: false, anonymous: false },\n },\n messenger,\n state: {\n ...getDefaultKeyringState(),\n ...state,\n },\n });\n\n this.#keyringBuilders = keyringBuilders\n ? defaultKeyringBuilders.concat(keyringBuilders)\n : defaultKeyringBuilders;\n\n this.#encryptor = encryptor;\n this.#keyrings = [];\n this.#unsupportedKeyrings = [];\n\n // This option allows the controller to cache an exported key\n // for use in decrypting and encrypting data without password\n this.#cacheEncryptionKey = Boolean(options.cacheEncryptionKey);\n if (this.#cacheEncryptionKey) {\n assertIsExportableKeyEncryptor(encryptor);\n }\n\n this.#registerMessageHandlers();\n }\n\n /**\n * Adds a new account to the default (first) HD seed phrase keyring.\n *\n * @param accountCount - Number of accounts before adding a new one, used to\n * make the method idempotent.\n * @returns Promise resolving to the added account address.\n */\n async addNewAccount(accountCount?: number): Promise {\n return this.#persistOrRollback(async () => {\n const primaryKeyring = this.getKeyringsByType('HD Key Tree')[0] as\n | EthKeyring\n | undefined;\n if (!primaryKeyring) {\n throw new Error('No HD keyring found');\n }\n const oldAccounts = await primaryKeyring.getAccounts();\n\n if (accountCount && oldAccounts.length !== accountCount) {\n if (accountCount > oldAccounts.length) {\n throw new Error('Account out of sequence');\n }\n // we return the account already existing at index `accountCount`\n const existingAccount = oldAccounts[accountCount];\n\n if (!existingAccount) {\n throw new Error(`Can't find account at index ${accountCount}`);\n }\n\n return existingAccount;\n }\n\n const [addedAccountAddress] = await primaryKeyring.addAccounts(1);\n await this.verifySeedPhrase();\n\n return addedAccountAddress;\n });\n }\n\n /**\n * Adds a new account to the specified keyring.\n *\n * @param keyring - Keyring to add the account to.\n * @param accountCount - Number of accounts before adding a new one, used to make the method idempotent.\n * @returns Promise resolving to the added account address\n */\n async addNewAccountForKeyring(\n keyring: EthKeyring,\n accountCount?: number,\n ): Promise {\n // READ THIS CAREFULLY:\n // We still uses `Hex` here, since we are not using this method when creating\n // and account using a \"Snap Keyring\". This function assume the `keyring` is\n // ethereum compatible, but \"Snap Keyring\" might not be.\n return this.#persistOrRollback(async () => {\n const oldAccounts = await this.#getAccountsFromKeyrings();\n\n if (accountCount && oldAccounts.length !== accountCount) {\n if (accountCount > oldAccounts.length) {\n throw new Error('Account out of sequence');\n }\n\n const existingAccount = oldAccounts[accountCount];\n assertIsStrictHexString(existingAccount);\n\n return existingAccount;\n }\n\n await keyring.addAccounts(1);\n\n const addedAccountAddress = (await this.#getAccountsFromKeyrings()).find(\n (selectedAddress) => !oldAccounts.includes(selectedAddress),\n );\n assertIsStrictHexString(addedAccountAddress);\n\n return addedAccountAddress;\n });\n }\n\n /**\n * Adds a new account to the default (first) HD seed phrase keyring without updating identities in preferences.\n *\n * @returns Promise resolving to the added account address.\n */\n async addNewAccountWithoutUpdate(): Promise {\n return this.#persistOrRollback(async () => {\n const primaryKeyring = this.getKeyringsByType('HD Key Tree')[0] as\n | EthKeyring\n | undefined;\n if (!primaryKeyring) {\n throw new Error('No HD keyring found');\n }\n const [addedAccountAddress] = await primaryKeyring.addAccounts(1);\n await this.verifySeedPhrase();\n return addedAccountAddress;\n });\n }\n\n /**\n * Effectively the same as creating a new keychain then populating it\n * using the given seed phrase.\n *\n * @param password - Password to unlock keychain.\n * @param seed - A BIP39-compliant seed phrase as Uint8Array,\n * either as a string or an array of UTF-8 bytes that represent the string.\n * @returns Promise resolving when the operation ends successfully.\n */\n async createNewVaultAndRestore(\n password: string,\n seed: Uint8Array,\n ): Promise {\n return this.#persistOrRollback(async () => {\n assertIsValidPassword(password);\n\n await this.#createNewVaultWithKeyring(password, {\n type: KeyringTypes.hd,\n opts: {\n mnemonic: seed,\n numberOfAccounts: 1,\n },\n });\n });\n }\n\n /**\n * Create a new primary keychain and wipe any previous keychains.\n *\n * @param password - Password to unlock the new vault.\n * @returns Promise resolving when the operation ends successfully.\n */\n async createNewVaultAndKeychain(password: string) {\n return this.#persistOrRollback(async () => {\n const accounts = await this.#getAccountsFromKeyrings();\n if (!accounts.length) {\n await this.#createNewVaultWithKeyring(password, {\n type: KeyringTypes.hd,\n });\n }\n });\n }\n\n /**\n * Adds a new keyring of the given `type`.\n *\n * @param type - Keyring type name.\n * @param opts - Keyring options.\n * @throws If a builder for the given `type` does not exist.\n * @returns Promise resolving to the added keyring.\n */\n async addNewKeyring(\n type: KeyringTypes | string,\n opts?: unknown,\n ): Promise {\n if (type === KeyringTypes.qr) {\n return this.getOrAddQRKeyring();\n }\n\n return this.#persistOrRollback(async () => this.#newKeyring(type, opts));\n }\n\n /**\n * Method to verify a given password validity. Throws an\n * error if the password is invalid.\n *\n * @param password - Password of the keyring.\n */\n async verifyPassword(password: string) {\n if (!this.state.vault) {\n throw new Error(KeyringControllerError.VaultError);\n }\n await this.#encryptor.decrypt(password, this.state.vault);\n }\n\n /**\n * Returns the status of the vault.\n *\n * @returns Boolean returning true if the vault is unlocked.\n */\n isUnlocked(): boolean {\n return this.state.isUnlocked;\n }\n\n /**\n * Gets the seed phrase of the HD keyring.\n *\n * @param password - Password of the keyring.\n * @returns Promise resolving to the seed phrase.\n */\n async exportSeedPhrase(password: string): Promise {\n await this.verifyPassword(password);\n assertHasUint8ArrayMnemonic(this.#keyrings[0]);\n return this.#keyrings[0].mnemonic;\n }\n\n /**\n * Gets the private key from the keyring controlling an address.\n *\n * @param password - Password of the keyring.\n * @param address - Address to export.\n * @returns Promise resolving to the private key for an address.\n */\n async exportAccount(password: string, address: string): Promise {\n await this.verifyPassword(password);\n\n const keyring = (await this.getKeyringForAccount(\n address,\n )) as EthKeyring;\n if (!keyring.exportAccount) {\n throw new Error(KeyringControllerError.UnsupportedExportAccount);\n }\n\n return await keyring.exportAccount(normalize(address) as Hex);\n }\n\n /**\n * Returns the public addresses of all accounts from every keyring.\n *\n * @returns A promise resolving to an array of addresses.\n */\n async getAccounts(): Promise {\n return this.state.keyrings.reduce(\n (accounts, keyring) => accounts.concat(keyring.accounts),\n [],\n );\n }\n\n /**\n * Get encryption public key.\n *\n * @param account - An account address.\n * @param opts - Additional encryption options.\n * @throws If the `account` does not exist or does not support the `getEncryptionPublicKey` method\n * @returns Promise resolving to encyption public key of the `account` if one exists.\n */\n async getEncryptionPublicKey(\n account: string,\n opts?: Record,\n ): Promise {\n const address = ethNormalize(account) as Hex;\n const keyring = (await this.getKeyringForAccount(\n account,\n )) as EthKeyring;\n if (!keyring.getEncryptionPublicKey) {\n throw new Error(KeyringControllerError.UnsupportedGetEncryptionPublicKey);\n }\n\n return await keyring.getEncryptionPublicKey(address, opts);\n }\n\n /**\n * Attempts to decrypt the provided message parameters.\n *\n * @param messageParams - The decryption message parameters.\n * @param messageParams.from - The address of the account you want to use to decrypt the message.\n * @param messageParams.data - The encrypted data that you want to decrypt.\n * @returns The raw decryption result.\n */\n async decryptMessage(messageParams: {\n from: string;\n data: Eip1024EncryptedData;\n }): Promise {\n const address = ethNormalize(messageParams.from) as Hex;\n const keyring = (await this.getKeyringForAccount(\n address,\n )) as EthKeyring;\n if (!keyring.decryptMessage) {\n throw new Error(KeyringControllerError.UnsupportedDecryptMessage);\n }\n\n return keyring.decryptMessage(address, messageParams.data);\n }\n\n /**\n * Returns the currently initialized keyring that manages\n * the specified `address` if one exists.\n *\n * @deprecated Use of this method is discouraged as actions executed directly on\n * keyrings are not being reflected in the KeyringController state and not\n * persisted in the vault. Use `withKeyring` instead.\n * @param account - An account address.\n * @returns Promise resolving to keyring of the `account` if one exists.\n */\n async getKeyringForAccount(account: string): Promise {\n const address = normalize(account);\n\n const candidates = await Promise.all(\n this.#keyrings.map(async (keyring) => {\n return Promise.all([keyring, keyring.getAccounts()]);\n }),\n );\n\n const winners = candidates.filter((candidate) => {\n const accounts = candidate[1].map(normalize);\n return accounts.includes(address);\n });\n\n if (winners.length && winners[0]?.length) {\n return winners[0][0];\n }\n\n // Adding more info to the error\n let errorInfo = '';\n if (!candidates.length) {\n errorInfo = 'There are no keyrings';\n } else if (!winners.length) {\n errorInfo = 'There are keyrings, but none match the address';\n }\n throw new Error(\n `${KeyringControllerError.NoKeyring}. Error info: ${errorInfo}`,\n );\n }\n\n /**\n * Returns all keyrings of the given type.\n *\n * @deprecated Use of this method is discouraged as actions executed directly on\n * keyrings are not being reflected in the KeyringController state and not\n * persisted in the vault. Use `withKeyring` instead.\n * @param type - Keyring type name.\n * @returns An array of keyrings of the given type.\n */\n getKeyringsByType(type: KeyringTypes | string): unknown[] {\n return this.#keyrings.filter((keyring) => keyring.type === type);\n }\n\n /**\n * Persist all serialized keyrings in the vault.\n *\n * @deprecated This method is being phased out in favor of `withKeyring`.\n * @returns Promise resolving with `true` value when the\n * operation completes.\n */\n async persistAllKeyrings(): Promise {\n return this.#persistOrRollback(async () => true);\n }\n\n /**\n * Imports an account with the specified import strategy.\n *\n * @param strategy - Import strategy name.\n * @param args - Array of arguments to pass to the underlying stategy.\n * @throws Will throw when passed an unrecognized strategy.\n * @returns Promise resolving to the imported account address.\n */\n async importAccountWithStrategy(\n strategy: AccountImportStrategy,\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n args: any[],\n ): Promise {\n return this.#persistOrRollback(async () => {\n let privateKey;\n switch (strategy) {\n case 'privateKey':\n const [importedKey] = args;\n if (!importedKey) {\n throw new Error('Cannot import an empty key.');\n }\n const prefixed = add0x(importedKey);\n\n let bufferedPrivateKey;\n try {\n bufferedPrivateKey = toBuffer(prefixed);\n } catch {\n throw new Error('Cannot import invalid private key.');\n }\n\n if (\n !isValidPrivate(bufferedPrivateKey) ||\n // ensures that the key is 64 bytes long\n getBinarySize(prefixed) !== 64 + '0x'.length\n ) {\n throw new Error('Cannot import invalid private key.');\n }\n\n privateKey = remove0x(prefixed);\n break;\n case 'json':\n let wallet;\n const [input, password] = args;\n try {\n wallet = importers.fromEtherWallet(input, password);\n } catch (e) {\n wallet = wallet || (await Wallet.fromV3(input, password, true));\n }\n privateKey = bytesToHex(wallet.getPrivateKey());\n break;\n default:\n throw new Error(`Unexpected import strategy: '${strategy}'`);\n }\n const newKeyring = (await this.#newKeyring(KeyringTypes.simple, [\n privateKey,\n ])) as EthKeyring;\n const accounts = await newKeyring.getAccounts();\n return accounts[0];\n });\n }\n\n /**\n * Removes an account from keyring state.\n *\n * @param address - Address of the account to remove.\n * @fires KeyringController:accountRemoved\n * @returns Promise resolving when the account is removed.\n */\n async removeAccount(address: string): Promise {\n await this.#persistOrRollback(async () => {\n const keyring = (await this.getKeyringForAccount(\n address,\n )) as EthKeyring;\n\n // Not all the keyrings support this, so we have to check\n if (!keyring.removeAccount) {\n throw new Error(KeyringControllerError.UnsupportedRemoveAccount);\n }\n\n // The `removeAccount` method of snaps keyring is async. We have to update\n // the interface of the other keyrings to be async as well.\n // eslint-disable-next-line @typescript-eslint/await-thenable\n // FIXME: We do cast to `Hex` to makes the type checker happy here, and\n // because `Keyring.removeAccount` requires address to be `Hex`. Those\n // type would need to be updated for a full non-EVM support.\n await keyring.removeAccount(address as Hex);\n\n const accounts = await keyring.getAccounts();\n // Check if this was the last/only account\n if (accounts.length === 0) {\n await this.#removeEmptyKeyrings();\n }\n });\n\n this.messagingSystem.publish(`${name}:accountRemoved`, address);\n }\n\n /**\n * Deallocates all secrets and locks the wallet.\n *\n * @returns Promise resolving when the operation completes.\n */\n async setLocked(): Promise {\n return this.#withRollback(async () => {\n this.#unsubscribeFromQRKeyringsEvents();\n\n this.#password = undefined;\n await this.#clearKeyrings();\n\n this.update((state) => {\n state.isUnlocked = false;\n state.keyrings = [];\n });\n\n this.messagingSystem.publish(`${name}:lock`);\n });\n }\n\n /**\n * Signs message by calling down into a specific keyring.\n *\n * @param messageParams - PersonalMessageParams object to sign.\n * @returns Promise resolving to a signed message string.\n */\n async signMessage(messageParams: PersonalMessageParams): Promise {\n if (!messageParams.data) {\n throw new Error(\"Can't sign an empty message\");\n }\n\n const address = ethNormalize(messageParams.from) as Hex;\n const keyring = (await this.getKeyringForAccount(\n address,\n )) as EthKeyring;\n if (!keyring.signMessage) {\n throw new Error(KeyringControllerError.UnsupportedSignMessage);\n }\n\n return await keyring.signMessage(address, messageParams.data);\n }\n\n /**\n * Signs personal message by calling down into a specific keyring.\n *\n * @param messageParams - PersonalMessageParams object to sign.\n * @returns Promise resolving to a signed message string.\n */\n async signPersonalMessage(messageParams: PersonalMessageParams) {\n const address = ethNormalize(messageParams.from) as Hex;\n const keyring = (await this.getKeyringForAccount(\n address,\n )) as EthKeyring;\n if (!keyring.signPersonalMessage) {\n throw new Error(KeyringControllerError.UnsupportedSignPersonalMessage);\n }\n\n const normalizedData = normalize(messageParams.data) as Hex;\n\n return await keyring.signPersonalMessage(address, normalizedData);\n }\n\n /**\n * Signs typed message by calling down into a specific keyring.\n *\n * @param messageParams - TypedMessageParams object to sign.\n * @param version - Compatibility version EIP712.\n * @throws Will throw when passed an unrecognized version.\n * @returns Promise resolving to a signed message string or an error if any.\n */\n async signTypedMessage(\n messageParams: TypedMessageParams,\n version: SignTypedDataVersion,\n ): Promise {\n try {\n if (\n ![\n SignTypedDataVersion.V1,\n SignTypedDataVersion.V3,\n SignTypedDataVersion.V4,\n ].includes(version)\n ) {\n throw new Error(`Unexpected signTypedMessage version: '${version}'`);\n }\n\n // Cast to `Hex` here is safe here because `messageParams.from` is not nullish.\n // `normalize` returns `Hex` unless given a nullish value.\n const address = ethNormalize(messageParams.from) as Hex;\n const keyring = (await this.getKeyringForAccount(\n address,\n )) as EthKeyring;\n if (!keyring.signTypedData) {\n throw new Error(KeyringControllerError.UnsupportedSignTypedMessage);\n }\n\n return await keyring.signTypedData(\n address,\n version !== SignTypedDataVersion.V1 &&\n typeof messageParams.data === 'string'\n ? JSON.parse(messageParams.data)\n : messageParams.data,\n { version },\n );\n } catch (error) {\n throw new Error(`Keyring Controller signTypedMessage: ${error}`);\n }\n }\n\n /**\n * Signs a transaction by calling down into a specific keyring.\n *\n * @param transaction - Transaction object to sign. Must be a `ethereumjs-tx` transaction instance.\n * @param from - Address to sign from, should be in keychain.\n * @param opts - An optional options object.\n * @returns Promise resolving to a signed transaction string.\n */\n async signTransaction(\n transaction: TypedTransaction,\n from: string,\n opts?: Record,\n ): Promise {\n const address = ethNormalize(from) as Hex;\n const keyring = (await this.getKeyringForAccount(\n address,\n )) as EthKeyring;\n if (!keyring.signTransaction) {\n throw new Error(KeyringControllerError.UnsupportedSignTransaction);\n }\n\n return await keyring.signTransaction(address, transaction, opts);\n }\n\n /**\n * Convert a base transaction to a base UserOperation.\n *\n * @param from - Address of the sender.\n * @param transactions - Base transactions to include in the UserOperation.\n * @param executionContext - The execution context to use for the UserOperation.\n * @returns A pseudo-UserOperation that can be used to construct a real.\n */\n async prepareUserOperation(\n from: string,\n transactions: EthBaseTransaction[],\n executionContext: KeyringExecutionContext,\n ): Promise {\n const address = ethNormalize(from) as Hex;\n const keyring = (await this.getKeyringForAccount(\n address,\n )) as EthKeyring;\n\n if (!keyring.prepareUserOperation) {\n throw new Error(KeyringControllerError.UnsupportedPrepareUserOperation);\n }\n\n return await keyring.prepareUserOperation(\n address,\n transactions,\n executionContext,\n );\n }\n\n /**\n * Patches properties of a UserOperation. Currently, only the\n * `paymasterAndData` can be patched.\n *\n * @param from - Address of the sender.\n * @param userOp - UserOperation to patch.\n * @param executionContext - The execution context to use for the UserOperation.\n * @returns A patch to apply to the UserOperation.\n */\n async patchUserOperation(\n from: string,\n userOp: EthUserOperation,\n executionContext: KeyringExecutionContext,\n ): Promise {\n const address = ethNormalize(from) as Hex;\n const keyring = (await this.getKeyringForAccount(\n address,\n )) as EthKeyring;\n\n if (!keyring.patchUserOperation) {\n throw new Error(KeyringControllerError.UnsupportedPatchUserOperation);\n }\n\n return await keyring.patchUserOperation(address, userOp, executionContext);\n }\n\n /**\n * Signs an UserOperation.\n *\n * @param from - Address of the sender.\n * @param userOp - UserOperation to sign.\n * @param executionContext - The execution context to use for the UserOperation.\n * @returns The signature of the UserOperation.\n */\n async signUserOperation(\n from: string,\n userOp: EthUserOperation,\n executionContext: KeyringExecutionContext,\n ): Promise {\n const address = ethNormalize(from) as Hex;\n const keyring = (await this.getKeyringForAccount(\n address,\n )) as EthKeyring;\n\n if (!keyring.signUserOperation) {\n throw new Error(KeyringControllerError.UnsupportedSignUserOperation);\n }\n\n return await keyring.signUserOperation(address, userOp, executionContext);\n }\n\n /**\n * Changes the password used to encrypt the vault.\n *\n * @param password - The new password.\n * @returns Promise resolving when the operation completes.\n */\n changePassword(password: string): Promise {\n return this.#persistOrRollback(async () => {\n if (!this.state.isUnlocked) {\n throw new Error(KeyringControllerError.MissingCredentials);\n }\n\n assertIsValidPassword(password);\n\n this.#password = password;\n // We need to clear encryption key and salt from state\n // to force the controller to re-encrypt the vault using\n // the new password.\n if (this.#cacheEncryptionKey) {\n this.update((state) => {\n delete state.encryptionKey;\n delete state.encryptionSalt;\n });\n }\n });\n }\n\n /**\n * Attempts to decrypt the current vault and load its keyrings,\n * using the given encryption key and salt.\n *\n * @param encryptionKey - Key to unlock the keychain.\n * @param encryptionSalt - Salt to unlock the keychain.\n * @returns Promise resolving when the operation completes.\n */\n async submitEncryptionKey(\n encryptionKey: string,\n encryptionSalt: string,\n ): Promise {\n return this.#withRollback(async () => {\n this.#keyrings = await this.#unlockKeyrings(\n undefined,\n encryptionKey,\n encryptionSalt,\n );\n this.#setUnlocked();\n });\n }\n\n /**\n * Attempts to decrypt the current vault and load its keyrings,\n * using the given password.\n *\n * @param password - Password to unlock the keychain.\n * @returns Promise resolving when the operation completes.\n */\n async submitPassword(password: string): Promise {\n return this.#withRollback(async () => {\n this.#keyrings = await this.#unlockKeyrings(password);\n this.#setUnlocked();\n });\n }\n\n /**\n * Verifies the that the seed phrase restores the current keychain's accounts.\n *\n * @returns Promise resolving to the seed phrase as Uint8Array.\n */\n async verifySeedPhrase(): Promise {\n const primaryKeyring = this.getKeyringsByType(KeyringTypes.hd)[0] as\n | EthKeyring\n | undefined;\n if (!primaryKeyring) {\n throw new Error('No HD keyring found.');\n }\n\n assertHasUint8ArrayMnemonic(primaryKeyring);\n\n const seedWords = primaryKeyring.mnemonic;\n const accounts = await primaryKeyring.getAccounts();\n /* istanbul ignore if */\n if (accounts.length === 0) {\n throw new Error('Cannot verify an empty keyring.');\n }\n\n // The HD Keyring Builder is a default keyring builder\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const hdKeyringBuilder = this.#getKeyringBuilderForType(KeyringTypes.hd)!;\n\n const hdKeyring = hdKeyringBuilder();\n // @ts-expect-error @metamask/eth-hd-keyring correctly handles\n // Uint8Array seed phrases in the `deserialize` method.\n await hdKeyring.deserialize({\n mnemonic: seedWords,\n numberOfAccounts: accounts.length,\n });\n const testAccounts = await hdKeyring.getAccounts();\n /* istanbul ignore if */\n if (testAccounts.length !== accounts.length) {\n throw new Error('Seed phrase imported incorrect number of accounts.');\n }\n\n testAccounts.forEach((account: string, i: number) => {\n /* istanbul ignore if */\n if (account.toLowerCase() !== accounts[i].toLowerCase()) {\n throw new Error('Seed phrase imported different accounts.');\n }\n });\n\n return seedWords;\n }\n\n /**\n * Select a keyring and execute the given operation with\n * the selected keyring, as a mutually exclusive atomic\n * operation.\n *\n * The method automatically persists changes at the end of the\n * function execution, or rolls back the changes if an error\n * is thrown.\n *\n * @param selector - Keyring selector object.\n * @param operation - Function to execute with the selected keyring.\n * @param options - Additional options.\n * @param options.createIfMissing - Whether to create a new keyring if the selected one is missing.\n * @param options.createWithData - Optional data to use when creating a new keyring.\n * @returns Promise resolving to the result of the function execution.\n * @template SelectedKeyring - The type of the selected keyring.\n * @template CallbackResult - The type of the value resolved by the callback function.\n * @deprecated This method overload is deprecated. Use `withKeyring` without options instead.\n */\n async withKeyring<\n SelectedKeyring extends EthKeyring = EthKeyring,\n CallbackResult = void,\n >(\n selector: KeyringSelector,\n operation: (keyring: SelectedKeyring) => Promise,\n // eslint-disable-next-line @typescript-eslint/unified-signatures\n options:\n | { createIfMissing?: false }\n | { createIfMissing: true; createWithData?: unknown },\n ): Promise;\n\n /**\n * Select a keyring and execute the given operation with\n * the selected keyring, as a mutually exclusive atomic\n * operation.\n *\n * The method automatically persists changes at the end of the\n * function execution, or rolls back the changes if an error\n * is thrown.\n *\n * @param selector - Keyring selector object.\n * @param operation - Function to execute with the selected keyring.\n * @returns Promise resolving to the result of the function execution.\n * @template SelectedKeyring - The type of the selected keyring.\n * @template CallbackResult - The type of the value resolved by the callback function.\n */\n async withKeyring<\n SelectedKeyring extends EthKeyring = EthKeyring,\n CallbackResult = void,\n >(\n selector: KeyringSelector,\n operation: (keyring: SelectedKeyring) => Promise,\n ): Promise;\n\n async withKeyring<\n SelectedKeyring extends EthKeyring = EthKeyring,\n CallbackResult = void,\n >(\n selector: KeyringSelector,\n operation: (keyring: SelectedKeyring) => Promise,\n options:\n | { createIfMissing?: false }\n | { createIfMissing: true; createWithData?: unknown } = {\n createIfMissing: false,\n },\n ): Promise {\n return this.#persistOrRollback(async () => {\n let keyring: SelectedKeyring | undefined;\n\n if ('address' in selector) {\n keyring = (await this.getKeyringForAccount(selector.address)) as\n | SelectedKeyring\n | undefined;\n } else {\n keyring = this.getKeyringsByType(selector.type)[selector.index || 0] as\n | SelectedKeyring\n | undefined;\n\n if (!keyring && options.createIfMissing) {\n keyring = (await this.#newKeyring(\n selector.type,\n options.createWithData,\n )) as SelectedKeyring;\n }\n }\n\n if (!keyring) {\n throw new Error(KeyringControllerError.KeyringNotFound);\n }\n\n const result = await operation(keyring);\n\n if (Object.is(result, keyring)) {\n // Access to a keyring instance outside of controller safeguards\n // should be discouraged, as it can lead to unexpected behavior.\n // This error is thrown to prevent consumers using `withKeyring`\n // as a way to get a reference to a keyring instance.\n throw new Error(KeyringControllerError.UnsafeDirectKeyringAccess);\n }\n\n return result;\n });\n }\n\n // QR Hardware related methods\n\n /**\n * Get QR Hardware keyring.\n *\n * @returns The QR Keyring if defined, otherwise undefined\n */\n getQRKeyring(): QRKeyring | undefined {\n // QRKeyring is not yet compatible with Keyring type from @metamask/utils\n return this.getKeyringsByType(KeyringTypes.qr)[0] as unknown as QRKeyring;\n }\n\n /**\n * Get QR hardware keyring. If it doesn't exist, add it.\n *\n * @returns The added keyring\n */\n async getOrAddQRKeyring(): Promise {\n return (\n this.getQRKeyring() ||\n (await this.#persistOrRollback(async () => this.#addQRKeyring()))\n );\n }\n\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n async restoreQRKeyring(serialized: any): Promise {\n return this.#persistOrRollback(async () => {\n const keyring = this.getQRKeyring() || (await this.#addQRKeyring());\n keyring.deserialize(serialized);\n });\n }\n\n async resetQRKeyringState(): Promise {\n (await this.getOrAddQRKeyring()).resetStore();\n }\n\n async getQRKeyringState(): Promise {\n return (await this.getOrAddQRKeyring()).getMemStore();\n }\n\n async submitQRCryptoHDKey(cryptoHDKey: string): Promise {\n (await this.getOrAddQRKeyring()).submitCryptoHDKey(cryptoHDKey);\n }\n\n async submitQRCryptoAccount(cryptoAccount: string): Promise {\n (await this.getOrAddQRKeyring()).submitCryptoAccount(cryptoAccount);\n }\n\n async submitQRSignature(\n requestId: string,\n ethSignature: string,\n ): Promise {\n (await this.getOrAddQRKeyring()).submitSignature(requestId, ethSignature);\n }\n\n async cancelQRSignRequest(): Promise {\n (await this.getOrAddQRKeyring()).cancelSignRequest();\n }\n\n /**\n * Cancels qr keyring sync.\n */\n async cancelQRSynchronization(): Promise {\n // eslint-disable-next-line n/no-sync\n (await this.getOrAddQRKeyring()).cancelSync();\n }\n\n async connectQRHardware(\n page: number,\n ): Promise<{ balance: string; address: string; index: number }[]> {\n return this.#persistOrRollback(async () => {\n try {\n const keyring = this.getQRKeyring() || (await this.#addQRKeyring());\n let accounts;\n switch (page) {\n case -1:\n accounts = await keyring.getPreviousPage();\n break;\n case 1:\n accounts = await keyring.getNextPage();\n break;\n default:\n accounts = await keyring.getFirstPage();\n }\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return accounts.map((account: any) => {\n return {\n ...account,\n balance: '0x0',\n };\n });\n } catch (e) {\n // TODO: Add test case for when keyring throws\n /* istanbul ignore next */\n throw new Error(`Unspecified error when connect QR Hardware, ${e}`);\n }\n });\n }\n\n async unlockQRHardwareWalletAccount(index: number): Promise {\n return this.#persistOrRollback(async () => {\n const keyring = this.getQRKeyring() || (await this.#addQRKeyring());\n\n keyring.setAccountToUnlock(index);\n await keyring.addAccounts(1);\n });\n }\n\n async getAccountKeyringType(account: string): Promise {\n const keyring = (await this.getKeyringForAccount(\n account,\n )) as EthKeyring;\n return keyring.type;\n }\n\n async forgetQRDevice(): Promise<{\n removedAccounts: string[];\n remainingAccounts: string[];\n }> {\n return this.#persistOrRollback(async () => {\n const keyring = this.getQRKeyring();\n\n if (!keyring) {\n return { removedAccounts: [], remainingAccounts: [] };\n }\n\n const allAccounts = (await this.#getAccountsFromKeyrings()) as string[];\n keyring.forgetDevice();\n const remainingAccounts =\n (await this.#getAccountsFromKeyrings()) as string[];\n const removedAccounts = allAccounts.filter(\n (address: string) => !remainingAccounts.includes(address),\n );\n return { removedAccounts, remainingAccounts };\n });\n }\n\n /**\n * Constructor helper for registering this controller's messaging system\n * actions.\n */\n #registerMessageHandlers() {\n this.messagingSystem.registerActionHandler(\n `${name}:signMessage`,\n this.signMessage.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:signPersonalMessage`,\n this.signPersonalMessage.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:signTypedMessage`,\n this.signTypedMessage.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:decryptMessage`,\n this.decryptMessage.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:getEncryptionPublicKey`,\n this.getEncryptionPublicKey.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:getAccounts`,\n this.getAccounts.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:getKeyringsByType`,\n this.getKeyringsByType.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:getKeyringForAccount`,\n this.getKeyringForAccount.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:persistAllKeyrings`,\n this.persistAllKeyrings.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:prepareUserOperation`,\n this.prepareUserOperation.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:patchUserOperation`,\n this.patchUserOperation.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:signUserOperation`,\n this.signUserOperation.bind(this),\n );\n }\n\n /**\n * Get the keyring builder for the given `type`.\n *\n * @param type - The type of keyring to get the builder for.\n * @returns The keyring builder, or undefined if none exists.\n */\n #getKeyringBuilderForType(\n type: string,\n ): { (): EthKeyring; type: string } | undefined {\n return this.#keyringBuilders.find(\n (keyringBuilder) => keyringBuilder.type === type,\n );\n }\n\n /**\n * Add qr hardware keyring.\n *\n * @returns The added keyring\n * @throws If a QRKeyring builder is not provided\n * when initializing the controller\n */\n async #addQRKeyring(): Promise {\n this.#assertControllerMutexIsLocked();\n\n // QRKeyring is not yet compatible with Keyring type from @metamask/utils\n return (await this.#newKeyring(KeyringTypes.qr)) as unknown as QRKeyring;\n }\n\n /**\n * Subscribe to a QRKeyring state change events and\n * forward them through the messaging system.\n *\n * @param qrKeyring - The QRKeyring instance to subscribe to\n */\n #subscribeToQRKeyringEvents(qrKeyring: QRKeyring) {\n this.#qrKeyringStateListener = (state) => {\n this.messagingSystem.publish(`${name}:qrKeyringStateChange`, state);\n };\n\n qrKeyring.getMemStore().subscribe(this.#qrKeyringStateListener);\n }\n\n #unsubscribeFromQRKeyringsEvents() {\n const qrKeyrings = this.getKeyringsByType(\n KeyringTypes.qr,\n ) as unknown as QRKeyring[];\n\n qrKeyrings.forEach((qrKeyring) => {\n if (this.#qrKeyringStateListener) {\n qrKeyring.getMemStore().unsubscribe(this.#qrKeyringStateListener);\n }\n });\n }\n\n /**\n * Create new vault with an initial keyring\n *\n * Destroys any old encrypted storage,\n * creates a new encrypted store with the given password,\n * creates a new wallet with 1 account.\n *\n * @fires KeyringController:unlock\n * @param password - The password to encrypt the vault with.\n * @param keyring - A object containing the params to instantiate a new keyring.\n * @param keyring.type - The keyring type.\n * @param keyring.opts - Optional parameters required to instantiate the keyring.\n * @returns A promise that resolves to the state.\n */\n async #createNewVaultWithKeyring(\n password: string,\n keyring: {\n type: string;\n opts?: unknown;\n },\n ): Promise {\n this.#assertControllerMutexIsLocked();\n\n if (typeof password !== 'string') {\n throw new TypeError(KeyringControllerError.WrongPasswordType);\n }\n this.#password = password;\n\n await this.#clearKeyrings();\n await this.#createKeyringWithFirstAccount(keyring.type, keyring.opts);\n this.#setUnlocked();\n }\n\n /**\n * Get the updated array of each keyring's type and\n * accounts list.\n *\n * @returns A promise resolving to the updated keyrings array.\n */\n async #getUpdatedKeyrings(): Promise {\n return Promise.all(this.#keyrings.map(displayForKeyring));\n }\n\n /**\n * Serialize the current array of keyring instances,\n * including unsupported keyrings by default.\n *\n * @param options - Method options.\n * @param options.includeUnsupported - Whether to include unsupported keyrings.\n * @returns The serialized keyrings.\n */\n async #getSerializedKeyrings(\n { includeUnsupported }: { includeUnsupported: boolean } = {\n includeUnsupported: true,\n },\n ): Promise {\n const serializedKeyrings = await Promise.all(\n this.#keyrings.map(async (keyring) => {\n const [type, data] = await Promise.all([\n keyring.type,\n keyring.serialize(),\n ]);\n return { type, data };\n }),\n );\n\n if (includeUnsupported) {\n serializedKeyrings.push(...this.#unsupportedKeyrings);\n }\n\n return serializedKeyrings;\n }\n\n /**\n * Restore a serialized keyrings array.\n *\n * @param serializedKeyrings - The serialized keyrings array.\n */\n async #restoreSerializedKeyrings(\n serializedKeyrings: SerializedKeyring[],\n ): Promise {\n await this.#clearKeyrings();\n\n for (const serializedKeyring of serializedKeyrings) {\n await this.#restoreKeyring(serializedKeyring);\n }\n }\n\n /**\n * Unlock Keyrings, decrypting the vault and deserializing all\n * keyrings contained in it, using a password or an encryption key with salt.\n *\n * @param password - The keyring controller password.\n * @param encryptionKey - An exported key string to unlock keyrings with.\n * @param encryptionSalt - The salt used to encrypt the vault.\n * @returns A promise resolving to the deserialized keyrings array.\n */\n async #unlockKeyrings(\n password: string | undefined,\n encryptionKey?: string,\n encryptionSalt?: string,\n ): Promise[]> {\n return this.#withVaultLock(async ({ releaseLock }) => {\n const encryptedVault = this.state.vault;\n if (!encryptedVault) {\n throw new Error(KeyringControllerError.VaultError);\n }\n\n let vault;\n const updatedState: Partial = {};\n\n if (this.#cacheEncryptionKey) {\n assertIsExportableKeyEncryptor(this.#encryptor);\n\n if (password) {\n const result = await this.#encryptor.decryptWithDetail(\n password,\n encryptedVault,\n );\n vault = result.vault;\n this.#password = password;\n\n updatedState.encryptionKey = result.exportedKeyString;\n updatedState.encryptionSalt = result.salt;\n } else {\n const parsedEncryptedVault = JSON.parse(encryptedVault);\n\n if (encryptionSalt !== parsedEncryptedVault.salt) {\n throw new Error(KeyringControllerError.ExpiredCredentials);\n }\n\n if (typeof encryptionKey !== 'string') {\n throw new TypeError(KeyringControllerError.WrongPasswordType);\n }\n\n const key = await this.#encryptor.importKey(encryptionKey);\n vault = await this.#encryptor.decryptWithKey(\n key,\n parsedEncryptedVault,\n );\n\n // This call is required on the first call because encryptionKey\n // is not yet inside the memStore\n updatedState.encryptionKey = encryptionKey;\n // we can safely assume that encryptionSalt is defined here\n // because we compare it with the salt from the vault\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n updatedState.encryptionSalt = encryptionSalt!;\n }\n } else {\n if (typeof password !== 'string') {\n throw new TypeError(KeyringControllerError.WrongPasswordType);\n }\n\n vault = await this.#encryptor.decrypt(password, encryptedVault);\n this.#password = password;\n }\n\n if (!isSerializedKeyringsArray(vault)) {\n throw new Error(KeyringControllerError.VaultDataError);\n }\n\n await this.#restoreSerializedKeyrings(vault);\n const updatedKeyrings = await this.#getUpdatedKeyrings();\n\n this.update((state) => {\n state.keyrings = updatedKeyrings;\n if (updatedState.encryptionKey || updatedState.encryptionSalt) {\n state.encryptionKey = updatedState.encryptionKey;\n state.encryptionSalt = updatedState.encryptionSalt;\n }\n });\n\n if (\n this.#password &&\n (!this.#cacheEncryptionKey || !encryptionKey) &&\n this.#encryptor.isVaultUpdated &&\n !this.#encryptor.isVaultUpdated(encryptedVault)\n ) {\n // The lock needs to be released before persisting the keyrings\n // to avoid deadlock\n releaseLock();\n // Re-encrypt the vault with safer method if one is available\n await this.#updateVault();\n }\n\n return this.#keyrings;\n });\n }\n\n /**\n * Update the vault with the current keyrings.\n *\n * @returns A promise resolving to `true` if the operation is successful.\n */\n #updateVault(): Promise {\n return this.#withVaultLock(async () => {\n const { encryptionKey, encryptionSalt } = this.state;\n\n if (!this.#password && !encryptionKey) {\n throw new Error(KeyringControllerError.MissingCredentials);\n }\n\n const serializedKeyrings = await this.#getSerializedKeyrings();\n\n if (\n !serializedKeyrings.some((keyring) => keyring.type === KeyringTypes.hd)\n ) {\n throw new Error(KeyringControllerError.NoHdKeyring);\n }\n\n const updatedState: Partial = {};\n\n if (this.#cacheEncryptionKey) {\n assertIsExportableKeyEncryptor(this.#encryptor);\n\n if (encryptionKey) {\n const key = await this.#encryptor.importKey(encryptionKey);\n const vaultJSON = await this.#encryptor.encryptWithKey(\n key,\n serializedKeyrings,\n );\n vaultJSON.salt = encryptionSalt;\n updatedState.vault = JSON.stringify(vaultJSON);\n } else if (this.#password) {\n const { vault: newVault, exportedKeyString } =\n await this.#encryptor.encryptWithDetail(\n this.#password,\n serializedKeyrings,\n );\n\n updatedState.vault = newVault;\n updatedState.encryptionKey = exportedKeyString;\n }\n } else {\n assertIsValidPassword(this.#password);\n updatedState.vault = await this.#encryptor.encrypt(\n this.#password,\n serializedKeyrings,\n );\n }\n\n if (!updatedState.vault) {\n throw new Error(KeyringControllerError.MissingVaultData);\n }\n\n const updatedKeyrings = await this.#getUpdatedKeyrings();\n this.update((state) => {\n state.vault = updatedState.vault;\n state.keyrings = updatedKeyrings;\n if (updatedState.encryptionKey) {\n state.encryptionKey = updatedState.encryptionKey;\n state.encryptionSalt = JSON.parse(updatedState.vault as string).salt;\n }\n });\n\n return true;\n });\n }\n\n /**\n * Retrieves all the accounts from keyrings instances\n * that are currently in memory.\n *\n * @returns A promise resolving to an array of accounts.\n */\n async #getAccountsFromKeyrings(): Promise {\n const keyrings = this.#keyrings;\n\n const keyringArrays = await Promise.all(\n keyrings.map(async (keyring) => keyring.getAccounts()),\n );\n const addresses = keyringArrays.reduce((res, arr) => {\n return res.concat(arr);\n }, []);\n\n // Cast to `string[]` here is safe here because `addresses` has no nullish\n // values, and `normalize` returns `string` unless given a nullish value\n return addresses.map(normalize) as string[];\n }\n\n /**\n * Create a new keyring, ensuring that the first account is\n * also created.\n *\n * @param type - Keyring type to instantiate.\n * @param opts - Optional parameters required to instantiate the keyring.\n * @returns A promise that resolves if the operation is successful.\n */\n async #createKeyringWithFirstAccount(type: string, opts?: unknown) {\n this.#assertControllerMutexIsLocked();\n\n const keyring = (await this.#newKeyring(type, opts)) as EthKeyring;\n\n const [firstAccount] = await keyring.getAccounts();\n if (!firstAccount) {\n throw new Error(KeyringControllerError.NoFirstAccount);\n }\n }\n\n /**\n * Instantiate, initialize and return a new keyring of the given `type`,\n * using the given `opts`. The keyring is built using the keyring builder\n * registered for the given `type`.\n *\n *\n * @param type - The type of keyring to add.\n * @param data - The data to restore a previously serialized keyring.\n * @returns The new keyring.\n * @throws If the keyring includes duplicated accounts.\n */\n async #newKeyring(type: string, data?: unknown): Promise> {\n this.#assertControllerMutexIsLocked();\n\n const keyringBuilder = this.#getKeyringBuilderForType(type);\n\n if (!keyringBuilder) {\n throw new Error(\n `${KeyringControllerError.NoKeyringBuilder}. Keyring type: ${type}`,\n );\n }\n\n const keyring = keyringBuilder();\n\n // @ts-expect-error Enforce data type after updating clients\n await keyring.deserialize(data);\n\n if (keyring.init) {\n await keyring.init();\n }\n\n if (type === KeyringTypes.hd && (!isObject(data) || !data.mnemonic)) {\n if (!keyring.generateRandomMnemonic) {\n throw new Error(\n KeyringControllerError.UnsupportedGenerateRandomMnemonic,\n );\n }\n\n keyring.generateRandomMnemonic();\n await keyring.addAccounts(1);\n }\n\n await this.#checkForDuplicate(type, await keyring.getAccounts());\n\n if (type === KeyringTypes.qr) {\n // In case of a QR keyring type, we need to subscribe\n // to its events after creating it\n this.#subscribeToQRKeyringEvents(keyring as unknown as QRKeyring);\n }\n\n this.#keyrings.push(keyring);\n\n return keyring;\n }\n\n /**\n * Remove all managed keyrings, destroying all their\n * instances in memory.\n */\n async #clearKeyrings() {\n this.#assertControllerMutexIsLocked();\n for (const keyring of this.#keyrings) {\n await this.#destroyKeyring(keyring);\n }\n this.#keyrings = [];\n }\n\n /**\n * Restore a Keyring from a provided serialized payload.\n * On success, returns the resulting keyring instance.\n *\n * @param serialized - The serialized keyring.\n * @returns The deserialized keyring or undefined if the keyring type is unsupported.\n */\n async #restoreKeyring(\n serialized: SerializedKeyring,\n ): Promise | undefined> {\n this.#assertControllerMutexIsLocked();\n\n try {\n const { type, data } = serialized;\n return await this.#newKeyring(type, data);\n } catch (_) {\n this.#unsupportedKeyrings.push(serialized);\n return undefined;\n }\n }\n\n /**\n * Destroy Keyring\n *\n * Some keyrings support a method called `destroy`, that destroys the\n * keyring along with removing all its event listeners and, in some cases,\n * clears the keyring bridge iframe from the DOM.\n *\n * @param keyring - The keyring to destroy.\n */\n async #destroyKeyring(keyring: EthKeyring) {\n await keyring.destroy?.();\n }\n\n /**\n * Remove empty keyrings.\n *\n * Loops through the keyrings and removes the ones with empty accounts\n * (usually after removing the last / only account) from a keyring.\n */\n async #removeEmptyKeyrings(): Promise {\n this.#assertControllerMutexIsLocked();\n const validKeyrings: EthKeyring[] = [];\n\n // Since getAccounts returns a Promise\n // We need to wait to hear back form each keyring\n // in order to decide which ones are now valid (accounts.length > 0)\n\n await Promise.all(\n this.#keyrings.map(async (keyring: EthKeyring) => {\n const accounts = await keyring.getAccounts();\n if (accounts.length > 0) {\n validKeyrings.push(keyring);\n } else {\n await this.#destroyKeyring(keyring);\n }\n }),\n );\n this.#keyrings = validKeyrings;\n }\n\n /**\n * Checks for duplicate keypairs, using the the first account in the given\n * array. Rejects if a duplicate is found.\n *\n * Only supports 'Simple Key Pair'.\n *\n * @param type - The key pair type to check for.\n * @param newAccountArray - Array of new accounts.\n * @returns The account, if no duplicate is found.\n */\n async #checkForDuplicate(\n type: string,\n newAccountArray: string[],\n ): Promise {\n const accounts = await this.#getAccountsFromKeyrings();\n\n switch (type) {\n case KeyringTypes.simple: {\n const isIncluded = Boolean(\n accounts.find(\n (key) =>\n newAccountArray[0] &&\n (key === newAccountArray[0] ||\n key === remove0x(newAccountArray[0])),\n ),\n );\n\n if (isIncluded) {\n throw new Error(KeyringControllerError.DuplicatedAccount);\n }\n return newAccountArray;\n }\n\n default: {\n return newAccountArray;\n }\n }\n }\n\n /**\n * Set the `isUnlocked` to true and notify listeners\n * through the messenger.\n *\n * @fires KeyringController:unlock\n */\n #setUnlocked(): void {\n this.#assertControllerMutexIsLocked();\n\n this.update((state) => {\n state.isUnlocked = true;\n });\n this.messagingSystem.publish(`${name}:unlock`);\n }\n\n /**\n * Execute the given function after acquiring the controller lock\n * and save the keyrings to state after it, or rollback to their\n * previous state in case of error.\n *\n * @param fn - The function to execute.\n * @returns The result of the function.\n */\n async #persistOrRollback(fn: MutuallyExclusiveCallback): Promise {\n return this.#withRollback(async ({ releaseLock }) => {\n const callbackResult = await fn({ releaseLock });\n // State is committed only if the operation is successful\n await this.#updateVault();\n\n return callbackResult;\n });\n }\n\n /**\n * Execute the given function after acquiring the controller lock\n * and rollback keyrings and password states in case of error.\n *\n * @param fn - The function to execute atomically.\n * @returns The result of the function.\n */\n async #withRollback(fn: MutuallyExclusiveCallback): Promise {\n return this.#withControllerLock(async ({ releaseLock }) => {\n const currentSerializedKeyrings = await this.#getSerializedKeyrings();\n const currentPassword = this.#password;\n\n try {\n return await fn({ releaseLock });\n } catch (e) {\n // Keyrings and password are restored to their previous state\n await this.#restoreSerializedKeyrings(currentSerializedKeyrings);\n this.#password = currentPassword;\n\n throw e;\n }\n });\n }\n\n /**\n * Assert that the controller mutex is locked.\n *\n * @throws If the controller mutex is not locked.\n */\n #assertControllerMutexIsLocked() {\n if (!this.#controllerOperationMutex.isLocked()) {\n throw new Error(KeyringControllerError.ControllerLockRequired);\n }\n }\n\n /**\n * Lock the controller mutex before executing the given function,\n * and release it after the function is resolved or after an\n * error is thrown.\n *\n * This wrapper ensures that each mutable operation that interacts with the\n * controller and that changes its state is executed in a mutually exclusive way,\n * preventing unsafe concurrent access that could lead to unpredictable behavior.\n *\n * @param fn - The function to execute while the controller mutex is locked.\n * @returns The result of the function.\n */\n async #withControllerLock(fn: MutuallyExclusiveCallback): Promise {\n return withLock(this.#controllerOperationMutex, fn);\n }\n\n /**\n * Lock the vault mutex before executing the given function,\n * and release it after the function is resolved or after an\n * error is thrown.\n *\n * This ensures that each operation that interacts with the vault\n * is executed in a mutually exclusive way.\n *\n * @param fn - The function to execute while the vault mutex is locked.\n * @returns The result of the function.\n */\n async #withVaultLock(fn: MutuallyExclusiveCallback): Promise {\n this.#assertControllerMutexIsLocked();\n\n return withLock(this.#vaultOperationMutex, fn);\n }\n}\n\n/**\n * Lock the given mutex before executing the given function,\n * and release it after the function is resolved or after an\n * error is thrown.\n *\n * @param mutex - The mutex to lock.\n * @param fn - The function to execute while the mutex is locked.\n * @returns The result of the function.\n */\nasync function withLock(\n mutex: Mutex,\n fn: MutuallyExclusiveCallback,\n): Promise {\n const releaseLock = await mutex.acquire();\n\n try {\n return await fn({ releaseLock });\n } finally {\n releaseLock();\n }\n}\n\nexport default KeyringController;\n"]} -\ No newline at end of file -diff --git a/dist/chunk-L4UUWIZA.js b/dist/chunk-L4UUWIZA.js -new file mode 100644 -index 0000000000000000000000000000000000000000..3e85597548d0825ba3e1e7d938def8c630ba161a ---- /dev/null -+++ b/dist/chunk-L4UUWIZA.js -@@ -0,0 +1,1506 @@ -+"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } -+ -+ -+ -+ -+var _chunkNOCGQCUMjs = require('./chunk-NOCGQCUM.js'); -+ -+// src/KeyringController.ts -+var _util = require('@ethereumjs/util'); -+var _basecontroller = require('@metamask/base-controller'); -+var _browserpassworder = require('@metamask/browser-passworder'); var encryptorUtils = _interopRequireWildcard(_browserpassworder); -+var _ethhdkeyring = require('@metamask/eth-hd-keyring'); var _ethhdkeyring2 = _interopRequireDefault(_ethhdkeyring); -+var _ethsigutil = require('@metamask/eth-sig-util'); -+var _ethsimplekeyring = require('@metamask/eth-simple-keyring'); var _ethsimplekeyring2 = _interopRequireDefault(_ethsimplekeyring); -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+var _utils = require('@metamask/utils'); -+var _asyncmutex = require('async-mutex'); -+var _ethereumjswallet = require('ethereumjs-wallet'); var _ethereumjswallet2 = _interopRequireDefault(_ethereumjswallet); -+var name = "KeyringController"; -+var KeyringTypes = /* @__PURE__ */ ((KeyringTypes2) => { -+ KeyringTypes2["simple"] = "Simple Key Pair"; -+ KeyringTypes2["hd"] = "HD Key Tree"; -+ KeyringTypes2["qr"] = "QR Hardware Wallet Device"; -+ KeyringTypes2["trezor"] = "Trezor Hardware"; -+ KeyringTypes2["ledger"] = "Ledger Hardware"; -+ KeyringTypes2["lattice"] = "Lattice Hardware"; -+ KeyringTypes2["snap"] = "Snap Keyring"; -+ return KeyringTypes2; -+})(KeyringTypes || {}); -+var isCustodyKeyring = (keyringType) => { -+ return keyringType.startsWith("Custody"); -+}; -+var AccountImportStrategy = /* @__PURE__ */ ((AccountImportStrategy2) => { -+ AccountImportStrategy2["privateKey"] = "privateKey"; -+ AccountImportStrategy2["json"] = "json"; -+ return AccountImportStrategy2; -+})(AccountImportStrategy || {}); -+var SignTypedDataVersion = /* @__PURE__ */ ((SignTypedDataVersion2) => { -+ SignTypedDataVersion2["V1"] = "V1"; -+ SignTypedDataVersion2["V3"] = "V3"; -+ SignTypedDataVersion2["V4"] = "V4"; -+ return SignTypedDataVersion2; -+})(SignTypedDataVersion || {}); -+function keyringBuilderFactory(KeyringConstructor) { -+ const builder = () => new KeyringConstructor(); -+ builder.type = KeyringConstructor.type; -+ return builder; -+} -+var defaultKeyringBuilders = [ -+ keyringBuilderFactory(_ethsimplekeyring2.default), -+ keyringBuilderFactory(_ethhdkeyring2.default) -+]; -+var getDefaultKeyringState = () => { -+ return { -+ isUnlocked: false, -+ keyrings: [] -+ }; -+}; -+function assertHasUint8ArrayMnemonic(keyring) { -+ if (!(_utils.hasProperty.call(void 0, keyring, "mnemonic") && keyring.mnemonic instanceof Uint8Array)) { -+ throw new Error("Can't get mnemonic bytes from keyring"); -+ } -+} -+function assertIsExportableKeyEncryptor(encryptor) { -+ if (!("importKey" in encryptor && typeof encryptor.importKey === "function" && "decryptWithKey" in encryptor && typeof encryptor.decryptWithKey === "function" && "encryptWithKey" in encryptor && typeof encryptor.encryptWithKey === "function")) { -+ throw new Error("KeyringController - The encryptor does not support encryption key export." /* UnsupportedEncryptionKeyExport */); -+ } -+} -+function assertIsValidPassword(password) { -+ if (typeof password !== "string") { -+ throw new Error("KeyringController - Password must be of type string." /* WrongPasswordType */); -+ } -+ if (!password || !password.length) { -+ throw new Error("KeyringController - Password cannot be empty." /* InvalidEmptyPassword */); -+ } -+} -+function isSerializedKeyringsArray(array) { -+ return typeof array === "object" && Array.isArray(array) && array.every((value) => value.type && _utils.isValidJson.call(void 0, value.data)); -+} -+async function displayForKeyring(keyring) { -+ const accounts = await keyring.getAccounts(); -+ return { -+ type: keyring.type, -+ // Cast to `string[]` here is safe here because `accounts` has no nullish -+ // values, and `normalize` returns `string` unless given a nullish value -+ accounts: accounts.map(normalize) -+ }; -+} -+function isEthAddress(address) { -+ return ( -+ // NOTE: This function only checks for lowercased strings -+ _utils.isStrictHexString.call(void 0, address.toLowerCase()) && // This checks for lowercased addresses and checksum addresses too -+ _utils.isValidHexAddress.call(void 0, address) -+ ); -+} -+function normalize(address) { -+ return isEthAddress(address) ? _ethsigutil.normalize.call(void 0, address) : address; -+} -+var _controllerOperationMutex, _vaultOperationMutex, _keyringBuilders, _keyrings, _unsupportedKeyrings, _password, _encryptor, _cacheEncryptionKey, _qrKeyringStateListener, _registerMessageHandlers, registerMessageHandlers_fn, _getKeyringBuilderForType, getKeyringBuilderForType_fn, _addQRKeyring, addQRKeyring_fn, _subscribeToQRKeyringEvents, subscribeToQRKeyringEvents_fn, _unsubscribeFromQRKeyringsEvents, unsubscribeFromQRKeyringsEvents_fn, _createNewVaultWithKeyring, createNewVaultWithKeyring_fn, _getUpdatedKeyrings, getUpdatedKeyrings_fn, _getSerializedKeyrings, getSerializedKeyrings_fn, _restoreSerializedKeyrings, restoreSerializedKeyrings_fn, _unlockKeyrings, unlockKeyrings_fn, _updateVault, updateVault_fn, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn, _createKeyringWithFirstAccount, createKeyringWithFirstAccount_fn, _newKeyring, newKeyring_fn, _clearKeyrings, clearKeyrings_fn, _restoreKeyring, restoreKeyring_fn, _destroyKeyring, destroyKeyring_fn, _removeEmptyKeyrings, removeEmptyKeyrings_fn, _checkForDuplicate, checkForDuplicate_fn, _setUnlocked, setUnlocked_fn, _persistOrRollback, persistOrRollback_fn, _withRollback, withRollback_fn, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn, _withControllerLock, withControllerLock_fn, _withVaultLock, withVaultLock_fn; -+var KeyringController = class extends _basecontroller.BaseController { -+ /** -+ * Creates a KeyringController instance. -+ * -+ * @param options - Initial options used to configure this controller -+ * @param options.encryptor - An optional object for defining encryption schemes. -+ * @param options.keyringBuilders - Set a new name for account. -+ * @param options.cacheEncryptionKey - Whether to cache or not encryption key. -+ * @param options.messenger - A restricted controller messenger. -+ * @param options.state - Initial state to set on this controller. -+ */ -+ constructor(options) { -+ const { -+ encryptor = encryptorUtils, -+ keyringBuilders, -+ messenger, -+ state -+ } = options; -+ super({ -+ name, -+ metadata: { -+ vault: { persist: true, anonymous: false }, -+ isUnlocked: { persist: false, anonymous: true }, -+ keyrings: { persist: false, anonymous: false }, -+ encryptionKey: { persist: false, anonymous: false }, -+ encryptionSalt: { persist: false, anonymous: false } -+ }, -+ messenger, -+ state: { -+ ...getDefaultKeyringState(), -+ ...state -+ } -+ }); -+ /** -+ * Constructor helper for registering this controller's messaging system -+ * actions. -+ */ -+ _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _registerMessageHandlers); -+ /** -+ * Get the keyring builder for the given `type`. -+ * -+ * @param type - The type of keyring to get the builder for. -+ * @returns The keyring builder, or undefined if none exists. -+ */ -+ _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _getKeyringBuilderForType); -+ /** -+ * Add qr hardware keyring. -+ * -+ * @returns The added keyring -+ * @throws If a QRKeyring builder is not provided -+ * when initializing the controller -+ */ -+ _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _addQRKeyring); -+ /** -+ * Subscribe to a QRKeyring state change events and -+ * forward them through the messaging system. -+ * -+ * @param qrKeyring - The QRKeyring instance to subscribe to -+ */ -+ _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _subscribeToQRKeyringEvents); -+ _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _unsubscribeFromQRKeyringsEvents); -+ /** -+ * Create new vault with an initial keyring -+ * -+ * Destroys any old encrypted storage, -+ * creates a new encrypted store with the given password, -+ * creates a new wallet with 1 account. -+ * -+ * @fires KeyringController:unlock -+ * @param password - The password to encrypt the vault with. -+ * @param keyring - A object containing the params to instantiate a new keyring. -+ * @param keyring.type - The keyring type. -+ * @param keyring.opts - Optional parameters required to instantiate the keyring. -+ * @returns A promise that resolves to the state. -+ */ -+ _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _createNewVaultWithKeyring); -+ /** -+ * Get the updated array of each keyring's type and -+ * accounts list. -+ * -+ * @returns A promise resolving to the updated keyrings array. -+ */ -+ _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _getUpdatedKeyrings); -+ /** -+ * Serialize the current array of keyring instances, -+ * including unsupported keyrings by default. -+ * -+ * @param options - Method options. -+ * @param options.includeUnsupported - Whether to include unsupported keyrings. -+ * @returns The serialized keyrings. -+ */ -+ _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _getSerializedKeyrings); -+ /** -+ * Restore a serialized keyrings array. -+ * -+ * @param serializedKeyrings - The serialized keyrings array. -+ */ -+ _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _restoreSerializedKeyrings); -+ /** -+ * Unlock Keyrings, decrypting the vault and deserializing all -+ * keyrings contained in it, using a password or an encryption key with salt. -+ * -+ * @param password - The keyring controller password. -+ * @param encryptionKey - An exported key string to unlock keyrings with. -+ * @param encryptionSalt - The salt used to encrypt the vault. -+ * @returns A promise resolving to the deserialized keyrings array. -+ */ -+ _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _unlockKeyrings); -+ /** -+ * Update the vault with the current keyrings. -+ * -+ * @returns A promise resolving to `true` if the operation is successful. -+ */ -+ _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _updateVault); -+ /** -+ * Retrieves all the accounts from keyrings instances -+ * that are currently in memory. -+ * -+ * @returns A promise resolving to an array of accounts. -+ */ -+ _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _getAccountsFromKeyrings); -+ /** -+ * Create a new keyring, ensuring that the first account is -+ * also created. -+ * -+ * @param type - Keyring type to instantiate. -+ * @param opts - Optional parameters required to instantiate the keyring. -+ * @returns A promise that resolves if the operation is successful. -+ */ -+ _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _createKeyringWithFirstAccount); -+ /** -+ * Instantiate, initialize and return a new keyring of the given `type`, -+ * using the given `opts`. The keyring is built using the keyring builder -+ * registered for the given `type`. -+ * -+ * -+ * @param type - The type of keyring to add. -+ * @param data - The data to restore a previously serialized keyring. -+ * @returns The new keyring. -+ * @throws If the keyring includes duplicated accounts. -+ */ -+ _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _newKeyring); -+ /** -+ * Remove all managed keyrings, destroying all their -+ * instances in memory. -+ */ -+ _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _clearKeyrings); -+ /** -+ * Restore a Keyring from a provided serialized payload. -+ * On success, returns the resulting keyring instance. -+ * -+ * @param serialized - The serialized keyring. -+ * @returns The deserialized keyring or undefined if the keyring type is unsupported. -+ */ -+ _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _restoreKeyring); -+ /** -+ * Destroy Keyring -+ * -+ * Some keyrings support a method called `destroy`, that destroys the -+ * keyring along with removing all its event listeners and, in some cases, -+ * clears the keyring bridge iframe from the DOM. -+ * -+ * @param keyring - The keyring to destroy. -+ */ -+ _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _destroyKeyring); -+ /** -+ * Remove empty keyrings. -+ * -+ * Loops through the keyrings and removes the ones with empty accounts -+ * (usually after removing the last / only account) from a keyring. -+ */ -+ _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _removeEmptyKeyrings); -+ /** -+ * Checks for duplicate keypairs, using the the first account in the given -+ * array. Rejects if a duplicate is found. -+ * -+ * Only supports 'Simple Key Pair'. -+ * -+ * @param type - The key pair type to check for. -+ * @param newAccountArray - Array of new accounts. -+ * @returns The account, if no duplicate is found. -+ */ -+ _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _checkForDuplicate); -+ /** -+ * Set the `isUnlocked` to true and notify listeners -+ * through the messenger. -+ * -+ * @fires KeyringController:unlock -+ */ -+ _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _setUnlocked); -+ /** -+ * Execute the given function after acquiring the controller lock -+ * and save the keyrings to state after it, or rollback to their -+ * previous state in case of error. -+ * -+ * @param fn - The function to execute. -+ * @returns The result of the function. -+ */ -+ _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _persistOrRollback); -+ /** -+ * Execute the given function after acquiring the controller lock -+ * and rollback keyrings and password states in case of error. -+ * -+ * @param fn - The function to execute atomically. -+ * @returns The result of the function. -+ */ -+ _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _withRollback); -+ /** -+ * Assert that the controller mutex is locked. -+ * -+ * @throws If the controller mutex is not locked. -+ */ -+ _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _assertControllerMutexIsLocked); -+ /** -+ * Lock the controller mutex before executing the given function, -+ * and release it after the function is resolved or after an -+ * error is thrown. -+ * -+ * This wrapper ensures that each mutable operation that interacts with the -+ * controller and that changes its state is executed in a mutually exclusive way, -+ * preventing unsafe concurrent access that could lead to unpredictable behavior. -+ * -+ * @param fn - The function to execute while the controller mutex is locked. -+ * @returns The result of the function. -+ */ -+ _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _withControllerLock); -+ /** -+ * Lock the vault mutex before executing the given function, -+ * and release it after the function is resolved or after an -+ * error is thrown. -+ * -+ * This ensures that each operation that interacts with the vault -+ * is executed in a mutually exclusive way. -+ * -+ * @param fn - The function to execute while the vault mutex is locked. -+ * @returns The result of the function. -+ */ -+ _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _withVaultLock); -+ _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _controllerOperationMutex, new (0, _asyncmutex.Mutex)()); -+ _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _vaultOperationMutex, new (0, _asyncmutex.Mutex)()); -+ _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _keyringBuilders, void 0); -+ _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _keyrings, void 0); -+ _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _unsupportedKeyrings, void 0); -+ _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _password, void 0); -+ _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _encryptor, void 0); -+ _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _cacheEncryptionKey, void 0); -+ _chunkNOCGQCUMjs.__privateAdd.call(void 0, this, _qrKeyringStateListener, void 0); -+ _chunkNOCGQCUMjs.__privateSet.call(void 0, this, _keyringBuilders, keyringBuilders ? defaultKeyringBuilders.concat(keyringBuilders) : defaultKeyringBuilders); -+ _chunkNOCGQCUMjs.__privateSet.call(void 0, this, _encryptor, encryptor); -+ _chunkNOCGQCUMjs.__privateSet.call(void 0, this, _keyrings, []); -+ _chunkNOCGQCUMjs.__privateSet.call(void 0, this, _unsupportedKeyrings, []); -+ _chunkNOCGQCUMjs.__privateSet.call(void 0, this, _cacheEncryptionKey, Boolean(options.cacheEncryptionKey)); -+ if (_chunkNOCGQCUMjs.__privateGet.call(void 0, this, _cacheEncryptionKey)) { -+ assertIsExportableKeyEncryptor(encryptor); -+ } -+ _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _registerMessageHandlers, registerMessageHandlers_fn).call(this); -+ } -+ /** -+ * Adds a new account to the default (first) HD seed phrase keyring. -+ * -+ * @param accountCount - Number of accounts before adding a new one, used to -+ * make the method idempotent. -+ * @returns Promise resolving to the added account address. -+ */ -+ async addNewAccount(accountCount) { -+ return _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -+ const primaryKeyring = this.getKeyringsByType("HD Key Tree")[0]; -+ if (!primaryKeyring) { -+ throw new Error("No HD keyring found"); -+ } -+ const oldAccounts = await primaryKeyring.getAccounts(); -+ if (accountCount && oldAccounts.length !== accountCount) { -+ if (accountCount > oldAccounts.length) { -+ throw new Error("Account out of sequence"); -+ } -+ const existingAccount = oldAccounts[accountCount]; -+ if (!existingAccount) { -+ throw new Error(`Can't find account at index ${accountCount}`); -+ } -+ return existingAccount; -+ } -+ const [addedAccountAddress] = await primaryKeyring.addAccounts(1); -+ await this.verifySeedPhrase(); -+ return addedAccountAddress; -+ }); -+ } -+ /** -+ * Adds a new account to the specified keyring. -+ * -+ * @param keyring - Keyring to add the account to. -+ * @param accountCount - Number of accounts before adding a new one, used to make the method idempotent. -+ * @returns Promise resolving to the added account address -+ */ -+ async addNewAccountForKeyring(keyring, accountCount) { -+ return _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -+ const oldAccounts = await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn).call(this); -+ if (accountCount && oldAccounts.length !== accountCount) { -+ if (accountCount > oldAccounts.length) { -+ throw new Error("Account out of sequence"); -+ } -+ const existingAccount = oldAccounts[accountCount]; -+ _utils.assertIsStrictHexString.call(void 0, existingAccount); -+ return existingAccount; -+ } -+ await keyring.addAccounts(1); -+ const addedAccountAddress = (await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn).call(this)).find( -+ (selectedAddress) => !oldAccounts.includes(selectedAddress) -+ ); -+ _utils.assertIsStrictHexString.call(void 0, addedAccountAddress); -+ return addedAccountAddress; -+ }); -+ } -+ /** -+ * Adds a new account to the default (first) HD seed phrase keyring without updating identities in preferences. -+ * -+ * @returns Promise resolving to the added account address. -+ */ -+ async addNewAccountWithoutUpdate() { -+ return _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -+ const primaryKeyring = this.getKeyringsByType("HD Key Tree")[0]; -+ if (!primaryKeyring) { -+ throw new Error("No HD keyring found"); -+ } -+ const [addedAccountAddress] = await primaryKeyring.addAccounts(1); -+ await this.verifySeedPhrase(); -+ return addedAccountAddress; -+ }); -+ } -+ /** -+ * Effectively the same as creating a new keychain then populating it -+ * using the given seed phrase. -+ * -+ * @param password - Password to unlock keychain. -+ * @param seed - A BIP39-compliant seed phrase as Uint8Array, -+ * either as a string or an array of UTF-8 bytes that represent the string. -+ * @returns Promise resolving when the operation ends successfully. -+ */ -+ async createNewVaultAndRestore(password, seed) { -+ return _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -+ assertIsValidPassword(password); -+ await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _createNewVaultWithKeyring, createNewVaultWithKeyring_fn).call(this, password, { -+ type: "HD Key Tree" /* hd */, -+ opts: { -+ mnemonic: seed, -+ numberOfAccounts: 1 -+ } -+ }); -+ }); -+ } -+ /** -+ * Create a new primary keychain and wipe any previous keychains. -+ * -+ * @param password - Password to unlock the new vault. -+ * @returns Promise resolving when the operation ends successfully. -+ */ -+ async createNewVaultAndKeychain(password) { -+ return _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -+ const accounts = await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn).call(this); -+ if (!accounts.length) { -+ await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _createNewVaultWithKeyring, createNewVaultWithKeyring_fn).call(this, password, { -+ type: "HD Key Tree" /* hd */ -+ }); -+ } -+ }); -+ } -+ /** -+ * Adds a new keyring of the given `type`. -+ * -+ * @param type - Keyring type name. -+ * @param opts - Keyring options. -+ * @throws If a builder for the given `type` does not exist. -+ * @returns Promise resolving to the added keyring. -+ */ -+ async addNewKeyring(type, opts) { -+ if (type === "QR Hardware Wallet Device" /* qr */) { -+ return this.getOrAddQRKeyring(); -+ } -+ return _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _persistOrRollback, persistOrRollback_fn).call(this, async () => _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _newKeyring, newKeyring_fn).call(this, type, opts)); -+ } -+ /** -+ * Method to verify a given password validity. Throws an -+ * error if the password is invalid. -+ * -+ * @param password - Password of the keyring. -+ */ -+ async verifyPassword(password) { -+ if (!this.state.vault) { -+ throw new Error("KeyringController - Cannot unlock without a previous vault." /* VaultError */); -+ } -+ await _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _encryptor).decrypt(password, this.state.vault); -+ } -+ /** -+ * Returns the status of the vault. -+ * -+ * @returns Boolean returning true if the vault is unlocked. -+ */ -+ isUnlocked() { -+ return this.state.isUnlocked; -+ } -+ /** -+ * Gets the seed phrase of the HD keyring. -+ * -+ * @param password - Password of the keyring. -+ * @returns Promise resolving to the seed phrase. -+ */ -+ async exportSeedPhrase(password) { -+ await this.verifyPassword(password); -+ assertHasUint8ArrayMnemonic(_chunkNOCGQCUMjs.__privateGet.call(void 0, this, _keyrings)[0]); -+ return _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _keyrings)[0].mnemonic; -+ } -+ /** -+ * Gets the private key from the keyring controlling an address. -+ * -+ * @param password - Password of the keyring. -+ * @param address - Address to export. -+ * @returns Promise resolving to the private key for an address. -+ */ -+ async exportAccount(password, address) { -+ await this.verifyPassword(password); -+ const keyring = await this.getKeyringForAccount( -+ address -+ ); -+ if (!keyring.exportAccount) { -+ throw new Error("`KeyringController - The keyring for the current address does not support the method exportAccount" /* UnsupportedExportAccount */); -+ } -+ return await keyring.exportAccount(normalize(address)); -+ } -+ /** -+ * Returns the public addresses of all accounts from every keyring. -+ * -+ * @returns A promise resolving to an array of addresses. -+ */ -+ async getAccounts() { -+ return this.state.keyrings.reduce( -+ (accounts, keyring) => accounts.concat(keyring.accounts), -+ [] -+ ); -+ } -+ /** -+ * Get encryption public key. -+ * -+ * @param account - An account address. -+ * @param opts - Additional encryption options. -+ * @throws If the `account` does not exist or does not support the `getEncryptionPublicKey` method -+ * @returns Promise resolving to encyption public key of the `account` if one exists. -+ */ -+ async getEncryptionPublicKey(account, opts) { -+ const address = _ethsigutil.normalize.call(void 0, account); -+ const keyring = await this.getKeyringForAccount( -+ account -+ ); -+ if (!keyring.getEncryptionPublicKey) { -+ throw new Error("KeyringController - The keyring for the current address does not support the method getEncryptionPublicKey." /* UnsupportedGetEncryptionPublicKey */); -+ } -+ return await keyring.getEncryptionPublicKey(address, opts); -+ } -+ /** -+ * Attempts to decrypt the provided message parameters. -+ * -+ * @param messageParams - The decryption message parameters. -+ * @param messageParams.from - The address of the account you want to use to decrypt the message. -+ * @param messageParams.data - The encrypted data that you want to decrypt. -+ * @returns The raw decryption result. -+ */ -+ async decryptMessage(messageParams) { -+ const address = _ethsigutil.normalize.call(void 0, messageParams.from); -+ const keyring = await this.getKeyringForAccount( -+ address -+ ); -+ if (!keyring.decryptMessage) { -+ throw new Error("KeyringController - The keyring for the current address does not support the method decryptMessage." /* UnsupportedDecryptMessage */); -+ } -+ return keyring.decryptMessage(address, messageParams.data); -+ } -+ /** -+ * Returns the currently initialized keyring that manages -+ * the specified `address` if one exists. -+ * -+ * @deprecated Use of this method is discouraged as actions executed directly on -+ * keyrings are not being reflected in the KeyringController state and not -+ * persisted in the vault. Use `withKeyring` instead. -+ * @param account - An account address. -+ * @returns Promise resolving to keyring of the `account` if one exists. -+ */ -+ async getKeyringForAccount(account) { -+ const address = normalize(account); -+ const candidates = await Promise.all( -+ _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _keyrings).map(async (keyring) => { -+ return Promise.all([keyring, keyring.getAccounts()]); -+ }) -+ ); -+ const winners = candidates.filter((candidate) => { -+ const accounts = candidate[1].map(normalize); -+ return accounts.includes(address); -+ }); -+ if (winners.length && winners[0]?.length) { -+ return winners[0][0]; -+ } -+ let errorInfo = ""; -+ if (!candidates.length) { -+ errorInfo = "There are no keyrings"; -+ } else if (!winners.length) { -+ errorInfo = "There are keyrings, but none match the address"; -+ } -+ throw new Error( -+ `${"KeyringController - No keyring found" /* NoKeyring */}. Error info: ${errorInfo}` -+ ); -+ } -+ /** -+ * Returns all keyrings of the given type. -+ * -+ * @deprecated Use of this method is discouraged as actions executed directly on -+ * keyrings are not being reflected in the KeyringController state and not -+ * persisted in the vault. Use `withKeyring` instead. -+ * @param type - Keyring type name. -+ * @returns An array of keyrings of the given type. -+ */ -+ getKeyringsByType(type) { -+ return _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _keyrings).filter((keyring) => keyring.type === type); -+ } -+ /** -+ * Persist all serialized keyrings in the vault. -+ * -+ * @deprecated This method is being phased out in favor of `withKeyring`. -+ * @returns Promise resolving with `true` value when the -+ * operation completes. -+ */ -+ async persistAllKeyrings() { -+ return _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _persistOrRollback, persistOrRollback_fn).call(this, async () => true); -+ } -+ /** -+ * Imports an account with the specified import strategy. -+ * -+ * @param strategy - Import strategy name. -+ * @param args - Array of arguments to pass to the underlying stategy. -+ * @throws Will throw when passed an unrecognized strategy. -+ * @returns Promise resolving to the imported account address. -+ */ -+ async importAccountWithStrategy(strategy, args) { -+ return _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -+ let privateKey; -+ switch (strategy) { -+ case "privateKey": -+ const [importedKey] = args; -+ if (!importedKey) { -+ throw new Error("Cannot import an empty key."); -+ } -+ const prefixed = _utils.add0x.call(void 0, importedKey); -+ let bufferedPrivateKey; -+ try { -+ bufferedPrivateKey = _util.toBuffer.call(void 0, prefixed); -+ } catch { -+ throw new Error("Cannot import invalid private key."); -+ } -+ if (!_util.isValidPrivate.call(void 0, bufferedPrivateKey) || // ensures that the key is 64 bytes long -+ _util.getBinarySize.call(void 0, prefixed) !== 64 + "0x".length) { -+ throw new Error("Cannot import invalid private key."); -+ } -+ privateKey = _utils.remove0x.call(void 0, prefixed); -+ break; -+ case "json": -+ let wallet; -+ const [input, password] = args; -+ try { -+ wallet = _ethereumjswallet.thirdparty.fromEtherWallet(input, password); -+ } catch (e) { -+ wallet = wallet || await _ethereumjswallet2.default.fromV3(input, password, true); -+ } -+ privateKey = _utils.bytesToHex.call(void 0, wallet.getPrivateKey()); -+ break; -+ default: -+ throw new Error(`Unexpected import strategy: '${strategy}'`); -+ } -+ const newKeyring = await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _newKeyring, newKeyring_fn).call(this, "Simple Key Pair" /* simple */, [ -+ privateKey -+ ]); -+ const accounts = await newKeyring.getAccounts(); -+ return accounts[0]; -+ }); -+ } -+ /** -+ * Removes an account from keyring state. -+ * -+ * @param address - Address of the account to remove. -+ * @fires KeyringController:accountRemoved -+ * @returns Promise resolving when the account is removed. -+ */ -+ async removeAccount(address) { -+ await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -+ const keyring = await this.getKeyringForAccount( -+ address -+ ); -+ if (!keyring.removeAccount) { -+ throw new Error("`KeyringController - The keyring for the current address does not support the method removeAccount" /* UnsupportedRemoveAccount */); -+ } -+ await keyring.removeAccount(address); -+ const accounts = await keyring.getAccounts(); -+ if (accounts.length === 0) { -+ await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _removeEmptyKeyrings, removeEmptyKeyrings_fn).call(this); -+ } -+ }); -+ this.messagingSystem.publish(`${name}:accountRemoved`, address); -+ } -+ /** -+ * Deallocates all secrets and locks the wallet. -+ * -+ * @returns Promise resolving when the operation completes. -+ */ -+ async setLocked() { -+ return _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _withRollback, withRollback_fn).call(this, async () => { -+ _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _unsubscribeFromQRKeyringsEvents, unsubscribeFromQRKeyringsEvents_fn).call(this); -+ _chunkNOCGQCUMjs.__privateSet.call(void 0, this, _password, void 0); -+ await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _clearKeyrings, clearKeyrings_fn).call(this); -+ this.update((state) => { -+ state.isUnlocked = false; -+ state.keyrings = []; -+ delete state.encryptionKey; -+ delete state.encryptionSalt; -+ }); -+ this.messagingSystem.publish(`${name}:lock`); -+ }); -+ } -+ /** -+ * Signs message by calling down into a specific keyring. -+ * -+ * @param messageParams - PersonalMessageParams object to sign. -+ * @returns Promise resolving to a signed message string. -+ */ -+ async signMessage(messageParams) { -+ if (!messageParams.data) { -+ throw new Error("Can't sign an empty message"); -+ } -+ const address = _ethsigutil.normalize.call(void 0, messageParams.from); -+ const keyring = await this.getKeyringForAccount( -+ address -+ ); -+ if (!keyring.signMessage) { -+ throw new Error("KeyringController - The keyring for the current address does not support the method signMessage." /* UnsupportedSignMessage */); -+ } -+ return await keyring.signMessage(address, messageParams.data); -+ } -+ /** -+ * Signs personal message by calling down into a specific keyring. -+ * -+ * @param messageParams - PersonalMessageParams object to sign. -+ * @returns Promise resolving to a signed message string. -+ */ -+ async signPersonalMessage(messageParams) { -+ const address = _ethsigutil.normalize.call(void 0, messageParams.from); -+ const keyring = await this.getKeyringForAccount( -+ address -+ ); -+ if (!keyring.signPersonalMessage) { -+ throw new Error("KeyringController - The keyring for the current address does not support the method signPersonalMessage." /* UnsupportedSignPersonalMessage */); -+ } -+ const normalizedData = normalize(messageParams.data); -+ return await keyring.signPersonalMessage(address, normalizedData); -+ } -+ /** -+ * Signs typed message by calling down into a specific keyring. -+ * -+ * @param messageParams - TypedMessageParams object to sign. -+ * @param version - Compatibility version EIP712. -+ * @throws Will throw when passed an unrecognized version. -+ * @returns Promise resolving to a signed message string or an error if any. -+ */ -+ async signTypedMessage(messageParams, version) { -+ try { -+ if (![ -+ "V1" /* V1 */, -+ "V3" /* V3 */, -+ "V4" /* V4 */ -+ ].includes(version)) { -+ throw new Error(`Unexpected signTypedMessage version: '${version}'`); -+ } -+ const address = _ethsigutil.normalize.call(void 0, messageParams.from); -+ const keyring = await this.getKeyringForAccount( -+ address -+ ); -+ if (!keyring.signTypedData) { -+ throw new Error("KeyringController - The keyring for the current address does not support the method signTypedMessage." /* UnsupportedSignTypedMessage */); -+ } -+ return await keyring.signTypedData( -+ address, -+ version !== "V1" /* V1 */ && typeof messageParams.data === "string" ? JSON.parse(messageParams.data) : messageParams.data, -+ { version } -+ ); -+ } catch (error) { -+ throw new Error(`Keyring Controller signTypedMessage: ${error}`); -+ } -+ } -+ /** -+ * Signs a transaction by calling down into a specific keyring. -+ * -+ * @param transaction - Transaction object to sign. Must be a `ethereumjs-tx` transaction instance. -+ * @param from - Address to sign from, should be in keychain. -+ * @param opts - An optional options object. -+ * @returns Promise resolving to a signed transaction string. -+ */ -+ async signTransaction(transaction, from, opts) { -+ const address = _ethsigutil.normalize.call(void 0, from); -+ const keyring = await this.getKeyringForAccount( -+ address -+ ); -+ if (!keyring.signTransaction) { -+ throw new Error("KeyringController - The keyring for the current address does not support the method signTransaction." /* UnsupportedSignTransaction */); -+ } -+ return await keyring.signTransaction(address, transaction, opts); -+ } -+ /** -+ * Convert a base transaction to a base UserOperation. -+ * -+ * @param from - Address of the sender. -+ * @param transactions - Base transactions to include in the UserOperation. -+ * @param executionContext - The execution context to use for the UserOperation. -+ * @returns A pseudo-UserOperation that can be used to construct a real. -+ */ -+ async prepareUserOperation(from, transactions, executionContext) { -+ const address = _ethsigutil.normalize.call(void 0, from); -+ const keyring = await this.getKeyringForAccount( -+ address -+ ); -+ if (!keyring.prepareUserOperation) { -+ throw new Error("KeyringController - The keyring for the current address does not support the method prepareUserOperation." /* UnsupportedPrepareUserOperation */); -+ } -+ return await keyring.prepareUserOperation( -+ address, -+ transactions, -+ executionContext -+ ); -+ } -+ /** -+ * Patches properties of a UserOperation. Currently, only the -+ * `paymasterAndData` can be patched. -+ * -+ * @param from - Address of the sender. -+ * @param userOp - UserOperation to patch. -+ * @param executionContext - The execution context to use for the UserOperation. -+ * @returns A patch to apply to the UserOperation. -+ */ -+ async patchUserOperation(from, userOp, executionContext) { -+ const address = _ethsigutil.normalize.call(void 0, from); -+ const keyring = await this.getKeyringForAccount( -+ address -+ ); -+ if (!keyring.patchUserOperation) { -+ throw new Error("KeyringController - The keyring for the current address does not support the method patchUserOperation." /* UnsupportedPatchUserOperation */); -+ } -+ return await keyring.patchUserOperation(address, userOp, executionContext); -+ } -+ /** -+ * Signs an UserOperation. -+ * -+ * @param from - Address of the sender. -+ * @param userOp - UserOperation to sign. -+ * @param executionContext - The execution context to use for the UserOperation. -+ * @returns The signature of the UserOperation. -+ */ -+ async signUserOperation(from, userOp, executionContext) { -+ const address = _ethsigutil.normalize.call(void 0, from); -+ const keyring = await this.getKeyringForAccount( -+ address -+ ); -+ if (!keyring.signUserOperation) { -+ throw new Error("KeyringController - The keyring for the current address does not support the method signUserOperation." /* UnsupportedSignUserOperation */); -+ } -+ return await keyring.signUserOperation(address, userOp, executionContext); -+ } -+ /** -+ * Changes the password used to encrypt the vault. -+ * -+ * @param password - The new password. -+ * @returns Promise resolving when the operation completes. -+ */ -+ changePassword(password) { -+ return _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -+ if (!this.state.isUnlocked) { -+ throw new Error("KeyringController - Cannot persist vault without password and encryption key" /* MissingCredentials */); -+ } -+ assertIsValidPassword(password); -+ _chunkNOCGQCUMjs.__privateSet.call(void 0, this, _password, password); -+ if (_chunkNOCGQCUMjs.__privateGet.call(void 0, this, _cacheEncryptionKey)) { -+ this.update((state) => { -+ delete state.encryptionKey; -+ delete state.encryptionSalt; -+ }); -+ } -+ }); -+ } -+ /** -+ * Attempts to decrypt the current vault and load its keyrings, -+ * using the given encryption key and salt. -+ * -+ * @param encryptionKey - Key to unlock the keychain. -+ * @param encryptionSalt - Salt to unlock the keychain. -+ * @returns Promise resolving when the operation completes. -+ */ -+ async submitEncryptionKey(encryptionKey, encryptionSalt) { -+ return _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _withRollback, withRollback_fn).call(this, async () => { -+ _chunkNOCGQCUMjs.__privateSet.call(void 0, this, _keyrings, await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _unlockKeyrings, unlockKeyrings_fn).call(this, void 0, encryptionKey, encryptionSalt)); -+ _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _setUnlocked, setUnlocked_fn).call(this); -+ }); -+ } -+ /** -+ * Attempts to decrypt the current vault and load its keyrings, -+ * using the given password. -+ * -+ * @param password - Password to unlock the keychain. -+ * @returns Promise resolving when the operation completes. -+ */ -+ async submitPassword(password) { -+ return _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _withRollback, withRollback_fn).call(this, async () => { -+ _chunkNOCGQCUMjs.__privateSet.call(void 0, this, _keyrings, await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _unlockKeyrings, unlockKeyrings_fn).call(this, password)); -+ _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _setUnlocked, setUnlocked_fn).call(this); -+ }); -+ } -+ /** -+ * Verifies the that the seed phrase restores the current keychain's accounts. -+ * -+ * @returns Promise resolving to the seed phrase as Uint8Array. -+ */ -+ async verifySeedPhrase() { -+ const primaryKeyring = this.getKeyringsByType("HD Key Tree" /* hd */)[0]; -+ if (!primaryKeyring) { -+ throw new Error("No HD keyring found."); -+ } -+ assertHasUint8ArrayMnemonic(primaryKeyring); -+ const seedWords = primaryKeyring.mnemonic; -+ const accounts = await primaryKeyring.getAccounts(); -+ if (accounts.length === 0) { -+ throw new Error("Cannot verify an empty keyring."); -+ } -+ const hdKeyringBuilder = _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _getKeyringBuilderForType, getKeyringBuilderForType_fn).call(this, "HD Key Tree" /* hd */); -+ const hdKeyring = hdKeyringBuilder(); -+ await hdKeyring.deserialize({ -+ mnemonic: seedWords, -+ numberOfAccounts: accounts.length -+ }); -+ const testAccounts = await hdKeyring.getAccounts(); -+ if (testAccounts.length !== accounts.length) { -+ throw new Error("Seed phrase imported incorrect number of accounts."); -+ } -+ testAccounts.forEach((account, i) => { -+ if (account.toLowerCase() !== accounts[i].toLowerCase()) { -+ throw new Error("Seed phrase imported different accounts."); -+ } -+ }); -+ return seedWords; -+ } -+ async withKeyring(selector, operation, options = { -+ createIfMissing: false -+ }) { -+ return _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -+ let keyring; -+ if ("address" in selector) { -+ keyring = await this.getKeyringForAccount(selector.address); -+ } else { -+ keyring = this.getKeyringsByType(selector.type)[selector.index || 0]; -+ if (!keyring && options.createIfMissing) { -+ keyring = await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _newKeyring, newKeyring_fn).call(this, selector.type, options.createWithData); -+ } -+ } -+ if (!keyring) { -+ throw new Error("KeyringController - Keyring not found." /* KeyringNotFound */); -+ } -+ const result = await operation(keyring); -+ if (Object.is(result, keyring)) { -+ throw new Error("KeyringController - Returning keyring instances is unsafe" /* UnsafeDirectKeyringAccess */); -+ } -+ return result; -+ }); -+ } -+ // QR Hardware related methods -+ /** -+ * Get QR Hardware keyring. -+ * -+ * @returns The QR Keyring if defined, otherwise undefined -+ */ -+ getQRKeyring() { -+ return this.getKeyringsByType("QR Hardware Wallet Device" /* qr */)[0]; -+ } -+ /** -+ * Get QR hardware keyring. If it doesn't exist, add it. -+ * -+ * @returns The added keyring -+ */ -+ async getOrAddQRKeyring() { -+ return this.getQRKeyring() || await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _persistOrRollback, persistOrRollback_fn).call(this, async () => _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _addQRKeyring, addQRKeyring_fn).call(this)); -+ } -+ // TODO: Replace `any` with type -+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -+ async restoreQRKeyring(serialized) { -+ return _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -+ const keyring = this.getQRKeyring() || await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _addQRKeyring, addQRKeyring_fn).call(this); -+ keyring.deserialize(serialized); -+ }); -+ } -+ async resetQRKeyringState() { -+ (await this.getOrAddQRKeyring()).resetStore(); -+ } -+ async getQRKeyringState() { -+ return (await this.getOrAddQRKeyring()).getMemStore(); -+ } -+ async submitQRCryptoHDKey(cryptoHDKey) { -+ (await this.getOrAddQRKeyring()).submitCryptoHDKey(cryptoHDKey); -+ } -+ async submitQRCryptoAccount(cryptoAccount) { -+ (await this.getOrAddQRKeyring()).submitCryptoAccount(cryptoAccount); -+ } -+ async submitQRSignature(requestId, ethSignature) { -+ (await this.getOrAddQRKeyring()).submitSignature(requestId, ethSignature); -+ } -+ async cancelQRSignRequest() { -+ (await this.getOrAddQRKeyring()).cancelSignRequest(); -+ } -+ /** -+ * Cancels qr keyring sync. -+ */ -+ async cancelQRSynchronization() { -+ (await this.getOrAddQRKeyring()).cancelSync(); -+ } -+ async connectQRHardware(page) { -+ return _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -+ try { -+ const keyring = this.getQRKeyring() || await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _addQRKeyring, addQRKeyring_fn).call(this); -+ let accounts; -+ switch (page) { -+ case -1: -+ accounts = await keyring.getPreviousPage(); -+ break; -+ case 1: -+ accounts = await keyring.getNextPage(); -+ break; -+ default: -+ accounts = await keyring.getFirstPage(); -+ } -+ return accounts.map((account) => { -+ return { -+ ...account, -+ balance: "0x0" -+ }; -+ }); -+ } catch (e) { -+ throw new Error(`Unspecified error when connect QR Hardware, ${e}`); -+ } -+ }); -+ } -+ async unlockQRHardwareWalletAccount(index) { -+ return _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -+ const keyring = this.getQRKeyring() || await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _addQRKeyring, addQRKeyring_fn).call(this); -+ keyring.setAccountToUnlock(index); -+ await keyring.addAccounts(1); -+ }); -+ } -+ async getAccountKeyringType(account) { -+ const keyring = await this.getKeyringForAccount( -+ account -+ ); -+ return keyring.type; -+ } -+ async forgetQRDevice() { -+ return _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -+ const keyring = this.getQRKeyring(); -+ if (!keyring) { -+ return { removedAccounts: [], remainingAccounts: [] }; -+ } -+ const allAccounts = await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn).call(this); -+ keyring.forgetDevice(); -+ const remainingAccounts = await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn).call(this); -+ const removedAccounts = allAccounts.filter( -+ (address) => !remainingAccounts.includes(address) -+ ); -+ return { removedAccounts, remainingAccounts }; -+ }); -+ } -+}; -+_controllerOperationMutex = new WeakMap(); -+_vaultOperationMutex = new WeakMap(); -+_keyringBuilders = new WeakMap(); -+_keyrings = new WeakMap(); -+_unsupportedKeyrings = new WeakMap(); -+_password = new WeakMap(); -+_encryptor = new WeakMap(); -+_cacheEncryptionKey = new WeakMap(); -+_qrKeyringStateListener = new WeakMap(); -+_registerMessageHandlers = new WeakSet(); -+registerMessageHandlers_fn = function() { -+ this.messagingSystem.registerActionHandler( -+ `${name}:signMessage`, -+ this.signMessage.bind(this) -+ ); -+ this.messagingSystem.registerActionHandler( -+ `${name}:signPersonalMessage`, -+ this.signPersonalMessage.bind(this) -+ ); -+ this.messagingSystem.registerActionHandler( -+ `${name}:signTypedMessage`, -+ this.signTypedMessage.bind(this) -+ ); -+ this.messagingSystem.registerActionHandler( -+ `${name}:decryptMessage`, -+ this.decryptMessage.bind(this) -+ ); -+ this.messagingSystem.registerActionHandler( -+ `${name}:getEncryptionPublicKey`, -+ this.getEncryptionPublicKey.bind(this) -+ ); -+ this.messagingSystem.registerActionHandler( -+ `${name}:getAccounts`, -+ this.getAccounts.bind(this) -+ ); -+ this.messagingSystem.registerActionHandler( -+ `${name}:getKeyringsByType`, -+ this.getKeyringsByType.bind(this) -+ ); -+ this.messagingSystem.registerActionHandler( -+ `${name}:getKeyringForAccount`, -+ this.getKeyringForAccount.bind(this) -+ ); -+ this.messagingSystem.registerActionHandler( -+ `${name}:persistAllKeyrings`, -+ this.persistAllKeyrings.bind(this) -+ ); -+ this.messagingSystem.registerActionHandler( -+ `${name}:prepareUserOperation`, -+ this.prepareUserOperation.bind(this) -+ ); -+ this.messagingSystem.registerActionHandler( -+ `${name}:patchUserOperation`, -+ this.patchUserOperation.bind(this) -+ ); -+ this.messagingSystem.registerActionHandler( -+ `${name}:signUserOperation`, -+ this.signUserOperation.bind(this) -+ ); -+}; -+_getKeyringBuilderForType = new WeakSet(); -+getKeyringBuilderForType_fn = function(type) { -+ return _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _keyringBuilders).find( -+ (keyringBuilder) => keyringBuilder.type === type -+ ); -+}; -+_addQRKeyring = new WeakSet(); -+addQRKeyring_fn = async function() { -+ _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this); -+ return await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _newKeyring, newKeyring_fn).call(this, "QR Hardware Wallet Device" /* qr */); -+}; -+_subscribeToQRKeyringEvents = new WeakSet(); -+subscribeToQRKeyringEvents_fn = function(qrKeyring) { -+ _chunkNOCGQCUMjs.__privateSet.call(void 0, this, _qrKeyringStateListener, (state) => { -+ this.messagingSystem.publish(`${name}:qrKeyringStateChange`, state); -+ }); -+ qrKeyring.getMemStore().subscribe(_chunkNOCGQCUMjs.__privateGet.call(void 0, this, _qrKeyringStateListener)); -+}; -+_unsubscribeFromQRKeyringsEvents = new WeakSet(); -+unsubscribeFromQRKeyringsEvents_fn = function() { -+ const qrKeyrings = this.getKeyringsByType( -+ "QR Hardware Wallet Device" /* qr */ -+ ); -+ qrKeyrings.forEach((qrKeyring) => { -+ if (_chunkNOCGQCUMjs.__privateGet.call(void 0, this, _qrKeyringStateListener)) { -+ qrKeyring.getMemStore().unsubscribe(_chunkNOCGQCUMjs.__privateGet.call(void 0, this, _qrKeyringStateListener)); -+ } -+ }); -+}; -+_createNewVaultWithKeyring = new WeakSet(); -+createNewVaultWithKeyring_fn = async function(password, keyring) { -+ _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this); -+ if (typeof password !== "string") { -+ throw new TypeError("KeyringController - Password must be of type string." /* WrongPasswordType */); -+ } -+ this.update((state) => { -+ delete state.encryptionKey; -+ delete state.encryptionSalt; -+ }); -+ _chunkNOCGQCUMjs.__privateSet.call(void 0, this, _password, password); -+ await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _clearKeyrings, clearKeyrings_fn).call(this); -+ await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _createKeyringWithFirstAccount, createKeyringWithFirstAccount_fn).call(this, keyring.type, keyring.opts); -+ _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _setUnlocked, setUnlocked_fn).call(this); -+}; -+_getUpdatedKeyrings = new WeakSet(); -+getUpdatedKeyrings_fn = async function() { -+ return Promise.all(_chunkNOCGQCUMjs.__privateGet.call(void 0, this, _keyrings).map(displayForKeyring)); -+}; -+_getSerializedKeyrings = new WeakSet(); -+getSerializedKeyrings_fn = async function({ includeUnsupported } = { -+ includeUnsupported: true -+}) { -+ const serializedKeyrings = await Promise.all( -+ _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _keyrings).map(async (keyring) => { -+ const [type, data] = await Promise.all([ -+ keyring.type, -+ keyring.serialize() -+ ]); -+ return { type, data }; -+ }) -+ ); -+ if (includeUnsupported) { -+ serializedKeyrings.push(..._chunkNOCGQCUMjs.__privateGet.call(void 0, this, _unsupportedKeyrings)); -+ } -+ return serializedKeyrings; -+}; -+_restoreSerializedKeyrings = new WeakSet(); -+restoreSerializedKeyrings_fn = async function(serializedKeyrings) { -+ await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _clearKeyrings, clearKeyrings_fn).call(this); -+ for (const serializedKeyring of serializedKeyrings) { -+ await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _restoreKeyring, restoreKeyring_fn).call(this, serializedKeyring); -+ } -+}; -+_unlockKeyrings = new WeakSet(); -+unlockKeyrings_fn = async function(password, encryptionKey, encryptionSalt) { -+ return _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _withVaultLock, withVaultLock_fn).call(this, async ({ releaseLock }) => { -+ const encryptedVault = this.state.vault; -+ if (!encryptedVault) { -+ throw new Error("KeyringController - Cannot unlock without a previous vault." /* VaultError */); -+ } -+ let vault; -+ const updatedState = {}; -+ if (_chunkNOCGQCUMjs.__privateGet.call(void 0, this, _cacheEncryptionKey)) { -+ assertIsExportableKeyEncryptor(_chunkNOCGQCUMjs.__privateGet.call(void 0, this, _encryptor)); -+ if (password) { -+ const result = await _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _encryptor).decryptWithDetail( -+ password, -+ encryptedVault -+ ); -+ vault = result.vault; -+ _chunkNOCGQCUMjs.__privateSet.call(void 0, this, _password, password); -+ updatedState.encryptionKey = result.exportedKeyString; -+ updatedState.encryptionSalt = result.salt; -+ } else { -+ const parsedEncryptedVault = JSON.parse(encryptedVault); -+ if (encryptionSalt !== parsedEncryptedVault.salt) { -+ throw new Error("KeyringController - Encryption key and salt provided are expired" /* ExpiredCredentials */); -+ } -+ if (typeof encryptionKey !== "string") { -+ throw new TypeError("KeyringController - Password must be of type string." /* WrongPasswordType */); -+ } -+ const key = await _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _encryptor).importKey(encryptionKey); -+ vault = await _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _encryptor).decryptWithKey( -+ key, -+ parsedEncryptedVault -+ ); -+ updatedState.encryptionKey = encryptionKey; -+ updatedState.encryptionSalt = encryptionSalt; -+ } -+ } else { -+ if (typeof password !== "string") { -+ throw new TypeError("KeyringController - Password must be of type string." /* WrongPasswordType */); -+ } -+ vault = await _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _encryptor).decrypt(password, encryptedVault); -+ _chunkNOCGQCUMjs.__privateSet.call(void 0, this, _password, password); -+ } -+ if (!isSerializedKeyringsArray(vault)) { -+ throw new Error("KeyringController - The decrypted vault has an unexpected shape." /* VaultDataError */); -+ } -+ await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _restoreSerializedKeyrings, restoreSerializedKeyrings_fn).call(this, vault); -+ const updatedKeyrings = await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _getUpdatedKeyrings, getUpdatedKeyrings_fn).call(this); -+ this.update((state) => { -+ state.keyrings = updatedKeyrings; -+ if (updatedState.encryptionKey || updatedState.encryptionSalt) { -+ state.encryptionKey = updatedState.encryptionKey; -+ state.encryptionSalt = updatedState.encryptionSalt; -+ } -+ }); -+ if (_chunkNOCGQCUMjs.__privateGet.call(void 0, this, _password) && (!_chunkNOCGQCUMjs.__privateGet.call(void 0, this, _cacheEncryptionKey) || !encryptionKey) && _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _encryptor).isVaultUpdated && !_chunkNOCGQCUMjs.__privateGet.call(void 0, this, _encryptor).isVaultUpdated(encryptedVault)) { -+ releaseLock(); -+ await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _updateVault, updateVault_fn).call(this); -+ } -+ return _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _keyrings); -+ }); -+}; -+_updateVault = new WeakSet(); -+updateVault_fn = function() { -+ return _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _withVaultLock, withVaultLock_fn).call(this, async () => { -+ const { encryptionKey, encryptionSalt } = this.state; -+ if (!_chunkNOCGQCUMjs.__privateGet.call(void 0, this, _password) && !encryptionKey) { -+ throw new Error("KeyringController - Cannot persist vault without password and encryption key" /* MissingCredentials */); -+ } -+ const serializedKeyrings = await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _getSerializedKeyrings, getSerializedKeyrings_fn).call(this); -+ if (!serializedKeyrings.some((keyring) => keyring.type === "HD Key Tree" /* hd */)) { -+ throw new Error("KeyringController - No HD Keyring found" /* NoHdKeyring */); -+ } -+ const updatedState = {}; -+ if (_chunkNOCGQCUMjs.__privateGet.call(void 0, this, _cacheEncryptionKey)) { -+ assertIsExportableKeyEncryptor(_chunkNOCGQCUMjs.__privateGet.call(void 0, this, _encryptor)); -+ if (encryptionKey) { -+ const key = await _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _encryptor).importKey(encryptionKey); -+ const vaultJSON = await _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _encryptor).encryptWithKey( -+ key, -+ serializedKeyrings -+ ); -+ vaultJSON.salt = encryptionSalt; -+ updatedState.vault = JSON.stringify(vaultJSON); -+ } else if (_chunkNOCGQCUMjs.__privateGet.call(void 0, this, _password)) { -+ const { vault: newVault, exportedKeyString } = await _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _encryptor).encryptWithDetail( -+ _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _password), -+ serializedKeyrings -+ ); -+ updatedState.vault = newVault; -+ updatedState.encryptionKey = exportedKeyString; -+ } -+ } else { -+ assertIsValidPassword(_chunkNOCGQCUMjs.__privateGet.call(void 0, this, _password)); -+ updatedState.vault = await _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _encryptor).encrypt( -+ _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _password), -+ serializedKeyrings -+ ); -+ } -+ if (!updatedState.vault) { -+ throw new Error("KeyringController - Cannot persist vault without vault information" /* MissingVaultData */); -+ } -+ const updatedKeyrings = await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _getUpdatedKeyrings, getUpdatedKeyrings_fn).call(this); -+ this.update((state) => { -+ state.vault = updatedState.vault; -+ state.keyrings = updatedKeyrings; -+ if (updatedState.encryptionKey) { -+ state.encryptionKey = updatedState.encryptionKey; -+ state.encryptionSalt = JSON.parse(updatedState.vault).salt; -+ } -+ }); -+ return true; -+ }); -+}; -+_getAccountsFromKeyrings = new WeakSet(); -+getAccountsFromKeyrings_fn = async function() { -+ const keyrings = _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _keyrings); -+ const keyringArrays = await Promise.all( -+ keyrings.map(async (keyring) => keyring.getAccounts()) -+ ); -+ const addresses = keyringArrays.reduce((res, arr) => { -+ return res.concat(arr); -+ }, []); -+ return addresses.map(normalize); -+}; -+_createKeyringWithFirstAccount = new WeakSet(); -+createKeyringWithFirstAccount_fn = async function(type, opts) { -+ _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this); -+ const keyring = await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _newKeyring, newKeyring_fn).call(this, type, opts); -+ const [firstAccount] = await keyring.getAccounts(); -+ if (!firstAccount) { -+ throw new Error("KeyringController - First Account not found." /* NoFirstAccount */); -+ } -+}; -+_newKeyring = new WeakSet(); -+newKeyring_fn = async function(type, data) { -+ _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this); -+ const keyringBuilder = _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _getKeyringBuilderForType, getKeyringBuilderForType_fn).call(this, type); -+ if (!keyringBuilder) { -+ throw new Error( -+ `${"KeyringController - No keyringBuilder found for keyring" /* NoKeyringBuilder */}. Keyring type: ${type}` -+ ); -+ } -+ const keyring = keyringBuilder(); -+ await keyring.deserialize(data); -+ if (keyring.init) { -+ await keyring.init(); -+ } -+ if (type === "HD Key Tree" /* hd */ && (!_utils.isObject.call(void 0, data) || !data.mnemonic)) { -+ if (!keyring.generateRandomMnemonic) { -+ throw new Error( -+ "KeyringController - The current keyring does not support the method generateRandomMnemonic." /* UnsupportedGenerateRandomMnemonic */ -+ ); -+ } -+ keyring.generateRandomMnemonic(); -+ await keyring.addAccounts(1); -+ } -+ await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _checkForDuplicate, checkForDuplicate_fn).call(this, type, await keyring.getAccounts()); -+ if (type === "QR Hardware Wallet Device" /* qr */) { -+ _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _subscribeToQRKeyringEvents, subscribeToQRKeyringEvents_fn).call(this, keyring); -+ } -+ _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _keyrings).push(keyring); -+ return keyring; -+}; -+_clearKeyrings = new WeakSet(); -+clearKeyrings_fn = async function() { -+ _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this); -+ for (const keyring of _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _keyrings)) { -+ await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _destroyKeyring, destroyKeyring_fn).call(this, keyring); -+ } -+ _chunkNOCGQCUMjs.__privateSet.call(void 0, this, _keyrings, []); -+}; -+_restoreKeyring = new WeakSet(); -+restoreKeyring_fn = async function(serialized) { -+ _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this); -+ try { -+ const { type, data } = serialized; -+ return await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _newKeyring, newKeyring_fn).call(this, type, data); -+ } catch (_) { -+ _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _unsupportedKeyrings).push(serialized); -+ return void 0; -+ } -+}; -+_destroyKeyring = new WeakSet(); -+destroyKeyring_fn = async function(keyring) { -+ await keyring.destroy?.(); -+}; -+_removeEmptyKeyrings = new WeakSet(); -+removeEmptyKeyrings_fn = async function() { -+ _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this); -+ const validKeyrings = []; -+ await Promise.all( -+ _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _keyrings).map(async (keyring) => { -+ const accounts = await keyring.getAccounts(); -+ if (accounts.length > 0) { -+ validKeyrings.push(keyring); -+ } else { -+ await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _destroyKeyring, destroyKeyring_fn).call(this, keyring); -+ } -+ }) -+ ); -+ _chunkNOCGQCUMjs.__privateSet.call(void 0, this, _keyrings, validKeyrings); -+}; -+_checkForDuplicate = new WeakSet(); -+checkForDuplicate_fn = async function(type, newAccountArray) { -+ const accounts = await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn).call(this); -+ switch (type) { -+ case "Simple Key Pair" /* simple */: { -+ const isIncluded = Boolean( -+ accounts.find( -+ (key) => newAccountArray[0] && (key === newAccountArray[0] || key === _utils.remove0x.call(void 0, newAccountArray[0])) -+ ) -+ ); -+ if (isIncluded) { -+ throw new Error("KeyringController - The account you are trying to import is a duplicate" /* DuplicatedAccount */); -+ } -+ return newAccountArray; -+ } -+ default: { -+ return newAccountArray; -+ } -+ } -+}; -+_setUnlocked = new WeakSet(); -+setUnlocked_fn = function() { -+ _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this); -+ this.update((state) => { -+ state.isUnlocked = true; -+ }); -+ this.messagingSystem.publish(`${name}:unlock`); -+}; -+_persistOrRollback = new WeakSet(); -+persistOrRollback_fn = async function(fn) { -+ return _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _withRollback, withRollback_fn).call(this, async ({ releaseLock }) => { -+ const callbackResult = await fn({ releaseLock }); -+ await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _updateVault, updateVault_fn).call(this); -+ return callbackResult; -+ }); -+}; -+_withRollback = new WeakSet(); -+withRollback_fn = async function(fn) { -+ return _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _withControllerLock, withControllerLock_fn).call(this, async ({ releaseLock }) => { -+ const currentSerializedKeyrings = await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _getSerializedKeyrings, getSerializedKeyrings_fn).call(this); -+ const currentPassword = _chunkNOCGQCUMjs.__privateGet.call(void 0, this, _password); -+ try { -+ return await fn({ releaseLock }); -+ } catch (e) { -+ await _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _restoreSerializedKeyrings, restoreSerializedKeyrings_fn).call(this, currentSerializedKeyrings); -+ _chunkNOCGQCUMjs.__privateSet.call(void 0, this, _password, currentPassword); -+ throw e; -+ } -+ }); -+}; -+_assertControllerMutexIsLocked = new WeakSet(); -+assertControllerMutexIsLocked_fn = function() { -+ if (!_chunkNOCGQCUMjs.__privateGet.call(void 0, this, _controllerOperationMutex).isLocked()) { -+ throw new Error("KeyringController - attempt to update vault during a non mutually exclusive operation" /* ControllerLockRequired */); -+ } -+}; -+_withControllerLock = new WeakSet(); -+withControllerLock_fn = async function(fn) { -+ return withLock(_chunkNOCGQCUMjs.__privateGet.call(void 0, this, _controllerOperationMutex), fn); -+}; -+_withVaultLock = new WeakSet(); -+withVaultLock_fn = async function(fn) { -+ _chunkNOCGQCUMjs.__privateMethod.call(void 0, this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this); -+ return withLock(_chunkNOCGQCUMjs.__privateGet.call(void 0, this, _vaultOperationMutex), fn); -+}; -+async function withLock(mutex, fn) { -+ const releaseLock = await mutex.acquire(); -+ try { -+ return await fn({ releaseLock }); -+ } finally { -+ releaseLock(); -+ } -+} -+var KeyringController_default = KeyringController; -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+exports.KeyringTypes = KeyringTypes; exports.isCustodyKeyring = isCustodyKeyring; exports.AccountImportStrategy = AccountImportStrategy; exports.SignTypedDataVersion = SignTypedDataVersion; exports.keyringBuilderFactory = keyringBuilderFactory; exports.getDefaultKeyringState = getDefaultKeyringState; exports.KeyringController = KeyringController; exports.KeyringController_default = KeyringController_default; -+//# sourceMappingURL=chunk-L4UUWIZA.js.map -\ No newline at end of file -diff --git a/dist/chunk-L4UUWIZA.js.map b/dist/chunk-L4UUWIZA.js.map -new file mode 100644 -index 0000000000000000000000000000000000000000..6eb539d49ddeacd961a2282b062088d22ab81bf1 ---- /dev/null -+++ b/dist/chunk-L4UUWIZA.js.map -@@ -0,0 +1 @@ -+{"version":3,"sources":["../src/KeyringController.ts"],"names":["KeyringTypes","AccountImportStrategy","SignTypedDataVersion"],"mappings":";;;;;;;;AACA,SAAS,gBAAgB,UAAU,qBAAqB;AAMxD,SAAS,sBAAsB;AAC/B,YAAY,oBAAoB;AAChC,OAAO,eAAe;AACtB,SAAS,aAAa,oBAAoB;AAC1C,OAAO,mBAAmB;AAmB1B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,aAAa;AAEtB,OAAO,UAAU,cAAc,iBAAiB;AAKhD,IAAM,OAAO;AAKN,IAAK,eAAL,kBAAKA,kBAAL;AACL,EAAAA,cAAA,YAAS;AACT,EAAAA,cAAA,QAAK;AACL,EAAAA,cAAA,QAAK;AACL,EAAAA,cAAA,YAAS;AACT,EAAAA,cAAA,YAAS;AACT,EAAAA,cAAA,aAAU;AACV,EAAAA,cAAA,UAAO;AAPG,SAAAA;AAAA,GAAA;AAgBL,IAAM,mBAAmB,CAAC,gBAAiC;AAChE,SAAO,YAAY,WAAW,SAAS;AACzC;AAgLO,IAAK,wBAAL,kBAAKC,2BAAL;AACL,EAAAA,uBAAA,gBAAa;AACb,EAAAA,uBAAA,UAAO;AAFG,SAAAA;AAAA,GAAA;AAUL,IAAK,uBAAL,kBAAKC,0BAAL;AACL,EAAAA,sBAAA,QAAK;AACL,EAAAA,sBAAA,QAAK;AACL,EAAAA,sBAAA,QAAK;AAHK,SAAAA;AAAA,GAAA;AA2IL,SAAS,sBAAsB,oBAAwC;AAC5E,QAAM,UAAU,MAAM,IAAI,mBAAmB;AAE7C,UAAQ,OAAO,mBAAmB;AAElC,SAAO;AACT;AAEA,IAAM,yBAAyB;AAAA,EAC7B,sBAAsB,aAAa;AAAA,EACnC,sBAAsB,SAAS;AACjC;AAEO,IAAM,yBAAyB,MAA8B;AAClE,SAAO;AAAA,IACL,YAAY;AAAA,IACZ,UAAU,CAAC;AAAA,EACb;AACF;AASA,SAAS,4BACP,SACgE;AAChE,MACE,EACE,YAAY,SAAS,UAAU,KAAK,QAAQ,oBAAoB,aAElE;AACA,UAAM,IAAI,MAAM,uCAAuC;AAAA,EACzD;AACF;AASA,SAAS,+BACP,WAC6C;AAC7C,MACE,EACE,eAAe,aACf,OAAO,UAAU,cAAc,cAC/B,oBAAoB,aACpB,OAAO,UAAU,mBAAmB,cACpC,oBAAoB,aACpB,OAAO,UAAU,mBAAmB,aAEtC;AACA,UAAM,IAAI,sHAA2D;AAAA,EACvE;AACF;AAQA,SAAS,sBAAsB,UAA+C;AAC5E,MAAI,OAAO,aAAa,UAAU;AAChC,UAAM,IAAI,oFAA8C;AAAA,EAC1D;AAEA,MAAI,CAAC,YAAY,CAAC,SAAS,QAAQ;AACjC,UAAM,IAAI,gFAAiD;AAAA,EAC7D;AACF;AAQA,SAAS,0BACP,OAC8B;AAC9B,SACE,OAAO,UAAU,YACjB,MAAM,QAAQ,KAAK,KACnB,MAAM,MAAM,CAAC,UAAU,MAAM,QAAQ,YAAY,MAAM,IAAI,CAAC;AAEhE;AAUA,eAAe,kBACb,SAC+C;AAC/C,QAAM,WAAW,MAAM,QAAQ,YAAY;AAE3C,SAAO;AAAA,IACL,MAAM,QAAQ;AAAA;AAAA;AAAA,IAGd,UAAU,SAAS,IAAI,SAAS;AAAA,EAClC;AACF;AAQA,SAAS,aAAa,SAA0B;AAG9C;AAAA;AAAA,IAEE,kBAAkB,QAAQ,YAAY,CAAC;AAAA,IAEvC,kBAAkB,OAAc;AAAA;AAEpC;AAQA,SAAS,UAAU,SAAqC;AAMtD,SAAO,aAAa,OAAO,IAAI,aAAa,OAAO,IAAI;AACzD;AA9hBA;AAyiBO,IAAM,oBAAN,cAAgC,eAIrC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA+BA,YAAY,SAAmC;AAC7C,UAAM;AAAA,MACJ,YAAY;AAAA,MACZ;AAAA,MACA;AAAA,MACA;AAAA,IACF,IAAI;AAEJ,UAAM;AAAA,MACJ;AAAA,MACA,UAAU;AAAA,QACR,OAAO,EAAE,SAAS,MAAM,WAAW,MAAM;AAAA,QACzC,YAAY,EAAE,SAAS,OAAO,WAAW,KAAK;AAAA,QAC9C,UAAU,EAAE,SAAS,OAAO,WAAW,MAAM;AAAA,QAC7C,eAAe,EAAE,SAAS,OAAO,WAAW,MAAM;AAAA,QAClD,gBAAgB,EAAE,SAAS,OAAO,WAAW,MAAM;AAAA,MACrD;AAAA,MACA;AAAA,MACA,OAAO;AAAA,QACL,GAAG,uBAAuB;AAAA,QAC1B,GAAG;AAAA,MACL;AAAA,IACF,CAAC;AAmgCH;AAAA;AAAA;AAAA;AAAA;AAoEA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAeA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAAM;AAaN;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQA;AA0BA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAAM;AA+BN;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAAM;AAYN;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAAM;AA2BN;AAAA;AAAA;AAAA;AAAA;AAAA,uBAAM;AAmBN;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAAM;AAkGN;AAAA;AAAA;AAAA;AAAA;AAAA;AAuEA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAAM;AAuBN;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAAM;AAsBN;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAAM;AAgDN;AAAA;AAAA;AAAA;AAAA,uBAAM;AAeN;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAAM;AAuBN;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAAM;AAUN;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAAM;AA+BN;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAAM;AAmCN;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiBA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAAM;AAiBN;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAAM;AAsBN;AAAA;AAAA;AAAA;AAAA;AAAA;AAkBA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAAM;AAeN;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAAM;AAnuDN,uBAAS,2BAA4B,IAAI,MAAM;AAE/C,uBAAS,sBAAuB,IAAI,MAAM;AAE1C;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAsCE,uBAAK,kBAAmB,kBACpB,uBAAuB,OAAO,eAAe,IAC7C;AAEJ,uBAAK,YAAa;AAClB,uBAAK,WAAY,CAAC;AAClB,uBAAK,sBAAuB,CAAC;AAI7B,uBAAK,qBAAsB,QAAQ,QAAQ,kBAAkB;AAC7D,QAAI,mBAAK,sBAAqB;AAC5B,qCAA+B,SAAS;AAAA,IAC1C;AAEA,0BAAK,sDAAL;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,cAAc,cAAwC;AAC1D,WAAO,sBAAK,0CAAL,WAAwB,YAAY;AACzC,YAAM,iBAAiB,KAAK,kBAAkB,aAAa,EAAE,CAAC;AAG9D,UAAI,CAAC,gBAAgB;AACnB,cAAM,IAAI,MAAM,qBAAqB;AAAA,MACvC;AACA,YAAM,cAAc,MAAM,eAAe,YAAY;AAErD,UAAI,gBAAgB,YAAY,WAAW,cAAc;AACvD,YAAI,eAAe,YAAY,QAAQ;AACrC,gBAAM,IAAI,MAAM,yBAAyB;AAAA,QAC3C;AAEA,cAAM,kBAAkB,YAAY,YAAY;AAEhD,YAAI,CAAC,iBAAiB;AACpB,gBAAM,IAAI,MAAM,+BAA+B,YAAY,EAAE;AAAA,QAC/D;AAEA,eAAO;AAAA,MACT;AAEA,YAAM,CAAC,mBAAmB,IAAI,MAAM,eAAe,YAAY,CAAC;AAChE,YAAM,KAAK,iBAAiB;AAE5B,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,wBACJ,SACA,cACc;AAKd,WAAO,sBAAK,0CAAL,WAAwB,YAAY;AACzC,YAAM,cAAc,MAAM,sBAAK,sDAAL;AAE1B,UAAI,gBAAgB,YAAY,WAAW,cAAc;AACvD,YAAI,eAAe,YAAY,QAAQ;AACrC,gBAAM,IAAI,MAAM,yBAAyB;AAAA,QAC3C;AAEA,cAAM,kBAAkB,YAAY,YAAY;AAChD,gCAAwB,eAAe;AAEvC,eAAO;AAAA,MACT;AAEA,YAAM,QAAQ,YAAY,CAAC;AAE3B,YAAM,uBAAuB,MAAM,sBAAK,sDAAL,YAAiC;AAAA,QAClE,CAAC,oBAAoB,CAAC,YAAY,SAAS,eAAe;AAAA,MAC5D;AACA,8BAAwB,mBAAmB;AAE3C,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,6BAA8C;AAClD,WAAO,sBAAK,0CAAL,WAAwB,YAAY;AACzC,YAAM,iBAAiB,KAAK,kBAAkB,aAAa,EAAE,CAAC;AAG9D,UAAI,CAAC,gBAAgB;AACnB,cAAM,IAAI,MAAM,qBAAqB;AAAA,MACvC;AACA,YAAM,CAAC,mBAAmB,IAAI,MAAM,eAAe,YAAY,CAAC;AAChE,YAAM,KAAK,iBAAiB;AAC5B,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,yBACJ,UACA,MACe;AACf,WAAO,sBAAK,0CAAL,WAAwB,YAAY;AACzC,4BAAsB,QAAQ;AAE9B,YAAM,sBAAK,0DAAL,WAAgC,UAAU;AAAA,QAC9C,MAAM;AAAA,QACN,MAAM;AAAA,UACJ,UAAU;AAAA,UACV,kBAAkB;AAAA,QACpB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,0BAA0B,UAAkB;AAChD,WAAO,sBAAK,0CAAL,WAAwB,YAAY;AACzC,YAAM,WAAW,MAAM,sBAAK,sDAAL;AACvB,UAAI,CAAC,SAAS,QAAQ;AACpB,cAAM,sBAAK,0DAAL,WAAgC,UAAU;AAAA,UAC9C,MAAM;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,cACJ,MACA,MACkB;AAClB,QAAI,SAAS,sCAAiB;AAC5B,aAAO,KAAK,kBAAkB;AAAA,IAChC;AAEA,WAAO,sBAAK,0CAAL,WAAwB,YAAY,sBAAK,4BAAL,WAAiB,MAAM;AAAA,EACpE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,eAAe,UAAkB;AACrC,QAAI,CAAC,KAAK,MAAM,OAAO;AACrB,YAAM,IAAI,oFAAuC;AAAA,IACnD;AACA,UAAM,mBAAK,YAAW,QAAQ,UAAU,KAAK,MAAM,KAAK;AAAA,EAC1D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,aAAsB;AACpB,WAAO,KAAK,MAAM;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,iBAAiB,UAAuC;AAC5D,UAAM,KAAK,eAAe,QAAQ;AAClC,gCAA4B,mBAAK,WAAU,CAAC,CAAC;AAC7C,WAAO,mBAAK,WAAU,CAAC,EAAE;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,cAAc,UAAkB,SAAkC;AACtE,UAAM,KAAK,eAAe,QAAQ;AAElC,UAAM,UAAW,MAAM,KAAK;AAAA,MAC1B;AAAA,IACF;AACA,QAAI,CAAC,QAAQ,eAAe;AAC1B,YAAM,IAAI,yIAAqD;AAAA,IACjE;AAEA,WAAO,MAAM,QAAQ,cAAc,UAAU,OAAO,CAAQ;AAAA,EAC9D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,cAAiC;AACrC,WAAO,KAAK,MAAM,SAAS;AAAA,MACzB,CAAC,UAAU,YAAY,SAAS,OAAO,QAAQ,QAAQ;AAAA,MACvD,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,uBACJ,SACA,MACiB;AACjB,UAAM,UAAU,aAAa,OAAO;AACpC,UAAM,UAAW,MAAM,KAAK;AAAA,MAC1B;AAAA,IACF;AACA,QAAI,CAAC,QAAQ,wBAAwB;AACnC,YAAM,IAAI,2JAA8D;AAAA,IAC1E;AAEA,WAAO,MAAM,QAAQ,uBAAuB,SAAS,IAAI;AAAA,EAC3D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,eAAe,eAGD;AAClB,UAAM,UAAU,aAAa,cAAc,IAAI;AAC/C,UAAM,UAAW,MAAM,KAAK;AAAA,MAC1B;AAAA,IACF;AACA,QAAI,CAAC,QAAQ,gBAAgB;AAC3B,YAAM,IAAI,2IAAsD;AAAA,IAClE;AAEA,WAAO,QAAQ,eAAe,SAAS,cAAc,IAAI;AAAA,EAC3D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,qBAAqB,SAAmC;AAC5D,UAAM,UAAU,UAAU,OAAO;AAEjC,UAAM,aAAa,MAAM,QAAQ;AAAA,MAC/B,mBAAK,WAAU,IAAI,OAAO,YAAY;AACpC,eAAO,QAAQ,IAAI,CAAC,SAAS,QAAQ,YAAY,CAAC,CAAC;AAAA,MACrD,CAAC;AAAA,IACH;AAEA,UAAM,UAAU,WAAW,OAAO,CAAC,cAAc;AAC/C,YAAM,WAAW,UAAU,CAAC,EAAE,IAAI,SAAS;AAC3C,aAAO,SAAS,SAAS,OAAO;AAAA,IAClC,CAAC;AAED,QAAI,QAAQ,UAAU,QAAQ,CAAC,GAAG,QAAQ;AACxC,aAAO,QAAQ,CAAC,EAAE,CAAC;AAAA,IACrB;AAGA,QAAI,YAAY;AAChB,QAAI,CAAC,WAAW,QAAQ;AACtB,kBAAY;AAAA,IACd,WAAW,CAAC,QAAQ,QAAQ;AAC1B,kBAAY;AAAA,IACd;AACA,UAAM,IAAI;AAAA,MACR,yDAAmC,iBAAiB,SAAS;AAAA,IAC/D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,kBAAkB,MAAwC;AACxD,WAAO,mBAAK,WAAU,OAAO,CAAC,YAAY,QAAQ,SAAS,IAAI;AAAA,EACjE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,qBAAuC;AAC3C,WAAO,sBAAK,0CAAL,WAAwB,YAAY;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,0BACJ,UAGA,MACiB;AACjB,WAAO,sBAAK,0CAAL,WAAwB,YAAY;AACzC,UAAI;AACJ,cAAQ,UAAU;AAAA,QAChB,KAAK;AACH,gBAAM,CAAC,WAAW,IAAI;AACtB,cAAI,CAAC,aAAa;AAChB,kBAAM,IAAI,MAAM,6BAA6B;AAAA,UAC/C;AACA,gBAAM,WAAW,MAAM,WAAW;AAElC,cAAI;AACJ,cAAI;AACF,iCAAqB,SAAS,QAAQ;AAAA,UACxC,QAAQ;AACN,kBAAM,IAAI,MAAM,oCAAoC;AAAA,UACtD;AAEA,cACE,CAAC,eAAe,kBAAkB;AAAA,UAElC,cAAc,QAAQ,MAAM,KAAK,KAAK,QACtC;AACA,kBAAM,IAAI,MAAM,oCAAoC;AAAA,UACtD;AAEA,uBAAa,SAAS,QAAQ;AAC9B;AAAA,QACF,KAAK;AACH,cAAI;AACJ,gBAAM,CAAC,OAAO,QAAQ,IAAI;AAC1B,cAAI;AACF,qBAAS,UAAU,gBAAgB,OAAO,QAAQ;AAAA,UACpD,SAAS,GAAG;AACV,qBAAS,UAAW,MAAM,OAAO,OAAO,OAAO,UAAU,IAAI;AAAA,UAC/D;AACA,uBAAa,WAAW,OAAO,cAAc,CAAC;AAC9C;AAAA,QACF;AACE,gBAAM,IAAI,MAAM,gCAAgC,QAAQ,GAAG;AAAA,MAC/D;AACA,YAAM,aAAc,MAAM,sBAAK,4BAAL,WAAiB,gCAAqB;AAAA,QAC9D;AAAA,MACF;AACA,YAAM,WAAW,MAAM,WAAW,YAAY;AAC9C,aAAO,SAAS,CAAC;AAAA,IACnB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,cAAc,SAAgC;AAClD,UAAM,sBAAK,0CAAL,WAAwB,YAAY;AACxC,YAAM,UAAW,MAAM,KAAK;AAAA,QAC1B;AAAA,MACF;AAGA,UAAI,CAAC,QAAQ,eAAe;AAC1B,cAAM,IAAI,yIAAqD;AAAA,MACjE;AAQA,YAAM,QAAQ,cAAc,OAAc;AAE1C,YAAM,WAAW,MAAM,QAAQ,YAAY;AAE3C,UAAI,SAAS,WAAW,GAAG;AACzB,cAAM,sBAAK,8CAAL;AAAA,MACR;AAAA,IACF;AAEA,SAAK,gBAAgB,QAAQ,GAAG,IAAI,mBAAmB,OAAO;AAAA,EAChE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,YAA2B;AAC/B,WAAO,sBAAK,gCAAL,WAAmB,YAAY;AACpC,4BAAK,sEAAL;AAEA,yBAAK,WAAY;AACjB,YAAM,sBAAK,kCAAL;AAEN,WAAK,OAAO,CAAC,UAAU;AACrB,cAAM,aAAa;AACnB,cAAM,WAAW,CAAC;AAClB,eAAO,MAAM;AACb,eAAO,MAAM;AAAA,MACf,CAAC;AAED,WAAK,gBAAgB,QAAQ,GAAG,IAAI,OAAO;AAAA,IAC7C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,YAAY,eAAuD;AACvE,QAAI,CAAC,cAAc,MAAM;AACvB,YAAM,IAAI,MAAM,6BAA6B;AAAA,IAC/C;AAEA,UAAM,UAAU,aAAa,cAAc,IAAI;AAC/C,UAAM,UAAW,MAAM,KAAK;AAAA,MAC1B;AAAA,IACF;AACA,QAAI,CAAC,QAAQ,aAAa;AACxB,YAAM,IAAI,qIAAmD;AAAA,IAC/D;AAEA,WAAO,MAAM,QAAQ,YAAY,SAAS,cAAc,IAAI;AAAA,EAC9D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,oBAAoB,eAAsC;AAC9D,UAAM,UAAU,aAAa,cAAc,IAAI;AAC/C,UAAM,UAAW,MAAM,KAAK;AAAA,MAC1B;AAAA,IACF;AACA,QAAI,CAAC,QAAQ,qBAAqB;AAChC,YAAM,IAAI,qJAA2D;AAAA,IACvE;AAEA,UAAM,iBAAiB,UAAU,cAAc,IAAI;AAEnD,WAAO,MAAM,QAAQ,oBAAoB,SAAS,cAAc;AAAA,EAClE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,iBACJ,eACA,SACiB;AACjB,QAAI;AACF,UACE,CAAC;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,MACF,EAAE,SAAS,OAAO,GAClB;AACA,cAAM,IAAI,MAAM,yCAAyC,OAAO,GAAG;AAAA,MACrE;AAIA,YAAM,UAAU,aAAa,cAAc,IAAI;AAC/C,YAAM,UAAW,MAAM,KAAK;AAAA,QAC1B;AAAA,MACF;AACA,UAAI,CAAC,QAAQ,eAAe;AAC1B,cAAM,IAAI,+IAAwD;AAAA,MACpE;AAEA,aAAO,MAAM,QAAQ;AAAA,QACnB;AAAA,QACA,YAAY,iBACV,OAAO,cAAc,SAAS,WAC5B,KAAK,MAAM,cAAc,IAAI,IAC7B,cAAc;AAAA,QAClB,EAAE,QAAQ;AAAA,MACZ;AAAA,IACF,SAAS,OAAO;AACd,YAAM,IAAI,MAAM,wCAAwC,KAAK,EAAE;AAAA,IACjE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,gBACJ,aACA,MACA,MACiB;AACjB,UAAM,UAAU,aAAa,IAAI;AACjC,UAAM,UAAW,MAAM,KAAK;AAAA,MAC1B;AAAA,IACF;AACA,QAAI,CAAC,QAAQ,iBAAiB;AAC5B,YAAM,IAAI,6IAAuD;AAAA,IACnE;AAEA,WAAO,MAAM,QAAQ,gBAAgB,SAAS,aAAa,IAAI;AAAA,EACjE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,qBACJ,MACA,cACA,kBAC+B;AAC/B,UAAM,UAAU,aAAa,IAAI;AACjC,UAAM,UAAW,MAAM,KAAK;AAAA,MAC1B;AAAA,IACF;AAEA,QAAI,CAAC,QAAQ,sBAAsB;AACjC,YAAM,IAAI,uJAA4D;AAAA,IACxE;AAEA,WAAO,MAAM,QAAQ;AAAA,MACnB;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,mBACJ,MACA,QACA,kBACgC;AAChC,UAAM,UAAU,aAAa,IAAI;AACjC,UAAM,UAAW,MAAM,KAAK;AAAA,MAC1B;AAAA,IACF;AAEA,QAAI,CAAC,QAAQ,oBAAoB;AAC/B,YAAM,IAAI,mJAA0D;AAAA,IACtE;AAEA,WAAO,MAAM,QAAQ,mBAAmB,SAAS,QAAQ,gBAAgB;AAAA,EAC3E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,kBACJ,MACA,QACA,kBACiB;AACjB,UAAM,UAAU,aAAa,IAAI;AACjC,UAAM,UAAW,MAAM,KAAK;AAAA,MAC1B;AAAA,IACF;AAEA,QAAI,CAAC,QAAQ,mBAAmB;AAC9B,YAAM,IAAI,iJAAyD;AAAA,IACrE;AAEA,WAAO,MAAM,QAAQ,kBAAkB,SAAS,QAAQ,gBAAgB;AAAA,EAC1E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,eAAe,UAAiC;AAC9C,WAAO,sBAAK,0CAAL,WAAwB,YAAY;AACzC,UAAI,CAAC,KAAK,MAAM,YAAY;AAC1B,cAAM,IAAI,6GAA+C;AAAA,MAC3D;AAEA,4BAAsB,QAAQ;AAE9B,yBAAK,WAAY;AAIjB,UAAI,mBAAK,sBAAqB;AAC5B,aAAK,OAAO,CAAC,UAAU;AACrB,iBAAO,MAAM;AACb,iBAAO,MAAM;AAAA,QACf,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,oBACJ,eACA,gBACe;AACf,WAAO,sBAAK,gCAAL,WAAmB,YAAY;AACpC,yBAAK,WAAY,MAAM,sBAAK,oCAAL,WACrB,QACA,eACA;AAEF,4BAAK,8BAAL;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,eAAe,UAAiC;AACpD,WAAO,sBAAK,gCAAL,WAAmB,YAAY;AACpC,yBAAK,WAAY,MAAM,sBAAK,oCAAL,WAAqB;AAC5C,4BAAK,8BAAL;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,mBAAwC;AAC5C,UAAM,iBAAiB,KAAK,kBAAkB,sBAAe,EAAE,CAAC;AAGhE,QAAI,CAAC,gBAAgB;AACnB,YAAM,IAAI,MAAM,sBAAsB;AAAA,IACxC;AAEA,gCAA4B,cAAc;AAE1C,UAAM,YAAY,eAAe;AACjC,UAAM,WAAW,MAAM,eAAe,YAAY;AAElD,QAAI,SAAS,WAAW,GAAG;AACzB,YAAM,IAAI,MAAM,iCAAiC;AAAA,IACnD;AAIA,UAAM,mBAAmB,sBAAK,wDAAL,WAA+B;AAExD,UAAM,YAAY,iBAAiB;AAGnC,UAAM,UAAU,YAAY;AAAA,MAC1B,UAAU;AAAA,MACV,kBAAkB,SAAS;AAAA,IAC7B,CAAC;AACD,UAAM,eAAe,MAAM,UAAU,YAAY;AAEjD,QAAI,aAAa,WAAW,SAAS,QAAQ;AAC3C,YAAM,IAAI,MAAM,oDAAoD;AAAA,IACtE;AAEA,iBAAa,QAAQ,CAAC,SAAiB,MAAc;AAEnD,UAAI,QAAQ,YAAY,MAAM,SAAS,CAAC,EAAE,YAAY,GAAG;AACvD,cAAM,IAAI,MAAM,0CAA0C;AAAA,MAC5D;AAAA,IACF,CAAC;AAED,WAAO;AAAA,EACT;AAAA,EAwDA,MAAM,YAIJ,UACA,WACA,UAE0D;AAAA,IACxD,iBAAiB;AAAA,EACnB,GACyB;AACzB,WAAO,sBAAK,0CAAL,WAAwB,YAAY;AACzC,UAAI;AAEJ,UAAI,aAAa,UAAU;AACzB,kBAAW,MAAM,KAAK,qBAAqB,SAAS,OAAO;AAAA,MAG7D,OAAO;AACL,kBAAU,KAAK,kBAAkB,SAAS,IAAI,EAAE,SAAS,SAAS,CAAC;AAInE,YAAI,CAAC,WAAW,QAAQ,iBAAiB;AACvC,oBAAW,MAAM,sBAAK,4BAAL,WACf,SAAS,MACT,QAAQ;AAAA,QAEZ;AAAA,MACF;AAEA,UAAI,CAAC,SAAS;AACZ,cAAM,IAAI,oEAA4C;AAAA,MACxD;AAEA,YAAM,SAAS,MAAM,UAAU,OAAO;AAEtC,UAAI,OAAO,GAAG,QAAQ,OAAO,GAAG;AAK9B,cAAM,IAAI,iGAAsD;AAAA,MAClE;AAEA,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,eAAsC;AAEpC,WAAO,KAAK,kBAAkB,oCAAe,EAAE,CAAC;AAAA,EAClD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,oBAAwC;AAC5C,WACE,KAAK,aAAa,KACjB,MAAM,sBAAK,0CAAL,WAAwB,YAAY,sBAAK,gCAAL;AAAA,EAE/C;AAAA;AAAA;AAAA,EAIA,MAAM,iBAAiB,YAAgC;AACrD,WAAO,sBAAK,0CAAL,WAAwB,YAAY;AACzC,YAAM,UAAU,KAAK,aAAa,KAAM,MAAM,sBAAK,gCAAL;AAC9C,cAAQ,YAAY,UAAU;AAAA,IAChC;AAAA,EACF;AAAA,EAEA,MAAM,sBAAqC;AACzC,KAAC,MAAM,KAAK,kBAAkB,GAAG,WAAW;AAAA,EAC9C;AAAA,EAEA,MAAM,oBAA8C;AAClD,YAAQ,MAAM,KAAK,kBAAkB,GAAG,YAAY;AAAA,EACtD;AAAA,EAEA,MAAM,oBAAoB,aAAoC;AAC5D,KAAC,MAAM,KAAK,kBAAkB,GAAG,kBAAkB,WAAW;AAAA,EAChE;AAAA,EAEA,MAAM,sBAAsB,eAAsC;AAChE,KAAC,MAAM,KAAK,kBAAkB,GAAG,oBAAoB,aAAa;AAAA,EACpE;AAAA,EAEA,MAAM,kBACJ,WACA,cACe;AACf,KAAC,MAAM,KAAK,kBAAkB,GAAG,gBAAgB,WAAW,YAAY;AAAA,EAC1E;AAAA,EAEA,MAAM,sBAAqC;AACzC,KAAC,MAAM,KAAK,kBAAkB,GAAG,kBAAkB;AAAA,EACrD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,0BAAyC;AAE7C,KAAC,MAAM,KAAK,kBAAkB,GAAG,WAAW;AAAA,EAC9C;AAAA,EAEA,MAAM,kBACJ,MACgE;AAChE,WAAO,sBAAK,0CAAL,WAAwB,YAAY;AACzC,UAAI;AACF,cAAM,UAAU,KAAK,aAAa,KAAM,MAAM,sBAAK,gCAAL;AAC9C,YAAI;AACJ,gBAAQ,MAAM;AAAA,UACZ,KAAK;AACH,uBAAW,MAAM,QAAQ,gBAAgB;AACzC;AAAA,UACF,KAAK;AACH,uBAAW,MAAM,QAAQ,YAAY;AACrC;AAAA,UACF;AACE,uBAAW,MAAM,QAAQ,aAAa;AAAA,QAC1C;AAGA,eAAO,SAAS,IAAI,CAAC,YAAiB;AACpC,iBAAO;AAAA,YACL,GAAG;AAAA,YACH,SAAS;AAAA,UACX;AAAA,QACF,CAAC;AAAA,MACH,SAAS,GAAG;AAGV,cAAM,IAAI,MAAM,+CAA+C,CAAC,EAAE;AAAA,MACpE;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,8BAA8B,OAA8B;AAChE,WAAO,sBAAK,0CAAL,WAAwB,YAAY;AACzC,YAAM,UAAU,KAAK,aAAa,KAAM,MAAM,sBAAK,gCAAL;AAE9C,cAAQ,mBAAmB,KAAK;AAChC,YAAM,QAAQ,YAAY,CAAC;AAAA,IAC7B;AAAA,EACF;AAAA,EAEA,MAAM,sBAAsB,SAAkC;AAC5D,UAAM,UAAW,MAAM,KAAK;AAAA,MAC1B;AAAA,IACF;AACA,WAAO,QAAQ;AAAA,EACjB;AAAA,EAEA,MAAM,iBAGH;AACD,WAAO,sBAAK,0CAAL,WAAwB,YAAY;AACzC,YAAM,UAAU,KAAK,aAAa;AAElC,UAAI,CAAC,SAAS;AACZ,eAAO,EAAE,iBAAiB,CAAC,GAAG,mBAAmB,CAAC,EAAE;AAAA,MACtD;AAEA,YAAM,cAAe,MAAM,sBAAK,sDAAL;AAC3B,cAAQ,aAAa;AACrB,YAAM,oBACH,MAAM,sBAAK,sDAAL;AACT,YAAM,kBAAkB,YAAY;AAAA,QAClC,CAAC,YAAoB,CAAC,kBAAkB,SAAS,OAAO;AAAA,MAC1D;AACA,aAAO,EAAE,iBAAiB,kBAAkB;AAAA,IAC9C;AAAA,EACF;AAurBF;AAxuDW;AAEA;AAET;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAuiCA;AAAA,6BAAwB,WAAG;AACzB,OAAK,gBAAgB;AAAA,IACnB,GAAG,IAAI;AAAA,IACP,KAAK,YAAY,KAAK,IAAI;AAAA,EAC5B;AAEA,OAAK,gBAAgB;AAAA,IACnB,GAAG,IAAI;AAAA,IACP,KAAK,oBAAoB,KAAK,IAAI;AAAA,EACpC;AAEA,OAAK,gBAAgB;AAAA,IACnB,GAAG,IAAI;AAAA,IACP,KAAK,iBAAiB,KAAK,IAAI;AAAA,EACjC;AAEA,OAAK,gBAAgB;AAAA,IACnB,GAAG,IAAI;AAAA,IACP,KAAK,eAAe,KAAK,IAAI;AAAA,EAC/B;AAEA,OAAK,gBAAgB;AAAA,IACnB,GAAG,IAAI;AAAA,IACP,KAAK,uBAAuB,KAAK,IAAI;AAAA,EACvC;AAEA,OAAK,gBAAgB;AAAA,IACnB,GAAG,IAAI;AAAA,IACP,KAAK,YAAY,KAAK,IAAI;AAAA,EAC5B;AAEA,OAAK,gBAAgB;AAAA,IACnB,GAAG,IAAI;AAAA,IACP,KAAK,kBAAkB,KAAK,IAAI;AAAA,EAClC;AAEA,OAAK,gBAAgB;AAAA,IACnB,GAAG,IAAI;AAAA,IACP,KAAK,qBAAqB,KAAK,IAAI;AAAA,EACrC;AAEA,OAAK,gBAAgB;AAAA,IACnB,GAAG,IAAI;AAAA,IACP,KAAK,mBAAmB,KAAK,IAAI;AAAA,EACnC;AAEA,OAAK,gBAAgB;AAAA,IACnB,GAAG,IAAI;AAAA,IACP,KAAK,qBAAqB,KAAK,IAAI;AAAA,EACrC;AAEA,OAAK,gBAAgB;AAAA,IACnB,GAAG,IAAI;AAAA,IACP,KAAK,mBAAmB,KAAK,IAAI;AAAA,EACnC;AAEA,OAAK,gBAAgB;AAAA,IACnB,GAAG,IAAI;AAAA,IACP,KAAK,kBAAkB,KAAK,IAAI;AAAA,EAClC;AACF;AAQA;AAAA,8BAAyB,SACvB,MACoD;AACpD,SAAO,mBAAK,kBAAiB;AAAA,IAC3B,CAAC,mBAAmB,eAAe,SAAS;AAAA,EAC9C;AACF;AASM;AAAA,kBAAa,iBAAuB;AACxC,wBAAK,kEAAL;AAGA,SAAQ,MAAM,sBAAK,4BAAL,WAAiB;AACjC;AAQA;AAAA,gCAA2B,SAAC,WAAsB;AAChD,qBAAK,yBAA0B,CAAC,UAAU;AACxC,SAAK,gBAAgB,QAAQ,GAAG,IAAI,yBAAyB,KAAK;AAAA,EACpE;AAEA,YAAU,YAAY,EAAE,UAAU,mBAAK,wBAAuB;AAChE;AAEA;AAAA,qCAAgC,WAAG;AACjC,QAAM,aAAa,KAAK;AAAA,IACtB;AAAA,EACF;AAEA,aAAW,QAAQ,CAAC,cAAc;AAChC,QAAI,mBAAK,0BAAyB;AAChC,gBAAU,YAAY,EAAE,YAAY,mBAAK,wBAAuB;AAAA,IAClE;AAAA,EACF,CAAC;AACH;AAgBM;AAAA,+BAA0B,eAC9B,UACA,SAIe;AACf,wBAAK,kEAAL;AAEA,MAAI,OAAO,aAAa,UAAU;AAChC,UAAM,IAAI,wFAAkD;AAAA,EAC9D;AAEA,OAAK,OAAO,CAAC,UAAU;AACrB,WAAO,MAAM;AACb,WAAO,MAAM;AAAA,EACf,CAAC;AAED,qBAAK,WAAY;AAEjB,QAAM,sBAAK,kCAAL;AACN,QAAM,sBAAK,kEAAL,WAAoC,QAAQ,MAAM,QAAQ;AAChE,wBAAK,8BAAL;AACF;AAQM;AAAA,wBAAmB,iBAA6B;AACpD,SAAO,QAAQ,IAAI,mBAAK,WAAU,IAAI,iBAAiB,CAAC;AAC1D;AAUM;AAAA,2BAAsB,eAC1B,EAAE,mBAAmB,IAAqC;AAAA,EACxD,oBAAoB;AACtB,GAC8B;AAC9B,QAAM,qBAAqB,MAAM,QAAQ;AAAA,IACvC,mBAAK,WAAU,IAAI,OAAO,YAAY;AACpC,YAAM,CAAC,MAAM,IAAI,IAAI,MAAM,QAAQ,IAAI;AAAA,QACrC,QAAQ;AAAA,QACR,QAAQ,UAAU;AAAA,MACpB,CAAC;AACD,aAAO,EAAE,MAAM,KAAK;AAAA,IACtB,CAAC;AAAA,EACH;AAEA,MAAI,oBAAoB;AACtB,uBAAmB,KAAK,GAAG,mBAAK,qBAAoB;AAAA,EACtD;AAEA,SAAO;AACT;AAOM;AAAA,+BAA0B,eAC9B,oBACe;AACf,QAAM,sBAAK,kCAAL;AAEN,aAAW,qBAAqB,oBAAoB;AAClD,UAAM,sBAAK,oCAAL,WAAqB;AAAA,EAC7B;AACF;AAWM;AAAA,oBAAe,eACnB,UACA,eACA,gBAC6B;AAC7B,SAAO,sBAAK,kCAAL,WAAoB,OAAO,EAAE,YAAY,MAAM;AACpD,UAAM,iBAAiB,KAAK,MAAM;AAClC,QAAI,CAAC,gBAAgB;AACnB,YAAM,IAAI,oFAAuC;AAAA,IACnD;AAEA,QAAI;AACJ,UAAM,eAAgD,CAAC;AAEvD,QAAI,mBAAK,sBAAqB;AAC5B,qCAA+B,mBAAK,WAAU;AAE9C,UAAI,UAAU;AACZ,cAAM,SAAS,MAAM,mBAAK,YAAW;AAAA,UACnC;AAAA,UACA;AAAA,QACF;AACA,gBAAQ,OAAO;AACf,2BAAK,WAAY;AAEjB,qBAAa,gBAAgB,OAAO;AACpC,qBAAa,iBAAiB,OAAO;AAAA,MACvC,OAAO;AACL,cAAM,uBAAuB,KAAK,MAAM,cAAc;AAEtD,YAAI,mBAAmB,qBAAqB,MAAM;AAChD,gBAAM,IAAI,iGAA+C;AAAA,QAC3D;AAEA,YAAI,OAAO,kBAAkB,UAAU;AACrC,gBAAM,IAAI,wFAAkD;AAAA,QAC9D;AAEA,cAAM,MAAM,MAAM,mBAAK,YAAW,UAAU,aAAa;AACzD,gBAAQ,MAAM,mBAAK,YAAW;AAAA,UAC5B;AAAA,UACA;AAAA,QACF;AAIA,qBAAa,gBAAgB;AAI7B,qBAAa,iBAAiB;AAAA,MAChC;AAAA,IACF,OAAO;AACL,UAAI,OAAO,aAAa,UAAU;AAChC,cAAM,IAAI,wFAAkD;AAAA,MAC9D;AAEA,cAAQ,MAAM,mBAAK,YAAW,QAAQ,UAAU,cAAc;AAC9D,yBAAK,WAAY;AAAA,IACnB;AAEA,QAAI,CAAC,0BAA0B,KAAK,GAAG;AACrC,YAAM,IAAI,6FAA2C;AAAA,IACvD;AAEA,UAAM,sBAAK,0DAAL,WAAgC;AACtC,UAAM,kBAAkB,MAAM,sBAAK,4CAAL;AAE9B,SAAK,OAAO,CAAC,UAAU;AACrB,YAAM,WAAW;AACjB,UAAI,aAAa,iBAAiB,aAAa,gBAAgB;AAC7D,cAAM,gBAAgB,aAAa;AACnC,cAAM,iBAAiB,aAAa;AAAA,MACtC;AAAA,IACF,CAAC;AAED,QACE,mBAAK,eACJ,CAAC,mBAAK,wBAAuB,CAAC,kBAC/B,mBAAK,YAAW,kBAChB,CAAC,mBAAK,YAAW,eAAe,cAAc,GAC9C;AAGA,kBAAY;AAEZ,YAAM,sBAAK,8BAAL;AAAA,IACR;AAEA,WAAO,mBAAK;AAAA,EACd;AACF;AAOA;AAAA,iBAAY,WAAqB;AAC/B,SAAO,sBAAK,kCAAL,WAAoB,YAAY;AACrC,UAAM,EAAE,eAAe,eAAe,IAAI,KAAK;AAE/C,QAAI,CAAC,mBAAK,cAAa,CAAC,eAAe;AACrC,YAAM,IAAI,6GAA+C;AAAA,IAC3D;AAEA,UAAM,qBAAqB,MAAM,sBAAK,kDAAL;AAEjC,QACE,CAAC,mBAAmB,KAAK,CAAC,YAAY,QAAQ,SAAS,sBAAe,GACtE;AACA,YAAM,IAAI,iEAAwC;AAAA,IACpD;AAEA,UAAM,eAAgD,CAAC;AAEvD,QAAI,mBAAK,sBAAqB;AAC5B,qCAA+B,mBAAK,WAAU;AAE9C,UAAI,eAAe;AACjB,cAAM,MAAM,MAAM,mBAAK,YAAW,UAAU,aAAa;AACzD,cAAM,YAAY,MAAM,mBAAK,YAAW;AAAA,UACtC;AAAA,UACA;AAAA,QACF;AACA,kBAAU,OAAO;AACjB,qBAAa,QAAQ,KAAK,UAAU,SAAS;AAAA,MAC/C,WAAW,mBAAK,YAAW;AACzB,cAAM,EAAE,OAAO,UAAU,kBAAkB,IACzC,MAAM,mBAAK,YAAW;AAAA,UACpB,mBAAK;AAAA,UACL;AAAA,QACF;AAEF,qBAAa,QAAQ;AACrB,qBAAa,gBAAgB;AAAA,MAC/B;AAAA,IACF,OAAO;AACL,4BAAsB,mBAAK,UAAS;AACpC,mBAAa,QAAQ,MAAM,mBAAK,YAAW;AAAA,QACzC,mBAAK;AAAA,QACL;AAAA,MACF;AAAA,IACF;AAEA,QAAI,CAAC,aAAa,OAAO;AACvB,YAAM,IAAI,iGAA6C;AAAA,IACzD;AAEA,UAAM,kBAAkB,MAAM,sBAAK,4CAAL;AAC9B,SAAK,OAAO,CAAC,UAAU;AACrB,YAAM,QAAQ,aAAa;AAC3B,YAAM,WAAW;AACjB,UAAI,aAAa,eAAe;AAC9B,cAAM,gBAAgB,aAAa;AACnC,cAAM,iBAAiB,KAAK,MAAM,aAAa,KAAe,EAAE;AAAA,MAClE;AAAA,IACF,CAAC;AAED,WAAO;AAAA,EACT;AACF;AAQM;AAAA,6BAAwB,iBAAsB;AAClD,QAAM,WAAW,mBAAK;AAEtB,QAAM,gBAAgB,MAAM,QAAQ;AAAA,IAClC,SAAS,IAAI,OAAO,YAAY,QAAQ,YAAY,CAAC;AAAA,EACvD;AACA,QAAM,YAAY,cAAc,OAAO,CAAC,KAAK,QAAQ;AACnD,WAAO,IAAI,OAAO,GAAG;AAAA,EACvB,GAAG,CAAC,CAAC;AAIL,SAAO,UAAU,IAAI,SAAS;AAChC;AAUM;AAAA,mCAA8B,eAAC,MAAc,MAAgB;AACjE,wBAAK,kEAAL;AAEA,QAAM,UAAW,MAAM,sBAAK,4BAAL,WAAiB,MAAM;AAE9C,QAAM,CAAC,YAAY,IAAI,MAAM,QAAQ,YAAY;AACjD,MAAI,CAAC,cAAc;AACjB,UAAM,IAAI,yEAA2C;AAAA,EACvD;AACF;AAaM;AAAA,gBAAW,eAAC,MAAc,MAA2C;AACzE,wBAAK,kEAAL;AAEA,QAAM,iBAAiB,sBAAK,wDAAL,WAA+B;AAEtD,MAAI,CAAC,gBAAgB;AACnB,UAAM,IAAI;AAAA,MACR,mFAA0C,mBAAmB,IAAI;AAAA,IACnE;AAAA,EACF;AAEA,QAAM,UAAU,eAAe;AAG/B,QAAM,QAAQ,YAAY,IAAI;AAE9B,MAAI,QAAQ,MAAM;AAChB,UAAM,QAAQ,KAAK;AAAA,EACrB;AAEA,MAAI,SAAS,2BAAoB,CAAC,SAAS,IAAI,KAAK,CAAC,KAAK,WAAW;AACnE,QAAI,CAAC,QAAQ,wBAAwB;AACnC,YAAM,IAAI;AAAA;AAAA,MAEV;AAAA,IACF;AAEA,YAAQ,uBAAuB;AAC/B,UAAM,QAAQ,YAAY,CAAC;AAAA,EAC7B;AAEA,QAAM,sBAAK,0CAAL,WAAwB,MAAM,MAAM,QAAQ,YAAY;AAE9D,MAAI,SAAS,sCAAiB;AAG5B,0BAAK,4DAAL,WAAiC;AAAA,EACnC;AAEA,qBAAK,WAAU,KAAK,OAAO;AAE3B,SAAO;AACT;AAMM;AAAA,mBAAc,iBAAG;AACrB,wBAAK,kEAAL;AACA,aAAW,WAAW,mBAAK,YAAW;AACpC,UAAM,sBAAK,oCAAL,WAAqB;AAAA,EAC7B;AACA,qBAAK,WAAY,CAAC;AACpB;AASM;AAAA,oBAAe,eACnB,YACuC;AACvC,wBAAK,kEAAL;AAEA,MAAI;AACF,UAAM,EAAE,MAAM,KAAK,IAAI;AACvB,WAAO,MAAM,sBAAK,4BAAL,WAAiB,MAAM;AAAA,EACtC,SAAS,GAAG;AACV,uBAAK,sBAAqB,KAAK,UAAU;AACzC,WAAO;AAAA,EACT;AACF;AAWM;AAAA,oBAAe,eAAC,SAA2B;AAC/C,QAAM,QAAQ,UAAU;AAC1B;AAQM;AAAA,yBAAoB,iBAAkB;AAC1C,wBAAK,kEAAL;AACA,QAAM,gBAAoC,CAAC;AAM3C,QAAM,QAAQ;AAAA,IACZ,mBAAK,WAAU,IAAI,OAAO,YAA8B;AACtD,YAAM,WAAW,MAAM,QAAQ,YAAY;AAC3C,UAAI,SAAS,SAAS,GAAG;AACvB,sBAAc,KAAK,OAAO;AAAA,MAC5B,OAAO;AACL,cAAM,sBAAK,oCAAL,WAAqB;AAAA,MAC7B;AAAA,IACF,CAAC;AAAA,EACH;AACA,qBAAK,WAAY;AACnB;AAYM;AAAA,uBAAkB,eACtB,MACA,iBACmB;AACnB,QAAM,WAAW,MAAM,sBAAK,sDAAL;AAEvB,UAAQ,MAAM;AAAA,IACZ,KAAK,gCAAqB;AACxB,YAAM,aAAa;AAAA,QACjB,SAAS;AAAA,UACP,CAAC,QACC,gBAAgB,CAAC,MAChB,QAAQ,gBAAgB,CAAC,KACxB,QAAQ,SAAS,gBAAgB,CAAC,CAAC;AAAA,QACzC;AAAA,MACF;AAEA,UAAI,YAAY;AACd,cAAM,IAAI,uGAA8C;AAAA,MAC1D;AACA,aAAO;AAAA,IACT;AAAA,IAEA,SAAS;AACP,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAQA;AAAA,iBAAY,WAAS;AACnB,wBAAK,kEAAL;AAEA,OAAK,OAAO,CAAC,UAAU;AACrB,UAAM,aAAa;AAAA,EACrB,CAAC;AACD,OAAK,gBAAgB,QAAQ,GAAG,IAAI,SAAS;AAC/C;AAUM;AAAA,uBAAqB,eAAC,IAA8C;AACxE,SAAO,sBAAK,gCAAL,WAAmB,OAAO,EAAE,YAAY,MAAM;AACnD,UAAM,iBAAiB,MAAM,GAAG,EAAE,YAAY,CAAC;AAE/C,UAAM,sBAAK,8BAAL;AAEN,WAAO;AAAA,EACT;AACF;AASM;AAAA,kBAAgB,eAAC,IAA8C;AACnE,SAAO,sBAAK,4CAAL,WAAyB,OAAO,EAAE,YAAY,MAAM;AACzD,UAAM,4BAA4B,MAAM,sBAAK,kDAAL;AACxC,UAAM,kBAAkB,mBAAK;AAE7B,QAAI;AACF,aAAO,MAAM,GAAG,EAAE,YAAY,CAAC;AAAA,IACjC,SAAS,GAAG;AAEV,YAAM,sBAAK,0DAAL,WAAgC;AACtC,yBAAK,WAAY;AAEjB,YAAM;AAAA,IACR;AAAA,EACF;AACF;AAOA;AAAA,mCAA8B,WAAG;AAC/B,MAAI,CAAC,mBAAK,2BAA0B,SAAS,GAAG;AAC9C,UAAM,IAAI,0HAAmD;AAAA,EAC/D;AACF;AAcM;AAAA,wBAAsB,eAAC,IAA8C;AACzE,SAAO,SAAS,mBAAK,4BAA2B,EAAE;AACpD;AAaM;AAAA,mBAAiB,eAAC,IAA8C;AACpE,wBAAK,kEAAL;AAEA,SAAO,SAAS,mBAAK,uBAAsB,EAAE;AAC/C;AAYF,eAAe,SACb,OACA,IACY;AACZ,QAAM,cAAc,MAAM,MAAM,QAAQ;AAExC,MAAI;AACF,WAAO,MAAM,GAAG,EAAE,YAAY,CAAC;AAAA,EACjC,UAAE;AACA,gBAAY;AAAA,EACd;AACF;AAEA,IAAO,4BAAQ","sourcesContent":["import type { TxData, TypedTransaction } from '@ethereumjs/tx';\nimport { isValidPrivate, toBuffer, getBinarySize } from '@ethereumjs/util';\nimport type {\n MetaMaskKeyring as QRKeyring,\n IKeyringState as IQRKeyringState,\n} from '@keystonehq/metamask-airgapped-keyring';\nimport type { RestrictedControllerMessenger } from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport * as encryptorUtils from '@metamask/browser-passworder';\nimport HDKeyring from '@metamask/eth-hd-keyring';\nimport { normalize as ethNormalize } from '@metamask/eth-sig-util';\nimport SimpleKeyring from '@metamask/eth-simple-keyring';\nimport type {\n EthBaseTransaction,\n EthBaseUserOperation,\n EthKeyring,\n EthUserOperation,\n EthUserOperationPatch,\n KeyringExecutionContext,\n} from '@metamask/keyring-api';\nimport type {\n PersonalMessageParams,\n TypedMessageParams,\n} from '@metamask/message-manager';\nimport type {\n Eip1024EncryptedData,\n Hex,\n Json,\n KeyringClass,\n} from '@metamask/utils';\nimport {\n add0x,\n assertIsStrictHexString,\n bytesToHex,\n hasProperty,\n isObject,\n isStrictHexString,\n isValidHexAddress,\n isValidJson,\n remove0x,\n} from '@metamask/utils';\nimport { Mutex } from 'async-mutex';\nimport type { MutexInterface } from 'async-mutex';\nimport Wallet, { thirdparty as importers } from 'ethereumjs-wallet';\nimport type { Patch } from 'immer';\n\nimport { KeyringControllerError } from './constants';\n\nconst name = 'KeyringController';\n\n/**\n * Available keyring types\n */\nexport enum KeyringTypes {\n simple = 'Simple Key Pair',\n hd = 'HD Key Tree',\n qr = 'QR Hardware Wallet Device',\n trezor = 'Trezor Hardware',\n ledger = 'Ledger Hardware',\n lattice = 'Lattice Hardware',\n snap = 'Snap Keyring',\n}\n\n/**\n * Custody keyring types are a special case, as they are not a single type\n * but they all start with the prefix \"Custody\".\n * @param keyringType - The type of the keyring.\n * @returns Whether the keyring type is a custody keyring.\n */\nexport const isCustodyKeyring = (keyringType: string): boolean => {\n return keyringType.startsWith('Custody');\n};\n\n/**\n * @type KeyringControllerState\n *\n * Keyring controller state\n * @property vault - Encrypted string representing keyring data\n * @property isUnlocked - Whether vault is unlocked\n * @property keyringTypes - Account types\n * @property keyrings - Group of accounts\n * @property encryptionKey - Keyring encryption key\n * @property encryptionSalt - Keyring encryption salt\n */\nexport type KeyringControllerState = {\n vault?: string;\n isUnlocked: boolean;\n keyrings: KeyringObject[];\n encryptionKey?: string;\n encryptionSalt?: string;\n};\n\nexport type KeyringControllerMemState = Omit<\n KeyringControllerState,\n 'vault' | 'encryptionKey' | 'encryptionSalt'\n>;\n\nexport type KeyringControllerGetStateAction = {\n type: `${typeof name}:getState`;\n handler: () => KeyringControllerState;\n};\n\nexport type KeyringControllerSignMessageAction = {\n type: `${typeof name}:signMessage`;\n handler: KeyringController['signMessage'];\n};\n\nexport type KeyringControllerSignPersonalMessageAction = {\n type: `${typeof name}:signPersonalMessage`;\n handler: KeyringController['signPersonalMessage'];\n};\n\nexport type KeyringControllerSignTypedMessageAction = {\n type: `${typeof name}:signTypedMessage`;\n handler: KeyringController['signTypedMessage'];\n};\n\nexport type KeyringControllerDecryptMessageAction = {\n type: `${typeof name}:decryptMessage`;\n handler: KeyringController['decryptMessage'];\n};\n\nexport type KeyringControllerGetEncryptionPublicKeyAction = {\n type: `${typeof name}:getEncryptionPublicKey`;\n handler: KeyringController['getEncryptionPublicKey'];\n};\n\nexport type KeyringControllerGetKeyringsByTypeAction = {\n type: `${typeof name}:getKeyringsByType`;\n handler: KeyringController['getKeyringsByType'];\n};\n\nexport type KeyringControllerGetKeyringForAccountAction = {\n type: `${typeof name}:getKeyringForAccount`;\n handler: KeyringController['getKeyringForAccount'];\n};\n\nexport type KeyringControllerGetAccountsAction = {\n type: `${typeof name}:getAccounts`;\n handler: KeyringController['getAccounts'];\n};\n\nexport type KeyringControllerPersistAllKeyringsAction = {\n type: `${typeof name}:persistAllKeyrings`;\n handler: KeyringController['persistAllKeyrings'];\n};\n\nexport type KeyringControllerPrepareUserOperationAction = {\n type: `${typeof name}:prepareUserOperation`;\n handler: KeyringController['prepareUserOperation'];\n};\n\nexport type KeyringControllerPatchUserOperationAction = {\n type: `${typeof name}:patchUserOperation`;\n handler: KeyringController['patchUserOperation'];\n};\n\nexport type KeyringControllerSignUserOperationAction = {\n type: `${typeof name}:signUserOperation`;\n handler: KeyringController['signUserOperation'];\n};\n\nexport type KeyringControllerStateChangeEvent = {\n type: `${typeof name}:stateChange`;\n payload: [KeyringControllerState, Patch[]];\n};\n\nexport type KeyringControllerAccountRemovedEvent = {\n type: `${typeof name}:accountRemoved`;\n payload: [string];\n};\n\nexport type KeyringControllerLockEvent = {\n type: `${typeof name}:lock`;\n payload: [];\n};\n\nexport type KeyringControllerUnlockEvent = {\n type: `${typeof name}:unlock`;\n payload: [];\n};\n\nexport type KeyringControllerQRKeyringStateChangeEvent = {\n type: `${typeof name}:qrKeyringStateChange`;\n payload: [ReturnType];\n};\n\nexport type KeyringControllerActions =\n | KeyringControllerGetStateAction\n | KeyringControllerSignMessageAction\n | KeyringControllerSignPersonalMessageAction\n | KeyringControllerSignTypedMessageAction\n | KeyringControllerDecryptMessageAction\n | KeyringControllerGetEncryptionPublicKeyAction\n | KeyringControllerGetAccountsAction\n | KeyringControllerGetKeyringsByTypeAction\n | KeyringControllerGetKeyringForAccountAction\n | KeyringControllerPersistAllKeyringsAction\n | KeyringControllerPrepareUserOperationAction\n | KeyringControllerPatchUserOperationAction\n | KeyringControllerSignUserOperationAction;\n\nexport type KeyringControllerEvents =\n | KeyringControllerStateChangeEvent\n | KeyringControllerLockEvent\n | KeyringControllerUnlockEvent\n | KeyringControllerAccountRemovedEvent\n | KeyringControllerQRKeyringStateChangeEvent;\n\nexport type KeyringControllerMessenger = RestrictedControllerMessenger<\n typeof name,\n KeyringControllerActions,\n KeyringControllerEvents,\n never,\n never\n>;\n\nexport type KeyringControllerOptions = {\n keyringBuilders?: { (): EthKeyring; type: string }[];\n messenger: KeyringControllerMessenger;\n state?: { vault?: string };\n} & (\n | {\n cacheEncryptionKey: true;\n encryptor?: ExportableKeyEncryptor;\n }\n | {\n cacheEncryptionKey?: false;\n encryptor?: GenericEncryptor | ExportableKeyEncryptor;\n }\n);\n\n/**\n * @type KeyringObject\n *\n * Keyring object to return in fullUpdate\n * @property type - Keyring type\n * @property accounts - Associated accounts\n */\nexport type KeyringObject = {\n accounts: string[];\n type: string;\n};\n\n/**\n * A strategy for importing an account\n */\nexport enum AccountImportStrategy {\n privateKey = 'privateKey',\n json = 'json',\n}\n\n/**\n * The `signTypedMessage` version\n *\n * @see https://docs.metamask.io/guide/signing-data.html\n */\nexport enum SignTypedDataVersion {\n V1 = 'V1',\n V3 = 'V3',\n V4 = 'V4',\n}\n\n/**\n * A serialized keyring object.\n */\nexport type SerializedKeyring = {\n type: string;\n data: Json;\n};\n\n/**\n * A generic encryptor interface that supports encrypting and decrypting\n * serializable data with a password.\n */\nexport type GenericEncryptor = {\n /**\n * Encrypts the given object with the given password.\n *\n * @param password - The password to encrypt with.\n * @param object - The object to encrypt.\n * @returns The encrypted string.\n */\n encrypt: (password: string, object: Json) => Promise;\n /**\n * Decrypts the given encrypted string with the given password.\n *\n * @param password - The password to decrypt with.\n * @param encryptedString - The encrypted string to decrypt.\n * @returns The decrypted object.\n */\n decrypt: (password: string, encryptedString: string) => Promise;\n /**\n * Optional vault migration helper. Checks if the provided vault is up to date\n * with the desired encryption algorithm.\n *\n * @param vault - The encrypted string to check.\n * @param targetDerivationParams - The desired target derivation params.\n * @returns The updated encrypted string.\n */\n isVaultUpdated?: (\n vault: string,\n targetDerivationParams?: encryptorUtils.KeyDerivationOptions,\n ) => boolean;\n};\n\n/**\n * An encryptor interface that supports encrypting and decrypting\n * serializable data with a password, and exporting and importing keys.\n */\nexport type ExportableKeyEncryptor = GenericEncryptor & {\n /**\n * Encrypts the given object with the given encryption key.\n *\n * @param key - The encryption key to encrypt with.\n * @param object - The object to encrypt.\n * @returns The encryption result.\n */\n encryptWithKey: (\n key: unknown,\n object: Json,\n ) => Promise;\n /**\n * Encrypts the given object with the given password, and returns the\n * encryption result and the exported key string.\n *\n * @param password - The password to encrypt with.\n * @param object - The object to encrypt.\n * @param salt - The optional salt to use for encryption.\n * @returns The encrypted string and the exported key string.\n */\n encryptWithDetail: (\n password: string,\n object: Json,\n salt?: string,\n ) => Promise;\n /**\n * Decrypts the given encrypted string with the given encryption key.\n *\n * @param key - The encryption key to decrypt with.\n * @param encryptedString - The encrypted string to decrypt.\n * @returns The decrypted object.\n */\n decryptWithKey: (key: unknown, encryptedString: string) => Promise;\n /**\n * Decrypts the given encrypted string with the given password, and returns\n * the decrypted object and the salt and exported key string used for\n * encryption.\n *\n * @param password - The password to decrypt with.\n * @param encryptedString - The encrypted string to decrypt.\n * @returns The decrypted object and the salt and exported key string used for\n * encryption.\n */\n decryptWithDetail: (\n password: string,\n encryptedString: string,\n ) => Promise;\n /**\n * Generates an encryption key from exported key string.\n *\n * @param key - The exported key string.\n * @returns The encryption key.\n */\n importKey: (key: string) => Promise;\n};\n\nexport type KeyringSelector =\n | {\n type: string;\n index?: number;\n }\n | {\n address: Hex;\n };\n\n/**\n * A function executed within a mutually exclusive lock, with\n * a mutex releaser in its option bag.\n *\n * @param releaseLock - A function to release the lock.\n */\ntype MutuallyExclusiveCallback = ({\n releaseLock,\n}: {\n releaseLock: MutexInterface.Releaser;\n}) => Promise;\n\n/**\n * Get builder function for `Keyring`\n *\n * Returns a builder function for `Keyring` with a `type` property.\n *\n * @param KeyringConstructor - The Keyring class for the builder.\n * @returns A builder function for the given Keyring.\n */\nexport function keyringBuilderFactory(KeyringConstructor: KeyringClass) {\n const builder = () => new KeyringConstructor();\n\n builder.type = KeyringConstructor.type;\n\n return builder;\n}\n\nconst defaultKeyringBuilders = [\n keyringBuilderFactory(SimpleKeyring),\n keyringBuilderFactory(HDKeyring),\n];\n\nexport const getDefaultKeyringState = (): KeyringControllerState => {\n return {\n isUnlocked: false,\n keyrings: [],\n };\n};\n\n/**\n * Assert that the given keyring has an exportable\n * mnemonic.\n *\n * @param keyring - The keyring to check\n * @throws When the keyring does not have a mnemonic\n */\nfunction assertHasUint8ArrayMnemonic(\n keyring: EthKeyring,\n): asserts keyring is EthKeyring & { mnemonic: Uint8Array } {\n if (\n !(\n hasProperty(keyring, 'mnemonic') && keyring.mnemonic instanceof Uint8Array\n )\n ) {\n throw new Error(\"Can't get mnemonic bytes from keyring\");\n }\n}\n\n/**\n * Assert that the provided encryptor supports\n * encryption and encryption key export.\n *\n * @param encryptor - The encryptor to check.\n * @throws If the encryptor does not support key encryption.\n */\nfunction assertIsExportableKeyEncryptor(\n encryptor: GenericEncryptor | ExportableKeyEncryptor,\n): asserts encryptor is ExportableKeyEncryptor {\n if (\n !(\n 'importKey' in encryptor &&\n typeof encryptor.importKey === 'function' &&\n 'decryptWithKey' in encryptor &&\n typeof encryptor.decryptWithKey === 'function' &&\n 'encryptWithKey' in encryptor &&\n typeof encryptor.encryptWithKey === 'function'\n )\n ) {\n throw new Error(KeyringControllerError.UnsupportedEncryptionKeyExport);\n }\n}\n\n/**\n * Assert that the provided password is a valid non-empty string.\n *\n * @param password - The password to check.\n * @throws If the password is not a valid string.\n */\nfunction assertIsValidPassword(password: unknown): asserts password is string {\n if (typeof password !== 'string') {\n throw new Error(KeyringControllerError.WrongPasswordType);\n }\n\n if (!password || !password.length) {\n throw new Error(KeyringControllerError.InvalidEmptyPassword);\n }\n}\n\n/**\n * Checks if the provided value is a serialized keyrings array.\n *\n * @param array - The value to check.\n * @returns True if the value is a serialized keyrings array.\n */\nfunction isSerializedKeyringsArray(\n array: unknown,\n): array is SerializedKeyring[] {\n return (\n typeof array === 'object' &&\n Array.isArray(array) &&\n array.every((value) => value.type && isValidJson(value.data))\n );\n}\n\n/**\n * Display For Keyring\n *\n * Is used for adding the current keyrings to the state object.\n *\n * @param keyring - The keyring to display.\n * @returns A keyring display object, with type and accounts properties.\n */\nasync function displayForKeyring(\n keyring: EthKeyring,\n): Promise<{ type: string; accounts: string[] }> {\n const accounts = await keyring.getAccounts();\n\n return {\n type: keyring.type,\n // Cast to `string[]` here is safe here because `accounts` has no nullish\n // values, and `normalize` returns `string` unless given a nullish value\n accounts: accounts.map(normalize) as string[],\n };\n}\n\n/**\n * Check if address is an ethereum address\n *\n * @param address - An address.\n * @returns Returns true if the address is an ethereum one, false otherwise.\n */\nfunction isEthAddress(address: string): boolean {\n // We first check if it's a matching `Hex` string, so that is narrows down\n // `address` as an `Hex` type, allowing us to use `isValidHexAddress`\n return (\n // NOTE: This function only checks for lowercased strings\n isStrictHexString(address.toLowerCase()) &&\n // This checks for lowercased addresses and checksum addresses too\n isValidHexAddress(address as Hex)\n );\n}\n\n/**\n * Normalize ethereum or non-EVM address.\n *\n * @param address - Ethereum or non-EVM address.\n * @returns The normalized address.\n */\nfunction normalize(address: string): string | undefined {\n // Since the `KeyringController` is only dealing with address, we have\n // no other way to get the associated account type with this address. So we\n // are down to check the actual address format for now\n // TODO: Find a better way to not have those runtime checks based on the\n // address value!\n return isEthAddress(address) ? ethNormalize(address) : address;\n}\n\n/**\n * Controller responsible for establishing and managing user identity.\n *\n * This class is a wrapper around the `eth-keyring-controller` package. The\n * `eth-keyring-controller` manages the \"vault\", which is an encrypted store of private keys, and\n * it manages the wallet \"lock\" state. This wrapper class has convenience methods for interacting\n * with the internal keyring controller and handling certain complex operations that involve the\n * keyrings.\n */\nexport class KeyringController extends BaseController<\n typeof name,\n KeyringControllerState,\n KeyringControllerMessenger\n> {\n readonly #controllerOperationMutex = new Mutex();\n\n readonly #vaultOperationMutex = new Mutex();\n\n #keyringBuilders: { (): EthKeyring; type: string }[];\n\n #keyrings: EthKeyring[];\n\n #unsupportedKeyrings: SerializedKeyring[];\n\n #password?: string;\n\n #encryptor: GenericEncryptor | ExportableKeyEncryptor;\n\n #cacheEncryptionKey: boolean;\n\n #qrKeyringStateListener?: (\n state: ReturnType,\n ) => void;\n\n /**\n * Creates a KeyringController instance.\n *\n * @param options - Initial options used to configure this controller\n * @param options.encryptor - An optional object for defining encryption schemes.\n * @param options.keyringBuilders - Set a new name for account.\n * @param options.cacheEncryptionKey - Whether to cache or not encryption key.\n * @param options.messenger - A restricted controller messenger.\n * @param options.state - Initial state to set on this controller.\n */\n constructor(options: KeyringControllerOptions) {\n const {\n encryptor = encryptorUtils,\n keyringBuilders,\n messenger,\n state,\n } = options;\n\n super({\n name,\n metadata: {\n vault: { persist: true, anonymous: false },\n isUnlocked: { persist: false, anonymous: true },\n keyrings: { persist: false, anonymous: false },\n encryptionKey: { persist: false, anonymous: false },\n encryptionSalt: { persist: false, anonymous: false },\n },\n messenger,\n state: {\n ...getDefaultKeyringState(),\n ...state,\n },\n });\n\n this.#keyringBuilders = keyringBuilders\n ? defaultKeyringBuilders.concat(keyringBuilders)\n : defaultKeyringBuilders;\n\n this.#encryptor = encryptor;\n this.#keyrings = [];\n this.#unsupportedKeyrings = [];\n\n // This option allows the controller to cache an exported key\n // for use in decrypting and encrypting data without password\n this.#cacheEncryptionKey = Boolean(options.cacheEncryptionKey);\n if (this.#cacheEncryptionKey) {\n assertIsExportableKeyEncryptor(encryptor);\n }\n\n this.#registerMessageHandlers();\n }\n\n /**\n * Adds a new account to the default (first) HD seed phrase keyring.\n *\n * @param accountCount - Number of accounts before adding a new one, used to\n * make the method idempotent.\n * @returns Promise resolving to the added account address.\n */\n async addNewAccount(accountCount?: number): Promise {\n return this.#persistOrRollback(async () => {\n const primaryKeyring = this.getKeyringsByType('HD Key Tree')[0] as\n | EthKeyring\n | undefined;\n if (!primaryKeyring) {\n throw new Error('No HD keyring found');\n }\n const oldAccounts = await primaryKeyring.getAccounts();\n\n if (accountCount && oldAccounts.length !== accountCount) {\n if (accountCount > oldAccounts.length) {\n throw new Error('Account out of sequence');\n }\n // we return the account already existing at index `accountCount`\n const existingAccount = oldAccounts[accountCount];\n\n if (!existingAccount) {\n throw new Error(`Can't find account at index ${accountCount}`);\n }\n\n return existingAccount;\n }\n\n const [addedAccountAddress] = await primaryKeyring.addAccounts(1);\n await this.verifySeedPhrase();\n\n return addedAccountAddress;\n });\n }\n\n /**\n * Adds a new account to the specified keyring.\n *\n * @param keyring - Keyring to add the account to.\n * @param accountCount - Number of accounts before adding a new one, used to make the method idempotent.\n * @returns Promise resolving to the added account address\n */\n async addNewAccountForKeyring(\n keyring: EthKeyring,\n accountCount?: number,\n ): Promise {\n // READ THIS CAREFULLY:\n // We still uses `Hex` here, since we are not using this method when creating\n // and account using a \"Snap Keyring\". This function assume the `keyring` is\n // ethereum compatible, but \"Snap Keyring\" might not be.\n return this.#persistOrRollback(async () => {\n const oldAccounts = await this.#getAccountsFromKeyrings();\n\n if (accountCount && oldAccounts.length !== accountCount) {\n if (accountCount > oldAccounts.length) {\n throw new Error('Account out of sequence');\n }\n\n const existingAccount = oldAccounts[accountCount];\n assertIsStrictHexString(existingAccount);\n\n return existingAccount;\n }\n\n await keyring.addAccounts(1);\n\n const addedAccountAddress = (await this.#getAccountsFromKeyrings()).find(\n (selectedAddress) => !oldAccounts.includes(selectedAddress),\n );\n assertIsStrictHexString(addedAccountAddress);\n\n return addedAccountAddress;\n });\n }\n\n /**\n * Adds a new account to the default (first) HD seed phrase keyring without updating identities in preferences.\n *\n * @returns Promise resolving to the added account address.\n */\n async addNewAccountWithoutUpdate(): Promise {\n return this.#persistOrRollback(async () => {\n const primaryKeyring = this.getKeyringsByType('HD Key Tree')[0] as\n | EthKeyring\n | undefined;\n if (!primaryKeyring) {\n throw new Error('No HD keyring found');\n }\n const [addedAccountAddress] = await primaryKeyring.addAccounts(1);\n await this.verifySeedPhrase();\n return addedAccountAddress;\n });\n }\n\n /**\n * Effectively the same as creating a new keychain then populating it\n * using the given seed phrase.\n *\n * @param password - Password to unlock keychain.\n * @param seed - A BIP39-compliant seed phrase as Uint8Array,\n * either as a string or an array of UTF-8 bytes that represent the string.\n * @returns Promise resolving when the operation ends successfully.\n */\n async createNewVaultAndRestore(\n password: string,\n seed: Uint8Array,\n ): Promise {\n return this.#persistOrRollback(async () => {\n assertIsValidPassword(password);\n\n await this.#createNewVaultWithKeyring(password, {\n type: KeyringTypes.hd,\n opts: {\n mnemonic: seed,\n numberOfAccounts: 1,\n },\n });\n });\n }\n\n /**\n * Create a new primary keychain and wipe any previous keychains.\n *\n * @param password - Password to unlock the new vault.\n * @returns Promise resolving when the operation ends successfully.\n */\n async createNewVaultAndKeychain(password: string) {\n return this.#persistOrRollback(async () => {\n const accounts = await this.#getAccountsFromKeyrings();\n if (!accounts.length) {\n await this.#createNewVaultWithKeyring(password, {\n type: KeyringTypes.hd,\n });\n }\n });\n }\n\n /**\n * Adds a new keyring of the given `type`.\n *\n * @param type - Keyring type name.\n * @param opts - Keyring options.\n * @throws If a builder for the given `type` does not exist.\n * @returns Promise resolving to the added keyring.\n */\n async addNewKeyring(\n type: KeyringTypes | string,\n opts?: unknown,\n ): Promise {\n if (type === KeyringTypes.qr) {\n return this.getOrAddQRKeyring();\n }\n\n return this.#persistOrRollback(async () => this.#newKeyring(type, opts));\n }\n\n /**\n * Method to verify a given password validity. Throws an\n * error if the password is invalid.\n *\n * @param password - Password of the keyring.\n */\n async verifyPassword(password: string) {\n if (!this.state.vault) {\n throw new Error(KeyringControllerError.VaultError);\n }\n await this.#encryptor.decrypt(password, this.state.vault);\n }\n\n /**\n * Returns the status of the vault.\n *\n * @returns Boolean returning true if the vault is unlocked.\n */\n isUnlocked(): boolean {\n return this.state.isUnlocked;\n }\n\n /**\n * Gets the seed phrase of the HD keyring.\n *\n * @param password - Password of the keyring.\n * @returns Promise resolving to the seed phrase.\n */\n async exportSeedPhrase(password: string): Promise {\n await this.verifyPassword(password);\n assertHasUint8ArrayMnemonic(this.#keyrings[0]);\n return this.#keyrings[0].mnemonic;\n }\n\n /**\n * Gets the private key from the keyring controlling an address.\n *\n * @param password - Password of the keyring.\n * @param address - Address to export.\n * @returns Promise resolving to the private key for an address.\n */\n async exportAccount(password: string, address: string): Promise {\n await this.verifyPassword(password);\n\n const keyring = (await this.getKeyringForAccount(\n address,\n )) as EthKeyring;\n if (!keyring.exportAccount) {\n throw new Error(KeyringControllerError.UnsupportedExportAccount);\n }\n\n return await keyring.exportAccount(normalize(address) as Hex);\n }\n\n /**\n * Returns the public addresses of all accounts from every keyring.\n *\n * @returns A promise resolving to an array of addresses.\n */\n async getAccounts(): Promise {\n return this.state.keyrings.reduce(\n (accounts, keyring) => accounts.concat(keyring.accounts),\n [],\n );\n }\n\n /**\n * Get encryption public key.\n *\n * @param account - An account address.\n * @param opts - Additional encryption options.\n * @throws If the `account` does not exist or does not support the `getEncryptionPublicKey` method\n * @returns Promise resolving to encyption public key of the `account` if one exists.\n */\n async getEncryptionPublicKey(\n account: string,\n opts?: Record,\n ): Promise {\n const address = ethNormalize(account) as Hex;\n const keyring = (await this.getKeyringForAccount(\n account,\n )) as EthKeyring;\n if (!keyring.getEncryptionPublicKey) {\n throw new Error(KeyringControllerError.UnsupportedGetEncryptionPublicKey);\n }\n\n return await keyring.getEncryptionPublicKey(address, opts);\n }\n\n /**\n * Attempts to decrypt the provided message parameters.\n *\n * @param messageParams - The decryption message parameters.\n * @param messageParams.from - The address of the account you want to use to decrypt the message.\n * @param messageParams.data - The encrypted data that you want to decrypt.\n * @returns The raw decryption result.\n */\n async decryptMessage(messageParams: {\n from: string;\n data: Eip1024EncryptedData;\n }): Promise {\n const address = ethNormalize(messageParams.from) as Hex;\n const keyring = (await this.getKeyringForAccount(\n address,\n )) as EthKeyring;\n if (!keyring.decryptMessage) {\n throw new Error(KeyringControllerError.UnsupportedDecryptMessage);\n }\n\n return keyring.decryptMessage(address, messageParams.data);\n }\n\n /**\n * Returns the currently initialized keyring that manages\n * the specified `address` if one exists.\n *\n * @deprecated Use of this method is discouraged as actions executed directly on\n * keyrings are not being reflected in the KeyringController state and not\n * persisted in the vault. Use `withKeyring` instead.\n * @param account - An account address.\n * @returns Promise resolving to keyring of the `account` if one exists.\n */\n async getKeyringForAccount(account: string): Promise {\n const address = normalize(account);\n\n const candidates = await Promise.all(\n this.#keyrings.map(async (keyring) => {\n return Promise.all([keyring, keyring.getAccounts()]);\n }),\n );\n\n const winners = candidates.filter((candidate) => {\n const accounts = candidate[1].map(normalize);\n return accounts.includes(address);\n });\n\n if (winners.length && winners[0]?.length) {\n return winners[0][0];\n }\n\n // Adding more info to the error\n let errorInfo = '';\n if (!candidates.length) {\n errorInfo = 'There are no keyrings';\n } else if (!winners.length) {\n errorInfo = 'There are keyrings, but none match the address';\n }\n throw new Error(\n `${KeyringControllerError.NoKeyring}. Error info: ${errorInfo}`,\n );\n }\n\n /**\n * Returns all keyrings of the given type.\n *\n * @deprecated Use of this method is discouraged as actions executed directly on\n * keyrings are not being reflected in the KeyringController state and not\n * persisted in the vault. Use `withKeyring` instead.\n * @param type - Keyring type name.\n * @returns An array of keyrings of the given type.\n */\n getKeyringsByType(type: KeyringTypes | string): unknown[] {\n return this.#keyrings.filter((keyring) => keyring.type === type);\n }\n\n /**\n * Persist all serialized keyrings in the vault.\n *\n * @deprecated This method is being phased out in favor of `withKeyring`.\n * @returns Promise resolving with `true` value when the\n * operation completes.\n */\n async persistAllKeyrings(): Promise {\n return this.#persistOrRollback(async () => true);\n }\n\n /**\n * Imports an account with the specified import strategy.\n *\n * @param strategy - Import strategy name.\n * @param args - Array of arguments to pass to the underlying stategy.\n * @throws Will throw when passed an unrecognized strategy.\n * @returns Promise resolving to the imported account address.\n */\n async importAccountWithStrategy(\n strategy: AccountImportStrategy,\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n args: any[],\n ): Promise {\n return this.#persistOrRollback(async () => {\n let privateKey;\n switch (strategy) {\n case 'privateKey':\n const [importedKey] = args;\n if (!importedKey) {\n throw new Error('Cannot import an empty key.');\n }\n const prefixed = add0x(importedKey);\n\n let bufferedPrivateKey;\n try {\n bufferedPrivateKey = toBuffer(prefixed);\n } catch {\n throw new Error('Cannot import invalid private key.');\n }\n\n if (\n !isValidPrivate(bufferedPrivateKey) ||\n // ensures that the key is 64 bytes long\n getBinarySize(prefixed) !== 64 + '0x'.length\n ) {\n throw new Error('Cannot import invalid private key.');\n }\n\n privateKey = remove0x(prefixed);\n break;\n case 'json':\n let wallet;\n const [input, password] = args;\n try {\n wallet = importers.fromEtherWallet(input, password);\n } catch (e) {\n wallet = wallet || (await Wallet.fromV3(input, password, true));\n }\n privateKey = bytesToHex(wallet.getPrivateKey());\n break;\n default:\n throw new Error(`Unexpected import strategy: '${strategy}'`);\n }\n const newKeyring = (await this.#newKeyring(KeyringTypes.simple, [\n privateKey,\n ])) as EthKeyring;\n const accounts = await newKeyring.getAccounts();\n return accounts[0];\n });\n }\n\n /**\n * Removes an account from keyring state.\n *\n * @param address - Address of the account to remove.\n * @fires KeyringController:accountRemoved\n * @returns Promise resolving when the account is removed.\n */\n async removeAccount(address: string): Promise {\n await this.#persistOrRollback(async () => {\n const keyring = (await this.getKeyringForAccount(\n address,\n )) as EthKeyring;\n\n // Not all the keyrings support this, so we have to check\n if (!keyring.removeAccount) {\n throw new Error(KeyringControllerError.UnsupportedRemoveAccount);\n }\n\n // The `removeAccount` method of snaps keyring is async. We have to update\n // the interface of the other keyrings to be async as well.\n // eslint-disable-next-line @typescript-eslint/await-thenable\n // FIXME: We do cast to `Hex` to makes the type checker happy here, and\n // because `Keyring.removeAccount` requires address to be `Hex`. Those\n // type would need to be updated for a full non-EVM support.\n await keyring.removeAccount(address as Hex);\n\n const accounts = await keyring.getAccounts();\n // Check if this was the last/only account\n if (accounts.length === 0) {\n await this.#removeEmptyKeyrings();\n }\n });\n\n this.messagingSystem.publish(`${name}:accountRemoved`, address);\n }\n\n /**\n * Deallocates all secrets and locks the wallet.\n *\n * @returns Promise resolving when the operation completes.\n */\n async setLocked(): Promise {\n return this.#withRollback(async () => {\n this.#unsubscribeFromQRKeyringsEvents();\n\n this.#password = undefined;\n await this.#clearKeyrings();\n\n this.update((state) => {\n state.isUnlocked = false;\n state.keyrings = [];\n delete state.encryptionKey;\n delete state.encryptionSalt;\n });\n\n this.messagingSystem.publish(`${name}:lock`);\n });\n }\n\n /**\n * Signs message by calling down into a specific keyring.\n *\n * @param messageParams - PersonalMessageParams object to sign.\n * @returns Promise resolving to a signed message string.\n */\n async signMessage(messageParams: PersonalMessageParams): Promise {\n if (!messageParams.data) {\n throw new Error(\"Can't sign an empty message\");\n }\n\n const address = ethNormalize(messageParams.from) as Hex;\n const keyring = (await this.getKeyringForAccount(\n address,\n )) as EthKeyring;\n if (!keyring.signMessage) {\n throw new Error(KeyringControllerError.UnsupportedSignMessage);\n }\n\n return await keyring.signMessage(address, messageParams.data);\n }\n\n /**\n * Signs personal message by calling down into a specific keyring.\n *\n * @param messageParams - PersonalMessageParams object to sign.\n * @returns Promise resolving to a signed message string.\n */\n async signPersonalMessage(messageParams: PersonalMessageParams) {\n const address = ethNormalize(messageParams.from) as Hex;\n const keyring = (await this.getKeyringForAccount(\n address,\n )) as EthKeyring;\n if (!keyring.signPersonalMessage) {\n throw new Error(KeyringControllerError.UnsupportedSignPersonalMessage);\n }\n\n const normalizedData = normalize(messageParams.data) as Hex;\n\n return await keyring.signPersonalMessage(address, normalizedData);\n }\n\n /**\n * Signs typed message by calling down into a specific keyring.\n *\n * @param messageParams - TypedMessageParams object to sign.\n * @param version - Compatibility version EIP712.\n * @throws Will throw when passed an unrecognized version.\n * @returns Promise resolving to a signed message string or an error if any.\n */\n async signTypedMessage(\n messageParams: TypedMessageParams,\n version: SignTypedDataVersion,\n ): Promise {\n try {\n if (\n ![\n SignTypedDataVersion.V1,\n SignTypedDataVersion.V3,\n SignTypedDataVersion.V4,\n ].includes(version)\n ) {\n throw new Error(`Unexpected signTypedMessage version: '${version}'`);\n }\n\n // Cast to `Hex` here is safe here because `messageParams.from` is not nullish.\n // `normalize` returns `Hex` unless given a nullish value.\n const address = ethNormalize(messageParams.from) as Hex;\n const keyring = (await this.getKeyringForAccount(\n address,\n )) as EthKeyring;\n if (!keyring.signTypedData) {\n throw new Error(KeyringControllerError.UnsupportedSignTypedMessage);\n }\n\n return await keyring.signTypedData(\n address,\n version !== SignTypedDataVersion.V1 &&\n typeof messageParams.data === 'string'\n ? JSON.parse(messageParams.data)\n : messageParams.data,\n { version },\n );\n } catch (error) {\n throw new Error(`Keyring Controller signTypedMessage: ${error}`);\n }\n }\n\n /**\n * Signs a transaction by calling down into a specific keyring.\n *\n * @param transaction - Transaction object to sign. Must be a `ethereumjs-tx` transaction instance.\n * @param from - Address to sign from, should be in keychain.\n * @param opts - An optional options object.\n * @returns Promise resolving to a signed transaction string.\n */\n async signTransaction(\n transaction: TypedTransaction,\n from: string,\n opts?: Record,\n ): Promise {\n const address = ethNormalize(from) as Hex;\n const keyring = (await this.getKeyringForAccount(\n address,\n )) as EthKeyring;\n if (!keyring.signTransaction) {\n throw new Error(KeyringControllerError.UnsupportedSignTransaction);\n }\n\n return await keyring.signTransaction(address, transaction, opts);\n }\n\n /**\n * Convert a base transaction to a base UserOperation.\n *\n * @param from - Address of the sender.\n * @param transactions - Base transactions to include in the UserOperation.\n * @param executionContext - The execution context to use for the UserOperation.\n * @returns A pseudo-UserOperation that can be used to construct a real.\n */\n async prepareUserOperation(\n from: string,\n transactions: EthBaseTransaction[],\n executionContext: KeyringExecutionContext,\n ): Promise {\n const address = ethNormalize(from) as Hex;\n const keyring = (await this.getKeyringForAccount(\n address,\n )) as EthKeyring;\n\n if (!keyring.prepareUserOperation) {\n throw new Error(KeyringControllerError.UnsupportedPrepareUserOperation);\n }\n\n return await keyring.prepareUserOperation(\n address,\n transactions,\n executionContext,\n );\n }\n\n /**\n * Patches properties of a UserOperation. Currently, only the\n * `paymasterAndData` can be patched.\n *\n * @param from - Address of the sender.\n * @param userOp - UserOperation to patch.\n * @param executionContext - The execution context to use for the UserOperation.\n * @returns A patch to apply to the UserOperation.\n */\n async patchUserOperation(\n from: string,\n userOp: EthUserOperation,\n executionContext: KeyringExecutionContext,\n ): Promise {\n const address = ethNormalize(from) as Hex;\n const keyring = (await this.getKeyringForAccount(\n address,\n )) as EthKeyring;\n\n if (!keyring.patchUserOperation) {\n throw new Error(KeyringControllerError.UnsupportedPatchUserOperation);\n }\n\n return await keyring.patchUserOperation(address, userOp, executionContext);\n }\n\n /**\n * Signs an UserOperation.\n *\n * @param from - Address of the sender.\n * @param userOp - UserOperation to sign.\n * @param executionContext - The execution context to use for the UserOperation.\n * @returns The signature of the UserOperation.\n */\n async signUserOperation(\n from: string,\n userOp: EthUserOperation,\n executionContext: KeyringExecutionContext,\n ): Promise {\n const address = ethNormalize(from) as Hex;\n const keyring = (await this.getKeyringForAccount(\n address,\n )) as EthKeyring;\n\n if (!keyring.signUserOperation) {\n throw new Error(KeyringControllerError.UnsupportedSignUserOperation);\n }\n\n return await keyring.signUserOperation(address, userOp, executionContext);\n }\n\n /**\n * Changes the password used to encrypt the vault.\n *\n * @param password - The new password.\n * @returns Promise resolving when the operation completes.\n */\n changePassword(password: string): Promise {\n return this.#persistOrRollback(async () => {\n if (!this.state.isUnlocked) {\n throw new Error(KeyringControllerError.MissingCredentials);\n }\n\n assertIsValidPassword(password);\n\n this.#password = password;\n // We need to clear encryption key and salt from state\n // to force the controller to re-encrypt the vault using\n // the new password.\n if (this.#cacheEncryptionKey) {\n this.update((state) => {\n delete state.encryptionKey;\n delete state.encryptionSalt;\n });\n }\n });\n }\n\n /**\n * Attempts to decrypt the current vault and load its keyrings,\n * using the given encryption key and salt.\n *\n * @param encryptionKey - Key to unlock the keychain.\n * @param encryptionSalt - Salt to unlock the keychain.\n * @returns Promise resolving when the operation completes.\n */\n async submitEncryptionKey(\n encryptionKey: string,\n encryptionSalt: string,\n ): Promise {\n return this.#withRollback(async () => {\n this.#keyrings = await this.#unlockKeyrings(\n undefined,\n encryptionKey,\n encryptionSalt,\n );\n this.#setUnlocked();\n });\n }\n\n /**\n * Attempts to decrypt the current vault and load its keyrings,\n * using the given password.\n *\n * @param password - Password to unlock the keychain.\n * @returns Promise resolving when the operation completes.\n */\n async submitPassword(password: string): Promise {\n return this.#withRollback(async () => {\n this.#keyrings = await this.#unlockKeyrings(password);\n this.#setUnlocked();\n });\n }\n\n /**\n * Verifies the that the seed phrase restores the current keychain's accounts.\n *\n * @returns Promise resolving to the seed phrase as Uint8Array.\n */\n async verifySeedPhrase(): Promise {\n const primaryKeyring = this.getKeyringsByType(KeyringTypes.hd)[0] as\n | EthKeyring\n | undefined;\n if (!primaryKeyring) {\n throw new Error('No HD keyring found.');\n }\n\n assertHasUint8ArrayMnemonic(primaryKeyring);\n\n const seedWords = primaryKeyring.mnemonic;\n const accounts = await primaryKeyring.getAccounts();\n /* istanbul ignore if */\n if (accounts.length === 0) {\n throw new Error('Cannot verify an empty keyring.');\n }\n\n // The HD Keyring Builder is a default keyring builder\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const hdKeyringBuilder = this.#getKeyringBuilderForType(KeyringTypes.hd)!;\n\n const hdKeyring = hdKeyringBuilder();\n // @ts-expect-error @metamask/eth-hd-keyring correctly handles\n // Uint8Array seed phrases in the `deserialize` method.\n await hdKeyring.deserialize({\n mnemonic: seedWords,\n numberOfAccounts: accounts.length,\n });\n const testAccounts = await hdKeyring.getAccounts();\n /* istanbul ignore if */\n if (testAccounts.length !== accounts.length) {\n throw new Error('Seed phrase imported incorrect number of accounts.');\n }\n\n testAccounts.forEach((account: string, i: number) => {\n /* istanbul ignore if */\n if (account.toLowerCase() !== accounts[i].toLowerCase()) {\n throw new Error('Seed phrase imported different accounts.');\n }\n });\n\n return seedWords;\n }\n\n /**\n * Select a keyring and execute the given operation with\n * the selected keyring, as a mutually exclusive atomic\n * operation.\n *\n * The method automatically persists changes at the end of the\n * function execution, or rolls back the changes if an error\n * is thrown.\n *\n * @param selector - Keyring selector object.\n * @param operation - Function to execute with the selected keyring.\n * @param options - Additional options.\n * @param options.createIfMissing - Whether to create a new keyring if the selected one is missing.\n * @param options.createWithData - Optional data to use when creating a new keyring.\n * @returns Promise resolving to the result of the function execution.\n * @template SelectedKeyring - The type of the selected keyring.\n * @template CallbackResult - The type of the value resolved by the callback function.\n * @deprecated This method overload is deprecated. Use `withKeyring` without options instead.\n */\n async withKeyring<\n SelectedKeyring extends EthKeyring = EthKeyring,\n CallbackResult = void,\n >(\n selector: KeyringSelector,\n operation: (keyring: SelectedKeyring) => Promise,\n // eslint-disable-next-line @typescript-eslint/unified-signatures\n options:\n | { createIfMissing?: false }\n | { createIfMissing: true; createWithData?: unknown },\n ): Promise;\n\n /**\n * Select a keyring and execute the given operation with\n * the selected keyring, as a mutually exclusive atomic\n * operation.\n *\n * The method automatically persists changes at the end of the\n * function execution, or rolls back the changes if an error\n * is thrown.\n *\n * @param selector - Keyring selector object.\n * @param operation - Function to execute with the selected keyring.\n * @returns Promise resolving to the result of the function execution.\n * @template SelectedKeyring - The type of the selected keyring.\n * @template CallbackResult - The type of the value resolved by the callback function.\n */\n async withKeyring<\n SelectedKeyring extends EthKeyring = EthKeyring,\n CallbackResult = void,\n >(\n selector: KeyringSelector,\n operation: (keyring: SelectedKeyring) => Promise,\n ): Promise;\n\n async withKeyring<\n SelectedKeyring extends EthKeyring = EthKeyring,\n CallbackResult = void,\n >(\n selector: KeyringSelector,\n operation: (keyring: SelectedKeyring) => Promise,\n options:\n | { createIfMissing?: false }\n | { createIfMissing: true; createWithData?: unknown } = {\n createIfMissing: false,\n },\n ): Promise {\n return this.#persistOrRollback(async () => {\n let keyring: SelectedKeyring | undefined;\n\n if ('address' in selector) {\n keyring = (await this.getKeyringForAccount(selector.address)) as\n | SelectedKeyring\n | undefined;\n } else {\n keyring = this.getKeyringsByType(selector.type)[selector.index || 0] as\n | SelectedKeyring\n | undefined;\n\n if (!keyring && options.createIfMissing) {\n keyring = (await this.#newKeyring(\n selector.type,\n options.createWithData,\n )) as SelectedKeyring;\n }\n }\n\n if (!keyring) {\n throw new Error(KeyringControllerError.KeyringNotFound);\n }\n\n const result = await operation(keyring);\n\n if (Object.is(result, keyring)) {\n // Access to a keyring instance outside of controller safeguards\n // should be discouraged, as it can lead to unexpected behavior.\n // This error is thrown to prevent consumers using `withKeyring`\n // as a way to get a reference to a keyring instance.\n throw new Error(KeyringControllerError.UnsafeDirectKeyringAccess);\n }\n\n return result;\n });\n }\n\n // QR Hardware related methods\n\n /**\n * Get QR Hardware keyring.\n *\n * @returns The QR Keyring if defined, otherwise undefined\n */\n getQRKeyring(): QRKeyring | undefined {\n // QRKeyring is not yet compatible with Keyring type from @metamask/utils\n return this.getKeyringsByType(KeyringTypes.qr)[0] as unknown as QRKeyring;\n }\n\n /**\n * Get QR hardware keyring. If it doesn't exist, add it.\n *\n * @returns The added keyring\n */\n async getOrAddQRKeyring(): Promise {\n return (\n this.getQRKeyring() ||\n (await this.#persistOrRollback(async () => this.#addQRKeyring()))\n );\n }\n\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n async restoreQRKeyring(serialized: any): Promise {\n return this.#persistOrRollback(async () => {\n const keyring = this.getQRKeyring() || (await this.#addQRKeyring());\n keyring.deserialize(serialized);\n });\n }\n\n async resetQRKeyringState(): Promise {\n (await this.getOrAddQRKeyring()).resetStore();\n }\n\n async getQRKeyringState(): Promise {\n return (await this.getOrAddQRKeyring()).getMemStore();\n }\n\n async submitQRCryptoHDKey(cryptoHDKey: string): Promise {\n (await this.getOrAddQRKeyring()).submitCryptoHDKey(cryptoHDKey);\n }\n\n async submitQRCryptoAccount(cryptoAccount: string): Promise {\n (await this.getOrAddQRKeyring()).submitCryptoAccount(cryptoAccount);\n }\n\n async submitQRSignature(\n requestId: string,\n ethSignature: string,\n ): Promise {\n (await this.getOrAddQRKeyring()).submitSignature(requestId, ethSignature);\n }\n\n async cancelQRSignRequest(): Promise {\n (await this.getOrAddQRKeyring()).cancelSignRequest();\n }\n\n /**\n * Cancels qr keyring sync.\n */\n async cancelQRSynchronization(): Promise {\n // eslint-disable-next-line n/no-sync\n (await this.getOrAddQRKeyring()).cancelSync();\n }\n\n async connectQRHardware(\n page: number,\n ): Promise<{ balance: string; address: string; index: number }[]> {\n return this.#persistOrRollback(async () => {\n try {\n const keyring = this.getQRKeyring() || (await this.#addQRKeyring());\n let accounts;\n switch (page) {\n case -1:\n accounts = await keyring.getPreviousPage();\n break;\n case 1:\n accounts = await keyring.getNextPage();\n break;\n default:\n accounts = await keyring.getFirstPage();\n }\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return accounts.map((account: any) => {\n return {\n ...account,\n balance: '0x0',\n };\n });\n } catch (e) {\n // TODO: Add test case for when keyring throws\n /* istanbul ignore next */\n throw new Error(`Unspecified error when connect QR Hardware, ${e}`);\n }\n });\n }\n\n async unlockQRHardwareWalletAccount(index: number): Promise {\n return this.#persistOrRollback(async () => {\n const keyring = this.getQRKeyring() || (await this.#addQRKeyring());\n\n keyring.setAccountToUnlock(index);\n await keyring.addAccounts(1);\n });\n }\n\n async getAccountKeyringType(account: string): Promise {\n const keyring = (await this.getKeyringForAccount(\n account,\n )) as EthKeyring;\n return keyring.type;\n }\n\n async forgetQRDevice(): Promise<{\n removedAccounts: string[];\n remainingAccounts: string[];\n }> {\n return this.#persistOrRollback(async () => {\n const keyring = this.getQRKeyring();\n\n if (!keyring) {\n return { removedAccounts: [], remainingAccounts: [] };\n }\n\n const allAccounts = (await this.#getAccountsFromKeyrings()) as string[];\n keyring.forgetDevice();\n const remainingAccounts =\n (await this.#getAccountsFromKeyrings()) as string[];\n const removedAccounts = allAccounts.filter(\n (address: string) => !remainingAccounts.includes(address),\n );\n return { removedAccounts, remainingAccounts };\n });\n }\n\n /**\n * Constructor helper for registering this controller's messaging system\n * actions.\n */\n #registerMessageHandlers() {\n this.messagingSystem.registerActionHandler(\n `${name}:signMessage`,\n this.signMessage.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:signPersonalMessage`,\n this.signPersonalMessage.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:signTypedMessage`,\n this.signTypedMessage.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:decryptMessage`,\n this.decryptMessage.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:getEncryptionPublicKey`,\n this.getEncryptionPublicKey.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:getAccounts`,\n this.getAccounts.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:getKeyringsByType`,\n this.getKeyringsByType.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:getKeyringForAccount`,\n this.getKeyringForAccount.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:persistAllKeyrings`,\n this.persistAllKeyrings.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:prepareUserOperation`,\n this.prepareUserOperation.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:patchUserOperation`,\n this.patchUserOperation.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:signUserOperation`,\n this.signUserOperation.bind(this),\n );\n }\n\n /**\n * Get the keyring builder for the given `type`.\n *\n * @param type - The type of keyring to get the builder for.\n * @returns The keyring builder, or undefined if none exists.\n */\n #getKeyringBuilderForType(\n type: string,\n ): { (): EthKeyring; type: string } | undefined {\n return this.#keyringBuilders.find(\n (keyringBuilder) => keyringBuilder.type === type,\n );\n }\n\n /**\n * Add qr hardware keyring.\n *\n * @returns The added keyring\n * @throws If a QRKeyring builder is not provided\n * when initializing the controller\n */\n async #addQRKeyring(): Promise {\n this.#assertControllerMutexIsLocked();\n\n // QRKeyring is not yet compatible with Keyring type from @metamask/utils\n return (await this.#newKeyring(KeyringTypes.qr)) as unknown as QRKeyring;\n }\n\n /**\n * Subscribe to a QRKeyring state change events and\n * forward them through the messaging system.\n *\n * @param qrKeyring - The QRKeyring instance to subscribe to\n */\n #subscribeToQRKeyringEvents(qrKeyring: QRKeyring) {\n this.#qrKeyringStateListener = (state) => {\n this.messagingSystem.publish(`${name}:qrKeyringStateChange`, state);\n };\n\n qrKeyring.getMemStore().subscribe(this.#qrKeyringStateListener);\n }\n\n #unsubscribeFromQRKeyringsEvents() {\n const qrKeyrings = this.getKeyringsByType(\n KeyringTypes.qr,\n ) as unknown as QRKeyring[];\n\n qrKeyrings.forEach((qrKeyring) => {\n if (this.#qrKeyringStateListener) {\n qrKeyring.getMemStore().unsubscribe(this.#qrKeyringStateListener);\n }\n });\n }\n\n /**\n * Create new vault with an initial keyring\n *\n * Destroys any old encrypted storage,\n * creates a new encrypted store with the given password,\n * creates a new wallet with 1 account.\n *\n * @fires KeyringController:unlock\n * @param password - The password to encrypt the vault with.\n * @param keyring - A object containing the params to instantiate a new keyring.\n * @param keyring.type - The keyring type.\n * @param keyring.opts - Optional parameters required to instantiate the keyring.\n * @returns A promise that resolves to the state.\n */\n async #createNewVaultWithKeyring(\n password: string,\n keyring: {\n type: string;\n opts?: unknown;\n },\n ): Promise {\n this.#assertControllerMutexIsLocked();\n\n if (typeof password !== 'string') {\n throw new TypeError(KeyringControllerError.WrongPasswordType);\n }\n\n this.update((state) => {\n delete state.encryptionKey;\n delete state.encryptionSalt;\n });\n\n this.#password = password;\n\n await this.#clearKeyrings();\n await this.#createKeyringWithFirstAccount(keyring.type, keyring.opts);\n this.#setUnlocked();\n }\n\n /**\n * Get the updated array of each keyring's type and\n * accounts list.\n *\n * @returns A promise resolving to the updated keyrings array.\n */\n async #getUpdatedKeyrings(): Promise {\n return Promise.all(this.#keyrings.map(displayForKeyring));\n }\n\n /**\n * Serialize the current array of keyring instances,\n * including unsupported keyrings by default.\n *\n * @param options - Method options.\n * @param options.includeUnsupported - Whether to include unsupported keyrings.\n * @returns The serialized keyrings.\n */\n async #getSerializedKeyrings(\n { includeUnsupported }: { includeUnsupported: boolean } = {\n includeUnsupported: true,\n },\n ): Promise {\n const serializedKeyrings = await Promise.all(\n this.#keyrings.map(async (keyring) => {\n const [type, data] = await Promise.all([\n keyring.type,\n keyring.serialize(),\n ]);\n return { type, data };\n }),\n );\n\n if (includeUnsupported) {\n serializedKeyrings.push(...this.#unsupportedKeyrings);\n }\n\n return serializedKeyrings;\n }\n\n /**\n * Restore a serialized keyrings array.\n *\n * @param serializedKeyrings - The serialized keyrings array.\n */\n async #restoreSerializedKeyrings(\n serializedKeyrings: SerializedKeyring[],\n ): Promise {\n await this.#clearKeyrings();\n\n for (const serializedKeyring of serializedKeyrings) {\n await this.#restoreKeyring(serializedKeyring);\n }\n }\n\n /**\n * Unlock Keyrings, decrypting the vault and deserializing all\n * keyrings contained in it, using a password or an encryption key with salt.\n *\n * @param password - The keyring controller password.\n * @param encryptionKey - An exported key string to unlock keyrings with.\n * @param encryptionSalt - The salt used to encrypt the vault.\n * @returns A promise resolving to the deserialized keyrings array.\n */\n async #unlockKeyrings(\n password: string | undefined,\n encryptionKey?: string,\n encryptionSalt?: string,\n ): Promise[]> {\n return this.#withVaultLock(async ({ releaseLock }) => {\n const encryptedVault = this.state.vault;\n if (!encryptedVault) {\n throw new Error(KeyringControllerError.VaultError);\n }\n\n let vault;\n const updatedState: Partial = {};\n\n if (this.#cacheEncryptionKey) {\n assertIsExportableKeyEncryptor(this.#encryptor);\n\n if (password) {\n const result = await this.#encryptor.decryptWithDetail(\n password,\n encryptedVault,\n );\n vault = result.vault;\n this.#password = password;\n\n updatedState.encryptionKey = result.exportedKeyString;\n updatedState.encryptionSalt = result.salt;\n } else {\n const parsedEncryptedVault = JSON.parse(encryptedVault);\n\n if (encryptionSalt !== parsedEncryptedVault.salt) {\n throw new Error(KeyringControllerError.ExpiredCredentials);\n }\n\n if (typeof encryptionKey !== 'string') {\n throw new TypeError(KeyringControllerError.WrongPasswordType);\n }\n\n const key = await this.#encryptor.importKey(encryptionKey);\n vault = await this.#encryptor.decryptWithKey(\n key,\n parsedEncryptedVault,\n );\n\n // This call is required on the first call because encryptionKey\n // is not yet inside the memStore\n updatedState.encryptionKey = encryptionKey;\n // we can safely assume that encryptionSalt is defined here\n // because we compare it with the salt from the vault\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n updatedState.encryptionSalt = encryptionSalt!;\n }\n } else {\n if (typeof password !== 'string') {\n throw new TypeError(KeyringControllerError.WrongPasswordType);\n }\n\n vault = await this.#encryptor.decrypt(password, encryptedVault);\n this.#password = password;\n }\n\n if (!isSerializedKeyringsArray(vault)) {\n throw new Error(KeyringControllerError.VaultDataError);\n }\n\n await this.#restoreSerializedKeyrings(vault);\n const updatedKeyrings = await this.#getUpdatedKeyrings();\n\n this.update((state) => {\n state.keyrings = updatedKeyrings;\n if (updatedState.encryptionKey || updatedState.encryptionSalt) {\n state.encryptionKey = updatedState.encryptionKey;\n state.encryptionSalt = updatedState.encryptionSalt;\n }\n });\n\n if (\n this.#password &&\n (!this.#cacheEncryptionKey || !encryptionKey) &&\n this.#encryptor.isVaultUpdated &&\n !this.#encryptor.isVaultUpdated(encryptedVault)\n ) {\n // The lock needs to be released before persisting the keyrings\n // to avoid deadlock\n releaseLock();\n // Re-encrypt the vault with safer method if one is available\n await this.#updateVault();\n }\n\n return this.#keyrings;\n });\n }\n\n /**\n * Update the vault with the current keyrings.\n *\n * @returns A promise resolving to `true` if the operation is successful.\n */\n #updateVault(): Promise {\n return this.#withVaultLock(async () => {\n const { encryptionKey, encryptionSalt } = this.state;\n\n if (!this.#password && !encryptionKey) {\n throw new Error(KeyringControllerError.MissingCredentials);\n }\n\n const serializedKeyrings = await this.#getSerializedKeyrings();\n\n if (\n !serializedKeyrings.some((keyring) => keyring.type === KeyringTypes.hd)\n ) {\n throw new Error(KeyringControllerError.NoHdKeyring);\n }\n\n const updatedState: Partial = {};\n\n if (this.#cacheEncryptionKey) {\n assertIsExportableKeyEncryptor(this.#encryptor);\n\n if (encryptionKey) {\n const key = await this.#encryptor.importKey(encryptionKey);\n const vaultJSON = await this.#encryptor.encryptWithKey(\n key,\n serializedKeyrings,\n );\n vaultJSON.salt = encryptionSalt;\n updatedState.vault = JSON.stringify(vaultJSON);\n } else if (this.#password) {\n const { vault: newVault, exportedKeyString } =\n await this.#encryptor.encryptWithDetail(\n this.#password,\n serializedKeyrings,\n );\n\n updatedState.vault = newVault;\n updatedState.encryptionKey = exportedKeyString;\n }\n } else {\n assertIsValidPassword(this.#password);\n updatedState.vault = await this.#encryptor.encrypt(\n this.#password,\n serializedKeyrings,\n );\n }\n\n if (!updatedState.vault) {\n throw new Error(KeyringControllerError.MissingVaultData);\n }\n\n const updatedKeyrings = await this.#getUpdatedKeyrings();\n this.update((state) => {\n state.vault = updatedState.vault;\n state.keyrings = updatedKeyrings;\n if (updatedState.encryptionKey) {\n state.encryptionKey = updatedState.encryptionKey;\n state.encryptionSalt = JSON.parse(updatedState.vault as string).salt;\n }\n });\n\n return true;\n });\n }\n\n /**\n * Retrieves all the accounts from keyrings instances\n * that are currently in memory.\n *\n * @returns A promise resolving to an array of accounts.\n */\n async #getAccountsFromKeyrings(): Promise {\n const keyrings = this.#keyrings;\n\n const keyringArrays = await Promise.all(\n keyrings.map(async (keyring) => keyring.getAccounts()),\n );\n const addresses = keyringArrays.reduce((res, arr) => {\n return res.concat(arr);\n }, []);\n\n // Cast to `string[]` here is safe here because `addresses` has no nullish\n // values, and `normalize` returns `string` unless given a nullish value\n return addresses.map(normalize) as string[];\n }\n\n /**\n * Create a new keyring, ensuring that the first account is\n * also created.\n *\n * @param type - Keyring type to instantiate.\n * @param opts - Optional parameters required to instantiate the keyring.\n * @returns A promise that resolves if the operation is successful.\n */\n async #createKeyringWithFirstAccount(type: string, opts?: unknown) {\n this.#assertControllerMutexIsLocked();\n\n const keyring = (await this.#newKeyring(type, opts)) as EthKeyring;\n\n const [firstAccount] = await keyring.getAccounts();\n if (!firstAccount) {\n throw new Error(KeyringControllerError.NoFirstAccount);\n }\n }\n\n /**\n * Instantiate, initialize and return a new keyring of the given `type`,\n * using the given `opts`. The keyring is built using the keyring builder\n * registered for the given `type`.\n *\n *\n * @param type - The type of keyring to add.\n * @param data - The data to restore a previously serialized keyring.\n * @returns The new keyring.\n * @throws If the keyring includes duplicated accounts.\n */\n async #newKeyring(type: string, data?: unknown): Promise> {\n this.#assertControllerMutexIsLocked();\n\n const keyringBuilder = this.#getKeyringBuilderForType(type);\n\n if (!keyringBuilder) {\n throw new Error(\n `${KeyringControllerError.NoKeyringBuilder}. Keyring type: ${type}`,\n );\n }\n\n const keyring = keyringBuilder();\n\n // @ts-expect-error Enforce data type after updating clients\n await keyring.deserialize(data);\n\n if (keyring.init) {\n await keyring.init();\n }\n\n if (type === KeyringTypes.hd && (!isObject(data) || !data.mnemonic)) {\n if (!keyring.generateRandomMnemonic) {\n throw new Error(\n KeyringControllerError.UnsupportedGenerateRandomMnemonic,\n );\n }\n\n keyring.generateRandomMnemonic();\n await keyring.addAccounts(1);\n }\n\n await this.#checkForDuplicate(type, await keyring.getAccounts());\n\n if (type === KeyringTypes.qr) {\n // In case of a QR keyring type, we need to subscribe\n // to its events after creating it\n this.#subscribeToQRKeyringEvents(keyring as unknown as QRKeyring);\n }\n\n this.#keyrings.push(keyring);\n\n return keyring;\n }\n\n /**\n * Remove all managed keyrings, destroying all their\n * instances in memory.\n */\n async #clearKeyrings() {\n this.#assertControllerMutexIsLocked();\n for (const keyring of this.#keyrings) {\n await this.#destroyKeyring(keyring);\n }\n this.#keyrings = [];\n }\n\n /**\n * Restore a Keyring from a provided serialized payload.\n * On success, returns the resulting keyring instance.\n *\n * @param serialized - The serialized keyring.\n * @returns The deserialized keyring or undefined if the keyring type is unsupported.\n */\n async #restoreKeyring(\n serialized: SerializedKeyring,\n ): Promise | undefined> {\n this.#assertControllerMutexIsLocked();\n\n try {\n const { type, data } = serialized;\n return await this.#newKeyring(type, data);\n } catch (_) {\n this.#unsupportedKeyrings.push(serialized);\n return undefined;\n }\n }\n\n /**\n * Destroy Keyring\n *\n * Some keyrings support a method called `destroy`, that destroys the\n * keyring along with removing all its event listeners and, in some cases,\n * clears the keyring bridge iframe from the DOM.\n *\n * @param keyring - The keyring to destroy.\n */\n async #destroyKeyring(keyring: EthKeyring) {\n await keyring.destroy?.();\n }\n\n /**\n * Remove empty keyrings.\n *\n * Loops through the keyrings and removes the ones with empty accounts\n * (usually after removing the last / only account) from a keyring.\n */\n async #removeEmptyKeyrings(): Promise {\n this.#assertControllerMutexIsLocked();\n const validKeyrings: EthKeyring[] = [];\n\n // Since getAccounts returns a Promise\n // We need to wait to hear back form each keyring\n // in order to decide which ones are now valid (accounts.length > 0)\n\n await Promise.all(\n this.#keyrings.map(async (keyring: EthKeyring) => {\n const accounts = await keyring.getAccounts();\n if (accounts.length > 0) {\n validKeyrings.push(keyring);\n } else {\n await this.#destroyKeyring(keyring);\n }\n }),\n );\n this.#keyrings = validKeyrings;\n }\n\n /**\n * Checks for duplicate keypairs, using the the first account in the given\n * array. Rejects if a duplicate is found.\n *\n * Only supports 'Simple Key Pair'.\n *\n * @param type - The key pair type to check for.\n * @param newAccountArray - Array of new accounts.\n * @returns The account, if no duplicate is found.\n */\n async #checkForDuplicate(\n type: string,\n newAccountArray: string[],\n ): Promise {\n const accounts = await this.#getAccountsFromKeyrings();\n\n switch (type) {\n case KeyringTypes.simple: {\n const isIncluded = Boolean(\n accounts.find(\n (key) =>\n newAccountArray[0] &&\n (key === newAccountArray[0] ||\n key === remove0x(newAccountArray[0])),\n ),\n );\n\n if (isIncluded) {\n throw new Error(KeyringControllerError.DuplicatedAccount);\n }\n return newAccountArray;\n }\n\n default: {\n return newAccountArray;\n }\n }\n }\n\n /**\n * Set the `isUnlocked` to true and notify listeners\n * through the messenger.\n *\n * @fires KeyringController:unlock\n */\n #setUnlocked(): void {\n this.#assertControllerMutexIsLocked();\n\n this.update((state) => {\n state.isUnlocked = true;\n });\n this.messagingSystem.publish(`${name}:unlock`);\n }\n\n /**\n * Execute the given function after acquiring the controller lock\n * and save the keyrings to state after it, or rollback to their\n * previous state in case of error.\n *\n * @param fn - The function to execute.\n * @returns The result of the function.\n */\n async #persistOrRollback(fn: MutuallyExclusiveCallback): Promise {\n return this.#withRollback(async ({ releaseLock }) => {\n const callbackResult = await fn({ releaseLock });\n // State is committed only if the operation is successful\n await this.#updateVault();\n\n return callbackResult;\n });\n }\n\n /**\n * Execute the given function after acquiring the controller lock\n * and rollback keyrings and password states in case of error.\n *\n * @param fn - The function to execute atomically.\n * @returns The result of the function.\n */\n async #withRollback(fn: MutuallyExclusiveCallback): Promise {\n return this.#withControllerLock(async ({ releaseLock }) => {\n const currentSerializedKeyrings = await this.#getSerializedKeyrings();\n const currentPassword = this.#password;\n\n try {\n return await fn({ releaseLock });\n } catch (e) {\n // Keyrings and password are restored to their previous state\n await this.#restoreSerializedKeyrings(currentSerializedKeyrings);\n this.#password = currentPassword;\n\n throw e;\n }\n });\n }\n\n /**\n * Assert that the controller mutex is locked.\n *\n * @throws If the controller mutex is not locked.\n */\n #assertControllerMutexIsLocked() {\n if (!this.#controllerOperationMutex.isLocked()) {\n throw new Error(KeyringControllerError.ControllerLockRequired);\n }\n }\n\n /**\n * Lock the controller mutex before executing the given function,\n * and release it after the function is resolved or after an\n * error is thrown.\n *\n * This wrapper ensures that each mutable operation that interacts with the\n * controller and that changes its state is executed in a mutually exclusive way,\n * preventing unsafe concurrent access that could lead to unpredictable behavior.\n *\n * @param fn - The function to execute while the controller mutex is locked.\n * @returns The result of the function.\n */\n async #withControllerLock(fn: MutuallyExclusiveCallback): Promise {\n return withLock(this.#controllerOperationMutex, fn);\n }\n\n /**\n * Lock the vault mutex before executing the given function,\n * and release it after the function is resolved or after an\n * error is thrown.\n *\n * This ensures that each operation that interacts with the vault\n * is executed in a mutually exclusive way.\n *\n * @param fn - The function to execute while the vault mutex is locked.\n * @returns The result of the function.\n */\n async #withVaultLock(fn: MutuallyExclusiveCallback): Promise {\n this.#assertControllerMutexIsLocked();\n\n return withLock(this.#vaultOperationMutex, fn);\n }\n}\n\n/**\n * Lock the given mutex before executing the given function,\n * and release it after the function is resolved or after an\n * error is thrown.\n *\n * @param mutex - The mutex to lock.\n * @param fn - The function to execute while the mutex is locked.\n * @returns The result of the function.\n */\nasync function withLock(\n mutex: Mutex,\n fn: MutuallyExclusiveCallback,\n): Promise {\n const releaseLock = await mutex.acquire();\n\n try {\n return await fn({ releaseLock });\n } finally {\n releaseLock();\n }\n}\n\nexport default KeyringController;\n"]} -\ No newline at end of file -diff --git a/dist/chunk-STFS4REY.mjs b/dist/chunk-STFS4REY.mjs -deleted file mode 100644 -index 58e38b7016380f616d2bed694a35a81f639d304b..0000000000000000000000000000000000000000 ---- a/dist/chunk-STFS4REY.mjs -+++ /dev/null -@@ -1,1500 +0,0 @@ --import { -- __privateAdd, -- __privateGet, -- __privateMethod, -- __privateSet --} from "./chunk-F64I344Z.mjs"; -- --// src/KeyringController.ts --import { isValidPrivate, toBuffer, getBinarySize } from "@ethereumjs/util"; --import { BaseController } from "@metamask/base-controller"; --import * as encryptorUtils from "@metamask/browser-passworder"; --import HDKeyring from "@metamask/eth-hd-keyring"; --import { normalize as ethNormalize } from "@metamask/eth-sig-util"; --import SimpleKeyring from "@metamask/eth-simple-keyring"; --import { -- add0x, -- assertIsStrictHexString, -- bytesToHex, -- hasProperty, -- isObject, -- isStrictHexString, -- isValidHexAddress, -- isValidJson, -- remove0x --} from "@metamask/utils"; --import { Mutex } from "async-mutex"; --import Wallet, { thirdparty as importers } from "ethereumjs-wallet"; --var name = "KeyringController"; --var KeyringTypes = /* @__PURE__ */ ((KeyringTypes2) => { -- KeyringTypes2["simple"] = "Simple Key Pair"; -- KeyringTypes2["hd"] = "HD Key Tree"; -- KeyringTypes2["qr"] = "QR Hardware Wallet Device"; -- KeyringTypes2["trezor"] = "Trezor Hardware"; -- KeyringTypes2["ledger"] = "Ledger Hardware"; -- KeyringTypes2["lattice"] = "Lattice Hardware"; -- KeyringTypes2["snap"] = "Snap Keyring"; -- return KeyringTypes2; --})(KeyringTypes || {}); --var isCustodyKeyring = (keyringType) => { -- return keyringType.startsWith("Custody"); --}; --var AccountImportStrategy = /* @__PURE__ */ ((AccountImportStrategy2) => { -- AccountImportStrategy2["privateKey"] = "privateKey"; -- AccountImportStrategy2["json"] = "json"; -- return AccountImportStrategy2; --})(AccountImportStrategy || {}); --var SignTypedDataVersion = /* @__PURE__ */ ((SignTypedDataVersion2) => { -- SignTypedDataVersion2["V1"] = "V1"; -- SignTypedDataVersion2["V3"] = "V3"; -- SignTypedDataVersion2["V4"] = "V4"; -- return SignTypedDataVersion2; --})(SignTypedDataVersion || {}); --function keyringBuilderFactory(KeyringConstructor) { -- const builder = () => new KeyringConstructor(); -- builder.type = KeyringConstructor.type; -- return builder; --} --var defaultKeyringBuilders = [ -- keyringBuilderFactory(SimpleKeyring), -- keyringBuilderFactory(HDKeyring) --]; --var getDefaultKeyringState = () => { -- return { -- isUnlocked: false, -- keyrings: [] -- }; --}; --function assertHasUint8ArrayMnemonic(keyring) { -- if (!(hasProperty(keyring, "mnemonic") && keyring.mnemonic instanceof Uint8Array)) { -- throw new Error("Can't get mnemonic bytes from keyring"); -- } --} --function assertIsExportableKeyEncryptor(encryptor) { -- if (!("importKey" in encryptor && typeof encryptor.importKey === "function" && "decryptWithKey" in encryptor && typeof encryptor.decryptWithKey === "function" && "encryptWithKey" in encryptor && typeof encryptor.encryptWithKey === "function")) { -- throw new Error("KeyringController - The encryptor does not support encryption key export." /* UnsupportedEncryptionKeyExport */); -- } --} --function assertIsValidPassword(password) { -- if (typeof password !== "string") { -- throw new Error("KeyringController - Password must be of type string." /* WrongPasswordType */); -- } -- if (!password || !password.length) { -- throw new Error("KeyringController - Password cannot be empty." /* InvalidEmptyPassword */); -- } --} --function isSerializedKeyringsArray(array) { -- return typeof array === "object" && Array.isArray(array) && array.every((value) => value.type && isValidJson(value.data)); --} --async function displayForKeyring(keyring) { -- const accounts = await keyring.getAccounts(); -- return { -- type: keyring.type, -- // Cast to `string[]` here is safe here because `accounts` has no nullish -- // values, and `normalize` returns `string` unless given a nullish value -- accounts: accounts.map(normalize) -- }; --} --function isEthAddress(address) { -- return ( -- // NOTE: This function only checks for lowercased strings -- isStrictHexString(address.toLowerCase()) && // This checks for lowercased addresses and checksum addresses too -- isValidHexAddress(address) -- ); --} --function normalize(address) { -- return isEthAddress(address) ? ethNormalize(address) : address; --} --var _controllerOperationMutex, _vaultOperationMutex, _keyringBuilders, _keyrings, _unsupportedKeyrings, _password, _encryptor, _cacheEncryptionKey, _qrKeyringStateListener, _registerMessageHandlers, registerMessageHandlers_fn, _getKeyringBuilderForType, getKeyringBuilderForType_fn, _addQRKeyring, addQRKeyring_fn, _subscribeToQRKeyringEvents, subscribeToQRKeyringEvents_fn, _unsubscribeFromQRKeyringsEvents, unsubscribeFromQRKeyringsEvents_fn, _createNewVaultWithKeyring, createNewVaultWithKeyring_fn, _getUpdatedKeyrings, getUpdatedKeyrings_fn, _getSerializedKeyrings, getSerializedKeyrings_fn, _restoreSerializedKeyrings, restoreSerializedKeyrings_fn, _unlockKeyrings, unlockKeyrings_fn, _updateVault, updateVault_fn, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn, _createKeyringWithFirstAccount, createKeyringWithFirstAccount_fn, _newKeyring, newKeyring_fn, _clearKeyrings, clearKeyrings_fn, _restoreKeyring, restoreKeyring_fn, _destroyKeyring, destroyKeyring_fn, _removeEmptyKeyrings, removeEmptyKeyrings_fn, _checkForDuplicate, checkForDuplicate_fn, _setUnlocked, setUnlocked_fn, _persistOrRollback, persistOrRollback_fn, _withRollback, withRollback_fn, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn, _withControllerLock, withControllerLock_fn, _withVaultLock, withVaultLock_fn; --var KeyringController = class extends BaseController { -- /** -- * Creates a KeyringController instance. -- * -- * @param options - Initial options used to configure this controller -- * @param options.encryptor - An optional object for defining encryption schemes. -- * @param options.keyringBuilders - Set a new name for account. -- * @param options.cacheEncryptionKey - Whether to cache or not encryption key. -- * @param options.messenger - A restricted controller messenger. -- * @param options.state - Initial state to set on this controller. -- */ -- constructor(options) { -- const { -- encryptor = encryptorUtils, -- keyringBuilders, -- messenger, -- state -- } = options; -- super({ -- name, -- metadata: { -- vault: { persist: true, anonymous: false }, -- isUnlocked: { persist: false, anonymous: true }, -- keyrings: { persist: false, anonymous: false }, -- encryptionKey: { persist: false, anonymous: false }, -- encryptionSalt: { persist: false, anonymous: false } -- }, -- messenger, -- state: { -- ...getDefaultKeyringState(), -- ...state -- } -- }); -- /** -- * Constructor helper for registering this controller's messaging system -- * actions. -- */ -- __privateAdd(this, _registerMessageHandlers); -- /** -- * Get the keyring builder for the given `type`. -- * -- * @param type - The type of keyring to get the builder for. -- * @returns The keyring builder, or undefined if none exists. -- */ -- __privateAdd(this, _getKeyringBuilderForType); -- /** -- * Add qr hardware keyring. -- * -- * @returns The added keyring -- * @throws If a QRKeyring builder is not provided -- * when initializing the controller -- */ -- __privateAdd(this, _addQRKeyring); -- /** -- * Subscribe to a QRKeyring state change events and -- * forward them through the messaging system. -- * -- * @param qrKeyring - The QRKeyring instance to subscribe to -- */ -- __privateAdd(this, _subscribeToQRKeyringEvents); -- __privateAdd(this, _unsubscribeFromQRKeyringsEvents); -- /** -- * Create new vault with an initial keyring -- * -- * Destroys any old encrypted storage, -- * creates a new encrypted store with the given password, -- * creates a new wallet with 1 account. -- * -- * @fires KeyringController:unlock -- * @param password - The password to encrypt the vault with. -- * @param keyring - A object containing the params to instantiate a new keyring. -- * @param keyring.type - The keyring type. -- * @param keyring.opts - Optional parameters required to instantiate the keyring. -- * @returns A promise that resolves to the state. -- */ -- __privateAdd(this, _createNewVaultWithKeyring); -- /** -- * Get the updated array of each keyring's type and -- * accounts list. -- * -- * @returns A promise resolving to the updated keyrings array. -- */ -- __privateAdd(this, _getUpdatedKeyrings); -- /** -- * Serialize the current array of keyring instances, -- * including unsupported keyrings by default. -- * -- * @param options - Method options. -- * @param options.includeUnsupported - Whether to include unsupported keyrings. -- * @returns The serialized keyrings. -- */ -- __privateAdd(this, _getSerializedKeyrings); -- /** -- * Restore a serialized keyrings array. -- * -- * @param serializedKeyrings - The serialized keyrings array. -- */ -- __privateAdd(this, _restoreSerializedKeyrings); -- /** -- * Unlock Keyrings, decrypting the vault and deserializing all -- * keyrings contained in it, using a password or an encryption key with salt. -- * -- * @param password - The keyring controller password. -- * @param encryptionKey - An exported key string to unlock keyrings with. -- * @param encryptionSalt - The salt used to encrypt the vault. -- * @returns A promise resolving to the deserialized keyrings array. -- */ -- __privateAdd(this, _unlockKeyrings); -- /** -- * Update the vault with the current keyrings. -- * -- * @returns A promise resolving to `true` if the operation is successful. -- */ -- __privateAdd(this, _updateVault); -- /** -- * Retrieves all the accounts from keyrings instances -- * that are currently in memory. -- * -- * @returns A promise resolving to an array of accounts. -- */ -- __privateAdd(this, _getAccountsFromKeyrings); -- /** -- * Create a new keyring, ensuring that the first account is -- * also created. -- * -- * @param type - Keyring type to instantiate. -- * @param opts - Optional parameters required to instantiate the keyring. -- * @returns A promise that resolves if the operation is successful. -- */ -- __privateAdd(this, _createKeyringWithFirstAccount); -- /** -- * Instantiate, initialize and return a new keyring of the given `type`, -- * using the given `opts`. The keyring is built using the keyring builder -- * registered for the given `type`. -- * -- * -- * @param type - The type of keyring to add. -- * @param data - The data to restore a previously serialized keyring. -- * @returns The new keyring. -- * @throws If the keyring includes duplicated accounts. -- */ -- __privateAdd(this, _newKeyring); -- /** -- * Remove all managed keyrings, destroying all their -- * instances in memory. -- */ -- __privateAdd(this, _clearKeyrings); -- /** -- * Restore a Keyring from a provided serialized payload. -- * On success, returns the resulting keyring instance. -- * -- * @param serialized - The serialized keyring. -- * @returns The deserialized keyring or undefined if the keyring type is unsupported. -- */ -- __privateAdd(this, _restoreKeyring); -- /** -- * Destroy Keyring -- * -- * Some keyrings support a method called `destroy`, that destroys the -- * keyring along with removing all its event listeners and, in some cases, -- * clears the keyring bridge iframe from the DOM. -- * -- * @param keyring - The keyring to destroy. -- */ -- __privateAdd(this, _destroyKeyring); -- /** -- * Remove empty keyrings. -- * -- * Loops through the keyrings and removes the ones with empty accounts -- * (usually after removing the last / only account) from a keyring. -- */ -- __privateAdd(this, _removeEmptyKeyrings); -- /** -- * Checks for duplicate keypairs, using the the first account in the given -- * array. Rejects if a duplicate is found. -- * -- * Only supports 'Simple Key Pair'. -- * -- * @param type - The key pair type to check for. -- * @param newAccountArray - Array of new accounts. -- * @returns The account, if no duplicate is found. -- */ -- __privateAdd(this, _checkForDuplicate); -- /** -- * Set the `isUnlocked` to true and notify listeners -- * through the messenger. -- * -- * @fires KeyringController:unlock -- */ -- __privateAdd(this, _setUnlocked); -- /** -- * Execute the given function after acquiring the controller lock -- * and save the keyrings to state after it, or rollback to their -- * previous state in case of error. -- * -- * @param fn - The function to execute. -- * @returns The result of the function. -- */ -- __privateAdd(this, _persistOrRollback); -- /** -- * Execute the given function after acquiring the controller lock -- * and rollback keyrings and password states in case of error. -- * -- * @param fn - The function to execute atomically. -- * @returns The result of the function. -- */ -- __privateAdd(this, _withRollback); -- /** -- * Assert that the controller mutex is locked. -- * -- * @throws If the controller mutex is not locked. -- */ -- __privateAdd(this, _assertControllerMutexIsLocked); -- /** -- * Lock the controller mutex before executing the given function, -- * and release it after the function is resolved or after an -- * error is thrown. -- * -- * This wrapper ensures that each mutable operation that interacts with the -- * controller and that changes its state is executed in a mutually exclusive way, -- * preventing unsafe concurrent access that could lead to unpredictable behavior. -- * -- * @param fn - The function to execute while the controller mutex is locked. -- * @returns The result of the function. -- */ -- __privateAdd(this, _withControllerLock); -- /** -- * Lock the vault mutex before executing the given function, -- * and release it after the function is resolved or after an -- * error is thrown. -- * -- * This ensures that each operation that interacts with the vault -- * is executed in a mutually exclusive way. -- * -- * @param fn - The function to execute while the vault mutex is locked. -- * @returns The result of the function. -- */ -- __privateAdd(this, _withVaultLock); -- __privateAdd(this, _controllerOperationMutex, new Mutex()); -- __privateAdd(this, _vaultOperationMutex, new Mutex()); -- __privateAdd(this, _keyringBuilders, void 0); -- __privateAdd(this, _keyrings, void 0); -- __privateAdd(this, _unsupportedKeyrings, void 0); -- __privateAdd(this, _password, void 0); -- __privateAdd(this, _encryptor, void 0); -- __privateAdd(this, _cacheEncryptionKey, void 0); -- __privateAdd(this, _qrKeyringStateListener, void 0); -- __privateSet(this, _keyringBuilders, keyringBuilders ? defaultKeyringBuilders.concat(keyringBuilders) : defaultKeyringBuilders); -- __privateSet(this, _encryptor, encryptor); -- __privateSet(this, _keyrings, []); -- __privateSet(this, _unsupportedKeyrings, []); -- __privateSet(this, _cacheEncryptionKey, Boolean(options.cacheEncryptionKey)); -- if (__privateGet(this, _cacheEncryptionKey)) { -- assertIsExportableKeyEncryptor(encryptor); -- } -- __privateMethod(this, _registerMessageHandlers, registerMessageHandlers_fn).call(this); -- } -- /** -- * Adds a new account to the default (first) HD seed phrase keyring. -- * -- * @param accountCount - Number of accounts before adding a new one, used to -- * make the method idempotent. -- * @returns Promise resolving to the added account address. -- */ -- async addNewAccount(accountCount) { -- return __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -- const primaryKeyring = this.getKeyringsByType("HD Key Tree")[0]; -- if (!primaryKeyring) { -- throw new Error("No HD keyring found"); -- } -- const oldAccounts = await primaryKeyring.getAccounts(); -- if (accountCount && oldAccounts.length !== accountCount) { -- if (accountCount > oldAccounts.length) { -- throw new Error("Account out of sequence"); -- } -- const existingAccount = oldAccounts[accountCount]; -- if (!existingAccount) { -- throw new Error(`Can't find account at index ${accountCount}`); -- } -- return existingAccount; -- } -- const [addedAccountAddress] = await primaryKeyring.addAccounts(1); -- await this.verifySeedPhrase(); -- return addedAccountAddress; -- }); -- } -- /** -- * Adds a new account to the specified keyring. -- * -- * @param keyring - Keyring to add the account to. -- * @param accountCount - Number of accounts before adding a new one, used to make the method idempotent. -- * @returns Promise resolving to the added account address -- */ -- async addNewAccountForKeyring(keyring, accountCount) { -- return __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -- const oldAccounts = await __privateMethod(this, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn).call(this); -- if (accountCount && oldAccounts.length !== accountCount) { -- if (accountCount > oldAccounts.length) { -- throw new Error("Account out of sequence"); -- } -- const existingAccount = oldAccounts[accountCount]; -- assertIsStrictHexString(existingAccount); -- return existingAccount; -- } -- await keyring.addAccounts(1); -- const addedAccountAddress = (await __privateMethod(this, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn).call(this)).find( -- (selectedAddress) => !oldAccounts.includes(selectedAddress) -- ); -- assertIsStrictHexString(addedAccountAddress); -- return addedAccountAddress; -- }); -- } -- /** -- * Adds a new account to the default (first) HD seed phrase keyring without updating identities in preferences. -- * -- * @returns Promise resolving to the added account address. -- */ -- async addNewAccountWithoutUpdate() { -- return __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -- const primaryKeyring = this.getKeyringsByType("HD Key Tree")[0]; -- if (!primaryKeyring) { -- throw new Error("No HD keyring found"); -- } -- const [addedAccountAddress] = await primaryKeyring.addAccounts(1); -- await this.verifySeedPhrase(); -- return addedAccountAddress; -- }); -- } -- /** -- * Effectively the same as creating a new keychain then populating it -- * using the given seed phrase. -- * -- * @param password - Password to unlock keychain. -- * @param seed - A BIP39-compliant seed phrase as Uint8Array, -- * either as a string or an array of UTF-8 bytes that represent the string. -- * @returns Promise resolving when the operation ends successfully. -- */ -- async createNewVaultAndRestore(password, seed) { -- return __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -- assertIsValidPassword(password); -- await __privateMethod(this, _createNewVaultWithKeyring, createNewVaultWithKeyring_fn).call(this, password, { -- type: "HD Key Tree" /* hd */, -- opts: { -- mnemonic: seed, -- numberOfAccounts: 1 -- } -- }); -- }); -- } -- /** -- * Create a new primary keychain and wipe any previous keychains. -- * -- * @param password - Password to unlock the new vault. -- * @returns Promise resolving when the operation ends successfully. -- */ -- async createNewVaultAndKeychain(password) { -- return __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -- const accounts = await __privateMethod(this, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn).call(this); -- if (!accounts.length) { -- await __privateMethod(this, _createNewVaultWithKeyring, createNewVaultWithKeyring_fn).call(this, password, { -- type: "HD Key Tree" /* hd */ -- }); -- } -- }); -- } -- /** -- * Adds a new keyring of the given `type`. -- * -- * @param type - Keyring type name. -- * @param opts - Keyring options. -- * @throws If a builder for the given `type` does not exist. -- * @returns Promise resolving to the added keyring. -- */ -- async addNewKeyring(type, opts) { -- if (type === "QR Hardware Wallet Device" /* qr */) { -- return this.getOrAddQRKeyring(); -- } -- return __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => __privateMethod(this, _newKeyring, newKeyring_fn).call(this, type, opts)); -- } -- /** -- * Method to verify a given password validity. Throws an -- * error if the password is invalid. -- * -- * @param password - Password of the keyring. -- */ -- async verifyPassword(password) { -- if (!this.state.vault) { -- throw new Error("KeyringController - Cannot unlock without a previous vault." /* VaultError */); -- } -- await __privateGet(this, _encryptor).decrypt(password, this.state.vault); -- } -- /** -- * Returns the status of the vault. -- * -- * @returns Boolean returning true if the vault is unlocked. -- */ -- isUnlocked() { -- return this.state.isUnlocked; -- } -- /** -- * Gets the seed phrase of the HD keyring. -- * -- * @param password - Password of the keyring. -- * @returns Promise resolving to the seed phrase. -- */ -- async exportSeedPhrase(password) { -- await this.verifyPassword(password); -- assertHasUint8ArrayMnemonic(__privateGet(this, _keyrings)[0]); -- return __privateGet(this, _keyrings)[0].mnemonic; -- } -- /** -- * Gets the private key from the keyring controlling an address. -- * -- * @param password - Password of the keyring. -- * @param address - Address to export. -- * @returns Promise resolving to the private key for an address. -- */ -- async exportAccount(password, address) { -- await this.verifyPassword(password); -- const keyring = await this.getKeyringForAccount( -- address -- ); -- if (!keyring.exportAccount) { -- throw new Error("`KeyringController - The keyring for the current address does not support the method exportAccount" /* UnsupportedExportAccount */); -- } -- return await keyring.exportAccount(normalize(address)); -- } -- /** -- * Returns the public addresses of all accounts from every keyring. -- * -- * @returns A promise resolving to an array of addresses. -- */ -- async getAccounts() { -- return this.state.keyrings.reduce( -- (accounts, keyring) => accounts.concat(keyring.accounts), -- [] -- ); -- } -- /** -- * Get encryption public key. -- * -- * @param account - An account address. -- * @param opts - Additional encryption options. -- * @throws If the `account` does not exist or does not support the `getEncryptionPublicKey` method -- * @returns Promise resolving to encyption public key of the `account` if one exists. -- */ -- async getEncryptionPublicKey(account, opts) { -- const address = ethNormalize(account); -- const keyring = await this.getKeyringForAccount( -- account -- ); -- if (!keyring.getEncryptionPublicKey) { -- throw new Error("KeyringController - The keyring for the current address does not support the method getEncryptionPublicKey." /* UnsupportedGetEncryptionPublicKey */); -- } -- return await keyring.getEncryptionPublicKey(address, opts); -- } -- /** -- * Attempts to decrypt the provided message parameters. -- * -- * @param messageParams - The decryption message parameters. -- * @param messageParams.from - The address of the account you want to use to decrypt the message. -- * @param messageParams.data - The encrypted data that you want to decrypt. -- * @returns The raw decryption result. -- */ -- async decryptMessage(messageParams) { -- const address = ethNormalize(messageParams.from); -- const keyring = await this.getKeyringForAccount( -- address -- ); -- if (!keyring.decryptMessage) { -- throw new Error("KeyringController - The keyring for the current address does not support the method decryptMessage." /* UnsupportedDecryptMessage */); -- } -- return keyring.decryptMessage(address, messageParams.data); -- } -- /** -- * Returns the currently initialized keyring that manages -- * the specified `address` if one exists. -- * -- * @deprecated Use of this method is discouraged as actions executed directly on -- * keyrings are not being reflected in the KeyringController state and not -- * persisted in the vault. Use `withKeyring` instead. -- * @param account - An account address. -- * @returns Promise resolving to keyring of the `account` if one exists. -- */ -- async getKeyringForAccount(account) { -- const address = normalize(account); -- const candidates = await Promise.all( -- __privateGet(this, _keyrings).map(async (keyring) => { -- return Promise.all([keyring, keyring.getAccounts()]); -- }) -- ); -- const winners = candidates.filter((candidate) => { -- const accounts = candidate[1].map(normalize); -- return accounts.includes(address); -- }); -- if (winners.length && winners[0]?.length) { -- return winners[0][0]; -- } -- let errorInfo = ""; -- if (!candidates.length) { -- errorInfo = "There are no keyrings"; -- } else if (!winners.length) { -- errorInfo = "There are keyrings, but none match the address"; -- } -- throw new Error( -- `${"KeyringController - No keyring found" /* NoKeyring */}. Error info: ${errorInfo}` -- ); -- } -- /** -- * Returns all keyrings of the given type. -- * -- * @deprecated Use of this method is discouraged as actions executed directly on -- * keyrings are not being reflected in the KeyringController state and not -- * persisted in the vault. Use `withKeyring` instead. -- * @param type - Keyring type name. -- * @returns An array of keyrings of the given type. -- */ -- getKeyringsByType(type) { -- return __privateGet(this, _keyrings).filter((keyring) => keyring.type === type); -- } -- /** -- * Persist all serialized keyrings in the vault. -- * -- * @deprecated This method is being phased out in favor of `withKeyring`. -- * @returns Promise resolving with `true` value when the -- * operation completes. -- */ -- async persistAllKeyrings() { -- return __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => true); -- } -- /** -- * Imports an account with the specified import strategy. -- * -- * @param strategy - Import strategy name. -- * @param args - Array of arguments to pass to the underlying stategy. -- * @throws Will throw when passed an unrecognized strategy. -- * @returns Promise resolving to the imported account address. -- */ -- async importAccountWithStrategy(strategy, args) { -- return __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -- let privateKey; -- switch (strategy) { -- case "privateKey": -- const [importedKey] = args; -- if (!importedKey) { -- throw new Error("Cannot import an empty key."); -- } -- const prefixed = add0x(importedKey); -- let bufferedPrivateKey; -- try { -- bufferedPrivateKey = toBuffer(prefixed); -- } catch { -- throw new Error("Cannot import invalid private key."); -- } -- if (!isValidPrivate(bufferedPrivateKey) || // ensures that the key is 64 bytes long -- getBinarySize(prefixed) !== 64 + "0x".length) { -- throw new Error("Cannot import invalid private key."); -- } -- privateKey = remove0x(prefixed); -- break; -- case "json": -- let wallet; -- const [input, password] = args; -- try { -- wallet = importers.fromEtherWallet(input, password); -- } catch (e) { -- wallet = wallet || await Wallet.fromV3(input, password, true); -- } -- privateKey = bytesToHex(wallet.getPrivateKey()); -- break; -- default: -- throw new Error(`Unexpected import strategy: '${strategy}'`); -- } -- const newKeyring = await __privateMethod(this, _newKeyring, newKeyring_fn).call(this, "Simple Key Pair" /* simple */, [ -- privateKey -- ]); -- const accounts = await newKeyring.getAccounts(); -- return accounts[0]; -- }); -- } -- /** -- * Removes an account from keyring state. -- * -- * @param address - Address of the account to remove. -- * @fires KeyringController:accountRemoved -- * @returns Promise resolving when the account is removed. -- */ -- async removeAccount(address) { -- await __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -- const keyring = await this.getKeyringForAccount( -- address -- ); -- if (!keyring.removeAccount) { -- throw new Error("`KeyringController - The keyring for the current address does not support the method removeAccount" /* UnsupportedRemoveAccount */); -- } -- await keyring.removeAccount(address); -- const accounts = await keyring.getAccounts(); -- if (accounts.length === 0) { -- await __privateMethod(this, _removeEmptyKeyrings, removeEmptyKeyrings_fn).call(this); -- } -- }); -- this.messagingSystem.publish(`${name}:accountRemoved`, address); -- } -- /** -- * Deallocates all secrets and locks the wallet. -- * -- * @returns Promise resolving when the operation completes. -- */ -- async setLocked() { -- return __privateMethod(this, _withRollback, withRollback_fn).call(this, async () => { -- __privateMethod(this, _unsubscribeFromQRKeyringsEvents, unsubscribeFromQRKeyringsEvents_fn).call(this); -- __privateSet(this, _password, void 0); -- await __privateMethod(this, _clearKeyrings, clearKeyrings_fn).call(this); -- this.update((state) => { -- state.isUnlocked = false; -- state.keyrings = []; -- }); -- this.messagingSystem.publish(`${name}:lock`); -- }); -- } -- /** -- * Signs message by calling down into a specific keyring. -- * -- * @param messageParams - PersonalMessageParams object to sign. -- * @returns Promise resolving to a signed message string. -- */ -- async signMessage(messageParams) { -- if (!messageParams.data) { -- throw new Error("Can't sign an empty message"); -- } -- const address = ethNormalize(messageParams.from); -- const keyring = await this.getKeyringForAccount( -- address -- ); -- if (!keyring.signMessage) { -- throw new Error("KeyringController - The keyring for the current address does not support the method signMessage." /* UnsupportedSignMessage */); -- } -- return await keyring.signMessage(address, messageParams.data); -- } -- /** -- * Signs personal message by calling down into a specific keyring. -- * -- * @param messageParams - PersonalMessageParams object to sign. -- * @returns Promise resolving to a signed message string. -- */ -- async signPersonalMessage(messageParams) { -- const address = ethNormalize(messageParams.from); -- const keyring = await this.getKeyringForAccount( -- address -- ); -- if (!keyring.signPersonalMessage) { -- throw new Error("KeyringController - The keyring for the current address does not support the method signPersonalMessage." /* UnsupportedSignPersonalMessage */); -- } -- const normalizedData = normalize(messageParams.data); -- return await keyring.signPersonalMessage(address, normalizedData); -- } -- /** -- * Signs typed message by calling down into a specific keyring. -- * -- * @param messageParams - TypedMessageParams object to sign. -- * @param version - Compatibility version EIP712. -- * @throws Will throw when passed an unrecognized version. -- * @returns Promise resolving to a signed message string or an error if any. -- */ -- async signTypedMessage(messageParams, version) { -- try { -- if (![ -- "V1" /* V1 */, -- "V3" /* V3 */, -- "V4" /* V4 */ -- ].includes(version)) { -- throw new Error(`Unexpected signTypedMessage version: '${version}'`); -- } -- const address = ethNormalize(messageParams.from); -- const keyring = await this.getKeyringForAccount( -- address -- ); -- if (!keyring.signTypedData) { -- throw new Error("KeyringController - The keyring for the current address does not support the method signTypedMessage." /* UnsupportedSignTypedMessage */); -- } -- return await keyring.signTypedData( -- address, -- version !== "V1" /* V1 */ && typeof messageParams.data === "string" ? JSON.parse(messageParams.data) : messageParams.data, -- { version } -- ); -- } catch (error) { -- throw new Error(`Keyring Controller signTypedMessage: ${error}`); -- } -- } -- /** -- * Signs a transaction by calling down into a specific keyring. -- * -- * @param transaction - Transaction object to sign. Must be a `ethereumjs-tx` transaction instance. -- * @param from - Address to sign from, should be in keychain. -- * @param opts - An optional options object. -- * @returns Promise resolving to a signed transaction string. -- */ -- async signTransaction(transaction, from, opts) { -- const address = ethNormalize(from); -- const keyring = await this.getKeyringForAccount( -- address -- ); -- if (!keyring.signTransaction) { -- throw new Error("KeyringController - The keyring for the current address does not support the method signTransaction." /* UnsupportedSignTransaction */); -- } -- return await keyring.signTransaction(address, transaction, opts); -- } -- /** -- * Convert a base transaction to a base UserOperation. -- * -- * @param from - Address of the sender. -- * @param transactions - Base transactions to include in the UserOperation. -- * @param executionContext - The execution context to use for the UserOperation. -- * @returns A pseudo-UserOperation that can be used to construct a real. -- */ -- async prepareUserOperation(from, transactions, executionContext) { -- const address = ethNormalize(from); -- const keyring = await this.getKeyringForAccount( -- address -- ); -- if (!keyring.prepareUserOperation) { -- throw new Error("KeyringController - The keyring for the current address does not support the method prepareUserOperation." /* UnsupportedPrepareUserOperation */); -- } -- return await keyring.prepareUserOperation( -- address, -- transactions, -- executionContext -- ); -- } -- /** -- * Patches properties of a UserOperation. Currently, only the -- * `paymasterAndData` can be patched. -- * -- * @param from - Address of the sender. -- * @param userOp - UserOperation to patch. -- * @param executionContext - The execution context to use for the UserOperation. -- * @returns A patch to apply to the UserOperation. -- */ -- async patchUserOperation(from, userOp, executionContext) { -- const address = ethNormalize(from); -- const keyring = await this.getKeyringForAccount( -- address -- ); -- if (!keyring.patchUserOperation) { -- throw new Error("KeyringController - The keyring for the current address does not support the method patchUserOperation." /* UnsupportedPatchUserOperation */); -- } -- return await keyring.patchUserOperation(address, userOp, executionContext); -- } -- /** -- * Signs an UserOperation. -- * -- * @param from - Address of the sender. -- * @param userOp - UserOperation to sign. -- * @param executionContext - The execution context to use for the UserOperation. -- * @returns The signature of the UserOperation. -- */ -- async signUserOperation(from, userOp, executionContext) { -- const address = ethNormalize(from); -- const keyring = await this.getKeyringForAccount( -- address -- ); -- if (!keyring.signUserOperation) { -- throw new Error("KeyringController - The keyring for the current address does not support the method signUserOperation." /* UnsupportedSignUserOperation */); -- } -- return await keyring.signUserOperation(address, userOp, executionContext); -- } -- /** -- * Changes the password used to encrypt the vault. -- * -- * @param password - The new password. -- * @returns Promise resolving when the operation completes. -- */ -- changePassword(password) { -- return __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -- if (!this.state.isUnlocked) { -- throw new Error("KeyringController - Cannot persist vault without password and encryption key" /* MissingCredentials */); -- } -- assertIsValidPassword(password); -- __privateSet(this, _password, password); -- if (__privateGet(this, _cacheEncryptionKey)) { -- this.update((state) => { -- delete state.encryptionKey; -- delete state.encryptionSalt; -- }); -- } -- }); -- } -- /** -- * Attempts to decrypt the current vault and load its keyrings, -- * using the given encryption key and salt. -- * -- * @param encryptionKey - Key to unlock the keychain. -- * @param encryptionSalt - Salt to unlock the keychain. -- * @returns Promise resolving when the operation completes. -- */ -- async submitEncryptionKey(encryptionKey, encryptionSalt) { -- return __privateMethod(this, _withRollback, withRollback_fn).call(this, async () => { -- __privateSet(this, _keyrings, await __privateMethod(this, _unlockKeyrings, unlockKeyrings_fn).call(this, void 0, encryptionKey, encryptionSalt)); -- __privateMethod(this, _setUnlocked, setUnlocked_fn).call(this); -- }); -- } -- /** -- * Attempts to decrypt the current vault and load its keyrings, -- * using the given password. -- * -- * @param password - Password to unlock the keychain. -- * @returns Promise resolving when the operation completes. -- */ -- async submitPassword(password) { -- return __privateMethod(this, _withRollback, withRollback_fn).call(this, async () => { -- __privateSet(this, _keyrings, await __privateMethod(this, _unlockKeyrings, unlockKeyrings_fn).call(this, password)); -- __privateMethod(this, _setUnlocked, setUnlocked_fn).call(this); -- }); -- } -- /** -- * Verifies the that the seed phrase restores the current keychain's accounts. -- * -- * @returns Promise resolving to the seed phrase as Uint8Array. -- */ -- async verifySeedPhrase() { -- const primaryKeyring = this.getKeyringsByType("HD Key Tree" /* hd */)[0]; -- if (!primaryKeyring) { -- throw new Error("No HD keyring found."); -- } -- assertHasUint8ArrayMnemonic(primaryKeyring); -- const seedWords = primaryKeyring.mnemonic; -- const accounts = await primaryKeyring.getAccounts(); -- if (accounts.length === 0) { -- throw new Error("Cannot verify an empty keyring."); -- } -- const hdKeyringBuilder = __privateMethod(this, _getKeyringBuilderForType, getKeyringBuilderForType_fn).call(this, "HD Key Tree" /* hd */); -- const hdKeyring = hdKeyringBuilder(); -- await hdKeyring.deserialize({ -- mnemonic: seedWords, -- numberOfAccounts: accounts.length -- }); -- const testAccounts = await hdKeyring.getAccounts(); -- if (testAccounts.length !== accounts.length) { -- throw new Error("Seed phrase imported incorrect number of accounts."); -- } -- testAccounts.forEach((account, i) => { -- if (account.toLowerCase() !== accounts[i].toLowerCase()) { -- throw new Error("Seed phrase imported different accounts."); -- } -- }); -- return seedWords; -- } -- async withKeyring(selector, operation, options = { -- createIfMissing: false -- }) { -- return __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -- let keyring; -- if ("address" in selector) { -- keyring = await this.getKeyringForAccount(selector.address); -- } else { -- keyring = this.getKeyringsByType(selector.type)[selector.index || 0]; -- if (!keyring && options.createIfMissing) { -- keyring = await __privateMethod(this, _newKeyring, newKeyring_fn).call(this, selector.type, options.createWithData); -- } -- } -- if (!keyring) { -- throw new Error("KeyringController - Keyring not found." /* KeyringNotFound */); -- } -- const result = await operation(keyring); -- if (Object.is(result, keyring)) { -- throw new Error("KeyringController - Returning keyring instances is unsafe" /* UnsafeDirectKeyringAccess */); -- } -- return result; -- }); -- } -- // QR Hardware related methods -- /** -- * Get QR Hardware keyring. -- * -- * @returns The QR Keyring if defined, otherwise undefined -- */ -- getQRKeyring() { -- return this.getKeyringsByType("QR Hardware Wallet Device" /* qr */)[0]; -- } -- /** -- * Get QR hardware keyring. If it doesn't exist, add it. -- * -- * @returns The added keyring -- */ -- async getOrAddQRKeyring() { -- return this.getQRKeyring() || await __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => __privateMethod(this, _addQRKeyring, addQRKeyring_fn).call(this)); -- } -- // TODO: Replace `any` with type -- // eslint-disable-next-line @typescript-eslint/no-explicit-any -- async restoreQRKeyring(serialized) { -- return __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -- const keyring = this.getQRKeyring() || await __privateMethod(this, _addQRKeyring, addQRKeyring_fn).call(this); -- keyring.deserialize(serialized); -- }); -- } -- async resetQRKeyringState() { -- (await this.getOrAddQRKeyring()).resetStore(); -- } -- async getQRKeyringState() { -- return (await this.getOrAddQRKeyring()).getMemStore(); -- } -- async submitQRCryptoHDKey(cryptoHDKey) { -- (await this.getOrAddQRKeyring()).submitCryptoHDKey(cryptoHDKey); -- } -- async submitQRCryptoAccount(cryptoAccount) { -- (await this.getOrAddQRKeyring()).submitCryptoAccount(cryptoAccount); -- } -- async submitQRSignature(requestId, ethSignature) { -- (await this.getOrAddQRKeyring()).submitSignature(requestId, ethSignature); -- } -- async cancelQRSignRequest() { -- (await this.getOrAddQRKeyring()).cancelSignRequest(); -- } -- /** -- * Cancels qr keyring sync. -- */ -- async cancelQRSynchronization() { -- (await this.getOrAddQRKeyring()).cancelSync(); -- } -- async connectQRHardware(page) { -- return __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -- try { -- const keyring = this.getQRKeyring() || await __privateMethod(this, _addQRKeyring, addQRKeyring_fn).call(this); -- let accounts; -- switch (page) { -- case -1: -- accounts = await keyring.getPreviousPage(); -- break; -- case 1: -- accounts = await keyring.getNextPage(); -- break; -- default: -- accounts = await keyring.getFirstPage(); -- } -- return accounts.map((account) => { -- return { -- ...account, -- balance: "0x0" -- }; -- }); -- } catch (e) { -- throw new Error(`Unspecified error when connect QR Hardware, ${e}`); -- } -- }); -- } -- async unlockQRHardwareWalletAccount(index) { -- return __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -- const keyring = this.getQRKeyring() || await __privateMethod(this, _addQRKeyring, addQRKeyring_fn).call(this); -- keyring.setAccountToUnlock(index); -- await keyring.addAccounts(1); -- }); -- } -- async getAccountKeyringType(account) { -- const keyring = await this.getKeyringForAccount( -- account -- ); -- return keyring.type; -- } -- async forgetQRDevice() { -- return __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -- const keyring = this.getQRKeyring(); -- if (!keyring) { -- return { removedAccounts: [], remainingAccounts: [] }; -- } -- const allAccounts = await __privateMethod(this, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn).call(this); -- keyring.forgetDevice(); -- const remainingAccounts = await __privateMethod(this, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn).call(this); -- const removedAccounts = allAccounts.filter( -- (address) => !remainingAccounts.includes(address) -- ); -- return { removedAccounts, remainingAccounts }; -- }); -- } --}; --_controllerOperationMutex = new WeakMap(); --_vaultOperationMutex = new WeakMap(); --_keyringBuilders = new WeakMap(); --_keyrings = new WeakMap(); --_unsupportedKeyrings = new WeakMap(); --_password = new WeakMap(); --_encryptor = new WeakMap(); --_cacheEncryptionKey = new WeakMap(); --_qrKeyringStateListener = new WeakMap(); --_registerMessageHandlers = new WeakSet(); --registerMessageHandlers_fn = function() { -- this.messagingSystem.registerActionHandler( -- `${name}:signMessage`, -- this.signMessage.bind(this) -- ); -- this.messagingSystem.registerActionHandler( -- `${name}:signPersonalMessage`, -- this.signPersonalMessage.bind(this) -- ); -- this.messagingSystem.registerActionHandler( -- `${name}:signTypedMessage`, -- this.signTypedMessage.bind(this) -- ); -- this.messagingSystem.registerActionHandler( -- `${name}:decryptMessage`, -- this.decryptMessage.bind(this) -- ); -- this.messagingSystem.registerActionHandler( -- `${name}:getEncryptionPublicKey`, -- this.getEncryptionPublicKey.bind(this) -- ); -- this.messagingSystem.registerActionHandler( -- `${name}:getAccounts`, -- this.getAccounts.bind(this) -- ); -- this.messagingSystem.registerActionHandler( -- `${name}:getKeyringsByType`, -- this.getKeyringsByType.bind(this) -- ); -- this.messagingSystem.registerActionHandler( -- `${name}:getKeyringForAccount`, -- this.getKeyringForAccount.bind(this) -- ); -- this.messagingSystem.registerActionHandler( -- `${name}:persistAllKeyrings`, -- this.persistAllKeyrings.bind(this) -- ); -- this.messagingSystem.registerActionHandler( -- `${name}:prepareUserOperation`, -- this.prepareUserOperation.bind(this) -- ); -- this.messagingSystem.registerActionHandler( -- `${name}:patchUserOperation`, -- this.patchUserOperation.bind(this) -- ); -- this.messagingSystem.registerActionHandler( -- `${name}:signUserOperation`, -- this.signUserOperation.bind(this) -- ); --}; --_getKeyringBuilderForType = new WeakSet(); --getKeyringBuilderForType_fn = function(type) { -- return __privateGet(this, _keyringBuilders).find( -- (keyringBuilder) => keyringBuilder.type === type -- ); --}; --_addQRKeyring = new WeakSet(); --addQRKeyring_fn = async function() { -- __privateMethod(this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this); -- return await __privateMethod(this, _newKeyring, newKeyring_fn).call(this, "QR Hardware Wallet Device" /* qr */); --}; --_subscribeToQRKeyringEvents = new WeakSet(); --subscribeToQRKeyringEvents_fn = function(qrKeyring) { -- __privateSet(this, _qrKeyringStateListener, (state) => { -- this.messagingSystem.publish(`${name}:qrKeyringStateChange`, state); -- }); -- qrKeyring.getMemStore().subscribe(__privateGet(this, _qrKeyringStateListener)); --}; --_unsubscribeFromQRKeyringsEvents = new WeakSet(); --unsubscribeFromQRKeyringsEvents_fn = function() { -- const qrKeyrings = this.getKeyringsByType( -- "QR Hardware Wallet Device" /* qr */ -- ); -- qrKeyrings.forEach((qrKeyring) => { -- if (__privateGet(this, _qrKeyringStateListener)) { -- qrKeyring.getMemStore().unsubscribe(__privateGet(this, _qrKeyringStateListener)); -- } -- }); --}; --_createNewVaultWithKeyring = new WeakSet(); --createNewVaultWithKeyring_fn = async function(password, keyring) { -- __privateMethod(this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this); -- if (typeof password !== "string") { -- throw new TypeError("KeyringController - Password must be of type string." /* WrongPasswordType */); -- } -- __privateSet(this, _password, password); -- await __privateMethod(this, _clearKeyrings, clearKeyrings_fn).call(this); -- await __privateMethod(this, _createKeyringWithFirstAccount, createKeyringWithFirstAccount_fn).call(this, keyring.type, keyring.opts); -- __privateMethod(this, _setUnlocked, setUnlocked_fn).call(this); --}; --_getUpdatedKeyrings = new WeakSet(); --getUpdatedKeyrings_fn = async function() { -- return Promise.all(__privateGet(this, _keyrings).map(displayForKeyring)); --}; --_getSerializedKeyrings = new WeakSet(); --getSerializedKeyrings_fn = async function({ includeUnsupported } = { -- includeUnsupported: true --}) { -- const serializedKeyrings = await Promise.all( -- __privateGet(this, _keyrings).map(async (keyring) => { -- const [type, data] = await Promise.all([ -- keyring.type, -- keyring.serialize() -- ]); -- return { type, data }; -- }) -- ); -- if (includeUnsupported) { -- serializedKeyrings.push(...__privateGet(this, _unsupportedKeyrings)); -- } -- return serializedKeyrings; --}; --_restoreSerializedKeyrings = new WeakSet(); --restoreSerializedKeyrings_fn = async function(serializedKeyrings) { -- await __privateMethod(this, _clearKeyrings, clearKeyrings_fn).call(this); -- for (const serializedKeyring of serializedKeyrings) { -- await __privateMethod(this, _restoreKeyring, restoreKeyring_fn).call(this, serializedKeyring); -- } --}; --_unlockKeyrings = new WeakSet(); --unlockKeyrings_fn = async function(password, encryptionKey, encryptionSalt) { -- return __privateMethod(this, _withVaultLock, withVaultLock_fn).call(this, async ({ releaseLock }) => { -- const encryptedVault = this.state.vault; -- if (!encryptedVault) { -- throw new Error("KeyringController - Cannot unlock without a previous vault." /* VaultError */); -- } -- let vault; -- const updatedState = {}; -- if (__privateGet(this, _cacheEncryptionKey)) { -- assertIsExportableKeyEncryptor(__privateGet(this, _encryptor)); -- if (password) { -- const result = await __privateGet(this, _encryptor).decryptWithDetail( -- password, -- encryptedVault -- ); -- vault = result.vault; -- __privateSet(this, _password, password); -- updatedState.encryptionKey = result.exportedKeyString; -- updatedState.encryptionSalt = result.salt; -- } else { -- const parsedEncryptedVault = JSON.parse(encryptedVault); -- if (encryptionSalt !== parsedEncryptedVault.salt) { -- throw new Error("KeyringController - Encryption key and salt provided are expired" /* ExpiredCredentials */); -- } -- if (typeof encryptionKey !== "string") { -- throw new TypeError("KeyringController - Password must be of type string." /* WrongPasswordType */); -- } -- const key = await __privateGet(this, _encryptor).importKey(encryptionKey); -- vault = await __privateGet(this, _encryptor).decryptWithKey( -- key, -- parsedEncryptedVault -- ); -- updatedState.encryptionKey = encryptionKey; -- updatedState.encryptionSalt = encryptionSalt; -- } -- } else { -- if (typeof password !== "string") { -- throw new TypeError("KeyringController - Password must be of type string." /* WrongPasswordType */); -- } -- vault = await __privateGet(this, _encryptor).decrypt(password, encryptedVault); -- __privateSet(this, _password, password); -- } -- if (!isSerializedKeyringsArray(vault)) { -- throw new Error("KeyringController - The decrypted vault has an unexpected shape." /* VaultDataError */); -- } -- await __privateMethod(this, _restoreSerializedKeyrings, restoreSerializedKeyrings_fn).call(this, vault); -- const updatedKeyrings = await __privateMethod(this, _getUpdatedKeyrings, getUpdatedKeyrings_fn).call(this); -- this.update((state) => { -- state.keyrings = updatedKeyrings; -- if (updatedState.encryptionKey || updatedState.encryptionSalt) { -- state.encryptionKey = updatedState.encryptionKey; -- state.encryptionSalt = updatedState.encryptionSalt; -- } -- }); -- if (__privateGet(this, _password) && (!__privateGet(this, _cacheEncryptionKey) || !encryptionKey) && __privateGet(this, _encryptor).isVaultUpdated && !__privateGet(this, _encryptor).isVaultUpdated(encryptedVault)) { -- releaseLock(); -- await __privateMethod(this, _updateVault, updateVault_fn).call(this); -- } -- return __privateGet(this, _keyrings); -- }); --}; --_updateVault = new WeakSet(); --updateVault_fn = function() { -- return __privateMethod(this, _withVaultLock, withVaultLock_fn).call(this, async () => { -- const { encryptionKey, encryptionSalt } = this.state; -- if (!__privateGet(this, _password) && !encryptionKey) { -- throw new Error("KeyringController - Cannot persist vault without password and encryption key" /* MissingCredentials */); -- } -- const serializedKeyrings = await __privateMethod(this, _getSerializedKeyrings, getSerializedKeyrings_fn).call(this); -- if (!serializedKeyrings.some((keyring) => keyring.type === "HD Key Tree" /* hd */)) { -- throw new Error("KeyringController - No HD Keyring found" /* NoHdKeyring */); -- } -- const updatedState = {}; -- if (__privateGet(this, _cacheEncryptionKey)) { -- assertIsExportableKeyEncryptor(__privateGet(this, _encryptor)); -- if (encryptionKey) { -- const key = await __privateGet(this, _encryptor).importKey(encryptionKey); -- const vaultJSON = await __privateGet(this, _encryptor).encryptWithKey( -- key, -- serializedKeyrings -- ); -- vaultJSON.salt = encryptionSalt; -- updatedState.vault = JSON.stringify(vaultJSON); -- } else if (__privateGet(this, _password)) { -- const { vault: newVault, exportedKeyString } = await __privateGet(this, _encryptor).encryptWithDetail( -- __privateGet(this, _password), -- serializedKeyrings -- ); -- updatedState.vault = newVault; -- updatedState.encryptionKey = exportedKeyString; -- } -- } else { -- assertIsValidPassword(__privateGet(this, _password)); -- updatedState.vault = await __privateGet(this, _encryptor).encrypt( -- __privateGet(this, _password), -- serializedKeyrings -- ); -- } -- if (!updatedState.vault) { -- throw new Error("KeyringController - Cannot persist vault without vault information" /* MissingVaultData */); -- } -- const updatedKeyrings = await __privateMethod(this, _getUpdatedKeyrings, getUpdatedKeyrings_fn).call(this); -- this.update((state) => { -- state.vault = updatedState.vault; -- state.keyrings = updatedKeyrings; -- if (updatedState.encryptionKey) { -- state.encryptionKey = updatedState.encryptionKey; -- state.encryptionSalt = JSON.parse(updatedState.vault).salt; -- } -- }); -- return true; -- }); --}; --_getAccountsFromKeyrings = new WeakSet(); --getAccountsFromKeyrings_fn = async function() { -- const keyrings = __privateGet(this, _keyrings); -- const keyringArrays = await Promise.all( -- keyrings.map(async (keyring) => keyring.getAccounts()) -- ); -- const addresses = keyringArrays.reduce((res, arr) => { -- return res.concat(arr); -- }, []); -- return addresses.map(normalize); --}; --_createKeyringWithFirstAccount = new WeakSet(); --createKeyringWithFirstAccount_fn = async function(type, opts) { -- __privateMethod(this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this); -- const keyring = await __privateMethod(this, _newKeyring, newKeyring_fn).call(this, type, opts); -- const [firstAccount] = await keyring.getAccounts(); -- if (!firstAccount) { -- throw new Error("KeyringController - First Account not found." /* NoFirstAccount */); -- } --}; --_newKeyring = new WeakSet(); --newKeyring_fn = async function(type, data) { -- __privateMethod(this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this); -- const keyringBuilder = __privateMethod(this, _getKeyringBuilderForType, getKeyringBuilderForType_fn).call(this, type); -- if (!keyringBuilder) { -- throw new Error( -- `${"KeyringController - No keyringBuilder found for keyring" /* NoKeyringBuilder */}. Keyring type: ${type}` -- ); -- } -- const keyring = keyringBuilder(); -- await keyring.deserialize(data); -- if (keyring.init) { -- await keyring.init(); -- } -- if (type === "HD Key Tree" /* hd */ && (!isObject(data) || !data.mnemonic)) { -- if (!keyring.generateRandomMnemonic) { -- throw new Error( -- "KeyringController - The current keyring does not support the method generateRandomMnemonic." /* UnsupportedGenerateRandomMnemonic */ -- ); -- } -- keyring.generateRandomMnemonic(); -- await keyring.addAccounts(1); -- } -- await __privateMethod(this, _checkForDuplicate, checkForDuplicate_fn).call(this, type, await keyring.getAccounts()); -- if (type === "QR Hardware Wallet Device" /* qr */) { -- __privateMethod(this, _subscribeToQRKeyringEvents, subscribeToQRKeyringEvents_fn).call(this, keyring); -- } -- __privateGet(this, _keyrings).push(keyring); -- return keyring; --}; --_clearKeyrings = new WeakSet(); --clearKeyrings_fn = async function() { -- __privateMethod(this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this); -- for (const keyring of __privateGet(this, _keyrings)) { -- await __privateMethod(this, _destroyKeyring, destroyKeyring_fn).call(this, keyring); -- } -- __privateSet(this, _keyrings, []); --}; --_restoreKeyring = new WeakSet(); --restoreKeyring_fn = async function(serialized) { -- __privateMethod(this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this); -- try { -- const { type, data } = serialized; -- return await __privateMethod(this, _newKeyring, newKeyring_fn).call(this, type, data); -- } catch (_) { -- __privateGet(this, _unsupportedKeyrings).push(serialized); -- return void 0; -- } --}; --_destroyKeyring = new WeakSet(); --destroyKeyring_fn = async function(keyring) { -- await keyring.destroy?.(); --}; --_removeEmptyKeyrings = new WeakSet(); --removeEmptyKeyrings_fn = async function() { -- __privateMethod(this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this); -- const validKeyrings = []; -- await Promise.all( -- __privateGet(this, _keyrings).map(async (keyring) => { -- const accounts = await keyring.getAccounts(); -- if (accounts.length > 0) { -- validKeyrings.push(keyring); -- } else { -- await __privateMethod(this, _destroyKeyring, destroyKeyring_fn).call(this, keyring); -- } -- }) -- ); -- __privateSet(this, _keyrings, validKeyrings); --}; --_checkForDuplicate = new WeakSet(); --checkForDuplicate_fn = async function(type, newAccountArray) { -- const accounts = await __privateMethod(this, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn).call(this); -- switch (type) { -- case "Simple Key Pair" /* simple */: { -- const isIncluded = Boolean( -- accounts.find( -- (key) => newAccountArray[0] && (key === newAccountArray[0] || key === remove0x(newAccountArray[0])) -- ) -- ); -- if (isIncluded) { -- throw new Error("KeyringController - The account you are trying to import is a duplicate" /* DuplicatedAccount */); -- } -- return newAccountArray; -- } -- default: { -- return newAccountArray; -- } -- } --}; --_setUnlocked = new WeakSet(); --setUnlocked_fn = function() { -- __privateMethod(this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this); -- this.update((state) => { -- state.isUnlocked = true; -- }); -- this.messagingSystem.publish(`${name}:unlock`); --}; --_persistOrRollback = new WeakSet(); --persistOrRollback_fn = async function(fn) { -- return __privateMethod(this, _withRollback, withRollback_fn).call(this, async ({ releaseLock }) => { -- const callbackResult = await fn({ releaseLock }); -- await __privateMethod(this, _updateVault, updateVault_fn).call(this); -- return callbackResult; -- }); --}; --_withRollback = new WeakSet(); --withRollback_fn = async function(fn) { -- return __privateMethod(this, _withControllerLock, withControllerLock_fn).call(this, async ({ releaseLock }) => { -- const currentSerializedKeyrings = await __privateMethod(this, _getSerializedKeyrings, getSerializedKeyrings_fn).call(this); -- const currentPassword = __privateGet(this, _password); -- try { -- return await fn({ releaseLock }); -- } catch (e) { -- await __privateMethod(this, _restoreSerializedKeyrings, restoreSerializedKeyrings_fn).call(this, currentSerializedKeyrings); -- __privateSet(this, _password, currentPassword); -- throw e; -- } -- }); --}; --_assertControllerMutexIsLocked = new WeakSet(); --assertControllerMutexIsLocked_fn = function() { -- if (!__privateGet(this, _controllerOperationMutex).isLocked()) { -- throw new Error("KeyringController - attempt to update vault during a non mutually exclusive operation" /* ControllerLockRequired */); -- } --}; --_withControllerLock = new WeakSet(); --withControllerLock_fn = async function(fn) { -- return withLock(__privateGet(this, _controllerOperationMutex), fn); --}; --_withVaultLock = new WeakSet(); --withVaultLock_fn = async function(fn) { -- __privateMethod(this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this); -- return withLock(__privateGet(this, _vaultOperationMutex), fn); --}; --async function withLock(mutex, fn) { -- const releaseLock = await mutex.acquire(); -- try { -- return await fn({ releaseLock }); -- } finally { -- releaseLock(); -- } --} --var KeyringController_default = KeyringController; -- --export { -- KeyringTypes, -- isCustodyKeyring, -- AccountImportStrategy, -- SignTypedDataVersion, -- keyringBuilderFactory, -- getDefaultKeyringState, -- KeyringController, -- KeyringController_default --}; --//# sourceMappingURL=chunk-STFS4REY.mjs.map -\ No newline at end of file -diff --git a/dist/chunk-STFS4REY.mjs.map b/dist/chunk-STFS4REY.mjs.map -deleted file mode 100644 -index 3ed91da3b2a1e05d7fc7decac48e949923ff760c..0000000000000000000000000000000000000000 ---- a/dist/chunk-STFS4REY.mjs.map -+++ /dev/null -@@ -1 +0,0 @@ --{"version":3,"sources":["../src/KeyringController.ts"],"sourcesContent":["import type { TxData, TypedTransaction } from '@ethereumjs/tx';\nimport { isValidPrivate, toBuffer, getBinarySize } from '@ethereumjs/util';\nimport type {\n MetaMaskKeyring as QRKeyring,\n IKeyringState as IQRKeyringState,\n} from '@keystonehq/metamask-airgapped-keyring';\nimport type { RestrictedControllerMessenger } from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport * as encryptorUtils from '@metamask/browser-passworder';\nimport HDKeyring from '@metamask/eth-hd-keyring';\nimport { normalize as ethNormalize } from '@metamask/eth-sig-util';\nimport SimpleKeyring from '@metamask/eth-simple-keyring';\nimport type {\n EthBaseTransaction,\n EthBaseUserOperation,\n EthKeyring,\n EthUserOperation,\n EthUserOperationPatch,\n KeyringExecutionContext,\n} from '@metamask/keyring-api';\nimport type {\n PersonalMessageParams,\n TypedMessageParams,\n} from '@metamask/message-manager';\nimport type {\n Eip1024EncryptedData,\n Hex,\n Json,\n KeyringClass,\n} from '@metamask/utils';\nimport {\n add0x,\n assertIsStrictHexString,\n bytesToHex,\n hasProperty,\n isObject,\n isStrictHexString,\n isValidHexAddress,\n isValidJson,\n remove0x,\n} from '@metamask/utils';\nimport { Mutex } from 'async-mutex';\nimport type { MutexInterface } from 'async-mutex';\nimport Wallet, { thirdparty as importers } from 'ethereumjs-wallet';\nimport type { Patch } from 'immer';\n\nimport { KeyringControllerError } from './constants';\n\nconst name = 'KeyringController';\n\n/**\n * Available keyring types\n */\nexport enum KeyringTypes {\n simple = 'Simple Key Pair',\n hd = 'HD Key Tree',\n qr = 'QR Hardware Wallet Device',\n trezor = 'Trezor Hardware',\n ledger = 'Ledger Hardware',\n lattice = 'Lattice Hardware',\n snap = 'Snap Keyring',\n}\n\n/**\n * Custody keyring types are a special case, as they are not a single type\n * but they all start with the prefix \"Custody\".\n * @param keyringType - The type of the keyring.\n * @returns Whether the keyring type is a custody keyring.\n */\nexport const isCustodyKeyring = (keyringType: string): boolean => {\n return keyringType.startsWith('Custody');\n};\n\n/**\n * @type KeyringControllerState\n *\n * Keyring controller state\n * @property vault - Encrypted string representing keyring data\n * @property isUnlocked - Whether vault is unlocked\n * @property keyringTypes - Account types\n * @property keyrings - Group of accounts\n * @property encryptionKey - Keyring encryption key\n * @property encryptionSalt - Keyring encryption salt\n */\nexport type KeyringControllerState = {\n vault?: string;\n isUnlocked: boolean;\n keyrings: KeyringObject[];\n encryptionKey?: string;\n encryptionSalt?: string;\n};\n\nexport type KeyringControllerMemState = Omit<\n KeyringControllerState,\n 'vault' | 'encryptionKey' | 'encryptionSalt'\n>;\n\nexport type KeyringControllerGetStateAction = {\n type: `${typeof name}:getState`;\n handler: () => KeyringControllerState;\n};\n\nexport type KeyringControllerSignMessageAction = {\n type: `${typeof name}:signMessage`;\n handler: KeyringController['signMessage'];\n};\n\nexport type KeyringControllerSignPersonalMessageAction = {\n type: `${typeof name}:signPersonalMessage`;\n handler: KeyringController['signPersonalMessage'];\n};\n\nexport type KeyringControllerSignTypedMessageAction = {\n type: `${typeof name}:signTypedMessage`;\n handler: KeyringController['signTypedMessage'];\n};\n\nexport type KeyringControllerDecryptMessageAction = {\n type: `${typeof name}:decryptMessage`;\n handler: KeyringController['decryptMessage'];\n};\n\nexport type KeyringControllerGetEncryptionPublicKeyAction = {\n type: `${typeof name}:getEncryptionPublicKey`;\n handler: KeyringController['getEncryptionPublicKey'];\n};\n\nexport type KeyringControllerGetKeyringsByTypeAction = {\n type: `${typeof name}:getKeyringsByType`;\n handler: KeyringController['getKeyringsByType'];\n};\n\nexport type KeyringControllerGetKeyringForAccountAction = {\n type: `${typeof name}:getKeyringForAccount`;\n handler: KeyringController['getKeyringForAccount'];\n};\n\nexport type KeyringControllerGetAccountsAction = {\n type: `${typeof name}:getAccounts`;\n handler: KeyringController['getAccounts'];\n};\n\nexport type KeyringControllerPersistAllKeyringsAction = {\n type: `${typeof name}:persistAllKeyrings`;\n handler: KeyringController['persistAllKeyrings'];\n};\n\nexport type KeyringControllerPrepareUserOperationAction = {\n type: `${typeof name}:prepareUserOperation`;\n handler: KeyringController['prepareUserOperation'];\n};\n\nexport type KeyringControllerPatchUserOperationAction = {\n type: `${typeof name}:patchUserOperation`;\n handler: KeyringController['patchUserOperation'];\n};\n\nexport type KeyringControllerSignUserOperationAction = {\n type: `${typeof name}:signUserOperation`;\n handler: KeyringController['signUserOperation'];\n};\n\nexport type KeyringControllerStateChangeEvent = {\n type: `${typeof name}:stateChange`;\n payload: [KeyringControllerState, Patch[]];\n};\n\nexport type KeyringControllerAccountRemovedEvent = {\n type: `${typeof name}:accountRemoved`;\n payload: [string];\n};\n\nexport type KeyringControllerLockEvent = {\n type: `${typeof name}:lock`;\n payload: [];\n};\n\nexport type KeyringControllerUnlockEvent = {\n type: `${typeof name}:unlock`;\n payload: [];\n};\n\nexport type KeyringControllerQRKeyringStateChangeEvent = {\n type: `${typeof name}:qrKeyringStateChange`;\n payload: [ReturnType];\n};\n\nexport type KeyringControllerActions =\n | KeyringControllerGetStateAction\n | KeyringControllerSignMessageAction\n | KeyringControllerSignPersonalMessageAction\n | KeyringControllerSignTypedMessageAction\n | KeyringControllerDecryptMessageAction\n | KeyringControllerGetEncryptionPublicKeyAction\n | KeyringControllerGetAccountsAction\n | KeyringControllerGetKeyringsByTypeAction\n | KeyringControllerGetKeyringForAccountAction\n | KeyringControllerPersistAllKeyringsAction\n | KeyringControllerPrepareUserOperationAction\n | KeyringControllerPatchUserOperationAction\n | KeyringControllerSignUserOperationAction;\n\nexport type KeyringControllerEvents =\n | KeyringControllerStateChangeEvent\n | KeyringControllerLockEvent\n | KeyringControllerUnlockEvent\n | KeyringControllerAccountRemovedEvent\n | KeyringControllerQRKeyringStateChangeEvent;\n\nexport type KeyringControllerMessenger = RestrictedControllerMessenger<\n typeof name,\n KeyringControllerActions,\n KeyringControllerEvents,\n never,\n never\n>;\n\nexport type KeyringControllerOptions = {\n keyringBuilders?: { (): EthKeyring; type: string }[];\n messenger: KeyringControllerMessenger;\n state?: { vault?: string };\n} & (\n | {\n cacheEncryptionKey: true;\n encryptor?: ExportableKeyEncryptor;\n }\n | {\n cacheEncryptionKey?: false;\n encryptor?: GenericEncryptor | ExportableKeyEncryptor;\n }\n);\n\n/**\n * @type KeyringObject\n *\n * Keyring object to return in fullUpdate\n * @property type - Keyring type\n * @property accounts - Associated accounts\n */\nexport type KeyringObject = {\n accounts: string[];\n type: string;\n};\n\n/**\n * A strategy for importing an account\n */\nexport enum AccountImportStrategy {\n privateKey = 'privateKey',\n json = 'json',\n}\n\n/**\n * The `signTypedMessage` version\n *\n * @see https://docs.metamask.io/guide/signing-data.html\n */\nexport enum SignTypedDataVersion {\n V1 = 'V1',\n V3 = 'V3',\n V4 = 'V4',\n}\n\n/**\n * A serialized keyring object.\n */\nexport type SerializedKeyring = {\n type: string;\n data: Json;\n};\n\n/**\n * A generic encryptor interface that supports encrypting and decrypting\n * serializable data with a password.\n */\nexport type GenericEncryptor = {\n /**\n * Encrypts the given object with the given password.\n *\n * @param password - The password to encrypt with.\n * @param object - The object to encrypt.\n * @returns The encrypted string.\n */\n encrypt: (password: string, object: Json) => Promise;\n /**\n * Decrypts the given encrypted string with the given password.\n *\n * @param password - The password to decrypt with.\n * @param encryptedString - The encrypted string to decrypt.\n * @returns The decrypted object.\n */\n decrypt: (password: string, encryptedString: string) => Promise;\n /**\n * Optional vault migration helper. Checks if the provided vault is up to date\n * with the desired encryption algorithm.\n *\n * @param vault - The encrypted string to check.\n * @param targetDerivationParams - The desired target derivation params.\n * @returns The updated encrypted string.\n */\n isVaultUpdated?: (\n vault: string,\n targetDerivationParams?: encryptorUtils.KeyDerivationOptions,\n ) => boolean;\n};\n\n/**\n * An encryptor interface that supports encrypting and decrypting\n * serializable data with a password, and exporting and importing keys.\n */\nexport type ExportableKeyEncryptor = GenericEncryptor & {\n /**\n * Encrypts the given object with the given encryption key.\n *\n * @param key - The encryption key to encrypt with.\n * @param object - The object to encrypt.\n * @returns The encryption result.\n */\n encryptWithKey: (\n key: unknown,\n object: Json,\n ) => Promise;\n /**\n * Encrypts the given object with the given password, and returns the\n * encryption result and the exported key string.\n *\n * @param password - The password to encrypt with.\n * @param object - The object to encrypt.\n * @param salt - The optional salt to use for encryption.\n * @returns The encrypted string and the exported key string.\n */\n encryptWithDetail: (\n password: string,\n object: Json,\n salt?: string,\n ) => Promise;\n /**\n * Decrypts the given encrypted string with the given encryption key.\n *\n * @param key - The encryption key to decrypt with.\n * @param encryptedString - The encrypted string to decrypt.\n * @returns The decrypted object.\n */\n decryptWithKey: (key: unknown, encryptedString: string) => Promise;\n /**\n * Decrypts the given encrypted string with the given password, and returns\n * the decrypted object and the salt and exported key string used for\n * encryption.\n *\n * @param password - The password to decrypt with.\n * @param encryptedString - The encrypted string to decrypt.\n * @returns The decrypted object and the salt and exported key string used for\n * encryption.\n */\n decryptWithDetail: (\n password: string,\n encryptedString: string,\n ) => Promise;\n /**\n * Generates an encryption key from exported key string.\n *\n * @param key - The exported key string.\n * @returns The encryption key.\n */\n importKey: (key: string) => Promise;\n};\n\nexport type KeyringSelector =\n | {\n type: string;\n index?: number;\n }\n | {\n address: Hex;\n };\n\n/**\n * A function executed within a mutually exclusive lock, with\n * a mutex releaser in its option bag.\n *\n * @param releaseLock - A function to release the lock.\n */\ntype MutuallyExclusiveCallback = ({\n releaseLock,\n}: {\n releaseLock: MutexInterface.Releaser;\n}) => Promise;\n\n/**\n * Get builder function for `Keyring`\n *\n * Returns a builder function for `Keyring` with a `type` property.\n *\n * @param KeyringConstructor - The Keyring class for the builder.\n * @returns A builder function for the given Keyring.\n */\nexport function keyringBuilderFactory(KeyringConstructor: KeyringClass) {\n const builder = () => new KeyringConstructor();\n\n builder.type = KeyringConstructor.type;\n\n return builder;\n}\n\nconst defaultKeyringBuilders = [\n keyringBuilderFactory(SimpleKeyring),\n keyringBuilderFactory(HDKeyring),\n];\n\nexport const getDefaultKeyringState = (): KeyringControllerState => {\n return {\n isUnlocked: false,\n keyrings: [],\n };\n};\n\n/**\n * Assert that the given keyring has an exportable\n * mnemonic.\n *\n * @param keyring - The keyring to check\n * @throws When the keyring does not have a mnemonic\n */\nfunction assertHasUint8ArrayMnemonic(\n keyring: EthKeyring,\n): asserts keyring is EthKeyring & { mnemonic: Uint8Array } {\n if (\n !(\n hasProperty(keyring, 'mnemonic') && keyring.mnemonic instanceof Uint8Array\n )\n ) {\n throw new Error(\"Can't get mnemonic bytes from keyring\");\n }\n}\n\n/**\n * Assert that the provided encryptor supports\n * encryption and encryption key export.\n *\n * @param encryptor - The encryptor to check.\n * @throws If the encryptor does not support key encryption.\n */\nfunction assertIsExportableKeyEncryptor(\n encryptor: GenericEncryptor | ExportableKeyEncryptor,\n): asserts encryptor is ExportableKeyEncryptor {\n if (\n !(\n 'importKey' in encryptor &&\n typeof encryptor.importKey === 'function' &&\n 'decryptWithKey' in encryptor &&\n typeof encryptor.decryptWithKey === 'function' &&\n 'encryptWithKey' in encryptor &&\n typeof encryptor.encryptWithKey === 'function'\n )\n ) {\n throw new Error(KeyringControllerError.UnsupportedEncryptionKeyExport);\n }\n}\n\n/**\n * Assert that the provided password is a valid non-empty string.\n *\n * @param password - The password to check.\n * @throws If the password is not a valid string.\n */\nfunction assertIsValidPassword(password: unknown): asserts password is string {\n if (typeof password !== 'string') {\n throw new Error(KeyringControllerError.WrongPasswordType);\n }\n\n if (!password || !password.length) {\n throw new Error(KeyringControllerError.InvalidEmptyPassword);\n }\n}\n\n/**\n * Checks if the provided value is a serialized keyrings array.\n *\n * @param array - The value to check.\n * @returns True if the value is a serialized keyrings array.\n */\nfunction isSerializedKeyringsArray(\n array: unknown,\n): array is SerializedKeyring[] {\n return (\n typeof array === 'object' &&\n Array.isArray(array) &&\n array.every((value) => value.type && isValidJson(value.data))\n );\n}\n\n/**\n * Display For Keyring\n *\n * Is used for adding the current keyrings to the state object.\n *\n * @param keyring - The keyring to display.\n * @returns A keyring display object, with type and accounts properties.\n */\nasync function displayForKeyring(\n keyring: EthKeyring,\n): Promise<{ type: string; accounts: string[] }> {\n const accounts = await keyring.getAccounts();\n\n return {\n type: keyring.type,\n // Cast to `string[]` here is safe here because `accounts` has no nullish\n // values, and `normalize` returns `string` unless given a nullish value\n accounts: accounts.map(normalize) as string[],\n };\n}\n\n/**\n * Check if address is an ethereum address\n *\n * @param address - An address.\n * @returns Returns true if the address is an ethereum one, false otherwise.\n */\nfunction isEthAddress(address: string): boolean {\n // We first check if it's a matching `Hex` string, so that is narrows down\n // `address` as an `Hex` type, allowing us to use `isValidHexAddress`\n return (\n // NOTE: This function only checks for lowercased strings\n isStrictHexString(address.toLowerCase()) &&\n // This checks for lowercased addresses and checksum addresses too\n isValidHexAddress(address as Hex)\n );\n}\n\n/**\n * Normalize ethereum or non-EVM address.\n *\n * @param address - Ethereum or non-EVM address.\n * @returns The normalized address.\n */\nfunction normalize(address: string): string | undefined {\n // Since the `KeyringController` is only dealing with address, we have\n // no other way to get the associated account type with this address. So we\n // are down to check the actual address format for now\n // TODO: Find a better way to not have those runtime checks based on the\n // address value!\n return isEthAddress(address) ? ethNormalize(address) : address;\n}\n\n/**\n * Controller responsible for establishing and managing user identity.\n *\n * This class is a wrapper around the `eth-keyring-controller` package. The\n * `eth-keyring-controller` manages the \"vault\", which is an encrypted store of private keys, and\n * it manages the wallet \"lock\" state. This wrapper class has convenience methods for interacting\n * with the internal keyring controller and handling certain complex operations that involve the\n * keyrings.\n */\nexport class KeyringController extends BaseController<\n typeof name,\n KeyringControllerState,\n KeyringControllerMessenger\n> {\n readonly #controllerOperationMutex = new Mutex();\n\n readonly #vaultOperationMutex = new Mutex();\n\n #keyringBuilders: { (): EthKeyring; type: string }[];\n\n #keyrings: EthKeyring[];\n\n #unsupportedKeyrings: SerializedKeyring[];\n\n #password?: string;\n\n #encryptor: GenericEncryptor | ExportableKeyEncryptor;\n\n #cacheEncryptionKey: boolean;\n\n #qrKeyringStateListener?: (\n state: ReturnType,\n ) => void;\n\n /**\n * Creates a KeyringController instance.\n *\n * @param options - Initial options used to configure this controller\n * @param options.encryptor - An optional object for defining encryption schemes.\n * @param options.keyringBuilders - Set a new name for account.\n * @param options.cacheEncryptionKey - Whether to cache or not encryption key.\n * @param options.messenger - A restricted controller messenger.\n * @param options.state - Initial state to set on this controller.\n */\n constructor(options: KeyringControllerOptions) {\n const {\n encryptor = encryptorUtils,\n keyringBuilders,\n messenger,\n state,\n } = options;\n\n super({\n name,\n metadata: {\n vault: { persist: true, anonymous: false },\n isUnlocked: { persist: false, anonymous: true },\n keyrings: { persist: false, anonymous: false },\n encryptionKey: { persist: false, anonymous: false },\n encryptionSalt: { persist: false, anonymous: false },\n },\n messenger,\n state: {\n ...getDefaultKeyringState(),\n ...state,\n },\n });\n\n this.#keyringBuilders = keyringBuilders\n ? defaultKeyringBuilders.concat(keyringBuilders)\n : defaultKeyringBuilders;\n\n this.#encryptor = encryptor;\n this.#keyrings = [];\n this.#unsupportedKeyrings = [];\n\n // This option allows the controller to cache an exported key\n // for use in decrypting and encrypting data without password\n this.#cacheEncryptionKey = Boolean(options.cacheEncryptionKey);\n if (this.#cacheEncryptionKey) {\n assertIsExportableKeyEncryptor(encryptor);\n }\n\n this.#registerMessageHandlers();\n }\n\n /**\n * Adds a new account to the default (first) HD seed phrase keyring.\n *\n * @param accountCount - Number of accounts before adding a new one, used to\n * make the method idempotent.\n * @returns Promise resolving to the added account address.\n */\n async addNewAccount(accountCount?: number): Promise {\n return this.#persistOrRollback(async () => {\n const primaryKeyring = this.getKeyringsByType('HD Key Tree')[0] as\n | EthKeyring\n | undefined;\n if (!primaryKeyring) {\n throw new Error('No HD keyring found');\n }\n const oldAccounts = await primaryKeyring.getAccounts();\n\n if (accountCount && oldAccounts.length !== accountCount) {\n if (accountCount > oldAccounts.length) {\n throw new Error('Account out of sequence');\n }\n // we return the account already existing at index `accountCount`\n const existingAccount = oldAccounts[accountCount];\n\n if (!existingAccount) {\n throw new Error(`Can't find account at index ${accountCount}`);\n }\n\n return existingAccount;\n }\n\n const [addedAccountAddress] = await primaryKeyring.addAccounts(1);\n await this.verifySeedPhrase();\n\n return addedAccountAddress;\n });\n }\n\n /**\n * Adds a new account to the specified keyring.\n *\n * @param keyring - Keyring to add the account to.\n * @param accountCount - Number of accounts before adding a new one, used to make the method idempotent.\n * @returns Promise resolving to the added account address\n */\n async addNewAccountForKeyring(\n keyring: EthKeyring,\n accountCount?: number,\n ): Promise {\n // READ THIS CAREFULLY:\n // We still uses `Hex` here, since we are not using this method when creating\n // and account using a \"Snap Keyring\". This function assume the `keyring` is\n // ethereum compatible, but \"Snap Keyring\" might not be.\n return this.#persistOrRollback(async () => {\n const oldAccounts = await this.#getAccountsFromKeyrings();\n\n if (accountCount && oldAccounts.length !== accountCount) {\n if (accountCount > oldAccounts.length) {\n throw new Error('Account out of sequence');\n }\n\n const existingAccount = oldAccounts[accountCount];\n assertIsStrictHexString(existingAccount);\n\n return existingAccount;\n }\n\n await keyring.addAccounts(1);\n\n const addedAccountAddress = (await this.#getAccountsFromKeyrings()).find(\n (selectedAddress) => !oldAccounts.includes(selectedAddress),\n );\n assertIsStrictHexString(addedAccountAddress);\n\n return addedAccountAddress;\n });\n }\n\n /**\n * Adds a new account to the default (first) HD seed phrase keyring without updating identities in preferences.\n *\n * @returns Promise resolving to the added account address.\n */\n async addNewAccountWithoutUpdate(): Promise {\n return this.#persistOrRollback(async () => {\n const primaryKeyring = this.getKeyringsByType('HD Key Tree')[0] as\n | EthKeyring\n | undefined;\n if (!primaryKeyring) {\n throw new Error('No HD keyring found');\n }\n const [addedAccountAddress] = await primaryKeyring.addAccounts(1);\n await this.verifySeedPhrase();\n return addedAccountAddress;\n });\n }\n\n /**\n * Effectively the same as creating a new keychain then populating it\n * using the given seed phrase.\n *\n * @param password - Password to unlock keychain.\n * @param seed - A BIP39-compliant seed phrase as Uint8Array,\n * either as a string or an array of UTF-8 bytes that represent the string.\n * @returns Promise resolving when the operation ends successfully.\n */\n async createNewVaultAndRestore(\n password: string,\n seed: Uint8Array,\n ): Promise {\n return this.#persistOrRollback(async () => {\n assertIsValidPassword(password);\n\n await this.#createNewVaultWithKeyring(password, {\n type: KeyringTypes.hd,\n opts: {\n mnemonic: seed,\n numberOfAccounts: 1,\n },\n });\n });\n }\n\n /**\n * Create a new primary keychain and wipe any previous keychains.\n *\n * @param password - Password to unlock the new vault.\n * @returns Promise resolving when the operation ends successfully.\n */\n async createNewVaultAndKeychain(password: string) {\n return this.#persistOrRollback(async () => {\n const accounts = await this.#getAccountsFromKeyrings();\n if (!accounts.length) {\n await this.#createNewVaultWithKeyring(password, {\n type: KeyringTypes.hd,\n });\n }\n });\n }\n\n /**\n * Adds a new keyring of the given `type`.\n *\n * @param type - Keyring type name.\n * @param opts - Keyring options.\n * @throws If a builder for the given `type` does not exist.\n * @returns Promise resolving to the added keyring.\n */\n async addNewKeyring(\n type: KeyringTypes | string,\n opts?: unknown,\n ): Promise {\n if (type === KeyringTypes.qr) {\n return this.getOrAddQRKeyring();\n }\n\n return this.#persistOrRollback(async () => this.#newKeyring(type, opts));\n }\n\n /**\n * Method to verify a given password validity. Throws an\n * error if the password is invalid.\n *\n * @param password - Password of the keyring.\n */\n async verifyPassword(password: string) {\n if (!this.state.vault) {\n throw new Error(KeyringControllerError.VaultError);\n }\n await this.#encryptor.decrypt(password, this.state.vault);\n }\n\n /**\n * Returns the status of the vault.\n *\n * @returns Boolean returning true if the vault is unlocked.\n */\n isUnlocked(): boolean {\n return this.state.isUnlocked;\n }\n\n /**\n * Gets the seed phrase of the HD keyring.\n *\n * @param password - Password of the keyring.\n * @returns Promise resolving to the seed phrase.\n */\n async exportSeedPhrase(password: string): Promise {\n await this.verifyPassword(password);\n assertHasUint8ArrayMnemonic(this.#keyrings[0]);\n return this.#keyrings[0].mnemonic;\n }\n\n /**\n * Gets the private key from the keyring controlling an address.\n *\n * @param password - Password of the keyring.\n * @param address - Address to export.\n * @returns Promise resolving to the private key for an address.\n */\n async exportAccount(password: string, address: string): Promise {\n await this.verifyPassword(password);\n\n const keyring = (await this.getKeyringForAccount(\n address,\n )) as EthKeyring;\n if (!keyring.exportAccount) {\n throw new Error(KeyringControllerError.UnsupportedExportAccount);\n }\n\n return await keyring.exportAccount(normalize(address) as Hex);\n }\n\n /**\n * Returns the public addresses of all accounts from every keyring.\n *\n * @returns A promise resolving to an array of addresses.\n */\n async getAccounts(): Promise {\n return this.state.keyrings.reduce(\n (accounts, keyring) => accounts.concat(keyring.accounts),\n [],\n );\n }\n\n /**\n * Get encryption public key.\n *\n * @param account - An account address.\n * @param opts - Additional encryption options.\n * @throws If the `account` does not exist or does not support the `getEncryptionPublicKey` method\n * @returns Promise resolving to encyption public key of the `account` if one exists.\n */\n async getEncryptionPublicKey(\n account: string,\n opts?: Record,\n ): Promise {\n const address = ethNormalize(account) as Hex;\n const keyring = (await this.getKeyringForAccount(\n account,\n )) as EthKeyring;\n if (!keyring.getEncryptionPublicKey) {\n throw new Error(KeyringControllerError.UnsupportedGetEncryptionPublicKey);\n }\n\n return await keyring.getEncryptionPublicKey(address, opts);\n }\n\n /**\n * Attempts to decrypt the provided message parameters.\n *\n * @param messageParams - The decryption message parameters.\n * @param messageParams.from - The address of the account you want to use to decrypt the message.\n * @param messageParams.data - The encrypted data that you want to decrypt.\n * @returns The raw decryption result.\n */\n async decryptMessage(messageParams: {\n from: string;\n data: Eip1024EncryptedData;\n }): Promise {\n const address = ethNormalize(messageParams.from) as Hex;\n const keyring = (await this.getKeyringForAccount(\n address,\n )) as EthKeyring;\n if (!keyring.decryptMessage) {\n throw new Error(KeyringControllerError.UnsupportedDecryptMessage);\n }\n\n return keyring.decryptMessage(address, messageParams.data);\n }\n\n /**\n * Returns the currently initialized keyring that manages\n * the specified `address` if one exists.\n *\n * @deprecated Use of this method is discouraged as actions executed directly on\n * keyrings are not being reflected in the KeyringController state and not\n * persisted in the vault. Use `withKeyring` instead.\n * @param account - An account address.\n * @returns Promise resolving to keyring of the `account` if one exists.\n */\n async getKeyringForAccount(account: string): Promise {\n const address = normalize(account);\n\n const candidates = await Promise.all(\n this.#keyrings.map(async (keyring) => {\n return Promise.all([keyring, keyring.getAccounts()]);\n }),\n );\n\n const winners = candidates.filter((candidate) => {\n const accounts = candidate[1].map(normalize);\n return accounts.includes(address);\n });\n\n if (winners.length && winners[0]?.length) {\n return winners[0][0];\n }\n\n // Adding more info to the error\n let errorInfo = '';\n if (!candidates.length) {\n errorInfo = 'There are no keyrings';\n } else if (!winners.length) {\n errorInfo = 'There are keyrings, but none match the address';\n }\n throw new Error(\n `${KeyringControllerError.NoKeyring}. Error info: ${errorInfo}`,\n );\n }\n\n /**\n * Returns all keyrings of the given type.\n *\n * @deprecated Use of this method is discouraged as actions executed directly on\n * keyrings are not being reflected in the KeyringController state and not\n * persisted in the vault. Use `withKeyring` instead.\n * @param type - Keyring type name.\n * @returns An array of keyrings of the given type.\n */\n getKeyringsByType(type: KeyringTypes | string): unknown[] {\n return this.#keyrings.filter((keyring) => keyring.type === type);\n }\n\n /**\n * Persist all serialized keyrings in the vault.\n *\n * @deprecated This method is being phased out in favor of `withKeyring`.\n * @returns Promise resolving with `true` value when the\n * operation completes.\n */\n async persistAllKeyrings(): Promise {\n return this.#persistOrRollback(async () => true);\n }\n\n /**\n * Imports an account with the specified import strategy.\n *\n * @param strategy - Import strategy name.\n * @param args - Array of arguments to pass to the underlying stategy.\n * @throws Will throw when passed an unrecognized strategy.\n * @returns Promise resolving to the imported account address.\n */\n async importAccountWithStrategy(\n strategy: AccountImportStrategy,\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n args: any[],\n ): Promise {\n return this.#persistOrRollback(async () => {\n let privateKey;\n switch (strategy) {\n case 'privateKey':\n const [importedKey] = args;\n if (!importedKey) {\n throw new Error('Cannot import an empty key.');\n }\n const prefixed = add0x(importedKey);\n\n let bufferedPrivateKey;\n try {\n bufferedPrivateKey = toBuffer(prefixed);\n } catch {\n throw new Error('Cannot import invalid private key.');\n }\n\n if (\n !isValidPrivate(bufferedPrivateKey) ||\n // ensures that the key is 64 bytes long\n getBinarySize(prefixed) !== 64 + '0x'.length\n ) {\n throw new Error('Cannot import invalid private key.');\n }\n\n privateKey = remove0x(prefixed);\n break;\n case 'json':\n let wallet;\n const [input, password] = args;\n try {\n wallet = importers.fromEtherWallet(input, password);\n } catch (e) {\n wallet = wallet || (await Wallet.fromV3(input, password, true));\n }\n privateKey = bytesToHex(wallet.getPrivateKey());\n break;\n default:\n throw new Error(`Unexpected import strategy: '${strategy}'`);\n }\n const newKeyring = (await this.#newKeyring(KeyringTypes.simple, [\n privateKey,\n ])) as EthKeyring;\n const accounts = await newKeyring.getAccounts();\n return accounts[0];\n });\n }\n\n /**\n * Removes an account from keyring state.\n *\n * @param address - Address of the account to remove.\n * @fires KeyringController:accountRemoved\n * @returns Promise resolving when the account is removed.\n */\n async removeAccount(address: string): Promise {\n await this.#persistOrRollback(async () => {\n const keyring = (await this.getKeyringForAccount(\n address,\n )) as EthKeyring;\n\n // Not all the keyrings support this, so we have to check\n if (!keyring.removeAccount) {\n throw new Error(KeyringControllerError.UnsupportedRemoveAccount);\n }\n\n // The `removeAccount` method of snaps keyring is async. We have to update\n // the interface of the other keyrings to be async as well.\n // eslint-disable-next-line @typescript-eslint/await-thenable\n // FIXME: We do cast to `Hex` to makes the type checker happy here, and\n // because `Keyring.removeAccount` requires address to be `Hex`. Those\n // type would need to be updated for a full non-EVM support.\n await keyring.removeAccount(address as Hex);\n\n const accounts = await keyring.getAccounts();\n // Check if this was the last/only account\n if (accounts.length === 0) {\n await this.#removeEmptyKeyrings();\n }\n });\n\n this.messagingSystem.publish(`${name}:accountRemoved`, address);\n }\n\n /**\n * Deallocates all secrets and locks the wallet.\n *\n * @returns Promise resolving when the operation completes.\n */\n async setLocked(): Promise {\n return this.#withRollback(async () => {\n this.#unsubscribeFromQRKeyringsEvents();\n\n this.#password = undefined;\n await this.#clearKeyrings();\n\n this.update((state) => {\n state.isUnlocked = false;\n state.keyrings = [];\n });\n\n this.messagingSystem.publish(`${name}:lock`);\n });\n }\n\n /**\n * Signs message by calling down into a specific keyring.\n *\n * @param messageParams - PersonalMessageParams object to sign.\n * @returns Promise resolving to a signed message string.\n */\n async signMessage(messageParams: PersonalMessageParams): Promise {\n if (!messageParams.data) {\n throw new Error(\"Can't sign an empty message\");\n }\n\n const address = ethNormalize(messageParams.from) as Hex;\n const keyring = (await this.getKeyringForAccount(\n address,\n )) as EthKeyring;\n if (!keyring.signMessage) {\n throw new Error(KeyringControllerError.UnsupportedSignMessage);\n }\n\n return await keyring.signMessage(address, messageParams.data);\n }\n\n /**\n * Signs personal message by calling down into a specific keyring.\n *\n * @param messageParams - PersonalMessageParams object to sign.\n * @returns Promise resolving to a signed message string.\n */\n async signPersonalMessage(messageParams: PersonalMessageParams) {\n const address = ethNormalize(messageParams.from) as Hex;\n const keyring = (await this.getKeyringForAccount(\n address,\n )) as EthKeyring;\n if (!keyring.signPersonalMessage) {\n throw new Error(KeyringControllerError.UnsupportedSignPersonalMessage);\n }\n\n const normalizedData = normalize(messageParams.data) as Hex;\n\n return await keyring.signPersonalMessage(address, normalizedData);\n }\n\n /**\n * Signs typed message by calling down into a specific keyring.\n *\n * @param messageParams - TypedMessageParams object to sign.\n * @param version - Compatibility version EIP712.\n * @throws Will throw when passed an unrecognized version.\n * @returns Promise resolving to a signed message string or an error if any.\n */\n async signTypedMessage(\n messageParams: TypedMessageParams,\n version: SignTypedDataVersion,\n ): Promise {\n try {\n if (\n ![\n SignTypedDataVersion.V1,\n SignTypedDataVersion.V3,\n SignTypedDataVersion.V4,\n ].includes(version)\n ) {\n throw new Error(`Unexpected signTypedMessage version: '${version}'`);\n }\n\n // Cast to `Hex` here is safe here because `messageParams.from` is not nullish.\n // `normalize` returns `Hex` unless given a nullish value.\n const address = ethNormalize(messageParams.from) as Hex;\n const keyring = (await this.getKeyringForAccount(\n address,\n )) as EthKeyring;\n if (!keyring.signTypedData) {\n throw new Error(KeyringControllerError.UnsupportedSignTypedMessage);\n }\n\n return await keyring.signTypedData(\n address,\n version !== SignTypedDataVersion.V1 &&\n typeof messageParams.data === 'string'\n ? JSON.parse(messageParams.data)\n : messageParams.data,\n { version },\n );\n } catch (error) {\n throw new Error(`Keyring Controller signTypedMessage: ${error}`);\n }\n }\n\n /**\n * Signs a transaction by calling down into a specific keyring.\n *\n * @param transaction - Transaction object to sign. Must be a `ethereumjs-tx` transaction instance.\n * @param from - Address to sign from, should be in keychain.\n * @param opts - An optional options object.\n * @returns Promise resolving to a signed transaction string.\n */\n async signTransaction(\n transaction: TypedTransaction,\n from: string,\n opts?: Record,\n ): Promise {\n const address = ethNormalize(from) as Hex;\n const keyring = (await this.getKeyringForAccount(\n address,\n )) as EthKeyring;\n if (!keyring.signTransaction) {\n throw new Error(KeyringControllerError.UnsupportedSignTransaction);\n }\n\n return await keyring.signTransaction(address, transaction, opts);\n }\n\n /**\n * Convert a base transaction to a base UserOperation.\n *\n * @param from - Address of the sender.\n * @param transactions - Base transactions to include in the UserOperation.\n * @param executionContext - The execution context to use for the UserOperation.\n * @returns A pseudo-UserOperation that can be used to construct a real.\n */\n async prepareUserOperation(\n from: string,\n transactions: EthBaseTransaction[],\n executionContext: KeyringExecutionContext,\n ): Promise {\n const address = ethNormalize(from) as Hex;\n const keyring = (await this.getKeyringForAccount(\n address,\n )) as EthKeyring;\n\n if (!keyring.prepareUserOperation) {\n throw new Error(KeyringControllerError.UnsupportedPrepareUserOperation);\n }\n\n return await keyring.prepareUserOperation(\n address,\n transactions,\n executionContext,\n );\n }\n\n /**\n * Patches properties of a UserOperation. Currently, only the\n * `paymasterAndData` can be patched.\n *\n * @param from - Address of the sender.\n * @param userOp - UserOperation to patch.\n * @param executionContext - The execution context to use for the UserOperation.\n * @returns A patch to apply to the UserOperation.\n */\n async patchUserOperation(\n from: string,\n userOp: EthUserOperation,\n executionContext: KeyringExecutionContext,\n ): Promise {\n const address = ethNormalize(from) as Hex;\n const keyring = (await this.getKeyringForAccount(\n address,\n )) as EthKeyring;\n\n if (!keyring.patchUserOperation) {\n throw new Error(KeyringControllerError.UnsupportedPatchUserOperation);\n }\n\n return await keyring.patchUserOperation(address, userOp, executionContext);\n }\n\n /**\n * Signs an UserOperation.\n *\n * @param from - Address of the sender.\n * @param userOp - UserOperation to sign.\n * @param executionContext - The execution context to use for the UserOperation.\n * @returns The signature of the UserOperation.\n */\n async signUserOperation(\n from: string,\n userOp: EthUserOperation,\n executionContext: KeyringExecutionContext,\n ): Promise {\n const address = ethNormalize(from) as Hex;\n const keyring = (await this.getKeyringForAccount(\n address,\n )) as EthKeyring;\n\n if (!keyring.signUserOperation) {\n throw new Error(KeyringControllerError.UnsupportedSignUserOperation);\n }\n\n return await keyring.signUserOperation(address, userOp, executionContext);\n }\n\n /**\n * Changes the password used to encrypt the vault.\n *\n * @param password - The new password.\n * @returns Promise resolving when the operation completes.\n */\n changePassword(password: string): Promise {\n return this.#persistOrRollback(async () => {\n if (!this.state.isUnlocked) {\n throw new Error(KeyringControllerError.MissingCredentials);\n }\n\n assertIsValidPassword(password);\n\n this.#password = password;\n // We need to clear encryption key and salt from state\n // to force the controller to re-encrypt the vault using\n // the new password.\n if (this.#cacheEncryptionKey) {\n this.update((state) => {\n delete state.encryptionKey;\n delete state.encryptionSalt;\n });\n }\n });\n }\n\n /**\n * Attempts to decrypt the current vault and load its keyrings,\n * using the given encryption key and salt.\n *\n * @param encryptionKey - Key to unlock the keychain.\n * @param encryptionSalt - Salt to unlock the keychain.\n * @returns Promise resolving when the operation completes.\n */\n async submitEncryptionKey(\n encryptionKey: string,\n encryptionSalt: string,\n ): Promise {\n return this.#withRollback(async () => {\n this.#keyrings = await this.#unlockKeyrings(\n undefined,\n encryptionKey,\n encryptionSalt,\n );\n this.#setUnlocked();\n });\n }\n\n /**\n * Attempts to decrypt the current vault and load its keyrings,\n * using the given password.\n *\n * @param password - Password to unlock the keychain.\n * @returns Promise resolving when the operation completes.\n */\n async submitPassword(password: string): Promise {\n return this.#withRollback(async () => {\n this.#keyrings = await this.#unlockKeyrings(password);\n this.#setUnlocked();\n });\n }\n\n /**\n * Verifies the that the seed phrase restores the current keychain's accounts.\n *\n * @returns Promise resolving to the seed phrase as Uint8Array.\n */\n async verifySeedPhrase(): Promise {\n const primaryKeyring = this.getKeyringsByType(KeyringTypes.hd)[0] as\n | EthKeyring\n | undefined;\n if (!primaryKeyring) {\n throw new Error('No HD keyring found.');\n }\n\n assertHasUint8ArrayMnemonic(primaryKeyring);\n\n const seedWords = primaryKeyring.mnemonic;\n const accounts = await primaryKeyring.getAccounts();\n /* istanbul ignore if */\n if (accounts.length === 0) {\n throw new Error('Cannot verify an empty keyring.');\n }\n\n // The HD Keyring Builder is a default keyring builder\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const hdKeyringBuilder = this.#getKeyringBuilderForType(KeyringTypes.hd)!;\n\n const hdKeyring = hdKeyringBuilder();\n // @ts-expect-error @metamask/eth-hd-keyring correctly handles\n // Uint8Array seed phrases in the `deserialize` method.\n await hdKeyring.deserialize({\n mnemonic: seedWords,\n numberOfAccounts: accounts.length,\n });\n const testAccounts = await hdKeyring.getAccounts();\n /* istanbul ignore if */\n if (testAccounts.length !== accounts.length) {\n throw new Error('Seed phrase imported incorrect number of accounts.');\n }\n\n testAccounts.forEach((account: string, i: number) => {\n /* istanbul ignore if */\n if (account.toLowerCase() !== accounts[i].toLowerCase()) {\n throw new Error('Seed phrase imported different accounts.');\n }\n });\n\n return seedWords;\n }\n\n /**\n * Select a keyring and execute the given operation with\n * the selected keyring, as a mutually exclusive atomic\n * operation.\n *\n * The method automatically persists changes at the end of the\n * function execution, or rolls back the changes if an error\n * is thrown.\n *\n * @param selector - Keyring selector object.\n * @param operation - Function to execute with the selected keyring.\n * @param options - Additional options.\n * @param options.createIfMissing - Whether to create a new keyring if the selected one is missing.\n * @param options.createWithData - Optional data to use when creating a new keyring.\n * @returns Promise resolving to the result of the function execution.\n * @template SelectedKeyring - The type of the selected keyring.\n * @template CallbackResult - The type of the value resolved by the callback function.\n * @deprecated This method overload is deprecated. Use `withKeyring` without options instead.\n */\n async withKeyring<\n SelectedKeyring extends EthKeyring = EthKeyring,\n CallbackResult = void,\n >(\n selector: KeyringSelector,\n operation: (keyring: SelectedKeyring) => Promise,\n // eslint-disable-next-line @typescript-eslint/unified-signatures\n options:\n | { createIfMissing?: false }\n | { createIfMissing: true; createWithData?: unknown },\n ): Promise;\n\n /**\n * Select a keyring and execute the given operation with\n * the selected keyring, as a mutually exclusive atomic\n * operation.\n *\n * The method automatically persists changes at the end of the\n * function execution, or rolls back the changes if an error\n * is thrown.\n *\n * @param selector - Keyring selector object.\n * @param operation - Function to execute with the selected keyring.\n * @returns Promise resolving to the result of the function execution.\n * @template SelectedKeyring - The type of the selected keyring.\n * @template CallbackResult - The type of the value resolved by the callback function.\n */\n async withKeyring<\n SelectedKeyring extends EthKeyring = EthKeyring,\n CallbackResult = void,\n >(\n selector: KeyringSelector,\n operation: (keyring: SelectedKeyring) => Promise,\n ): Promise;\n\n async withKeyring<\n SelectedKeyring extends EthKeyring = EthKeyring,\n CallbackResult = void,\n >(\n selector: KeyringSelector,\n operation: (keyring: SelectedKeyring) => Promise,\n options:\n | { createIfMissing?: false }\n | { createIfMissing: true; createWithData?: unknown } = {\n createIfMissing: false,\n },\n ): Promise {\n return this.#persistOrRollback(async () => {\n let keyring: SelectedKeyring | undefined;\n\n if ('address' in selector) {\n keyring = (await this.getKeyringForAccount(selector.address)) as\n | SelectedKeyring\n | undefined;\n } else {\n keyring = this.getKeyringsByType(selector.type)[selector.index || 0] as\n | SelectedKeyring\n | undefined;\n\n if (!keyring && options.createIfMissing) {\n keyring = (await this.#newKeyring(\n selector.type,\n options.createWithData,\n )) as SelectedKeyring;\n }\n }\n\n if (!keyring) {\n throw new Error(KeyringControllerError.KeyringNotFound);\n }\n\n const result = await operation(keyring);\n\n if (Object.is(result, keyring)) {\n // Access to a keyring instance outside of controller safeguards\n // should be discouraged, as it can lead to unexpected behavior.\n // This error is thrown to prevent consumers using `withKeyring`\n // as a way to get a reference to a keyring instance.\n throw new Error(KeyringControllerError.UnsafeDirectKeyringAccess);\n }\n\n return result;\n });\n }\n\n // QR Hardware related methods\n\n /**\n * Get QR Hardware keyring.\n *\n * @returns The QR Keyring if defined, otherwise undefined\n */\n getQRKeyring(): QRKeyring | undefined {\n // QRKeyring is not yet compatible with Keyring type from @metamask/utils\n return this.getKeyringsByType(KeyringTypes.qr)[0] as unknown as QRKeyring;\n }\n\n /**\n * Get QR hardware keyring. If it doesn't exist, add it.\n *\n * @returns The added keyring\n */\n async getOrAddQRKeyring(): Promise {\n return (\n this.getQRKeyring() ||\n (await this.#persistOrRollback(async () => this.#addQRKeyring()))\n );\n }\n\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n async restoreQRKeyring(serialized: any): Promise {\n return this.#persistOrRollback(async () => {\n const keyring = this.getQRKeyring() || (await this.#addQRKeyring());\n keyring.deserialize(serialized);\n });\n }\n\n async resetQRKeyringState(): Promise {\n (await this.getOrAddQRKeyring()).resetStore();\n }\n\n async getQRKeyringState(): Promise {\n return (await this.getOrAddQRKeyring()).getMemStore();\n }\n\n async submitQRCryptoHDKey(cryptoHDKey: string): Promise {\n (await this.getOrAddQRKeyring()).submitCryptoHDKey(cryptoHDKey);\n }\n\n async submitQRCryptoAccount(cryptoAccount: string): Promise {\n (await this.getOrAddQRKeyring()).submitCryptoAccount(cryptoAccount);\n }\n\n async submitQRSignature(\n requestId: string,\n ethSignature: string,\n ): Promise {\n (await this.getOrAddQRKeyring()).submitSignature(requestId, ethSignature);\n }\n\n async cancelQRSignRequest(): Promise {\n (await this.getOrAddQRKeyring()).cancelSignRequest();\n }\n\n /**\n * Cancels qr keyring sync.\n */\n async cancelQRSynchronization(): Promise {\n // eslint-disable-next-line n/no-sync\n (await this.getOrAddQRKeyring()).cancelSync();\n }\n\n async connectQRHardware(\n page: number,\n ): Promise<{ balance: string; address: string; index: number }[]> {\n return this.#persistOrRollback(async () => {\n try {\n const keyring = this.getQRKeyring() || (await this.#addQRKeyring());\n let accounts;\n switch (page) {\n case -1:\n accounts = await keyring.getPreviousPage();\n break;\n case 1:\n accounts = await keyring.getNextPage();\n break;\n default:\n accounts = await keyring.getFirstPage();\n }\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return accounts.map((account: any) => {\n return {\n ...account,\n balance: '0x0',\n };\n });\n } catch (e) {\n // TODO: Add test case for when keyring throws\n /* istanbul ignore next */\n throw new Error(`Unspecified error when connect QR Hardware, ${e}`);\n }\n });\n }\n\n async unlockQRHardwareWalletAccount(index: number): Promise {\n return this.#persistOrRollback(async () => {\n const keyring = this.getQRKeyring() || (await this.#addQRKeyring());\n\n keyring.setAccountToUnlock(index);\n await keyring.addAccounts(1);\n });\n }\n\n async getAccountKeyringType(account: string): Promise {\n const keyring = (await this.getKeyringForAccount(\n account,\n )) as EthKeyring;\n return keyring.type;\n }\n\n async forgetQRDevice(): Promise<{\n removedAccounts: string[];\n remainingAccounts: string[];\n }> {\n return this.#persistOrRollback(async () => {\n const keyring = this.getQRKeyring();\n\n if (!keyring) {\n return { removedAccounts: [], remainingAccounts: [] };\n }\n\n const allAccounts = (await this.#getAccountsFromKeyrings()) as string[];\n keyring.forgetDevice();\n const remainingAccounts =\n (await this.#getAccountsFromKeyrings()) as string[];\n const removedAccounts = allAccounts.filter(\n (address: string) => !remainingAccounts.includes(address),\n );\n return { removedAccounts, remainingAccounts };\n });\n }\n\n /**\n * Constructor helper for registering this controller's messaging system\n * actions.\n */\n #registerMessageHandlers() {\n this.messagingSystem.registerActionHandler(\n `${name}:signMessage`,\n this.signMessage.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:signPersonalMessage`,\n this.signPersonalMessage.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:signTypedMessage`,\n this.signTypedMessage.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:decryptMessage`,\n this.decryptMessage.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:getEncryptionPublicKey`,\n this.getEncryptionPublicKey.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:getAccounts`,\n this.getAccounts.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:getKeyringsByType`,\n this.getKeyringsByType.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:getKeyringForAccount`,\n this.getKeyringForAccount.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:persistAllKeyrings`,\n this.persistAllKeyrings.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:prepareUserOperation`,\n this.prepareUserOperation.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:patchUserOperation`,\n this.patchUserOperation.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${name}:signUserOperation`,\n this.signUserOperation.bind(this),\n );\n }\n\n /**\n * Get the keyring builder for the given `type`.\n *\n * @param type - The type of keyring to get the builder for.\n * @returns The keyring builder, or undefined if none exists.\n */\n #getKeyringBuilderForType(\n type: string,\n ): { (): EthKeyring; type: string } | undefined {\n return this.#keyringBuilders.find(\n (keyringBuilder) => keyringBuilder.type === type,\n );\n }\n\n /**\n * Add qr hardware keyring.\n *\n * @returns The added keyring\n * @throws If a QRKeyring builder is not provided\n * when initializing the controller\n */\n async #addQRKeyring(): Promise {\n this.#assertControllerMutexIsLocked();\n\n // QRKeyring is not yet compatible with Keyring type from @metamask/utils\n return (await this.#newKeyring(KeyringTypes.qr)) as unknown as QRKeyring;\n }\n\n /**\n * Subscribe to a QRKeyring state change events and\n * forward them through the messaging system.\n *\n * @param qrKeyring - The QRKeyring instance to subscribe to\n */\n #subscribeToQRKeyringEvents(qrKeyring: QRKeyring) {\n this.#qrKeyringStateListener = (state) => {\n this.messagingSystem.publish(`${name}:qrKeyringStateChange`, state);\n };\n\n qrKeyring.getMemStore().subscribe(this.#qrKeyringStateListener);\n }\n\n #unsubscribeFromQRKeyringsEvents() {\n const qrKeyrings = this.getKeyringsByType(\n KeyringTypes.qr,\n ) as unknown as QRKeyring[];\n\n qrKeyrings.forEach((qrKeyring) => {\n if (this.#qrKeyringStateListener) {\n qrKeyring.getMemStore().unsubscribe(this.#qrKeyringStateListener);\n }\n });\n }\n\n /**\n * Create new vault with an initial keyring\n *\n * Destroys any old encrypted storage,\n * creates a new encrypted store with the given password,\n * creates a new wallet with 1 account.\n *\n * @fires KeyringController:unlock\n * @param password - The password to encrypt the vault with.\n * @param keyring - A object containing the params to instantiate a new keyring.\n * @param keyring.type - The keyring type.\n * @param keyring.opts - Optional parameters required to instantiate the keyring.\n * @returns A promise that resolves to the state.\n */\n async #createNewVaultWithKeyring(\n password: string,\n keyring: {\n type: string;\n opts?: unknown;\n },\n ): Promise {\n this.#assertControllerMutexIsLocked();\n\n if (typeof password !== 'string') {\n throw new TypeError(KeyringControllerError.WrongPasswordType);\n }\n this.#password = password;\n\n await this.#clearKeyrings();\n await this.#createKeyringWithFirstAccount(keyring.type, keyring.opts);\n this.#setUnlocked();\n }\n\n /**\n * Get the updated array of each keyring's type and\n * accounts list.\n *\n * @returns A promise resolving to the updated keyrings array.\n */\n async #getUpdatedKeyrings(): Promise {\n return Promise.all(this.#keyrings.map(displayForKeyring));\n }\n\n /**\n * Serialize the current array of keyring instances,\n * including unsupported keyrings by default.\n *\n * @param options - Method options.\n * @param options.includeUnsupported - Whether to include unsupported keyrings.\n * @returns The serialized keyrings.\n */\n async #getSerializedKeyrings(\n { includeUnsupported }: { includeUnsupported: boolean } = {\n includeUnsupported: true,\n },\n ): Promise {\n const serializedKeyrings = await Promise.all(\n this.#keyrings.map(async (keyring) => {\n const [type, data] = await Promise.all([\n keyring.type,\n keyring.serialize(),\n ]);\n return { type, data };\n }),\n );\n\n if (includeUnsupported) {\n serializedKeyrings.push(...this.#unsupportedKeyrings);\n }\n\n return serializedKeyrings;\n }\n\n /**\n * Restore a serialized keyrings array.\n *\n * @param serializedKeyrings - The serialized keyrings array.\n */\n async #restoreSerializedKeyrings(\n serializedKeyrings: SerializedKeyring[],\n ): Promise {\n await this.#clearKeyrings();\n\n for (const serializedKeyring of serializedKeyrings) {\n await this.#restoreKeyring(serializedKeyring);\n }\n }\n\n /**\n * Unlock Keyrings, decrypting the vault and deserializing all\n * keyrings contained in it, using a password or an encryption key with salt.\n *\n * @param password - The keyring controller password.\n * @param encryptionKey - An exported key string to unlock keyrings with.\n * @param encryptionSalt - The salt used to encrypt the vault.\n * @returns A promise resolving to the deserialized keyrings array.\n */\n async #unlockKeyrings(\n password: string | undefined,\n encryptionKey?: string,\n encryptionSalt?: string,\n ): Promise[]> {\n return this.#withVaultLock(async ({ releaseLock }) => {\n const encryptedVault = this.state.vault;\n if (!encryptedVault) {\n throw new Error(KeyringControllerError.VaultError);\n }\n\n let vault;\n const updatedState: Partial = {};\n\n if (this.#cacheEncryptionKey) {\n assertIsExportableKeyEncryptor(this.#encryptor);\n\n if (password) {\n const result = await this.#encryptor.decryptWithDetail(\n password,\n encryptedVault,\n );\n vault = result.vault;\n this.#password = password;\n\n updatedState.encryptionKey = result.exportedKeyString;\n updatedState.encryptionSalt = result.salt;\n } else {\n const parsedEncryptedVault = JSON.parse(encryptedVault);\n\n if (encryptionSalt !== parsedEncryptedVault.salt) {\n throw new Error(KeyringControllerError.ExpiredCredentials);\n }\n\n if (typeof encryptionKey !== 'string') {\n throw new TypeError(KeyringControllerError.WrongPasswordType);\n }\n\n const key = await this.#encryptor.importKey(encryptionKey);\n vault = await this.#encryptor.decryptWithKey(\n key,\n parsedEncryptedVault,\n );\n\n // This call is required on the first call because encryptionKey\n // is not yet inside the memStore\n updatedState.encryptionKey = encryptionKey;\n // we can safely assume that encryptionSalt is defined here\n // because we compare it with the salt from the vault\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n updatedState.encryptionSalt = encryptionSalt!;\n }\n } else {\n if (typeof password !== 'string') {\n throw new TypeError(KeyringControllerError.WrongPasswordType);\n }\n\n vault = await this.#encryptor.decrypt(password, encryptedVault);\n this.#password = password;\n }\n\n if (!isSerializedKeyringsArray(vault)) {\n throw new Error(KeyringControllerError.VaultDataError);\n }\n\n await this.#restoreSerializedKeyrings(vault);\n const updatedKeyrings = await this.#getUpdatedKeyrings();\n\n this.update((state) => {\n state.keyrings = updatedKeyrings;\n if (updatedState.encryptionKey || updatedState.encryptionSalt) {\n state.encryptionKey = updatedState.encryptionKey;\n state.encryptionSalt = updatedState.encryptionSalt;\n }\n });\n\n if (\n this.#password &&\n (!this.#cacheEncryptionKey || !encryptionKey) &&\n this.#encryptor.isVaultUpdated &&\n !this.#encryptor.isVaultUpdated(encryptedVault)\n ) {\n // The lock needs to be released before persisting the keyrings\n // to avoid deadlock\n releaseLock();\n // Re-encrypt the vault with safer method if one is available\n await this.#updateVault();\n }\n\n return this.#keyrings;\n });\n }\n\n /**\n * Update the vault with the current keyrings.\n *\n * @returns A promise resolving to `true` if the operation is successful.\n */\n #updateVault(): Promise {\n return this.#withVaultLock(async () => {\n const { encryptionKey, encryptionSalt } = this.state;\n\n if (!this.#password && !encryptionKey) {\n throw new Error(KeyringControllerError.MissingCredentials);\n }\n\n const serializedKeyrings = await this.#getSerializedKeyrings();\n\n if (\n !serializedKeyrings.some((keyring) => keyring.type === KeyringTypes.hd)\n ) {\n throw new Error(KeyringControllerError.NoHdKeyring);\n }\n\n const updatedState: Partial = {};\n\n if (this.#cacheEncryptionKey) {\n assertIsExportableKeyEncryptor(this.#encryptor);\n\n if (encryptionKey) {\n const key = await this.#encryptor.importKey(encryptionKey);\n const vaultJSON = await this.#encryptor.encryptWithKey(\n key,\n serializedKeyrings,\n );\n vaultJSON.salt = encryptionSalt;\n updatedState.vault = JSON.stringify(vaultJSON);\n } else if (this.#password) {\n const { vault: newVault, exportedKeyString } =\n await this.#encryptor.encryptWithDetail(\n this.#password,\n serializedKeyrings,\n );\n\n updatedState.vault = newVault;\n updatedState.encryptionKey = exportedKeyString;\n }\n } else {\n assertIsValidPassword(this.#password);\n updatedState.vault = await this.#encryptor.encrypt(\n this.#password,\n serializedKeyrings,\n );\n }\n\n if (!updatedState.vault) {\n throw new Error(KeyringControllerError.MissingVaultData);\n }\n\n const updatedKeyrings = await this.#getUpdatedKeyrings();\n this.update((state) => {\n state.vault = updatedState.vault;\n state.keyrings = updatedKeyrings;\n if (updatedState.encryptionKey) {\n state.encryptionKey = updatedState.encryptionKey;\n state.encryptionSalt = JSON.parse(updatedState.vault as string).salt;\n }\n });\n\n return true;\n });\n }\n\n /**\n * Retrieves all the accounts from keyrings instances\n * that are currently in memory.\n *\n * @returns A promise resolving to an array of accounts.\n */\n async #getAccountsFromKeyrings(): Promise {\n const keyrings = this.#keyrings;\n\n const keyringArrays = await Promise.all(\n keyrings.map(async (keyring) => keyring.getAccounts()),\n );\n const addresses = keyringArrays.reduce((res, arr) => {\n return res.concat(arr);\n }, []);\n\n // Cast to `string[]` here is safe here because `addresses` has no nullish\n // values, and `normalize` returns `string` unless given a nullish value\n return addresses.map(normalize) as string[];\n }\n\n /**\n * Create a new keyring, ensuring that the first account is\n * also created.\n *\n * @param type - Keyring type to instantiate.\n * @param opts - Optional parameters required to instantiate the keyring.\n * @returns A promise that resolves if the operation is successful.\n */\n async #createKeyringWithFirstAccount(type: string, opts?: unknown) {\n this.#assertControllerMutexIsLocked();\n\n const keyring = (await this.#newKeyring(type, opts)) as EthKeyring;\n\n const [firstAccount] = await keyring.getAccounts();\n if (!firstAccount) {\n throw new Error(KeyringControllerError.NoFirstAccount);\n }\n }\n\n /**\n * Instantiate, initialize and return a new keyring of the given `type`,\n * using the given `opts`. The keyring is built using the keyring builder\n * registered for the given `type`.\n *\n *\n * @param type - The type of keyring to add.\n * @param data - The data to restore a previously serialized keyring.\n * @returns The new keyring.\n * @throws If the keyring includes duplicated accounts.\n */\n async #newKeyring(type: string, data?: unknown): Promise> {\n this.#assertControllerMutexIsLocked();\n\n const keyringBuilder = this.#getKeyringBuilderForType(type);\n\n if (!keyringBuilder) {\n throw new Error(\n `${KeyringControllerError.NoKeyringBuilder}. Keyring type: ${type}`,\n );\n }\n\n const keyring = keyringBuilder();\n\n // @ts-expect-error Enforce data type after updating clients\n await keyring.deserialize(data);\n\n if (keyring.init) {\n await keyring.init();\n }\n\n if (type === KeyringTypes.hd && (!isObject(data) || !data.mnemonic)) {\n if (!keyring.generateRandomMnemonic) {\n throw new Error(\n KeyringControllerError.UnsupportedGenerateRandomMnemonic,\n );\n }\n\n keyring.generateRandomMnemonic();\n await keyring.addAccounts(1);\n }\n\n await this.#checkForDuplicate(type, await keyring.getAccounts());\n\n if (type === KeyringTypes.qr) {\n // In case of a QR keyring type, we need to subscribe\n // to its events after creating it\n this.#subscribeToQRKeyringEvents(keyring as unknown as QRKeyring);\n }\n\n this.#keyrings.push(keyring);\n\n return keyring;\n }\n\n /**\n * Remove all managed keyrings, destroying all their\n * instances in memory.\n */\n async #clearKeyrings() {\n this.#assertControllerMutexIsLocked();\n for (const keyring of this.#keyrings) {\n await this.#destroyKeyring(keyring);\n }\n this.#keyrings = [];\n }\n\n /**\n * Restore a Keyring from a provided serialized payload.\n * On success, returns the resulting keyring instance.\n *\n * @param serialized - The serialized keyring.\n * @returns The deserialized keyring or undefined if the keyring type is unsupported.\n */\n async #restoreKeyring(\n serialized: SerializedKeyring,\n ): Promise | undefined> {\n this.#assertControllerMutexIsLocked();\n\n try {\n const { type, data } = serialized;\n return await this.#newKeyring(type, data);\n } catch (_) {\n this.#unsupportedKeyrings.push(serialized);\n return undefined;\n }\n }\n\n /**\n * Destroy Keyring\n *\n * Some keyrings support a method called `destroy`, that destroys the\n * keyring along with removing all its event listeners and, in some cases,\n * clears the keyring bridge iframe from the DOM.\n *\n * @param keyring - The keyring to destroy.\n */\n async #destroyKeyring(keyring: EthKeyring) {\n await keyring.destroy?.();\n }\n\n /**\n * Remove empty keyrings.\n *\n * Loops through the keyrings and removes the ones with empty accounts\n * (usually after removing the last / only account) from a keyring.\n */\n async #removeEmptyKeyrings(): Promise {\n this.#assertControllerMutexIsLocked();\n const validKeyrings: EthKeyring[] = [];\n\n // Since getAccounts returns a Promise\n // We need to wait to hear back form each keyring\n // in order to decide which ones are now valid (accounts.length > 0)\n\n await Promise.all(\n this.#keyrings.map(async (keyring: EthKeyring) => {\n const accounts = await keyring.getAccounts();\n if (accounts.length > 0) {\n validKeyrings.push(keyring);\n } else {\n await this.#destroyKeyring(keyring);\n }\n }),\n );\n this.#keyrings = validKeyrings;\n }\n\n /**\n * Checks for duplicate keypairs, using the the first account in the given\n * array. Rejects if a duplicate is found.\n *\n * Only supports 'Simple Key Pair'.\n *\n * @param type - The key pair type to check for.\n * @param newAccountArray - Array of new accounts.\n * @returns The account, if no duplicate is found.\n */\n async #checkForDuplicate(\n type: string,\n newAccountArray: string[],\n ): Promise {\n const accounts = await this.#getAccountsFromKeyrings();\n\n switch (type) {\n case KeyringTypes.simple: {\n const isIncluded = Boolean(\n accounts.find(\n (key) =>\n newAccountArray[0] &&\n (key === newAccountArray[0] ||\n key === remove0x(newAccountArray[0])),\n ),\n );\n\n if (isIncluded) {\n throw new Error(KeyringControllerError.DuplicatedAccount);\n }\n return newAccountArray;\n }\n\n default: {\n return newAccountArray;\n }\n }\n }\n\n /**\n * Set the `isUnlocked` to true and notify listeners\n * through the messenger.\n *\n * @fires KeyringController:unlock\n */\n #setUnlocked(): void {\n this.#assertControllerMutexIsLocked();\n\n this.update((state) => {\n state.isUnlocked = true;\n });\n this.messagingSystem.publish(`${name}:unlock`);\n }\n\n /**\n * Execute the given function after acquiring the controller lock\n * and save the keyrings to state after it, or rollback to their\n * previous state in case of error.\n *\n * @param fn - The function to execute.\n * @returns The result of the function.\n */\n async #persistOrRollback(fn: MutuallyExclusiveCallback): Promise {\n return this.#withRollback(async ({ releaseLock }) => {\n const callbackResult = await fn({ releaseLock });\n // State is committed only if the operation is successful\n await this.#updateVault();\n\n return callbackResult;\n });\n }\n\n /**\n * Execute the given function after acquiring the controller lock\n * and rollback keyrings and password states in case of error.\n *\n * @param fn - The function to execute atomically.\n * @returns The result of the function.\n */\n async #withRollback(fn: MutuallyExclusiveCallback): Promise {\n return this.#withControllerLock(async ({ releaseLock }) => {\n const currentSerializedKeyrings = await this.#getSerializedKeyrings();\n const currentPassword = this.#password;\n\n try {\n return await fn({ releaseLock });\n } catch (e) {\n // Keyrings and password are restored to their previous state\n await this.#restoreSerializedKeyrings(currentSerializedKeyrings);\n this.#password = currentPassword;\n\n throw e;\n }\n });\n }\n\n /**\n * Assert that the controller mutex is locked.\n *\n * @throws If the controller mutex is not locked.\n */\n #assertControllerMutexIsLocked() {\n if (!this.#controllerOperationMutex.isLocked()) {\n throw new Error(KeyringControllerError.ControllerLockRequired);\n }\n }\n\n /**\n * Lock the controller mutex before executing the given function,\n * and release it after the function is resolved or after an\n * error is thrown.\n *\n * This wrapper ensures that each mutable operation that interacts with the\n * controller and that changes its state is executed in a mutually exclusive way,\n * preventing unsafe concurrent access that could lead to unpredictable behavior.\n *\n * @param fn - The function to execute while the controller mutex is locked.\n * @returns The result of the function.\n */\n async #withControllerLock(fn: MutuallyExclusiveCallback): Promise {\n return withLock(this.#controllerOperationMutex, fn);\n }\n\n /**\n * Lock the vault mutex before executing the given function,\n * and release it after the function is resolved or after an\n * error is thrown.\n *\n * This ensures that each operation that interacts with the vault\n * is executed in a mutually exclusive way.\n *\n * @param fn - The function to execute while the vault mutex is locked.\n * @returns The result of the function.\n */\n async #withVaultLock(fn: MutuallyExclusiveCallback): Promise {\n this.#assertControllerMutexIsLocked();\n\n return withLock(this.#vaultOperationMutex, fn);\n }\n}\n\n/**\n * Lock the given mutex before executing the given function,\n * and release it after the function is resolved or after an\n * error is thrown.\n *\n * @param mutex - The mutex to lock.\n * @param fn - The function to execute while the mutex is locked.\n * @returns The result of the function.\n */\nasync function withLock(\n mutex: Mutex,\n fn: MutuallyExclusiveCallback,\n): Promise {\n const releaseLock = await mutex.acquire();\n\n try {\n return await fn({ releaseLock });\n } finally {\n releaseLock();\n }\n}\n\nexport default KeyringController;\n"],"mappings":"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","names":["KeyringTypes","AccountImportStrategy","SignTypedDataVersion"]} -\ No newline at end of file -diff --git a/dist/index.js b/dist/index.js -index bf8713e2da2130714d42b1fa102fca536a58bd13..ee3560b42837d47440a3957c6403bb98e9e6330c 100644 ---- a/dist/index.js -+++ b/dist/index.js -@@ -6,7 +6,7 @@ - - - --var _chunkBRS27QHFjs = require('./chunk-BRS27QHF.js'); -+var _chunkL4UUWIZAjs = require('./chunk-L4UUWIZA.js'); - require('./chunk-NOCGQCUM.js'); - - -@@ -16,5 +16,5 @@ require('./chunk-NOCGQCUM.js'); - - - --exports.AccountImportStrategy = _chunkBRS27QHFjs.AccountImportStrategy; exports.KeyringController = _chunkBRS27QHFjs.KeyringController; exports.KeyringTypes = _chunkBRS27QHFjs.KeyringTypes; exports.SignTypedDataVersion = _chunkBRS27QHFjs.SignTypedDataVersion; exports.getDefaultKeyringState = _chunkBRS27QHFjs.getDefaultKeyringState; exports.isCustodyKeyring = _chunkBRS27QHFjs.isCustodyKeyring; exports.keyringBuilderFactory = _chunkBRS27QHFjs.keyringBuilderFactory; -+exports.AccountImportStrategy = _chunkL4UUWIZAjs.AccountImportStrategy; exports.KeyringController = _chunkL4UUWIZAjs.KeyringController; exports.KeyringTypes = _chunkL4UUWIZAjs.KeyringTypes; exports.SignTypedDataVersion = _chunkL4UUWIZAjs.SignTypedDataVersion; exports.getDefaultKeyringState = _chunkL4UUWIZAjs.getDefaultKeyringState; exports.isCustodyKeyring = _chunkL4UUWIZAjs.isCustodyKeyring; exports.keyringBuilderFactory = _chunkL4UUWIZAjs.keyringBuilderFactory; - //# sourceMappingURL=index.js.map -\ No newline at end of file -diff --git a/dist/index.mjs b/dist/index.mjs -index 8bbc57c7e56445cfea5b1ef6134d7b53ab941c9c..3b1a052f121db8ee7f10650f81332a2bbc0ae83d 100644 ---- a/dist/index.mjs -+++ b/dist/index.mjs -@@ -6,7 +6,7 @@ import { - getDefaultKeyringState, - isCustodyKeyring, - keyringBuilderFactory --} from "./chunk-STFS4REY.mjs"; -+} from "./chunk-7A7D7THR.mjs"; - import "./chunk-F64I344Z.mjs"; - export { - AccountImportStrategy, -diff --git a/dist/tsconfig.build.tsbuildinfo b/dist/tsconfig.build.tsbuildinfo -index 6cdcb0f629fb0ec66d4d98132e42d9d2dda0fbb2..9642fe3738f9b52ac9acc1a22f8dd81190372ddb 100644 ---- a/dist/tsconfig.build.tsbuildinfo -+++ b/dist/tsconfig.build.tsbuildinfo -@@ -1 +1 @@ --{"program":{"fileNames":["../../../node_modules/typescript/lib/lib.es5.d.ts","../../../node_modules/typescript/lib/lib.es2015.d.ts","../../../node_modules/typescript/lib/lib.es2016.d.ts","../../../node_modules/typescript/lib/lib.es2017.d.ts","../../../node_modules/typescript/lib/lib.es2018.d.ts","../../../node_modules/typescript/lib/lib.es2019.d.ts","../../../node_modules/typescript/lib/lib.es2020.d.ts","../../../node_modules/typescript/lib/lib.dom.d.ts","../../../node_modules/typescript/lib/lib.es2015.core.d.ts","../../../node_modules/typescript/lib/lib.es2015.collection.d.ts","../../../node_modules/typescript/lib/lib.es2015.generator.d.ts","../../../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../../node_modules/typescript/lib/lib.es2015.promise.d.ts","../../../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../../node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../../node_modules/typescript/lib/lib.es2017.object.d.ts","../../../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../../node_modules/typescript/lib/lib.es2017.string.d.ts","../../../node_modules/typescript/lib/lib.es2017.intl.d.ts","../../../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../../node_modules/typescript/lib/lib.es2018.intl.d.ts","../../../node_modules/typescript/lib/lib.es2018.promise.d.ts","../../../node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../../node_modules/typescript/lib/lib.es2019.array.d.ts","../../../node_modules/typescript/lib/lib.es2019.object.d.ts","../../../node_modules/typescript/lib/lib.es2019.string.d.ts","../../../node_modules/typescript/lib/lib.es2019.symbol.d.ts","../../../node_modules/typescript/lib/lib.es2019.intl.d.ts","../../../node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../../node_modules/typescript/lib/lib.es2020.date.d.ts","../../../node_modules/typescript/lib/lib.es2020.promise.d.ts","../../../node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../../../node_modules/typescript/lib/lib.es2020.string.d.ts","../../../node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../../../node_modules/typescript/lib/lib.es2020.intl.d.ts","../../../node_modules/typescript/lib/lib.es2020.number.d.ts","../../../node_modules/typescript/lib/lib.esnext.intl.d.ts","../../../types/eth-ens-namehash.d.ts","../../../types/ethereum-ens-network-map.d.ts","../../../types/global.d.ts","../../../types/single-call-balance-checker-abi.d.ts","../../../types/@metamask/contract-metadata.d.ts","../../../types/@metamask/eth-hd-keyring.d.ts","../../../types/@metamask/eth-simple-keyring.d.ts","../../../types/@metamask/ethjs-provider-http.d.ts","../../../types/@metamask/ethjs-unit.d.ts","../../../types/@metamask/metamask-eth-abis.d.ts","../../../types/eth-json-rpc-infura/src/createProvider.d.ts","../../../types/eth-phishing-detect/src/config.json.d.ts","../../../types/eth-phishing-detect/src/detector.d.ts","../../../node_modules/@types/node/assert.d.ts","../../../node_modules/@types/node/assert/strict.d.ts","../../../node_modules/@types/node/globals.d.ts","../../../node_modules/@types/node/async_hooks.d.ts","../../../node_modules/@types/node/buffer.d.ts","../../../node_modules/@types/node/child_process.d.ts","../../../node_modules/@types/node/cluster.d.ts","../../../node_modules/@types/node/console.d.ts","../../../node_modules/@types/node/constants.d.ts","../../../node_modules/@types/node/crypto.d.ts","../../../node_modules/@types/node/dgram.d.ts","../../../node_modules/@types/node/diagnostics_channel.d.ts","../../../node_modules/@types/node/dns.d.ts","../../../node_modules/@types/node/dns/promises.d.ts","../../../node_modules/@types/node/dom-events.d.ts","../../../node_modules/@types/node/domain.d.ts","../../../node_modules/@types/node/events.d.ts","../../../node_modules/@types/node/fs.d.ts","../../../node_modules/@types/node/fs/promises.d.ts","../../../node_modules/@types/node/http.d.ts","../../../node_modules/@types/node/http2.d.ts","../../../node_modules/@types/node/https.d.ts","../../../node_modules/@types/node/inspector.d.ts","../../../node_modules/@types/node/module.d.ts","../../../node_modules/@types/node/net.d.ts","../../../node_modules/@types/node/os.d.ts","../../../node_modules/@types/node/path.d.ts","../../../node_modules/@types/node/perf_hooks.d.ts","../../../node_modules/@types/node/process.d.ts","../../../node_modules/@types/node/punycode.d.ts","../../../node_modules/@types/node/querystring.d.ts","../../../node_modules/@types/node/readline.d.ts","../../../node_modules/@types/node/repl.d.ts","../../../node_modules/@types/node/stream.d.ts","../../../node_modules/@types/node/stream/promises.d.ts","../../../node_modules/@types/node/stream/consumers.d.ts","../../../node_modules/@types/node/stream/web.d.ts","../../../node_modules/@types/node/string_decoder.d.ts","../../../node_modules/@types/node/test.d.ts","../../../node_modules/@types/node/timers.d.ts","../../../node_modules/@types/node/timers/promises.d.ts","../../../node_modules/@types/node/tls.d.ts","../../../node_modules/@types/node/trace_events.d.ts","../../../node_modules/@types/node/tty.d.ts","../../../node_modules/@types/node/url.d.ts","../../../node_modules/@types/node/util.d.ts","../../../node_modules/@types/node/v8.d.ts","../../../node_modules/@types/node/vm.d.ts","../../../node_modules/@types/node/wasi.d.ts","../../../node_modules/@types/node/worker_threads.d.ts","../../../node_modules/@types/node/zlib.d.ts","../../../node_modules/@types/node/globals.global.d.ts","../../../node_modules/@types/node/index.d.ts","../../../node_modules/@ethereumjs/common/dist/enums.d.ts","../../../node_modules/@ethereumjs/common/dist/types.d.ts","../../../node_modules/buffer/index.d.ts","../../../node_modules/@ethereumjs/util/dist/constants.d.ts","../../../node_modules/@ethereumjs/util/dist/units.d.ts","../../../node_modules/@ethereumjs/util/dist/address.d.ts","../../../node_modules/@ethereumjs/util/dist/bytes.d.ts","../../../node_modules/@ethereumjs/util/dist/types.d.ts","../../../node_modules/@ethereumjs/util/dist/account.d.ts","../../../node_modules/@ethereumjs/util/dist/withdrawal.d.ts","../../../node_modules/@ethereumjs/util/dist/signature.d.ts","../../../node_modules/@ethereumjs/util/dist/encoding.d.ts","../../../node_modules/@ethereumjs/util/dist/asyncEventEmitter.d.ts","../../../node_modules/@ethereumjs/util/dist/internal.d.ts","../../../node_modules/@ethereumjs/util/dist/lock.d.ts","../../../node_modules/@ethereumjs/util/dist/provider.d.ts","../../../node_modules/@ethereumjs/util/dist/index.d.ts","../../../node_modules/@ethereumjs/common/dist/common.d.ts","../../../node_modules/@ethereumjs/common/dist/utils.d.ts","../../../node_modules/@ethereumjs/common/dist/index.d.ts","../../../node_modules/@ethereumjs/tx/dist/eip2930Transaction.d.ts","../../../node_modules/@ethereumjs/tx/dist/legacyTransaction.d.ts","../../../node_modules/@ethereumjs/tx/dist/types.d.ts","../../../node_modules/@ethereumjs/tx/dist/baseTransaction.d.ts","../../../node_modules/@ethereumjs/tx/dist/eip1559Transaction.d.ts","../../../node_modules/@ethereumjs/tx/dist/transactionFactory.d.ts","../../../node_modules/@ethereumjs/tx/dist/index.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/patchCBOR.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/lib/DataItem.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/lib/cbor-sync.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/lib/index.d.ts","../../../node_modules/@ngraveio/bc-ur/dist/ur.d.ts","../../../node_modules/@ngraveio/bc-ur/dist/urEncoder.d.ts","../../../node_modules/@ngraveio/bc-ur/dist/fountainEncoder.d.ts","../../../node_modules/@ngraveio/bc-ur/dist/fountainDecoder.d.ts","../../../node_modules/@ngraveio/bc-ur/dist/urDecoder.d.ts","../../../node_modules/@ngraveio/bc-ur/dist/index.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/RegistryType.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/RegistryItem.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/CryptoCoinInfo.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/PathComponent.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/CryptoKeypath.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/types.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/CryptoHDKey.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/CryptoECKey.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/Bytes.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/MultiKey.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/ScriptExpression.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/CryptoOutput.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/CryptoPSBT.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/CryptoAccount.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/Decoder/index.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/extended/CryptoMultiAccounts.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/errors/index.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/extended/DerivationSchema.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/extended/KeyDerivation.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/extended/QRHardwareCall.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/utils.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/index.d.ts","../../../node_modules/@keystonehq/bc-ur-registry-eth/dist/EthSignRequest.d.ts","../../../node_modules/@keystonehq/bc-ur-registry-eth/dist/EthSignature.d.ts","../../../node_modules/@keystonehq/bc-ur-registry-eth/dist/ETHNFTItem.d.ts","../../../node_modules/@keystonehq/bc-ur-registry-eth/dist/utlis.d.ts","../../../node_modules/@keystonehq/bc-ur-registry-eth/dist/index.d.ts","../../../node_modules/@keystonehq/base-eth-keyring/dist/InteractionProvider.d.ts","../../../node_modules/@keystonehq/base-eth-keyring/dist/BaseKeyring.d.ts","../../../node_modules/@keystonehq/base-eth-keyring/dist/index.d.ts","../../../node_modules/@types/readable-stream/node_modules/safe-buffer/index.d.ts","../../../node_modules/@types/readable-stream/index.d.ts","../../../node_modules/@metamask/safe-event-emitter/dist/cjs/index.d.ts","../../../node_modules/@metamask/obs-store/dist/ObservableStore.d.ts","../../../node_modules/@metamask/obs-store/dist/asStream.d.ts","../../../node_modules/@metamask/obs-store/dist/ComposedStore.d.ts","../../../node_modules/@metamask/obs-store/dist/MergedStore.d.ts","../../../node_modules/@metamask/obs-store/dist/transform.d.ts","../../../node_modules/@metamask/obs-store/dist/index.d.ts","../../../node_modules/@keystonehq/metamask-airgapped-keyring/dist/MetaMaskInteractionProvider.d.ts","../../../node_modules/@keystonehq/metamask-airgapped-keyring/dist/MetaMaskKeyring.d.ts","../../../node_modules/@keystonehq/metamask-airgapped-keyring/dist/index.d.ts","../../base-controller/dist/types/BaseControllerV1.d.ts","../../../node_modules/superstruct/dist/error.d.ts","../../../node_modules/superstruct/dist/utils.d.ts","../../../node_modules/superstruct/dist/struct.d.ts","../../../node_modules/superstruct/dist/structs/coercions.d.ts","../../../node_modules/superstruct/dist/structs/refinements.d.ts","../../../node_modules/superstruct/dist/structs/types.d.ts","../../../node_modules/superstruct/dist/structs/utilities.d.ts","../../../node_modules/superstruct/dist/index.d.ts","../../../node_modules/@metamask/utils/dist/types/assert.d.ts","../../../node_modules/@metamask/utils/dist/types/base64.d.ts","../../../node_modules/@metamask/utils/dist/types/hex.d.ts","../../../node_modules/@metamask/utils/dist/types/bytes.d.ts","../../../node_modules/@metamask/utils/dist/types/caip-types.d.ts","../../../node_modules/@metamask/utils/dist/types/checksum.d.ts","../../../node_modules/@metamask/utils/dist/types/coercers.d.ts","../../../node_modules/@metamask/utils/dist/types/collections.d.ts","../../../node_modules/@metamask/utils/dist/types/encryption-types.d.ts","../../../node_modules/@metamask/utils/dist/types/errors.d.ts","../../../node_modules/@metamask/utils/dist/types/json.d.ts","../../../node_modules/@metamask/utils/dist/types/keyring.d.ts","../../../node_modules/@types/ms/index.d.ts","../../../node_modules/@types/debug/index.d.ts","../../../node_modules/@metamask/utils/dist/types/logging.d.ts","../../../node_modules/@metamask/utils/dist/types/misc.d.ts","../../../node_modules/@metamask/utils/dist/types/number.d.ts","../../../node_modules/@metamask/utils/dist/types/opaque.d.ts","../../../node_modules/@metamask/utils/dist/types/promise.d.ts","../../../node_modules/@metamask/utils/dist/types/time.d.ts","../../../node_modules/@metamask/utils/dist/types/transaction-types.d.ts","../../../node_modules/@metamask/utils/dist/types/versions.d.ts","../../../node_modules/@metamask/utils/dist/types/index.d.ts","../../../node_modules/immer/dist/utils/env.d.ts","../../../node_modules/immer/dist/utils/errors.d.ts","../../../node_modules/immer/dist/types/types-external.d.ts","../../../node_modules/immer/dist/types/types-internal.d.ts","../../../node_modules/immer/dist/utils/common.d.ts","../../../node_modules/immer/dist/utils/plugins.d.ts","../../../node_modules/immer/dist/core/scope.d.ts","../../../node_modules/immer/dist/core/finalize.d.ts","../../../node_modules/immer/dist/core/proxy.d.ts","../../../node_modules/immer/dist/core/immerClass.d.ts","../../../node_modules/immer/dist/core/current.d.ts","../../../node_modules/immer/dist/internal.d.ts","../../../node_modules/immer/dist/plugins/es5.d.ts","../../../node_modules/immer/dist/plugins/patches.d.ts","../../../node_modules/immer/dist/plugins/mapset.d.ts","../../../node_modules/immer/dist/plugins/all.d.ts","../../../node_modules/immer/dist/immer.d.ts","../../base-controller/dist/types/RestrictedControllerMessenger.d.ts","../../base-controller/dist/types/ControllerMessenger.d.ts","../../base-controller/dist/types/BaseControllerV2.d.ts","../../base-controller/dist/types/index.d.ts","../../../node_modules/@metamask/browser-passworder/dist/index.d.ts","../../../node_modules/@metamask/eth-sig-util/dist/personal-sign.d.ts","../../../node_modules/@metamask/eth-sig-util/dist/sign-typed-data.d.ts","../../../node_modules/@metamask/eth-sig-util/dist/encryption.d.ts","../../../node_modules/@metamask/eth-sig-util/dist/utils.d.ts","../../../node_modules/@metamask/eth-sig-util/dist/index.d.ts","../../../node_modules/@metamask/eth-simple-keyring/dist/simple-keyring.d.ts","../../../node_modules/@metamask/eth-simple-keyring/dist/index.d.ts","../../../node_modules/@metamask/keyring-api/dist/base-types.d.ts","../../../node_modules/@metamask/keyring-api/dist/btc/types.d.ts","../../../node_modules/@metamask/keyring-api/dist/btc/index.d.ts","../../../node_modules/@metamask/keyring-api/dist/superstruct.d.ts","../../../node_modules/@metamask/keyring-api/dist/eth/erc4337/types.d.ts","../../../node_modules/@metamask/keyring-api/dist/eth/erc4337/index.d.ts","../../../node_modules/@metamask/keyring-api/dist/eth/types.d.ts","../../../node_modules/@metamask/keyring-api/dist/internal/api.d.ts","../../../node_modules/@metamask/keyring-api/dist/contexts.d.ts","../../../node_modules/@metamask/keyring-api/dist/internal/eth/EthKeyring.d.ts","../../../node_modules/@metamask/keyring-api/dist/internal/eth/index.d.ts","../../../node_modules/@metamask/keyring-api/dist/internal/events.d.ts","../../../node_modules/@metamask/keyring-api/dist/internal/rpc.d.ts","../../../node_modules/@metamask/keyring-api/dist/internal/types.d.ts","../../../node_modules/@metamask/keyring-api/dist/internal/index.d.ts","../../../node_modules/@metamask/keyring-api/dist/eth/utils.d.ts","../../../node_modules/@metamask/keyring-api/dist/eth/index.d.ts","../../../node_modules/@metamask/keyring-api/dist/api.d.ts","../../../node_modules/@metamask/keyring-api/dist/events.d.ts","../../../node_modules/@metamask/keyring-api/dist/JsonRpcRequest.d.ts","../../../node_modules/@metamask/keyring-api/dist/KeyringClient.d.ts","../../../node_modules/@metamask/rpc-errors/dist/types/utils.d.ts","../../../node_modules/@metamask/rpc-errors/dist/types/classes.d.ts","../../../node_modules/@metamask/rpc-errors/dist/types/errors.d.ts","../../../node_modules/@metamask/rpc-errors/dist/types/error-constants.d.ts","../../../node_modules/@metamask/rpc-errors/dist/types/index.d.ts","../../json-rpc-engine/src/JsonRpcEngine.ts","../../json-rpc-engine/src/createAsyncMiddleware.ts","../../json-rpc-engine/src/createScaffoldMiddleware.ts","../../json-rpc-engine/src/getUniqueId.ts","../../json-rpc-engine/src/idRemapMiddleware.ts","../../json-rpc-engine/src/mergeMiddleware.ts","../../json-rpc-engine/src/index.ts","../../../node_modules/@metamask/providers/dist/types/utils.d.ts","../../../node_modules/@metamask/providers/dist/types/BaseProvider.d.ts","../../../node_modules/@metamask/providers/dist/types/EIP6963.d.ts","../../../node_modules/@metamask/providers/dist/types/StreamProvider.d.ts","../../../node_modules/@metamask/providers/dist/types/extension-provider/createExternalExtensionProvider.d.ts","../../../node_modules/@metamask/providers/dist/types/MetaMaskInpageProvider.d.ts","../../../node_modules/@metamask/providers/dist/types/initializeInpageProvider.d.ts","../../../node_modules/@metamask/providers/dist/types/shimWeb3.d.ts","../../../node_modules/@metamask/providers/dist/types/index.d.ts","../../../node_modules/@metamask/keyring-api/dist/KeyringSnapRpcClient.d.ts","../../../node_modules/@metamask/keyring-api/dist/rpc-handler.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/errors.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/internals/error-wrappers.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/internals/errors.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/internals/helpers.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/internals/structs.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/create-interface.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/dialog.d.ts","../../../node_modules/@metamask/key-tree/dist/constants.d.cts","../../../node_modules/@metamask/key-tree/node_modules/@noble/curves/abstract/modular.d.ts","../../../node_modules/@metamask/key-tree/node_modules/@noble/curves/abstract/utils.d.ts","../../../node_modules/@metamask/key-tree/node_modules/@noble/curves/abstract/curve.d.ts","../../../node_modules/@metamask/key-tree/dist/curves/ed25519.d.cts","../../../node_modules/@metamask/key-tree/dist/curves/ed25519Bip32.d.cts","../../../node_modules/@metamask/key-tree/node_modules/@noble/curves/abstract/weierstrass.d.ts","../../../node_modules/@metamask/key-tree/dist/curves/secp256k1.d.cts","../../../node_modules/@metamask/key-tree/dist/curves/curve.d.cts","../../../node_modules/@metamask/key-tree/dist/curves/index.d.cts","../../../node_modules/@metamask/key-tree/dist/utils.d.cts","../../../node_modules/@metamask/key-tree/dist/BIP44CoinTypeNode.d.cts","../../../node_modules/@metamask/key-tree/dist/SLIP10Node.d.cts","../../../node_modules/@metamask/key-tree/dist/BIP44Node.d.cts","../../../node_modules/@metamask/key-tree/dist/derivers/bip32.d.cts","../../../node_modules/@metamask/key-tree/dist/derivers/bip39.d.cts","../../../node_modules/@metamask/key-tree/dist/derivers/cip3.d.cts","../../../node_modules/@metamask/key-tree/dist/derivers/slip10.d.cts","../../../node_modules/@metamask/key-tree/dist/derivers/index.d.cts","../../../node_modules/@metamask/key-tree/dist/index.d.cts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/caip.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/permissions.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/get-bip32-entropy.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/get-bip32-public-key.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/get-bip44-entropy.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/get-client-status.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/get-entropy.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/get-file.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/component.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/Address.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/Box.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/Copyable.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/Divider.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/form/Button.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/form/Input.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/form/Field.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/form/Form.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/form/index.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/formatting/Bold.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/formatting/Italic.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/formatting/index.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/Heading.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/Image.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/Link.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/Text.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/Row.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/Spinner.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/index.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/jsx-runtime.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/jsx-dev-runtime.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/validation.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/index.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/nodes.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/address.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/copyable.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/divider.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/heading.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/image.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/panel.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/spinner.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/text.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/row.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/button.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/input.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/form.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/index.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/component.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/index.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/interface.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/get-interface-state.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/get-locale.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/snap.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/get-snaps.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/invoke-snap.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/invoke-keyring.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/manage-accounts.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/manage-state.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/notify.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/request-snaps.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/update-interface.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/methods.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/index.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/provider.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/global.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/images.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/handlers/cronjob.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/handlers/home-page.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/handlers/keyring.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/handlers/lifecycle.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/handlers/name-lookup.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/handlers/rpc-request.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/handlers/transaction.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/handlers/signature.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/handlers/user-input.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/handlers/index.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/index.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/internals/jsx.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/internals/svg.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/internals/index.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/error-wrappers.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/images.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/index.d.ts","../../../node_modules/@metamask/keyring-api/dist/snap-utils.d.ts","../../../node_modules/@metamask/keyring-api/dist/index.d.ts","../../message-manager/dist/types/AbstractMessageManager.d.ts","../../controller-utils/dist/types/types.d.ts","../../controller-utils/dist/types/constants.d.ts","../../../node_modules/@metamask/eth-query/index.d.ts","../../../node_modules/@types/bn.js/index.d.ts","../../controller-utils/dist/types/util.d.ts","../../../node_modules/@spruceid/siwe-parser/dist/abnf.d.ts","../../../node_modules/@spruceid/siwe-parser/dist/utils.d.ts","../../../node_modules/@spruceid/siwe-parser/dist/parsers.d.ts","../../controller-utils/dist/types/siwe.d.ts","../../controller-utils/dist/types/index.d.ts","../../message-manager/dist/types/PersonalMessageManager.d.ts","../../message-manager/dist/types/TypedMessageManager.d.ts","../../message-manager/dist/types/EncryptionPublicKeyManager.d.ts","../../message-manager/dist/types/DecryptMessageManager.d.ts","../../message-manager/dist/types/index.d.ts","../../../node_modules/async-mutex/lib/MutexInterface.d.ts","../../../node_modules/async-mutex/lib/Mutex.d.ts","../../../node_modules/async-mutex/lib/SemaphoreInterface.d.ts","../../../node_modules/async-mutex/lib/Semaphore.d.ts","../../../node_modules/async-mutex/lib/withTimeout.d.ts","../../../node_modules/async-mutex/lib/tryAcquire.d.ts","../../../node_modules/async-mutex/lib/errors.d.ts","../../../node_modules/async-mutex/lib/index.d.ts","../../../node_modules/ethereumjs-wallet/dist/hdkey.d.ts","../../../node_modules/ethereumjs-wallet/dist/thirdparty.d.ts","../../../node_modules/ethereumjs-wallet/dist/index.d.ts","../src/constants.ts","../src/KeyringController.ts","../src/index.ts","../../../node_modules/@babel/types/lib/index.d.ts","../../../node_modules/@types/babel__generator/index.d.ts","../../../node_modules/@babel/parser/typings/babel-parser.d.ts","../../../node_modules/@types/babel__template/index.d.ts","../../../node_modules/@types/babel__traverse/index.d.ts","../../../node_modules/@types/babel__core/index.d.ts","../../../node_modules/@types/deep-freeze-strict/index.d.ts","../../../node_modules/@types/eslint/helpers.d.ts","../../../node_modules/@types/estree/index.d.ts","../../../node_modules/@types/json-schema/index.d.ts","../../../node_modules/@types/eslint/index.d.ts","../../../node_modules/@types/graceful-fs/index.d.ts","../../../node_modules/@types/istanbul-lib-coverage/index.d.ts","../../../node_modules/@types/istanbul-lib-report/index.d.ts","../../../node_modules/@types/istanbul-reports/index.d.ts","../../../node_modules/chalk/index.d.ts","../../../node_modules/jest-diff/build/cleanupSemantic.d.ts","../../../node_modules/pretty-format/build/types.d.ts","../../../node_modules/pretty-format/build/index.d.ts","../../../node_modules/jest-diff/build/types.d.ts","../../../node_modules/jest-diff/build/diffLines.d.ts","../../../node_modules/jest-diff/build/printDiffs.d.ts","../../../node_modules/jest-diff/build/index.d.ts","../../../node_modules/jest-matcher-utils/build/index.d.ts","../../../node_modules/@types/jest/index.d.ts","../../../node_modules/@types/jest-when/index.d.ts","../../../node_modules/@types/json5/index.d.ts","../../../node_modules/@types/lodash/common/common.d.ts","../../../node_modules/@types/lodash/common/array.d.ts","../../../node_modules/@types/lodash/common/collection.d.ts","../../../node_modules/@types/lodash/common/date.d.ts","../../../node_modules/@types/lodash/common/function.d.ts","../../../node_modules/@types/lodash/common/lang.d.ts","../../../node_modules/@types/lodash/common/math.d.ts","../../../node_modules/@types/lodash/common/number.d.ts","../../../node_modules/@types/lodash/common/object.d.ts","../../../node_modules/@types/lodash/common/seq.d.ts","../../../node_modules/@types/lodash/common/string.d.ts","../../../node_modules/@types/lodash/common/util.d.ts","../../../node_modules/@types/lodash/index.d.ts","../../../node_modules/@types/minimatch/index.d.ts","../../../node_modules/@types/parse-json/index.d.ts","../../../node_modules/@types/pbkdf2/index.d.ts","../../../node_modules/@types/prettier/index.d.ts","../../../node_modules/@types/punycode/index.d.ts","../../../node_modules/@types/secp256k1/index.d.ts","../../../node_modules/@types/semver/classes/semver.d.ts","../../../node_modules/@types/semver/functions/parse.d.ts","../../../node_modules/@types/semver/functions/valid.d.ts","../../../node_modules/@types/semver/functions/clean.d.ts","../../../node_modules/@types/semver/functions/inc.d.ts","../../../node_modules/@types/semver/functions/diff.d.ts","../../../node_modules/@types/semver/functions/major.d.ts","../../../node_modules/@types/semver/functions/minor.d.ts","../../../node_modules/@types/semver/functions/patch.d.ts","../../../node_modules/@types/semver/functions/prerelease.d.ts","../../../node_modules/@types/semver/functions/compare.d.ts","../../../node_modules/@types/semver/functions/rcompare.d.ts","../../../node_modules/@types/semver/functions/compare-loose.d.ts","../../../node_modules/@types/semver/functions/compare-build.d.ts","../../../node_modules/@types/semver/functions/sort.d.ts","../../../node_modules/@types/semver/functions/rsort.d.ts","../../../node_modules/@types/semver/functions/gt.d.ts","../../../node_modules/@types/semver/functions/lt.d.ts","../../../node_modules/@types/semver/functions/eq.d.ts","../../../node_modules/@types/semver/functions/neq.d.ts","../../../node_modules/@types/semver/functions/gte.d.ts","../../../node_modules/@types/semver/functions/lte.d.ts","../../../node_modules/@types/semver/functions/cmp.d.ts","../../../node_modules/@types/semver/functions/coerce.d.ts","../../../node_modules/@types/semver/classes/comparator.d.ts","../../../node_modules/@types/semver/classes/range.d.ts","../../../node_modules/@types/semver/functions/satisfies.d.ts","../../../node_modules/@types/semver/ranges/max-satisfying.d.ts","../../../node_modules/@types/semver/ranges/min-satisfying.d.ts","../../../node_modules/@types/semver/ranges/to-comparators.d.ts","../../../node_modules/@types/semver/ranges/min-version.d.ts","../../../node_modules/@types/semver/ranges/valid.d.ts","../../../node_modules/@types/semver/ranges/outside.d.ts","../../../node_modules/@types/semver/ranges/gtr.d.ts","../../../node_modules/@types/semver/ranges/ltr.d.ts","../../../node_modules/@types/semver/ranges/intersects.d.ts","../../../node_modules/@types/semver/ranges/simplify.d.ts","../../../node_modules/@types/semver/ranges/subset.d.ts","../../../node_modules/@types/semver/internals/identifiers.d.ts","../../../node_modules/@types/semver/index.d.ts","../../../node_modules/@types/sinonjs__fake-timers/index.d.ts","../../../node_modules/@types/sinon/index.d.ts","../../../node_modules/@types/stack-utils/index.d.ts","../../../node_modules/@types/uuid/index.d.ts","../../../node_modules/@types/yargs-parser/index.d.ts","../../../node_modules/@types/yargs/index.d.ts"],"fileInfos":[{"version":"8730f4bf322026ff5229336391a18bcaa1f94d4f82416c8b2f3954e2ccaae2ba","affectsGlobalScope":true},"dc47c4fa66b9b9890cf076304de2a9c5201e94b740cffdf09f87296d877d71f6","7a387c58583dfca701b6c85e0adaf43fb17d590fb16d5b2dc0a2fbd89f35c467","8a12173c586e95f4433e0c6dc446bc88346be73ffe9ca6eec7aa63c8f3dca7f9","5f4e733ced4e129482ae2186aae29fde948ab7182844c3a5a51dd346182c7b06","4b421cbfb3a38a27c279dec1e9112c3d1da296f77a1a85ddadf7e7a425d45d18","1fc5ab7a764205c68fa10d381b08417795fc73111d6dd16b5b1ed36badb743d9",{"version":"3aafcb693fe5b5c3bd277bd4c3a617b53db474fe498fc5df067c5603b1eebde7","affectsGlobalScope":true},{"version":"adb996790133eb33b33aadb9c09f15c2c575e71fb57a62de8bf74dbf59ec7dfb","affectsGlobalScope":true},{"version":"8cc8c5a3bac513368b0157f3d8b31cfdcfe78b56d3724f30f80ed9715e404af8","affectsGlobalScope":true},{"version":"cdccba9a388c2ee3fd6ad4018c640a471a6c060e96f1232062223063b0a5ac6a","affectsGlobalScope":true},{"version":"c5c05907c02476e4bde6b7e76a79ffcd948aedd14b6a8f56e4674221b0417398","affectsGlobalScope":true},{"version":"5f406584aef28a331c36523df688ca3650288d14f39c5d2e555c95f0d2ff8f6f","affectsGlobalScope":true},{"version":"22f230e544b35349cfb3bd9110b6ef37b41c6d6c43c3314a31bd0d9652fcec72","affectsGlobalScope":true},{"version":"7ea0b55f6b315cf9ac2ad622b0a7813315bb6e97bf4bb3fbf8f8affbca7dc695","affectsGlobalScope":true},{"version":"3013574108c36fd3aaca79764002b3717da09725a36a6fc02eac386593110f93","affectsGlobalScope":true},{"version":"eb26de841c52236d8222f87e9e6a235332e0788af8c87a71e9e210314300410a","affectsGlobalScope":true},{"version":"3be5a1453daa63e031d266bf342f3943603873d890ab8b9ada95e22389389006","affectsGlobalScope":true},{"version":"17bb1fc99591b00515502d264fa55dc8370c45c5298f4a5c2083557dccba5a2a","affectsGlobalScope":true},{"version":"7ce9f0bde3307ca1f944119f6365f2d776d281a393b576a18a2f2893a2d75c98","affectsGlobalScope":true},{"version":"6a6b173e739a6a99629a8594bfb294cc7329bfb7b227f12e1f7c11bc163b8577","affectsGlobalScope":true},{"version":"81cac4cbc92c0c839c70f8ffb94eb61e2d32dc1c3cf6d95844ca099463cf37ea","affectsGlobalScope":true},{"version":"b0124885ef82641903d232172577f2ceb5d3e60aed4da1153bab4221e1f6dd4e","affectsGlobalScope":true},{"version":"0eb85d6c590b0d577919a79e0084fa1744c1beba6fd0d4e951432fa1ede5510a","affectsGlobalScope":true},{"version":"da233fc1c8a377ba9e0bed690a73c290d843c2c3d23a7bd7ec5cd3d7d73ba1e0","affectsGlobalScope":true},{"version":"d154ea5bb7f7f9001ed9153e876b2d5b8f5c2bb9ec02b3ae0d239ec769f1f2ae","affectsGlobalScope":true},{"version":"bb2d3fb05a1d2ffbca947cc7cbc95d23e1d053d6595391bd325deb265a18d36c","affectsGlobalScope":true},{"version":"c80df75850fea5caa2afe43b9949338ce4e2de086f91713e9af1a06f973872b8","affectsGlobalScope":true},{"version":"9d57b2b5d15838ed094aa9ff1299eecef40b190722eb619bac4616657a05f951","affectsGlobalScope":true},{"version":"6c51b5dd26a2c31dbf37f00cfc32b2aa6a92e19c995aefb5b97a3a64f1ac99de","affectsGlobalScope":true},{"version":"6e7997ef61de3132e4d4b2250e75343f487903ddf5370e7ce33cf1b9db9a63ed","affectsGlobalScope":true},{"version":"2ad234885a4240522efccd77de6c7d99eecf9b4de0914adb9a35c0c22433f993","affectsGlobalScope":true},{"version":"5e5e095c4470c8bab227dbbc61374878ecead104c74ab9960d3adcccfee23205","affectsGlobalScope":true},{"version":"09aa50414b80c023553090e2f53827f007a301bc34b0495bfb2c3c08ab9ad1eb","affectsGlobalScope":true},{"version":"d7f680a43f8cd12a6b6122c07c54ba40952b0c8aa140dcfcf32eb9e6cb028596","affectsGlobalScope":true},{"version":"3787b83e297de7c315d55d4a7c546ae28e5f6c0a361b7a1dcec1f1f50a54ef11","affectsGlobalScope":true},{"version":"e7e8e1d368290e9295ef18ca23f405cf40d5456fa9f20db6373a61ca45f75f40","affectsGlobalScope":true},{"version":"faf0221ae0465363c842ce6aa8a0cbda5d9296940a8e26c86e04cc4081eea21e","affectsGlobalScope":true},{"version":"06393d13ea207a1bfe08ec8d7be562549c5e2da8983f2ee074e00002629d1871","affectsGlobalScope":true},{"version":"2768ef564cfc0689a1b76106c421a2909bdff0acbe87da010785adab80efdd5c","affectsGlobalScope":true},{"version":"b248e32ca52e8f5571390a4142558ae4f203ae2f94d5bac38a3084d529ef4e58","affectsGlobalScope":true},{"version":"52d1bb7ab7a3306fd0375c8bff560feed26ed676a5b0457fa8027b563aecb9a4","affectsGlobalScope":true},"70bbfaec021ac4a0c805374225b55d70887f987df8b8dd7711d79464bb7b4385","869089d60b67219f63e6aca810284c89bae1b384b5cbc7ce64e53d82ad223ed5",{"version":"18338b6a4b920ec7d49b4ffafcbf0fa8a86b4bfd432966efd722dab611157cf4","affectsGlobalScope":true},"62a0875a0397b35a2364f1d401c0ce17975dfa4d47bf6844de858ae04da349f9","ee7491d0318d1fafcba97d5b72b450eb52671570f7a4ecd9e8898d40eaae9472","e3e7d217d89b380c1f34395eadc9289542851b0f0a64007dfe1fb7cf7423d24e","fd79909e93b4d50fd0ed9f3d39ddf8ba0653290bac25c295aac49f6befbd081b","345a9cc2945406f53051cd0e9b51f82e1e53929848eab046fdda91ee8aa7da31","9debe2de883da37a914e5e784a7be54c201b8f1d783822ad6f443ff409a5ea21","dee5d5c5440cda1f3668f11809a5503c30db0476ad117dd450f7ba5a45300e8f","f5e396c1424c391078c866d6f84afe0b4d2f7f85a160b9c756cd63b5b1775d93","5caa6f4fff16066d377d4e254f6c34c16540da3809cd66cd626a303bc33c419f","730d055528bdf12c8524870bb33d237991be9084c57634e56e5d8075f6605e02","5b3cd03ae354ea96eff1f74d7c410fe4852e6382227e8b0ecf87ab5e3a5bbcd4","7394959e5a741b185456e1ef5d64599c36c60a323207450991e7a42e08911419",{"version":"056097110efd16869ec118cedb44ecbac9a019576eee808d61304ca6d5cb2cbe","affectsGlobalScope":true},"f51b4042a3ac86f1f707500a9768f88d0b0c1fc3f3e45a73333283dea720cdc6",{"version":"6fb8358e10ed92a7f515b7d79da3904c955a3ffd4e14aa9df6f0ea113041f1cf","affectsGlobalScope":true},"45c831238c6dac21c72da5f335747736a56a3847192bf03c84b958a7e9ec93e2","661a11d16ad2e3543a77c53bcd4017ee9a450f47ab7def3ab493a86eae4d550c",{"version":"8cdc646cec7819581ef343b83855b1bfe4fe674f2c84f4fb8dc90d82fb56bd3a","affectsGlobalScope":true},"a40826e8476694e90da94aa008283a7de50d1dafd37beada623863f1901cb7fb","9dd56225cc2d8cb8fe5ceb0043ff386987637e12fecc6078896058a99deae284","2375ed4b439215aa3b6d0c6fd175c78a4384b30cb43cbadaecbf0a18954c98cb","7693b90b3075deaccafd5efb467bf9f2b747a3075be888652ef73e64396d8628","41231da15bb5e3e806a8395bd15c7befd2ec90f9f4e3c9d0ae1356bccb76dbb0","fccfef201d057cb407fa515311bd608549bab6c7b8adcf8f2df31f5d3b796478",{"version":"ee1ee365d88c4c6c0c0a5a5701d66ebc27ccd0bcfcfaa482c6e2e7fe7b98edf7","affectsGlobalScope":true},"5f20d20b7607174caf1a6da9141aeb9f2142159ae2410ca30c7a0fccd1d19c99",{"version":"464762c6213566d072f1ced5e8e9a954785ec5e53883b7397198abb5ef5b8f71","affectsGlobalScope":true},"6387920dc3e18927335b086deec75bf8e50f879a5e273d32ee7bb7a55ba50572","9bba37424094688c4663c177a1379b229f919b8912889a472f32fdc5f08ddb4d","29a4be13b3a30d3e66667b75c58ec61fb2df8fa0422534fdee3cfb30c5dbf450","83366d901beda79d6eb37aaaf6ca248dcd88946302b2a7d975590783be51e88e","bf268a0aea37ad4ae3b7a9b58559190b6fc01ea16a31e35cd05817a0a60f895a","43ec77c369473e92e2ecebf0554a0fdaa9c256644a6070f28228dfcceec77351",{"version":"d7dad6db394a3d9f7b49755e4b610fbf8ed6eb0c9810ae5f1a119f6b5d76de45","affectsGlobalScope":true},"95ed02bacb4502c985b69742ec82a4576d4ff4a6620ecc91593f611d502ae546","bf755525c4e6f85a970b98c4755d98e8aa1b6dbd83a5d8fcc57d3d497351b936","dd67d2b5e4e8a182a38de8e69fb736945eaa4588e0909c14e01a14bd3cc1fd1e",{"version":"28084e15b63e6211769db2fe646d8bc5c4c6776321e0deffe2d12eefd52cb6b9","affectsGlobalScope":true},{"version":"aed37dabf86c99d6c8508700576ecede86688397bc12523541858705a0c737c2","affectsGlobalScope":true},"cc6ef5733d4ea6d2e06310a32dffd2c16418b467c5033d49cecc4f3a25de7497","94768454c3348b6ebe48e45fbad8c92e2bb7af4a35243edbe2b90823d0bd7f9a","0be79b3ff0f16b6c2f9bc8c4cc7097ea417d8d67f8267f7e1eec8e32b548c2ff","1c61ffa3a71b77363b30d19832c269ef62fba787f5610cac7254728d3b69ab2e","84da3c28344e621fd1d591f2c09e9595292d2b70018da28a553268ac122597d4","269929a24b2816343a178008ac9ae9248304d92a8ba8e233055e0ed6dbe6ef71","6e191fea1db6e9e4fa828259cf489e820ec9170effff57fb081a2f3295db4722","aed943465fbce1efe49ee16b5ea409050f15cd8eaf116f6fadb64ef0772e7d95","70d08483a67bf7050dbedace398ef3fee9f436fcd60517c97c4c1e22e3c6f3e8","c40fdf7b2e18df49ce0568e37f0292c12807a0748be79e272745e7216bed2606",{"version":"e933de8143e1d12dd51d89b398760fd5a9081896be366dad88a922d0b29f3c69","affectsGlobalScope":true},"4e228e78c1e9b0a75c70588d59288f63a6258e8b1fe4a67b0c53fe03461421d9","b38d55d08708c2410a3039687db70b4a5bfa69fc4845617c313b5a10d9c5c637","205d50c24359ead003dc537b9b65d2a64208dfdffe368f403cf9e0357831db9e","1265fddcd0c68be9d2a3b29805d0280484c961264dd95e0b675f7bd91f777e78",{"version":"a05e2d784c9be7051c4ac87a407c66d2106e23490c18c038bbd0712bde7602fd","affectsGlobalScope":true},{"version":"df90b9d0e9980762da8daf8adf6ffa0c853e76bfd269c377be0d07a9ad87acd2","affectsGlobalScope":true},"cf434b5c04792f62d6f4bdd5e2c8673f36e638e910333c172614d5def9b17f98","1d65d4798df9c2df008884035c41d3e67731f29db5ecb64cd7378797c7c53a2f","0faee6b555890a1cb106e2adc5d3ffd89545b1da894d474e9d436596d654998f","c6c01ea1c42508edf11a36d13b70f6e35774f74355ba5d358354d4a77cc67ea1","867f95abf1df444aab146b19847391fc2f922a55f6a970a27ed8226766cee29f",{"version":"ab9b9a36e5284fd8d3bf2f7d5fcbc60052f25f27e4d20954782099282c60d23e","affectsGlobalScope":true},"b0297b09e607bec9698cac7cf55463d6731406efb1161ee4d448293b47397c84","175323e2a79a6076e0bada8a390d535a3ea817158bf1b1f46e31efca9028a0a2","7a10053aadc19335532a4d02756db4865974fd69bea5439ddcc5bfdf062d9476","4967529644e391115ca5592184d4b63980569adf60ee685f968fd59ab1557188","aed9e712a9b168345362e8f3a949f16c99ca1e05d21328f05735dfdbb24414ef","b04fe6922ed3db93afdbd49cdda8576aa75f744592fceea96fb0d5f32158c4f5","ed8d6c8de90fc2a4faaebc28e91f2469928738efd5208fb75ade0fa607e892b7","d7c52b198d680fe65b1a8d1b001f0173ffa2536ca2e7082431d726ce1f6714cd","c07f251e1c4e415a838e5498380b55cfea94f3513229de292d2aa85ae52fc3e9","0ed401424892d6bf294a5374efe512d6951b54a71e5dd0290c55b6d0d915f6f7","b945be6da6a3616ef3a250bfe223362b1c7c6872e775b0c4d82a1bf7a28ff902","beea49237dd7c7110fabf3c7509919c9cb9da841d847c53cac162dc3479e2f87","0f45f8a529c450d8f394106cc622bff79e44a1716e1ac9c3cc68b43f7ecf65ee","c624ce90b04c27ce4f318ba6330d39bde3d4e306f0f497ce78d4bda5ab8e22ca","9b8253aa5cb2c82d505f72afdbf96e83b15cc6b9a6f4fadbbbab46210d5f1977","86a8f52e4b1ac49155e889376bcfa8528a634c90c27fec65aa0e949f77b740c5","aab5dd41c1e2316cc0b42a7dd15684f8582d5a1d16c0516276a2a8a7d0fecd9c","59948226626ee210045296ba1fc6cb0fe748d1ff613204e08e7157ab6862dee7","ec3e54d8b713c170fdc8110a7e4a6a97513a7ab6b05ac9e1100cb064d2bb7349","43beb30ecb39a603fde4376554887310b0699f25f7f39c5c91e3147b51bb3a26","666b77d7f06f49da114b090a399abbfa66d5b6c01a3fd9dc4f063a52ace28507","31997714a93fbc570f52d47d6a8ebfb021a34a68ea9ba58bbb69cdec9565657e","6032e4262822160128e644de3fc4410bcd7517c2f137525fd2623d2bb23cb0d3","8bd5c9b1016629c144fd228983395b9dbf0676a576716bc3d316cab612c33cd5","2ed90bd3925b23aed8f859ffd0e885250be0424ca2b57e9866dabef152e1d6b7","93f6bd17d92dab9db7897e1430a5aeaa03bcf51623156213d8397710367a76ce","3f62b770a42e8c47c7008726f95aa383e69d97e85e680d237b99fcb0ee601dd8","5b84cfe78028c35c3bb89c042f18bf08d09da11e82d275c378ae4d07d8477e6c","8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","25139d6a726e0e19d9fc4fa3197367b4a82ec34a08a5ecf23963e142c202c0f3","e3328bffc8eab74665a4fe9c59d6f12f4c8570c3d858497e241eb37efe17dfcf","29389551e426a46421134b55182d6fcf5b143670998bf81db2619c1228235392","c18f7e16982695bdd04e3e183a327d116185f77f1a37b9b2e849d7d93269cd74","2cfb37011989c21dc70b91d521a2d5a4e0f18507f5f536b5dfe504edb15916e8","bb5e02df7aaec7a4ea642639a9963b24b8d9fd6798351f07d8c58616942fbcbf","299a899cb4d061f5d83843ec453e936e9659b2c435746823f90c40eddaef4745","d5610c0fd12870f644b0f42c1bcc4fa2295ac3e3ca01916bdb42c3bdc4c80c36","2c56a8e249b1f45dbdf973100cd37fe2ea68709573cf1fdf2e3052c593be68d8","3553da417ee7b07e388b13bd12a70a1c03e65a6132ba5427fe68f5b362373e6f","612358502042d351c227ba779fdcf6d875d827e424930e60297c533524e50668","d2b5be376ef162aa0c24a826e7dd2d77671a045c085e16d1c1276db4bdccbac7","c4138d8dcccedaff6621e009cf0a54a7bed2a5ad4c509a3513bccc4f417ef939","ad8747fe978dff3e80f4b12b48d37cc8dff11b61d04c035aefbc982ce21201ce","b154f789fd65298e1ba6cbba6944ea892d564c95f3d3700ed85baf8f80748473","c660265aedd7c5b236e2017e53095cb98da66200eb0e8d023b5bf713c36494e8","0efc36bf5c0daca6217fec7063359ccdab8c3a23bb405d25340fae22cf72d74f","5abff0c87d4f9c89715107042d4c73b68ef7a128759f451c8a0fc450cbaaf660","5a03308fbd1af441065149a84c692931bebc7e7735afc23be8684f4e10d3aa06","c787bf4f8f0abbf815cfbd348be41046f2b8f270be24fe7aa8a8fcdd2b7df8c2","e7a5191c663a3228f30104961d548b372e51c5936c01ffc8eddd262bb98d7d7c","43fdc9abe6f8640fda4cdc55a1ee5f666d3fce554277043df925c383137ddf69","f0b09665c9d52de465687fbd3cfb65111d3ffc59ae00c6f42654150f3db05518","72f8c078d06cff690e24ff2b0e118a9de2833dcebf7c53e762dcb505ddf36a68","9705efb0fd901180de84ca4dd11d86f87fd73f99d6a5660a664c048a7487e385","f9b9d0950fdfb90f57e3f045fe73dce7fa6e7921b37622fc12e64fcd90afbd0f","e61b36e7fde608f8bb4b9c973d81556553a715eaef42a181a16ddd7a28da4ac7","03b8389b222af729eae0fb3c33366dcbb1f5a0102ce319bf1d7d5ee987e59fd0","2bf6be7c04db280fdd9b786764f8650c23f9f4d533791cb25a11b25314b76a55","dbb5fc7edd36bfba95cc4dd564e4458276ced30eed18bc05fbda948b3fda8686","c2b556c7cff0dabce2e31cb373ac61c14d8ebc35f1086dff30b39e9ec5357d0d","f958af01131076e8af55d28c4835a51063226ab488ca8738fdee38aeef7d0d33","9f3797b01e3d83d4e4b875699ae984f380ca86aa0a0c9df43ac5bba1cb1f8b7b","752b15ad1b34887adeaa838fc55f5d4ca399026afd266d4ed4db0e3db02eae4e","778331eaea1093451e50be9844bd2b6937c3bb81b0b1ee700624c9774ecfcf2b","0ca0dfc9f657d0822eca9530c7870b22a1d2a5fc48182bdd4d0e6e88e4ad9c35","5c746f034288e6842dd1589b169dcfcc16c5ce5abbd928889ab67aea4fe0b501","92ce6dbbcc135cffd09a58e19fef34bf351391bec92c40d849e4e9997d475769","99e77d092fed72b6b8578d00c7af004f76e98b30ba99f1947535eb4c04a51676","5e379df3d61561c2ed7789b5995b9ba2143bbba21a905e2381e16efe7d1fa424","f07a137bbe2de7a122c37bfea00e761975fb264c49f18003d398d71b3fb35a5f","fcc8beef29f39f09b1d9c9f99c42f9fed605ab1c28d2a630185f732b9ba53763","b5ef52a9f9083724decc5d060f0b34e3a480deed71c32d55ca16c214eb4cc928","5d3d7938b2d7d0a9f851276741327c2ae4c013e7eb420fc3f7caed3b79c8f37f","14df6b81e50a035e9e391558cf30a0420d03e2eb42c7db9c57f44b818e5d5179","f100912a3785eed4a3d29c12f5910b101af9353454de5ddba9b4d43456c56dd1","446439eacf81a163fd7dfc53b28f80deca3d13b250d67639739aa25aa4491090","98034cd285344125f7165a3bb68246d38ab35fabe7f6d6a7c8f80407d31f548d","06b4a23064991251512df4edc12341d5bc69a17b942da18372312d383c90eee7","0f898802705f9a534b537f1be6c57265080e0abd6993d935554c255e6d56cc1a","745efa7b6e27b7216cccede166a822b56acc41b10a8090966c8cf2c96239cb83","75b22c74010ba649de1a1676a4c4b8b5bb4294fecd05089e2094429b16d7840c","5615ccf831db2ffc82145243081ebdb60ea8e1005ee8f975d1c0c1401a9c894e","38682ed3630bb6ecdace80d5a9adc811fc20a419f1940446e306c3a020d083b9","cc182e6e4f691cd6f7bf7cb491247a4c7818f9f1cb2db1d45c65ff906e3f741b","a50599c08934a62f11657bdbe0dc929ab66da1b1f09974408fd9a33ec1bb8060","5a20e7d6c630b91be15e9b837853173829d00273197481dc8d3e94df61105a71","8d478048d71cc16f806d4b71b252ecb67c7444ccf4f4b09b29a312712184f859","e0eda929c6b9b628cdeb0e54cd3582cb97e64f28aab34612fc1431c545899584","9df4662ca3dbc2522bc115833ee04faa1afbb4e249a85ef4a0a09c621346bd08","b25d9065cf1c1f537a140bbc508e953ed2262f77134574c432d206ff36f4bdbf","1b103313097041aa9cd705a682c652f08613cb5cf8663321061c0902f845e81c","68ccec8662818911d8a12b8ed028bc5729fb4f1d34793c4701265ba60bc73cf4","5f85b8b79dc4d36af672c035b2beb71545de63a5d60bccbeee64c260941672ab","b3d48529ae61dc27d0bfbfa2cb3e0dff8189644bd155bdf5df1e8e14669f7043","40fe4b689225816b31fe5794c0fbf3534568819709e40295ead998a2bc1ab237","f65b5e33b9ad545a1eebbd6afe857314725ad42aaf069913e33f928ab3e4990a","fb6f2a87beb7fb1f4c2b762d0c76a9459fc91f557231569b0ee21399e22aa13d","31c858dc85996fac4b7fa944e1016d5c72f514930a72357ab5001097bf6511c7","3de30a871b3340be8b679c52aa12f90dd1c8c60874517be58968fdbcc4d79445","6fd985bd31eaf77542625306fb0404d32bff978990f0a06428e5f0b9a3b58109","980d21b0081cbf81774083b1e3a46f4bbdcd2b68858df0f66d7fad9c82bc34bc","68cc8d6fcc2f270d7108f02f3ebc59480a54615be3e09a47e14527f349e9d53e","3eb11dbf3489064a47a2e1cf9d261b1f100ef0b3b50ffca6c44dd99d6dd81ac1","b17f3bb7d8333479c7e45e5f3d876761b9bca58f97594eca3f6a944fd825e632","3c1f1236cce6d6e0c4e2c1b4371e6f72d7c14842ecd76a98ed0748ee5730c8f3","6d7f58d5ea72d7834946fd7104a734dc7d40661be8b2e1eaced1ddce3268ebaf","4c26222991e6c97d5a8f541d4f2c67585eda9e8b33cf9f52931b098045236e88","277983d414aa99d78655186c3ee1e1c38c302e336aff1d77b47fcdc39d8273fe","47383b45796d525a4039cd22d2840ac55a1ff03a43d027f7f867ba7314a9cf53","6548773b3abbc18de29176c2141f766d4e437e40596ee480447abf83575445ad","6ddd27af0436ce59dd4c1896e2bfdb2bdb2529847d078b83ce67a144dff05491","816264799aef3fd5a09a3b6c25217d5ec26a9dfc7465eac7d6073bcdc7d88f3f","4df0891b133884cd9ed752d31c7d0ec0a09234e9ed5394abffd3c660761598db","b603b62d3dcd31ef757dc7339b4fa8acdbca318b0fb9ac485f9a1351955615f9","e642bd47b75ad6b53cbf0dfd7ddfa0f120bd10193f0c58ec37d87b59bf604aca","be90b24d2ee6f875ce3aaa482e7c41a54278856b03d04212681c4032df62baf9","78f5ff400b3cb37e7b90eef1ff311253ed31c8cb66505e9828fad099bffde021","372c47090e1131305d163469a895ff2938f33fa73aad988df31cd31743f9efb6","71c67dc6987bdbd5599353f90009ff825dd7db0450ef9a0aee5bb0c574d18512","6f12403b5eca6ae7ca8e3efe3eeb9c683b06ce3e3844ccfd04098d83cd7e4957","282c535df88175d64d9df4550d2fd1176fd940c1c6822f1e7584003237f179d3","c3a4752cf103e4c6034d5bd449c8f9d5e7b352d22a5f8f9a41a8efb11646f9c2","11a9e38611ac3c77c74240c58b6bd64a0032128b29354e999650f1de1e034b1c","4ed103ca6fff9cb244f7c4b86d1eb28ce8069c32db720784329946731badb5bb","d738f282842970e058672663311c6875482ee36607c88b98ffb6604fba99cb2a","ec859cd8226aa623e41bbb47c249a55ee16dc1b8647359585244d57d3a5ed0c7","8891c6e959d253a66434ff5dc9ae46058fb3493e84b4ca39f710ef2d350656b1","c4463cf02535444dcbc3e67ecd29f1972490f74e49957d6fd4282a1013796ba6","0cb0a957ff02de0b25fd0f3f37130ca7f22d1e0dea256569c714c1f73c6791f8","2f5075dc512d51786b1ba3b1696565641dfaae3ac854f5f13d61fa12ef81a47e","ca3353cc82b1981f0d25d71d7432d583a6ef882ccdea82d65fbe49af37be51cb","50679a8e27aacf72f8c40bcab15d7ef5e83494089b4726b83eec4554344d5cdc","45351e0d51780b6f4088277a4457b9879506ee2720a887de232df0f1efcb33d8","6ab2a6257ae7bb05559841100c786c845fe465a90be7b904db9096c2fb14696b","e87de5e2e71fe0513d6fbd5951a5f8e35595243bbb88fe00b6b2d9383f62fe59","ebb4f551ea58b96443365f487e5c49396c4811dc51e2fd5c3c45928e93047278","7cd0fabd9e9ae5a8faabc2f70d6d9ddd89c65719a30917eacdae9049c16e8a16","bb665725dcc2e2406c572a63038791a7118802ebd947c0e76b0eb38ccd99926c","0c58b5a414a48f68bfea86556a22f505bac4ce0e67ddd5e40e387a4641ce2b78","13de2d120d9122bbff92dca664ebd180241a856d23e705598eb259621a838f0f","2d072cf71b05c374b0406736ae1f402f4ebac96fab8e9e4d6a2ea4d147b1c26e","1507881fb12592d860e8b731a79cccd5880a9e3a3fdb71c8eeb08987420f7c8d","63e64a301fdbb7fb0b58e81b282608594b700e1af51d509de949e88e711a70e8","d5c19655468e29f60c871b21e73af8ebc653f736e7123ade916f22c4a5f80ce5","50aa290ee8f3ba75c7a3653613ead6594e2e034a7627b249c9a400858cac79f5","9138338d4fff57ba42d57c7316ad1d055b72b90c9e1acbbfa6cfe16db201802a","d5c19655468e29f60c871b21e73af8ebc653f736e7123ade916f22c4a5f80ce5","863416d62eb7dfa12b63b23c112fd0831aa330816c39d64ca88898ebe5fd62a3","9325a5ce0f09132607e27e599b568e3a67f80ea932f6bbb08bdc1bb7727e11a3","6a8649609161e2794b383ba275b0a6cb4a072dde7b954648f83dc6cdf1bfe4a8","6d3101b183ea67ef606b93fe42127f30b2db5ac3b72c34ca9d6d8b00eb85d0f6","f5d7a36ff056cc314b0f61c89a03c4c36a24183b246e61d958e75e86521304cd","f961ddb0abe88c9815a81132cc9984f0294fd920174fccbdde73d156b8a4ab39","6c951235cbf3c324a20c0e2dfdd013d7b226b0c2a72dbd84925682a8d7199237","aba578ce97acb630b406ffb6ed31302dbd8d2ffcfd671194b1d8704825086e05","3a971ea3e36685b96f24fbd53a94ad8dc061711b84e51fde4cf201f7041e618d","77234d8682b67d78748cb61a63407104dc2c8e3196dcf15a454aae26b42f3ee7","c8be9283a381044a392a0687af5d98d3f51cbada2320b1801a82c948b6e39499","d5cdc145bf5ec321674e31804c075ad408a68c86877ce293970e03634d3709f1","85052c71d72b9b017c88179f57a464d66e22619c7acd7d83b117a79cf1608979","9b6c162d20e2ad4abdcff61a24082564ac59e63092220618162aef6e440c9228","b0874729266d9f7fafb9ff1127fcbad2cf7972b5dcc1fdc104be79266a708bc2","9f9e5bae412fa5909fae636d6733aee27a108cc2ed5b13980611016336774d3c","662fe197bba64bd3f17ee118058cd2d0d2dbe33d7c0c865fd6365d90bfc44e1e","030519c351f800551cac2658038804969ca4584d2c0175a710602ac234ca1340","0278a6939ca83cd040b08ff8c5fc7838b6693ddc52f22526bf158e6b10e0246c","c2d6206e5ba4fd3063b01218c2b3b997afc1cfbeb49fcee991fa8595842ce53d","e9d61a89974e5d8231edab199fe1a5f912a344febc74146611c013435f906de3","813e6dc3098dc7b331ed17bb4a3f96cda83748e989a41c469160ef23776af0f4","3c0913724967da385abf69e4f50dc51d5de5ad7c13d8bedd6746063aab7dd6e0","1fdd00179cddca98c8cfa1c403016f6703a8c34858cfeda33769d86da188ff8d","47ca123995420c8579f010179d3d5e61399ce538fc6386f2438193d48c0013b9","599d6ebec95d21df7ee33d8eb8f0b791ffac4a32026f32cf91fdf417153be473","323a75e01c89a50bb8827d1d624e72c20b0d81d4647a30ee6a695dbb4d76f3b5","d1f010c19eb9c8190bd0859fa3b6f4975543b912b8b85e20bbb0b5bfbdf4d2b3","de4ccc96cef3f97fab148640799abb32a24b567a902a8233913f98481e3131bf",{"version":"801934aa449fe6df584bccdcc5d5b9280295cb7ac84918b6014fc5086e6f9ff6","affectsGlobalScope":true},"6af760fb9ea02dc807c5053d8aee86389c4fce72fbb26af7b9568cac6c4710d5","c62c4ba5e910b4523f7e7adf4a55ec45c2bac99d9d8e9b0fe0c2a800a6f641b9","92131434f876fdd6fcbc40bd54a9d7500c66974362b16bd42641f990468587f4","8cf023c0bd57992fdd2ce6a7030a1874f49c8edc62eaffa9bfffcf18d2a2a1a2","8ea8f3040e38fb50d7dc3653f3b8a0dbb5244e82111576f99ce096bdc0fbf94c","48ed788ad126545a6156fcc37cd3bcf17de18a3e3fe6b6ef62cfb8140d1a45a2","63c271a745f628ffd4bd7ad0a63b021c362c9bd6bf8b18441a7162892395a214","a867ba47f71fe3993cef54246893ff8f01411e12e411d8cf1bd038a448b36404","6a8096993458a3d71229031aa7415974eb5b47b320213e29660adfb519d6a3f4","cb7996a1af5b1d276483cd0c9b9de6540eff021abc90a720511ff4464519a2ff","9df6ec68878d65bc690ea3a33ce3ef5aa8254c36bc5f8346c0c2fd1f3b88a35c","a4fad04c4acc8a4b195cbbccef4c55019104753d547d5c94441643ccc89108a0","0244c23ea642361f7c192c1f0cfff9c12cfa5f51f9b155edd5c0a89fef308d34","ac5da520487547013c3abae0933d6366f51db6df31d1993ddb931ce04b083269","3c69a83bde847af6fc3a53e1bb6b13cd06d38a27a142814b8dacc374f3b93284","5b46f7113f54565e7ffc83f2b474f557a1f54c7e5946769d5be220454656be73","fb58035d39c5759283cb73cfb3548aefe370aa3ad4e81fdb4e46f0979eb7669f","1311c325948b2d5576cebc70b1bf968d3446b4630802bef54120daf04ce1f625","d0b3609e8e7afed0fd0570152255458407e67249b94f6603afdfd68599423f21","17f4c5a1d6eaa87ea27eadcdff9085af3190533d98f799dda79a3af6f9a630ea","3e6f734ddf40e2e99ff7fff9568b7d9720663af9a0632c26a352c8d3270a3f0e","ec13f78303abcf550c5569dfae1446b8ceb89050f68ce04491481e72e8122ae2","a3fc57dbaa7f1efb010399ad4ef4fd9b462aa4e93bf74a9a34b099b97ffcc9cb","ffddd7ec6a450b0cb6f2f73f80de1df963ead312d7c81a8440268f34146ecb87","5d6a36ca0087fd6876df654d1b4192f0e402adfde994ad47e5c065da33692f9c","eb157a09c5f543d98644e2a99a785f9e0e91f054f9fecbf1c3e15831ff5d63a7","edd5530e2b1ccdf65093296e40a8634fcb11ecda3c164c31383a8c34cb04bc9d","9dfaf96d090fe8d96143465d85b4837661ae535143eea9ef99cd20df2e66338e","209d45c27e03c1417c42985252de6c25a2ec23abdc199d88e6139c88b93abd11","0ee5cdba58cfde3012bb9ff2e9edcc4e35a651373a2aa2c83ff9eb7df635419a","540f4dca27ea5a232828b6d91e1b2fce2720bdabaa4c1f3fbf59b672cc58bd8a","ba086b99d545ec6c9ff356989f076b5652ea1b09bcc65b87dfc43a5195a2efcc","c85d9776b36166b928ab1488d9224ebf970d41b0a35f09a3ee0b9bee3e698061","683196f606c5dab1c8c4a24a66d26e00f16f2d4b2a5abe25ebedd37d2954f930","9c3a1b01cba1238fb723ce06b6c163ef6c53be755394406782564d5c42c636b2","6e795e6270d39e918c7a0e62ac73793cda06fcf4b3692ee46583e15f5bf57ab8","0e821ef1eb67fa6144ea4de4277d913f5b1982d7407afd5f93754a8239d41554","5c09195ef359ffa9c6bbdb4fefb101d87ede4b9e9c28213faf5b45d102e4c609","80b4d93a4dcc90a12f6f4bb7c6851a8182ae29e556716d0d80b5c012a5ef554a","2556ef9d1820e0b6bbca6dd65a50ea64f525c4d8247ab50dff44c3f0d14a5643","cbd1c836db190d6e3add07165afc228f04e1f6170e1fe3aa5e6fc24a7e9573a3","9b13881feb958237232586d888a10a39d47cdffe3ee34688ed41888fa7baad94","122fe82cf5af80f0b26832b258b537b7dfe3ec28449c301b259ab10204b50d45","c467dada8fea6d60dff8a8be2675f737cacc76e14e50b72daa0f0710376df84b","9cb80bba611c2dd155a446ce424fe4bb1df2129751bc9416b7e42c055d1ddbff","44f41abb29bf3f4c52270d8119a96c935131e42a9186da15216a76b35e793b4e","043783bebe87efb440183c9ebc8c4fdc1bb92060a5a0f7ce847e30dee7013ac3","e3dc0a97a59dea936b4fb7b1f6f4117b4aac9c86d0cd08b69bab2d0532a8a5e3","5d897601f8a4fe913057019d8211b99b06e3138f625a0cfb601d074f4278271d","cfde5d194dd858ad68f910defaed5b0d28730f8bf38359a9265a93ab29bc7bef","16b21bbe6ad41019c071677877b8fc5dbc8d39a8b0406f020261c5f6f3894be3","f20aae41b169cddcbf3fde8ac380443182c8d7225194e788c404d9e11e6dc75d","87fd9a98cb1e689320ab89adc65e85d140a61260b4f66d12c777f4bd7cae2060","c48566cb13403fca44192b4528e3f2ac993869d39526bd42cd2f2167c0285add","efae20e0c581240c7522e04829da4f0453ca263068596554d4b0e27878c7dfac","3af68ef927788cda7daab34be513fa4508229fdc6e5130d564a0a1ccb3fefafe","bbbd2cbb15a37d5f4dd54ad8c7c537d3df8352117523030fcec7dcbe62a05a58","b50d24ebc117f8805332e7e260e9587f572bb7b2ff0ca1ff6cfafb38015781f3","5cc8b8e18fe7fefab4b3c53a39467b5a0deb4200abae7f063ff0624b9e856c51","8e990781eb0107c25429b1274a31a4f3866a9a46290cce40f354b2a6e71c6c21","8616706e4bd72987bd86c1b4afafa90fa2d4ef2f71708de03a823ab4e9b48e60","b9ce4613536386a98897f1e3d8f61a851ce6cb34dc3c9db4f2ef5f55f007e9e1","77fe56751d7615743937268c72d797fba28309f13ec9079c018b232040fca86a","31b5f53e3d57470830e87f9e03c02d4569ac81d4a758fdda75092f9a3f58beba","d765fbab22fd7003a65ed670100362ec1c90d55a772e6773a774135594e7ea41","1bf86149ef215f258d479695aa35ac89a3d34a6356a6df04e1b5db869289e563","58f4da9e99a4bdbd2f54eeb9303d5b5634b25423d729d44abb3fc55c925495b3","f75cd30f162c2af5e5aca39c01c1a521bfa034fae523793de872815a3468bc08","0cf1123db73dabd86466a462375a6addae52f58d23030c6033f8aadc23539a36","e29cef4158591ed213b1c2cba8988237b1ff369f7a6ecd8cb8ac0302bad1fba8","5307876e4d0021ea01235eb2f7c24671f3d8b37590f4b446cd132a4e1dc9a335","92550acd737790dc60c4c130e6aac78656dd48a8334a4882f40e7f86bdf7a590","3df821880914f8bb3c8107b1107be75c8ddbe2120a2cefabbaf9b65936b5f4dd","20626e4260b7d621745b2e78e883d9de7cc94ec346ef13344dd96eb479813870","078b7043bea0968860374bf4671ed74dd9f6be4e28ab659517d81f74be463c51","68b139ebb9a7f3ee4ded6286d74f978a47968727665120f3bfc560476ce33c4d","56d02c29b2fd39b1b1a1265df291f3f98e6ec3e6119aff9f4cfa44fe888efaa7","2d01884891da6495cb4a2f060e4898209a507e711464c4c1480df85264e863ed","620eb3b3aafe33839ee0f50e2cb237450f066fd88c8367cd15d75d02f7c9146f","6a5a3a7ae4e448668f8986632d2b6adfeebfdc06b0f9256f35c10ec148fa01f0","080b1aa93227952b4dd74b9d2c6e4f6002eb8403533749116a1c53bb9961c02d","874087eec1d457f6e3baf5ac46c42ea200e55040b394fac667aa3a64c49f5f6c","6e8a5b04a18abb192abc89d7219b9c6f633cb3136777ec808673a65f111ca749","6db505486e882a6688c5525cb65f6f06d3c5f16f03f329fbdec01dd379c97f96","d74d2a92b54f95e47d2b76bd5ee516aab7ae93afb79cd34c6681dd29eb09e72a","747e6326a724bc54f799a466a5b5c4978a601a04a063a5bdabe150af2f25b9e2","b57e22e53b56cca7a57bfcfb234aa6a66f9b9e4c07159d7388f94f17a3eaee2c","e47709ec4d1618ef429648cd8ef967aef2005526b34fcbfac33037add347dc71","b81abb3e47fbbb3af41fa75bada89bbcfa4b0feed9a0d6d4b19ed1ce1033b53c","15b330546e9784461058e5fd6e2346bf272140fa6f0cda34e193ae501d8b17b1","4d8ce72fd080bf9a46bdcc274bcbacccedd66d84e203966b197ac25a96932183","73327e6ae34e3f6591877fb75b451cf620cbbd76ee2b678213a9f793633cd0d3","3f1ba2f69944fa346789db7f60d53c9bec00032de0d797967978dea42e77b941","3f5df31539fee4816b97d4e45b4344fbdaf3ca59f6df941f8d780ee441e92cc1","50aaf44eb4d0e086af13729b3471a0a7dce95ea35ebd21c762ba26e203134b2e","3857c1773b8503c3ca45b7bc09ac89c3930c85ce93021054503f73d5d9101b5c","72702bd07fd6fb3ef64aadbcb909103aadfe71ee76e9fdeb11e0c92693cff6cb","f0dd6f7c9783637655478db7d7caf6becd41a79d54482aa59578ce88ab38e9bf",{"version":"cd756ccdabf433dd02b84d755383e489f14b3c1aede0477783aa04830fd5d695","affectsGlobalScope":true},"a4c88dbecdf8ee0c79f5b7c2bf31cd77e593f5d78384e2b674f67d754a549a9e","9cbdff04326da794ba008c0fc977ab062d1fe3fa2e9759654c72ffbe54b64a7c","aa60f8d20d36116fe05edaab24adee3c275209f71b65e272692cf99daf9489e1","150855f967a6490161d5aeed4cc4adf31fcb8f5dbe54b75799c12b8687fc9cc2","cf08b7139adc21b94204e3d4b3daf9946e3462a9e3fdc3e94c87e767e7936e20","47ddb601df40bfa01cebdd06ee8b87d0b72aa1259a4ceba3ad3b5cf68130112a","6b6392704ddb3f50e647dbbb716782bdd0cf8ea9cc134aae256a26223e632b47","afc3ad2a50f7f4de908e26fcf467e09ab8528c0e90f91e602b4865d953839228","df90b0c6b1d81851364c4d97fa23b91a993482bcf4a7bed7c7a24aa41632d494","03c0bc80f67c6f75b02341fbeb9f6ee92c66b90597729377f478885e6ad15a88","11ee9ab699b4619d217c640d917ca198f58066a86bd58c2917197d62aa6601e0","cf9d589d9e73bf32c8e7a6cae6b4a1cf9bef39e5594072533fdce985581a6ddc","959544feb1ca2df29eec6c500f27ea10f4885df245ebd8418fb4b87914614383","6548ab4b57eb9d092471a04513091673345f2fd95d5b876f600402ea8d603ee0","2793e8c6a023d26f78d6777a6d7f20fae3a9a8169863d46d8d54c73071851232","d0f11e830aa1350a31d9c00a0197243e9711e4882947aef53a96c629f405cb10","6610b9f45f1f71d2b1fb67df49cbcabe3f9e668a1ccb7d8328a51407b259ffb3","abbcc437e0792ab2fe08797ceca1ec85a95ec413c51612313b18ab8e75f690f6","e29d76ef1183ac0edf94b4712b6e51730c447c7e773e75ceb44a720b0c9a9fd9","4ee6dc3424998eede9a2a9b114acaaf7969cdda67baf82ba2c9cf88a8eec0ab1","26958d6f77e6db2425ca65df0fbfaba439396ef7f4457f5643fc32e4b62568a6","5d697a4b315cc5bb3042ae869abffd10c3b0d7b182cda0e4c45d8819937e5796","89b040dec8fcfc1de98827e1f4d4977e6ff5d3302c6790e9f10b54b916e1c742","6ee58aa536dabb19b09bc036f1abe83feb51e13d63b23d30b2d0631a2de99b8f","8aceb205dcc6f814ad99635baf1e40b6e01d06d3fe27b72fd766c6d0b8c0c600","299567f84bfedd1468dca2755a829cb19e607a6811673788807dc8921e211bc9","795d9fb85aad92221504db74dd179b506bd189bba0c104426f7e7bb8a66ffee5","1311bc194e0a69fe61031e852c1c0b439e2a2a3d1d5e2d8ff795499b9f283459","4b7ce19369d7e7fae76720c2c6c7f671bf3fa0f7093edb864f1ac358ca7c456c","c972ef44deca1fa8fab465915ffa00f82e126aacf3dfc8979c03b1b066ce5bb6","30285a1011c6d6b52f3ba3abb0a984be8148c05cdefb8eb6eb562335a3991f35","e0de9f50e80fed1cc161b50e8e68dc056e38df75a4ef667a06b1922e372de169","6a8b31be08b212d1fc96de0ddd1ea49f32382ba712fea24c70bb56447f643f82","19ac6d624e4c18de4584db4bbdbc55387dbe3d19b3c134e50346bdf165658a17","54e3798c2801e8f3bc7a825d3d26c6a80ce763e19e6cb0b714594c430ef72332","70b8333214aadaccda8d38435911d3e3a686e503837dfda6b8c3f8c83e05729b","f3815045e126ec1b9d224782805a915ae01876a1c7d1eb9b3e320ffadbd63535","d07557f21b2ad690bfe37864aa28090bd7d01c7152b77938d92d97c8419c7144","b843ea5227a9873512aa1226b546a7e52ea5e922b89461f8b202a2f2a3f0b013","64b4d440f905da272e0568224ef8d62c5cd730755c6d453043f2e606e060ec5a","d6b58d955981bc1742501b792f1ab9f4cba0c4611f28dcf1c99376c1c33c9f9c","f0b9f6d5db82c3d1679f71b187c4451dbc2875ba734ce416a4804ad47390970a","a5c38939c3e22954a7166d80ab931ac6757283737b000f1e6dc924c6f4402b88","31a863da9da2a3edec16665695bdbc3134e853195f82dafec58e98c8e1bb3119","b382a659f417df3606f2fbd2d39a02f0aa81d846cd361e79656e135a7896b779","af21e37363b40696508be1e0f1189664d17bc215ac5e64c05f7eb086a6f2ea72","df470b1c65fc51db9486ced8ff89d19c5fa2cfc5c6b3eb32d6cbab354499801e",{"version":"a6703b8328a763c5148eddf07c5801c4b67de507bc25459532d0c0c6622d11c2","signature":"68260f4ebe8f11c39b1d43d6ea75d76da4e81e2965414db9b3bd5ef2a21cdf0e"},{"version":"47b156c2b6951459da66a7dad60da07fee0f3f5e15096362e01fb2c67b99f641","signature":"acf1e448964971d594529ec272a231c39326bafde595da38cd0797266d3b446f"},"40d81f5f052d5954b51f4f5ec258a2231cdba79232e823ba93dc6dce2af4a7ff","4489c6a9fde8934733aa7df6f7911461ee6e9e4ad092736bd416f6b2cc20b2c6","2c8e55457aaf4902941dfdba4061935922e8ee6e120539c9801cd7b400fae050","8041cfce439ff29d339742389de04c136e3029d6b1817f07b2d7fcbfb7534990","670a76db379b27c8ff42f1ba927828a22862e2ab0b0908e38b671f0e912cc5ed","9d38964b57191567a14b396422c87488cecd48f405c642daa734159875ee81d9","069bebfee29864e3955378107e243508b163e77ab10de6a5ee03ae06939f0bb9","8c95f96ccd4be0674944077aec1e4f2cccd515ca06d4327562dd017250e7d3fc",{"version":"64d4b35c5456adf258d2cf56c341e203a073253f229ef3208fc0d5020253b241","affectsGlobalScope":true},"ee7d8894904b465b072be0d2e4b45cf6b887cdba16a467645c4e200982ece7ea","f3d8c757e148ad968f0d98697987db363070abada5f503da3c06aefd9d4248c1","bc3cba7b0af2d52e7425299aee518db479d44004eff6fbbd206d1ee7e5ec3fb5","afe73051ff6a03a9565cbd8ebb0e956ee3df5e913ad5c1ded64218aabfa3dcb5","035a5df183489c2e22f3cf59fc1ed2b043d27f357eecc0eb8d8e840059d44245","a4809f4d92317535e6b22b01019437030077a76fec1d93b9881c9ed4738fcc54","5f53fa0bd22096d2a78533f94e02c899143b8f0f9891a46965294ee8b91a9434","0d14fa22c41fdc7277e6f71473b20ebc07f40f00e38875142335d5b63cdfc9d2","d8aab31ba8e618cc3eea10b0945de81cb93b7e8150a013a482332263b9305322","462bccdf75fcafc1ae8c30400c9425e1a4681db5d605d1a0edb4f990a54d8094","5923d8facbac6ecf7c84739a5c701a57af94a6f6648d6229a6c768cf28f0f8cb","7adecb2c3238794c378d336a8182d4c3dd2c4fa6fa1785e2797a3db550edea62","dc12dc0e5aa06f4e1a7692149b78f89116af823b9e1f1e4eae140cd3e0e674e6","1bfc6565b90c8771615cd8cfcf9b36efc0275e5e83ac7d9181307e96eb495161","8a8a96898906f065f296665e411f51010b51372fa260d5373bf9f64356703190","7f82ef88bdb67d9a850dd1c7cd2d690f33e0f0acd208e3c9eba086f3670d4f73",{"version":"ccfd8774cd9b929f63ff7dcf657977eb0652e3547f1fcac1b3a1dc5db22d4d58","affectsGlobalScope":true},"d92dc90fecd2552db74d8dc3c6fb4db9145b2aa0efe2c127236ba035969068d4","96d14f21b7652903852eef49379d04dbda28c16ed36468f8c9fa08f7c14c9538","b8442e9db28157344d1bc5d8a5a256f1692de213f0c0ddeb84359834015a008c","458111fc89d11d2151277c822dfdc1a28fa5b6b2493cf942e37d4cd0a6ee5f22","da2b6356b84a40111aaecb18304ea4e4fcb43d70efb1c13ca7d7a906445ee0d3","187119ff4f9553676a884e296089e131e8cc01691c546273b1d0089c3533ce42","febf0b2de54781102b00f61653b21377390a048fbf5262718c91860d11ff34a6","6f294731b495c65ecf46a5694f0082954b961cf05463bea823f8014098eaffa0","0aaef8cded245bf5036a7a40b65622dd6c4da71f7a35343112edbe112b348a1e","00baffbe8a2f2e4875367479489b5d43b5fc1429ecb4a4cc98cfc3009095f52a","68a0d0c508e1b6d8d23a519a8a0a3303dc5baa4849ca049f21e5bad41945e3fc","3c92b6dfd43cc1c2485d9eba5ff0b74a19bb8725b692773ef1d66dac48cda4bd","b03afe4bec768ae333582915146f48b161e567a81b5ebc31c4d78af089770ac9","df996e25faa505f85aeb294d15ebe61b399cf1d1e49959cdfaf2cc0815c203f9","4f6a12044ee6f458db11964153830abbc499e73d065c51c329ec97407f4b13dd","8841e2aa774b89bd23302dede20663306dc1b9902431ac64b24be8b8d0e3f649","916be7d770b0ae0406be9486ac12eb9825f21514961dd050594c4b250617d5a8","254d9fb8c872d73d34594be8a200fd7311dbfa10a4116bfc465fba408052f2b3","d88a5e779faf033be3d52142a04fbe1cb96009868e3bbdd296b2bc6c59e06c0e","2ccea88888048bbfcacbc9531a5596ea48a3e7dcd0a25f531a81bb717903ba4f","d8f7109e14f20eb735225a62fd3f8366da1a8349e90331cdad57f4b04caf6c5a","cf3d384d082b933d987c4e2fe7bfb8710adfd9dc8155190056ed6695a25a559e","9871b7ee672bc16c78833bdab3052615834b08375cb144e4d2cba74473f4a589","c863198dae89420f3c552b5a03da6ed6d0acfa3807a64772b895db624b0de707","8b03a5e327d7db67112ebbc93b4f744133eda2c1743dbb0a990c61a8007823ef","86c73f2ee1752bac8eeeece234fd05dfcf0637a4fbd8032e4f5f43102faa8eec","42fad1f540271e35ca37cecda12c4ce2eef27f0f5cf0f8dd761d723c744d3159","ff3743a5de32bee10906aff63d1de726f6a7fd6ee2da4b8229054dfa69de2c34","83acd370f7f84f203e71ebba33ba61b7f1291ca027d7f9a662c6307d74e4ac22","1445cec898f90bdd18b2949b9590b3c012f5b7e1804e6e329fb0fe053946d5ec","0e5318ec2275d8da858b541920d9306650ae6ac8012f0e872fe66eb50321a669","cf530297c3fb3a92ec9591dd4fa229d58b5981e45fe6702a0bd2bea53a5e59be","c1f6f7d08d42148ddfe164d36d7aba91f467dbcb3caa715966ff95f55048b3a4","f4e9bf9103191ef3b3612d3ec0044ca4044ca5be27711fe648ada06fad4bcc85","0c1ee27b8f6a00097c2d6d91a21ee4d096ab52c1e28350f6362542b55380059a","7677d5b0db9e020d3017720f853ba18f415219fb3a9597343b1b1012cfd699f7","bc1c6bc119c1784b1a2be6d9c47addec0d83ef0d52c8fbe1f14a51b4dfffc675","52cf2ce99c2a23de70225e252e9822a22b4e0adb82643ab0b710858810e00bf1","770625067bb27a20b9826255a8d47b6b5b0a2d3dfcbd21f89904c731f671ba77","d1ed6765f4d7906a05968fb5cd6d1db8afa14dbe512a4884e8ea5c0f5e142c80","799c0f1b07c092626cf1efd71d459997635911bb5f7fc1196efe449bba87e965","2a184e4462b9914a30b1b5c41cf80c6d3428f17b20d3afb711fff3f0644001fd","9eabde32a3aa5d80de34af2c2206cdc3ee094c6504a8d0c2d6d20c7c179503cc","397c8051b6cfcb48aa22656f0faca2553c5f56187262135162ee79d2b2f6c966","a8ead142e0c87dcd5dc130eba1f8eeed506b08952d905c47621dc2f583b1bff9","a02f10ea5f73130efca046429254a4e3c06b5475baecc8f7b99a0014731be8b3","c2576a4083232b0e2d9bd06875dd43d371dee2e090325a9eac0133fd5650c1cb","4c9a0564bb317349de6a24eb4efea8bb79898fa72ad63a1809165f5bd42970dd","f40ac11d8859092d20f953aae14ba967282c3bb056431a37fced1866ec7a2681","cc11e9e79d4746cc59e0e17473a59d6f104692fd0eeea1bdb2e206eabed83b03","b444a410d34fb5e98aa5ee2b381362044f4884652e8bc8a11c8fe14bbd85518e","c35808c1f5e16d2c571aa65067e3cb95afeff843b259ecfa2fc107a9519b5392","14d5dc055143e941c8743c6a21fa459f961cbc3deedf1bfe47b11587ca4b3ef5","a3ad4e1fc542751005267d50a6298e6765928c0c3a8dce1572f2ba6ca518661c","f237e7c97a3a89f4591afd49ecb3bd8d14f51a1c4adc8fcae3430febedff5eb6","3ffdfbec93b7aed71082af62b8c3e0cc71261cc68d796665faa1e91604fbae8f","662201f943ed45b1ad600d03a90dffe20841e725203ced8b708c91fcd7f9379a","c9ef74c64ed051ea5b958621e7fb853fe3b56e8787c1587aefc6ea988b3c7e79","2462ccfac5f3375794b861abaa81da380f1bbd9401de59ffa43119a0b644253d","34baf65cfee92f110d6653322e2120c2d368ee64b3c7981dff08ed105c4f19b0","7d8ddf0f021c53099e34ee831a06c394d50371816caa98684812f089b4c6b3d4","7d2a0ba1297be385a89b5515b88cd31b4a1eeef5236f710166dc1b36b1741e1b","9d92b037978bb9525bc4b673ebddd443277542e010c0aef019c03a170ccdaa73","ab82804a14454734010dcdcd43f564ff7b0389bee4c5692eec76ff5b30d4cf66","fab58e600970e66547644a44bc9918e3223aa2cbd9e8763cec004b2cfb48827e","bae8d023ef6b23df7da26f51cea44321f95817c190342a36882e93b80d07a960","ae271d475b632ce7b03fea6d9cf6da72439e57a109672671cbc79f54e1386938"],"options":{"composite":true,"declaration":true,"declarationMap":true,"emitDeclarationOnly":true,"esModuleInterop":true,"inlineSources":true,"module":1,"outDir":"./types","rootDir":"../src","sourceMap":true,"strict":true,"target":7},"fileIdsList":[[434],[72,108,109,110,125],[109,110,126,127],[108,109],[108,125,128,131],[108,128,131,132],[129,130,131,133,134],[108,131],[108,125,128,129,130,133],[108,116],[108],[72,108],[60,108],[112,113,114,115,116,117,118,119,120,121,122,123,124],[108,114,115],[108,114,116],[108,135,172,173],[172],[173,174],[108,167],[167,168,169,170,171],[108,139,146,147],[108,139,146,147,167],[108,139,146,147,151],[108,139,146,147,148,150,151],[108,139,146,147,149],[108,139,146,147,152,153,155,156],[145,167],[137,146,147,152,153],[139,145,146],[108,139,146,147,152],[108,139,146,147,150],[108,139,146,147,163],[108,139,146,147,164],[111,136,139,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166],[137],[137,138],[72,108,172,175,184],[175,185],[185,186],[242,243,244,245],[108,125],[247],[135,219,246],[300,309,310,313],[300,309,312],[300,309,311,313],[301,304,305,307],[301,302,303],[304,305,307,308],[301,302,306],[312,318],[300,309,312,318],[309,312,318],[309,312,314,315,316,317],[300,309,310,311,312,313,318],[300,309],[301],[196,219,252],[219,266,268],[219,268,269,290],[196,219,249,251,252,265],[196,219],[250],[253],[196,252],[254,255,264],[263],[251,252,257,263,265,266,267,269,291,292,402],[196,219,403],[219,257,265],[258],[256,259,260,261,262],[196,219,250,252,255],[219,267,401],[190,196],[179],[178],[108,177,179],[179,180,181,182,183],[108,177],[178,219,281,282],[283],[108,177,219,283,285],[108,177,178,219,281,283],[285],[282,283,284,285,286,287,288,289],[108,177,284,287],[282,287],[219,281],[219,270],[270,271],[270,271,272,273],[219],[108,219,293],[219,398],[367],[219,293,367,395,398,399,400],[108,219,274,293],[294,295,296,297,396,397],[190,196,395],[190,196,296],[328],[328,340],[328,329,342,344],[328,340,343],[328,334],[328,333,335],[333,334,335,336],[338,339],[329,330,331,332,337,340,341,342,343,344,345,346],[328,347,348,349,350],[328,347,398],[382],[395],[385,386,387,388,389,390,391,392,393],[219,320],[367,391,398],[320,395,398],[196],[320,321,368,371,381,382,383,394],[196,351,367],[395,398],[319,321],[321],[398],[368],[219,371],[298,299,322,323,324,325,326,327,369,370,372,373,374,375,376,377,378,379,380],[219,373],[298,299,322,323,324,325,326,327,369,370,372,373,374,375,376,377,378,379,398],[219,319,320],[290,381],[219,321],[365],[196,352],[353,354,355,356,357,358,359,360,361,362,363,364],[352,365,366],[199],[196,199],[197,198,199,200,201,202,203,204,205,206,207,208,211,212,213,214,215,216,217,218],[190,196,197],[135,199,205,207],[210],[199,200],[196,214],[108,142],[140,141,144],[140,143],[108,140],[410,411],[434,435,436,437,438],[434,436],[209],[441,442,443],[73,108],[446],[447],[458],[452,457],[461,463,464,465,466,467,468,469,470,471,472,473],[461,462,464,465,466,467,468,469,470,471,472,473],[462,463,464,465,466,467,468,469,470,471,472,473],[461,462,463,465,466,467,468,469,470,471,472,473],[461,462,463,464,466,467,468,469,470,471,472,473],[461,462,463,464,465,467,468,469,470,471,472,473],[461,462,463,464,465,466,468,469,470,471,472,473],[461,462,463,464,465,466,467,469,470,471,472,473],[461,462,463,464,465,466,467,468,470,471,472,473],[461,462,463,464,465,466,467,468,469,471,472,473],[461,462,463,464,465,466,467,468,469,470,472,473],[461,462,463,464,465,466,467,468,469,470,471,473],[461,462,463,464,465,466,467,468,469,470,471,472],[56],[59],[60,65,92],[61,72,73,80,89,100],[61,62,72,80],[63,101],[64,65,73,81],[65,89,97],[66,68,72,80],[67],[68,69],[72],[71,72],[59,72],[72,73,74,89,100],[72,73,74,89],[72,75,80,89,100],[72,73,75,76,80,89,97,100],[75,77,89,97,100],[56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107],[72,78],[79,100,105],[68,72,80,89],[81],[82],[59,83],[84,99,105],[85],[86],[72,87],[87,88,101,103],[60,72,89,90,91],[60,89,91],[89,90],[92],[93],[72,95,96],[95,96],[65,80,89,97],[98],[80,99],[60,75,86,100],[65,101],[89,102],[103],[104],[60,65,72,74,83,89,100,103,105],[89,106],[108,176],[480,519],[480,504,519],[519],[480],[480,505,519],[480,481,482,483,484,485,486,487,488,489,490,491,492,493,494,495,496,497,498,499,500,501,502,503,504,505,506,507,508,509,510,511,512,513,514,515,516,517,518],[505,519],[520],[524],[420],[422],[420,421,422,423,424,425,426],[420,422],[108,430],[108,428,429],[430],[231],[231,232,233,234,235],[220,221,222,223,224,225,226,227,228,229,230],[450,453],[450,453,454,455],[452],[449,456],[451],[189,191,192,193,194,195],[189,190],[191],[190,191],[189,191],[219,236,237,238],[237],[238],[188,237,238,239],[405],[405,406,409,413],[412],[219,407,408],[178,219,274],[219,275],[219,275,278],[275,276,277,278,279,280],[48,49,125,135,187,219,236,240,241,246,403,419,427,430,431],[432],[72,108,219,240],[404],[404,414],[404,415,416,417,418],[135,187,219,236,240,241,403,419]],"referencedMap":[[436,1],[126,2],[128,3],[110,4],[132,5],[133,6],[129,6],[135,7],[130,6],[134,8],[131,9],[117,10],[114,11],[121,12],[115,10],[112,13],[125,14],[119,11],[116,15],[118,16],[174,17],[173,18],[175,19],[170,20],[168,20],[169,20],[172,21],[154,22],[159,23],[148,22],[153,24],[152,25],[150,26],[157,27],[158,22],[160,28],[155,29],[147,30],[161,31],[163,32],[164,33],[165,34],[167,35],[138,36],[139,37],[185,38],[186,39],[187,40],[246,41],[242,42],[243,11],[245,42],[248,43],[247,44],[311,45],[313,46],[312,47],[308,48],[304,49],[305,49],[309,50],[307,51],[314,52],[315,53],[316,54],[318,55],[317,52],[319,56],[310,57],[303,58],[306,49],[268,59],[269,60],[291,61],[266,62],[249,63],[251,64],[250,63],[254,65],[253,66],[265,67],[255,63],[264,68],[403,69],[256,70],[258,71],[259,72],[260,63],[263,73],[262,74],[292,60],[402,75],[252,76],[181,77],[182,77],[179,78],[180,79],[184,80],[183,81],[283,82],[284,83],[287,84],[285,85],[286,86],[290,87],[288,88],[289,89],[282,90],[271,91],[272,92],[274,93],[270,94],[178,12],[399,95],[293,96],[400,97],[401,98],[294,99],[295,94],[296,94],[398,100],[396,101],[297,102],[328,94],[329,103],[330,103],[331,103],[332,103],[341,103],[342,103],[343,104],[345,105],[346,103],[344,106],[333,103],[335,107],[336,108],[334,103],[337,109],[338,103],[339,103],[340,110],[347,111],[351,112],[349,103],[348,103],[350,113],[383,114],[385,94],[386,115],[394,116],[387,94],[388,94],[389,117],[390,94],[392,118],[391,119],[393,120],[395,121],[368,122],[298,115],[299,123],[322,124],[323,125],[324,124],[326,94],[327,126],[369,127],[372,128],[381,129],[374,130],[373,94],[375,94],[376,96],[380,131],[377,126],[378,128],[379,115],[321,132],[382,133],[371,134],[366,135],[353,136],[362,136],[354,136],[355,136],[364,136],[356,136],[357,136],[365,137],[363,136],[358,136],[361,136],[359,136],[360,136],[367,138],[352,120],[197,120],[198,120],[200,139],[201,120],[202,120],[203,140],[199,120],[219,141],[207,142],[208,143],[211,144],[217,145],[218,146],[143,147],[142,11],[145,148],[140,11],[144,149],[141,150],[412,151],[439,152],[435,1],[437,153],[438,1],[408,11],[210,154],[444,155],[445,156],[447,157],[448,158],[459,159],[458,160],[462,161],[463,162],[461,163],[464,164],[465,165],[466,166],[467,167],[468,168],[469,169],[470,170],[471,171],[472,172],[473,173],[56,174],[57,174],[59,175],[60,176],[61,177],[62,178],[63,179],[64,180],[65,181],[66,182],[67,183],[68,184],[69,184],[70,185],[71,186],[72,187],[73,188],[74,189],[75,190],[76,191],[77,192],[108,193],[78,194],[79,195],[80,196],[81,197],[82,198],[83,199],[84,200],[85,201],[86,202],[87,203],[88,204],[89,205],[91,206],[90,207],[92,208],[93,209],[95,210],[96,211],[97,212],[98,213],[99,214],[100,215],[101,216],[102,217],[103,218],[104,219],[105,220],[106,221],[476,11],[177,222],[479,11],[504,223],[505,224],[480,225],[483,225],[502,223],[503,223],[493,223],[492,226],[490,223],[485,223],[498,223],[496,223],[500,223],[484,223],[497,223],[501,223],[486,223],[487,223],[499,223],[481,223],[488,223],[489,223],[491,223],[495,223],[506,227],[494,223],[482,223],[519,228],[513,227],[515,229],[514,227],[507,227],[508,227],[510,227],[512,227],[516,229],[517,229],[509,229],[511,229],[521,230],[525,231],[421,232],[423,233],[427,234],[425,235],[424,235],[428,236],[430,237],[429,238],[227,239],[229,239],[228,239],[226,239],[236,240],[231,241],[222,239],[223,239],[224,239],[225,239],[454,242],[456,243],[455,242],[453,244],[457,245],[452,246],[196,247],[191,248],[192,249],[193,249],[194,250],[195,250],[190,251],[239,252],[238,253],[237,254],[240,255],[406,256],[414,257],[413,258],[409,259],[275,260],[276,261],[277,261],[279,262],[281,263],[280,261],[432,264],[433,265],[404,266],[418,267],[417,267],[415,268],[416,267],[419,269]],"exportedModulesMap":[[436,1],[126,2],[128,3],[110,4],[132,5],[133,6],[129,6],[135,7],[130,6],[134,8],[131,9],[117,10],[114,11],[121,12],[115,10],[112,13],[125,14],[119,11],[116,15],[118,16],[174,17],[173,18],[175,19],[170,20],[168,20],[169,20],[172,21],[154,22],[159,23],[148,22],[153,24],[152,25],[150,26],[157,27],[158,22],[160,28],[155,29],[147,30],[161,31],[163,32],[164,33],[165,34],[167,35],[138,36],[139,37],[185,38],[186,39],[187,40],[246,41],[242,42],[243,11],[245,42],[248,43],[247,44],[311,45],[313,46],[312,47],[308,48],[304,49],[305,49],[309,50],[307,51],[314,52],[315,53],[316,54],[318,55],[317,52],[319,56],[310,57],[303,58],[306,49],[268,59],[269,60],[291,61],[266,62],[249,63],[251,64],[250,63],[254,65],[253,66],[265,67],[255,63],[264,68],[403,69],[256,70],[258,71],[259,72],[260,63],[263,73],[262,74],[292,60],[402,75],[252,76],[181,77],[182,77],[179,78],[180,79],[184,80],[183,81],[283,82],[284,83],[287,84],[285,85],[286,86],[290,87],[288,88],[289,89],[282,90],[271,91],[272,92],[274,93],[270,94],[178,12],[399,95],[293,96],[400,97],[401,98],[294,99],[295,94],[296,94],[398,100],[396,101],[297,102],[328,94],[329,103],[330,103],[331,103],[332,103],[341,103],[342,103],[343,104],[345,105],[346,103],[344,106],[333,103],[335,107],[336,108],[334,103],[337,109],[338,103],[339,103],[340,110],[347,111],[351,112],[349,103],[348,103],[350,113],[383,114],[385,94],[386,115],[394,116],[387,94],[388,94],[389,117],[390,94],[392,118],[391,119],[393,120],[395,121],[368,122],[298,115],[299,123],[322,124],[323,125],[324,124],[326,94],[327,126],[369,127],[372,128],[381,129],[374,130],[373,94],[375,94],[376,96],[380,131],[377,126],[378,128],[379,115],[321,132],[382,133],[371,134],[366,135],[353,136],[362,136],[354,136],[355,136],[364,136],[356,136],[357,136],[365,137],[363,136],[358,136],[361,136],[359,136],[360,136],[367,138],[352,120],[197,120],[198,120],[200,139],[201,120],[202,120],[203,140],[199,120],[219,141],[207,142],[208,143],[211,144],[217,145],[218,146],[143,147],[142,11],[145,148],[140,11],[144,149],[141,150],[412,151],[439,152],[435,1],[437,153],[438,1],[408,11],[210,154],[444,155],[445,156],[447,157],[448,158],[459,159],[458,160],[462,161],[463,162],[461,163],[464,164],[465,165],[466,166],[467,167],[468,168],[469,169],[470,170],[471,171],[472,172],[473,173],[56,174],[57,174],[59,175],[60,176],[61,177],[62,178],[63,179],[64,180],[65,181],[66,182],[67,183],[68,184],[69,184],[70,185],[71,186],[72,187],[73,188],[74,189],[75,190],[76,191],[77,192],[108,193],[78,194],[79,195],[80,196],[81,197],[82,198],[83,199],[84,200],[85,201],[86,202],[87,203],[88,204],[89,205],[91,206],[90,207],[92,208],[93,209],[95,210],[96,211],[97,212],[98,213],[99,214],[100,215],[101,216],[102,217],[103,218],[104,219],[105,220],[106,221],[476,11],[177,222],[479,11],[504,223],[505,224],[480,225],[483,225],[502,223],[503,223],[493,223],[492,226],[490,223],[485,223],[498,223],[496,223],[500,223],[484,223],[497,223],[501,223],[486,223],[487,223],[499,223],[481,223],[488,223],[489,223],[491,223],[495,223],[506,227],[494,223],[482,223],[519,228],[513,227],[515,229],[514,227],[507,227],[508,227],[510,227],[512,227],[516,229],[517,229],[509,229],[511,229],[521,230],[525,231],[421,232],[423,233],[427,234],[425,235],[424,235],[428,236],[430,237],[429,238],[227,239],[229,239],[228,239],[226,239],[236,240],[231,241],[222,239],[223,239],[224,239],[225,239],[454,242],[456,243],[455,242],[453,244],[457,245],[452,246],[196,247],[191,248],[192,249],[193,249],[194,250],[195,250],[190,251],[239,252],[238,253],[237,254],[240,255],[406,256],[414,257],[413,258],[409,259],[275,260],[276,261],[277,261],[279,262],[281,263],[280,261],[432,270],[433,265],[404,266],[418,267],[417,267],[415,268],[416,267],[419,269]],"semanticDiagnosticsPerFile":[436,434,126,109,128,110,127,132,133,129,135,130,134,131,117,114,121,115,112,120,125,122,123,124,119,116,113,118,174,173,175,170,168,169,172,171,154,159,148,153,152,150,157,158,160,155,149,147,146,156,162,161,163,164,165,167,137,138,139,136,151,166,185,186,187,241,407,244,246,242,243,245,248,247,311,313,312,300,308,304,305,309,307,314,315,316,318,317,319,310,303,301,302,306,268,269,291,266,249,251,250,257,254,253,265,255,264,267,403,256,258,259,260,263,261,262,292,402,252,181,182,179,180,184,183,283,284,287,285,286,290,288,289,282,271,273,272,274,270,178,399,293,400,401,294,295,296,398,396,297,397,328,329,330,331,332,341,342,343,345,346,344,333,335,336,334,337,338,339,340,347,351,349,348,350,320,383,385,386,394,387,388,389,390,392,391,393,384,395,368,298,299,322,323,324,325,326,327,369,370,372,381,374,373,375,376,380,377,378,379,321,382,371,366,353,362,354,355,364,356,357,365,363,358,361,359,360,367,352,197,198,200,201,202,203,204,205,206,199,219,207,208,211,212,213,214,215,216,217,218,143,142,145,140,144,141,410,412,411,439,435,437,438,408,210,440,441,444,442,445,446,447,448,459,458,443,460,462,463,461,464,465,466,467,468,469,470,471,472,473,474,209,56,57,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,58,107,75,76,77,108,78,79,80,81,82,83,84,85,86,87,88,89,91,90,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,475,476,477,478,177,176,479,504,505,480,483,502,503,493,492,490,485,498,496,500,484,497,501,486,487,499,481,488,489,491,495,506,494,482,519,518,513,515,514,507,508,510,512,516,517,509,511,521,520,522,523,524,525,421,420,423,422,426,427,425,424,111,449,428,430,429,230,227,229,228,226,236,231,235,232,234,233,222,223,224,220,221,225,450,454,456,455,453,457,452,451,189,196,191,192,193,194,195,190,8,10,9,2,11,12,13,14,15,16,17,18,3,4,22,19,20,21,23,24,25,5,26,27,28,29,6,33,30,31,32,34,7,35,40,41,36,37,38,39,1,42,188,239,238,237,240,406,414,413,405,409,275,276,277,278,279,281,280,432,431,433,404,418,417,415,416,419,47,48,49,50,51,52,43,53,54,55,44,45,46],"latestChangedDtsFile":"./types/index.d.ts"},"version":"4.9.5"} -\ No newline at end of file -+{"program":{"fileNames":["../../../node_modules/typescript/lib/lib.es5.d.ts","../../../node_modules/typescript/lib/lib.es2015.d.ts","../../../node_modules/typescript/lib/lib.es2016.d.ts","../../../node_modules/typescript/lib/lib.es2017.d.ts","../../../node_modules/typescript/lib/lib.es2018.d.ts","../../../node_modules/typescript/lib/lib.es2019.d.ts","../../../node_modules/typescript/lib/lib.es2020.d.ts","../../../node_modules/typescript/lib/lib.dom.d.ts","../../../node_modules/typescript/lib/lib.es2015.core.d.ts","../../../node_modules/typescript/lib/lib.es2015.collection.d.ts","../../../node_modules/typescript/lib/lib.es2015.generator.d.ts","../../../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../../node_modules/typescript/lib/lib.es2015.promise.d.ts","../../../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../../node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../../node_modules/typescript/lib/lib.es2017.object.d.ts","../../../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../../node_modules/typescript/lib/lib.es2017.string.d.ts","../../../node_modules/typescript/lib/lib.es2017.intl.d.ts","../../../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../../node_modules/typescript/lib/lib.es2018.intl.d.ts","../../../node_modules/typescript/lib/lib.es2018.promise.d.ts","../../../node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../../node_modules/typescript/lib/lib.es2019.array.d.ts","../../../node_modules/typescript/lib/lib.es2019.object.d.ts","../../../node_modules/typescript/lib/lib.es2019.string.d.ts","../../../node_modules/typescript/lib/lib.es2019.symbol.d.ts","../../../node_modules/typescript/lib/lib.es2019.intl.d.ts","../../../node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../../node_modules/typescript/lib/lib.es2020.date.d.ts","../../../node_modules/typescript/lib/lib.es2020.promise.d.ts","../../../node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../../../node_modules/typescript/lib/lib.es2020.string.d.ts","../../../node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../../../node_modules/typescript/lib/lib.es2020.intl.d.ts","../../../node_modules/typescript/lib/lib.es2020.number.d.ts","../../../node_modules/typescript/lib/lib.esnext.intl.d.ts","../../../types/eth-ens-namehash.d.ts","../../../types/ethereum-ens-network-map.d.ts","../../../types/global.d.ts","../../../types/single-call-balance-checker-abi.d.ts","../../../types/@metamask/contract-metadata.d.ts","../../../types/@metamask/eth-hd-keyring.d.ts","../../../types/@metamask/eth-simple-keyring.d.ts","../../../types/@metamask/ethjs-provider-http.d.ts","../../../types/@metamask/ethjs-unit.d.ts","../../../types/@metamask/metamask-eth-abis.d.ts","../../../types/eth-json-rpc-infura/src/createProvider.d.ts","../../../types/eth-phishing-detect/src/config.json.d.ts","../../../types/eth-phishing-detect/src/detector.d.ts","../../../node_modules/@types/node/assert.d.ts","../../../node_modules/@types/node/assert/strict.d.ts","../../../node_modules/@types/node/globals.d.ts","../../../node_modules/@types/node/async_hooks.d.ts","../../../node_modules/@types/node/buffer.d.ts","../../../node_modules/@types/node/child_process.d.ts","../../../node_modules/@types/node/cluster.d.ts","../../../node_modules/@types/node/console.d.ts","../../../node_modules/@types/node/constants.d.ts","../../../node_modules/@types/node/crypto.d.ts","../../../node_modules/@types/node/dgram.d.ts","../../../node_modules/@types/node/diagnostics_channel.d.ts","../../../node_modules/@types/node/dns.d.ts","../../../node_modules/@types/node/dns/promises.d.ts","../../../node_modules/@types/node/dom-events.d.ts","../../../node_modules/@types/node/domain.d.ts","../../../node_modules/@types/node/events.d.ts","../../../node_modules/@types/node/fs.d.ts","../../../node_modules/@types/node/fs/promises.d.ts","../../../node_modules/@types/node/http.d.ts","../../../node_modules/@types/node/http2.d.ts","../../../node_modules/@types/node/https.d.ts","../../../node_modules/@types/node/inspector.d.ts","../../../node_modules/@types/node/module.d.ts","../../../node_modules/@types/node/net.d.ts","../../../node_modules/@types/node/os.d.ts","../../../node_modules/@types/node/path.d.ts","../../../node_modules/@types/node/perf_hooks.d.ts","../../../node_modules/@types/node/process.d.ts","../../../node_modules/@types/node/punycode.d.ts","../../../node_modules/@types/node/querystring.d.ts","../../../node_modules/@types/node/readline.d.ts","../../../node_modules/@types/node/repl.d.ts","../../../node_modules/@types/node/stream.d.ts","../../../node_modules/@types/node/stream/promises.d.ts","../../../node_modules/@types/node/stream/consumers.d.ts","../../../node_modules/@types/node/stream/web.d.ts","../../../node_modules/@types/node/string_decoder.d.ts","../../../node_modules/@types/node/test.d.ts","../../../node_modules/@types/node/timers.d.ts","../../../node_modules/@types/node/timers/promises.d.ts","../../../node_modules/@types/node/tls.d.ts","../../../node_modules/@types/node/trace_events.d.ts","../../../node_modules/@types/node/tty.d.ts","../../../node_modules/@types/node/url.d.ts","../../../node_modules/@types/node/util.d.ts","../../../node_modules/@types/node/v8.d.ts","../../../node_modules/@types/node/vm.d.ts","../../../node_modules/@types/node/wasi.d.ts","../../../node_modules/@types/node/worker_threads.d.ts","../../../node_modules/@types/node/zlib.d.ts","../../../node_modules/@types/node/globals.global.d.ts","../../../node_modules/@types/node/index.d.ts","../../../node_modules/@ethereumjs/common/dist/enums.d.ts","../../../node_modules/@ethereumjs/common/dist/types.d.ts","../../../node_modules/buffer/index.d.ts","../../../node_modules/@ethereumjs/util/dist/constants.d.ts","../../../node_modules/@ethereumjs/util/dist/units.d.ts","../../../node_modules/@ethereumjs/util/dist/address.d.ts","../../../node_modules/@ethereumjs/util/dist/bytes.d.ts","../../../node_modules/@ethereumjs/util/dist/types.d.ts","../../../node_modules/@ethereumjs/util/dist/account.d.ts","../../../node_modules/@ethereumjs/util/dist/withdrawal.d.ts","../../../node_modules/@ethereumjs/util/dist/signature.d.ts","../../../node_modules/@ethereumjs/util/dist/encoding.d.ts","../../../node_modules/@ethereumjs/util/dist/asyncEventEmitter.d.ts","../../../node_modules/@ethereumjs/util/dist/internal.d.ts","../../../node_modules/@ethereumjs/util/dist/lock.d.ts","../../../node_modules/@ethereumjs/util/dist/provider.d.ts","../../../node_modules/@ethereumjs/util/dist/index.d.ts","../../../node_modules/@ethereumjs/common/dist/common.d.ts","../../../node_modules/@ethereumjs/common/dist/utils.d.ts","../../../node_modules/@ethereumjs/common/dist/index.d.ts","../../../node_modules/@ethereumjs/tx/dist/eip2930Transaction.d.ts","../../../node_modules/@ethereumjs/tx/dist/legacyTransaction.d.ts","../../../node_modules/@ethereumjs/tx/dist/types.d.ts","../../../node_modules/@ethereumjs/tx/dist/baseTransaction.d.ts","../../../node_modules/@ethereumjs/tx/dist/eip1559Transaction.d.ts","../../../node_modules/@ethereumjs/tx/dist/transactionFactory.d.ts","../../../node_modules/@ethereumjs/tx/dist/index.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/patchCBOR.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/lib/DataItem.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/lib/cbor-sync.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/lib/index.d.ts","../../../node_modules/@ngraveio/bc-ur/dist/ur.d.ts","../../../node_modules/@ngraveio/bc-ur/dist/urEncoder.d.ts","../../../node_modules/@ngraveio/bc-ur/dist/fountainEncoder.d.ts","../../../node_modules/@ngraveio/bc-ur/dist/fountainDecoder.d.ts","../../../node_modules/@ngraveio/bc-ur/dist/urDecoder.d.ts","../../../node_modules/@ngraveio/bc-ur/dist/index.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/RegistryType.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/RegistryItem.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/CryptoCoinInfo.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/PathComponent.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/CryptoKeypath.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/types.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/CryptoHDKey.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/CryptoECKey.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/Bytes.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/MultiKey.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/ScriptExpression.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/CryptoOutput.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/CryptoPSBT.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/CryptoAccount.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/Decoder/index.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/extended/CryptoMultiAccounts.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/errors/index.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/extended/DerivationSchema.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/extended/KeyDerivation.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/extended/QRHardwareCall.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/utils.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/index.d.ts","../../../node_modules/@keystonehq/bc-ur-registry-eth/dist/EthSignRequest.d.ts","../../../node_modules/@keystonehq/bc-ur-registry-eth/dist/EthSignature.d.ts","../../../node_modules/@keystonehq/bc-ur-registry-eth/dist/ETHNFTItem.d.ts","../../../node_modules/@keystonehq/bc-ur-registry-eth/dist/utlis.d.ts","../../../node_modules/@keystonehq/bc-ur-registry-eth/dist/index.d.ts","../../../node_modules/@keystonehq/base-eth-keyring/dist/InteractionProvider.d.ts","../../../node_modules/@keystonehq/base-eth-keyring/dist/BaseKeyring.d.ts","../../../node_modules/@keystonehq/base-eth-keyring/dist/index.d.ts","../../../node_modules/@types/readable-stream/node_modules/safe-buffer/index.d.ts","../../../node_modules/@types/readable-stream/index.d.ts","../../../node_modules/@metamask/safe-event-emitter/dist/cjs/index.d.ts","../../../node_modules/@metamask/obs-store/dist/ObservableStore.d.ts","../../../node_modules/@metamask/obs-store/dist/asStream.d.ts","../../../node_modules/@metamask/obs-store/dist/ComposedStore.d.ts","../../../node_modules/@metamask/obs-store/dist/MergedStore.d.ts","../../../node_modules/@metamask/obs-store/dist/transform.d.ts","../../../node_modules/@metamask/obs-store/dist/index.d.ts","../../../node_modules/@keystonehq/metamask-airgapped-keyring/dist/MetaMaskInteractionProvider.d.ts","../../../node_modules/@keystonehq/metamask-airgapped-keyring/dist/MetaMaskKeyring.d.ts","../../../node_modules/@keystonehq/metamask-airgapped-keyring/dist/index.d.ts","../../base-controller/dist/types/BaseControllerV1.d.ts","../../../node_modules/superstruct/dist/error.d.ts","../../../node_modules/superstruct/dist/utils.d.ts","../../../node_modules/superstruct/dist/struct.d.ts","../../../node_modules/superstruct/dist/structs/coercions.d.ts","../../../node_modules/superstruct/dist/structs/refinements.d.ts","../../../node_modules/superstruct/dist/structs/types.d.ts","../../../node_modules/superstruct/dist/structs/utilities.d.ts","../../../node_modules/superstruct/dist/index.d.ts","../../../node_modules/@metamask/utils/dist/types/assert.d.ts","../../../node_modules/@metamask/utils/dist/types/base64.d.ts","../../../node_modules/@metamask/utils/dist/types/hex.d.ts","../../../node_modules/@metamask/utils/dist/types/bytes.d.ts","../../../node_modules/@metamask/utils/dist/types/caip-types.d.ts","../../../node_modules/@metamask/utils/dist/types/checksum.d.ts","../../../node_modules/@metamask/utils/dist/types/coercers.d.ts","../../../node_modules/@metamask/utils/dist/types/collections.d.ts","../../../node_modules/@metamask/utils/dist/types/encryption-types.d.ts","../../../node_modules/@metamask/utils/dist/types/errors.d.ts","../../../node_modules/@metamask/utils/dist/types/json.d.ts","../../../node_modules/@metamask/utils/dist/types/keyring.d.ts","../../../node_modules/@types/ms/index.d.ts","../../../node_modules/@types/debug/index.d.ts","../../../node_modules/@metamask/utils/dist/types/logging.d.ts","../../../node_modules/@metamask/utils/dist/types/misc.d.ts","../../../node_modules/@metamask/utils/dist/types/number.d.ts","../../../node_modules/@metamask/utils/dist/types/opaque.d.ts","../../../node_modules/@metamask/utils/dist/types/promise.d.ts","../../../node_modules/@metamask/utils/dist/types/time.d.ts","../../../node_modules/@metamask/utils/dist/types/transaction-types.d.ts","../../../node_modules/@metamask/utils/dist/types/versions.d.ts","../../../node_modules/@metamask/utils/dist/types/index.d.ts","../../../node_modules/immer/dist/utils/env.d.ts","../../../node_modules/immer/dist/utils/errors.d.ts","../../../node_modules/immer/dist/types/types-external.d.ts","../../../node_modules/immer/dist/types/types-internal.d.ts","../../../node_modules/immer/dist/utils/common.d.ts","../../../node_modules/immer/dist/utils/plugins.d.ts","../../../node_modules/immer/dist/core/scope.d.ts","../../../node_modules/immer/dist/core/finalize.d.ts","../../../node_modules/immer/dist/core/proxy.d.ts","../../../node_modules/immer/dist/core/immerClass.d.ts","../../../node_modules/immer/dist/core/current.d.ts","../../../node_modules/immer/dist/internal.d.ts","../../../node_modules/immer/dist/plugins/es5.d.ts","../../../node_modules/immer/dist/plugins/patches.d.ts","../../../node_modules/immer/dist/plugins/mapset.d.ts","../../../node_modules/immer/dist/plugins/all.d.ts","../../../node_modules/immer/dist/immer.d.ts","../../base-controller/dist/types/RestrictedControllerMessenger.d.ts","../../base-controller/dist/types/ControllerMessenger.d.ts","../../base-controller/dist/types/BaseControllerV2.d.ts","../../base-controller/dist/types/index.d.ts","../../../node_modules/@metamask/browser-passworder/dist/index.d.ts","../../../node_modules/@metamask/eth-sig-util/dist/personal-sign.d.ts","../../../node_modules/@metamask/eth-sig-util/dist/sign-typed-data.d.ts","../../../node_modules/@metamask/eth-sig-util/dist/encryption.d.ts","../../../node_modules/@metamask/eth-sig-util/dist/utils.d.ts","../../../node_modules/@metamask/eth-sig-util/dist/index.d.ts","../../../node_modules/@metamask/eth-simple-keyring/dist/simple-keyring.d.ts","../../../node_modules/@metamask/eth-simple-keyring/dist/index.d.ts","../../../node_modules/@metamask/keyring-api/dist/base-types.d.ts","../../../node_modules/@metamask/keyring-api/dist/btc/types.d.ts","../../../node_modules/@metamask/keyring-api/dist/btc/index.d.ts","../../../node_modules/@metamask/keyring-api/dist/superstruct.d.ts","../../../node_modules/@metamask/keyring-api/dist/eth/erc4337/types.d.ts","../../../node_modules/@metamask/keyring-api/dist/eth/erc4337/index.d.ts","../../../node_modules/@metamask/keyring-api/dist/eth/types.d.ts","../../../node_modules/@metamask/keyring-api/dist/internal/api.d.ts","../../../node_modules/@metamask/keyring-api/dist/contexts.d.ts","../../../node_modules/@metamask/keyring-api/dist/internal/eth/EthKeyring.d.ts","../../../node_modules/@metamask/keyring-api/dist/internal/eth/index.d.ts","../../../node_modules/@metamask/keyring-api/dist/internal/events.d.ts","../../../node_modules/@metamask/keyring-api/dist/internal/rpc.d.ts","../../../node_modules/@metamask/keyring-api/dist/internal/types.d.ts","../../../node_modules/@metamask/keyring-api/dist/internal/index.d.ts","../../../node_modules/@metamask/keyring-api/dist/eth/utils.d.ts","../../../node_modules/@metamask/keyring-api/dist/eth/index.d.ts","../../../node_modules/@metamask/keyring-api/dist/api.d.ts","../../../node_modules/@metamask/keyring-api/dist/events.d.ts","../../../node_modules/@metamask/keyring-api/dist/JsonRpcRequest.d.ts","../../../node_modules/@metamask/keyring-api/dist/KeyringClient.d.ts","../../../node_modules/@metamask/rpc-errors/dist/types/utils.d.ts","../../../node_modules/@metamask/rpc-errors/dist/types/classes.d.ts","../../../node_modules/@metamask/rpc-errors/dist/types/errors.d.ts","../../../node_modules/@metamask/rpc-errors/dist/types/error-constants.d.ts","../../../node_modules/@metamask/rpc-errors/dist/types/index.d.ts","../../json-rpc-engine/src/JsonRpcEngine.ts","../../json-rpc-engine/src/createAsyncMiddleware.ts","../../json-rpc-engine/src/createScaffoldMiddleware.ts","../../json-rpc-engine/src/getUniqueId.ts","../../json-rpc-engine/src/idRemapMiddleware.ts","../../json-rpc-engine/src/mergeMiddleware.ts","../../json-rpc-engine/src/index.ts","../../../node_modules/@metamask/providers/dist/types/utils.d.ts","../../../node_modules/@metamask/providers/dist/types/BaseProvider.d.ts","../../../node_modules/@metamask/providers/dist/types/EIP6963.d.ts","../../../node_modules/@metamask/providers/dist/types/StreamProvider.d.ts","../../../node_modules/@metamask/providers/dist/types/extension-provider/createExternalExtensionProvider.d.ts","../../../node_modules/@metamask/providers/dist/types/MetaMaskInpageProvider.d.ts","../../../node_modules/@metamask/providers/dist/types/initializeInpageProvider.d.ts","../../../node_modules/@metamask/providers/dist/types/shimWeb3.d.ts","../../../node_modules/@metamask/providers/dist/types/index.d.ts","../../../node_modules/@metamask/keyring-api/dist/KeyringSnapRpcClient.d.ts","../../../node_modules/@metamask/keyring-api/dist/rpc-handler.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/errors.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/internals/error-wrappers.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/internals/errors.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/internals/helpers.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/internals/structs.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/create-interface.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/dialog.d.ts","../../../node_modules/@metamask/key-tree/dist/constants.d.cts","../../../node_modules/@metamask/key-tree/node_modules/@noble/curves/abstract/modular.d.ts","../../../node_modules/@metamask/key-tree/node_modules/@noble/curves/abstract/utils.d.ts","../../../node_modules/@metamask/key-tree/node_modules/@noble/curves/abstract/curve.d.ts","../../../node_modules/@metamask/key-tree/dist/curves/ed25519.d.cts","../../../node_modules/@metamask/key-tree/dist/curves/ed25519Bip32.d.cts","../../../node_modules/@metamask/key-tree/node_modules/@noble/curves/abstract/weierstrass.d.ts","../../../node_modules/@metamask/key-tree/dist/curves/secp256k1.d.cts","../../../node_modules/@metamask/key-tree/dist/curves/curve.d.cts","../../../node_modules/@metamask/key-tree/dist/curves/index.d.cts","../../../node_modules/@metamask/key-tree/dist/utils.d.cts","../../../node_modules/@metamask/key-tree/dist/BIP44CoinTypeNode.d.cts","../../../node_modules/@metamask/key-tree/dist/SLIP10Node.d.cts","../../../node_modules/@metamask/key-tree/dist/BIP44Node.d.cts","../../../node_modules/@metamask/key-tree/dist/derivers/bip32.d.cts","../../../node_modules/@metamask/key-tree/dist/derivers/bip39.d.cts","../../../node_modules/@metamask/key-tree/dist/derivers/cip3.d.cts","../../../node_modules/@metamask/key-tree/dist/derivers/slip10.d.cts","../../../node_modules/@metamask/key-tree/dist/derivers/index.d.cts","../../../node_modules/@metamask/key-tree/dist/index.d.cts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/caip.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/permissions.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/get-bip32-entropy.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/get-bip32-public-key.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/get-bip44-entropy.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/get-client-status.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/get-entropy.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/get-file.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/component.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/Address.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/Box.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/Copyable.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/Divider.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/form/Button.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/form/Input.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/form/Field.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/form/Form.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/form/index.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/formatting/Bold.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/formatting/Italic.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/formatting/index.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/Heading.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/Image.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/Link.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/Text.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/Row.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/Spinner.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/index.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/jsx-runtime.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/jsx-dev-runtime.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/validation.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/index.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/nodes.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/address.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/copyable.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/divider.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/heading.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/image.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/panel.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/spinner.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/text.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/row.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/button.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/input.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/form.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/index.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/component.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/index.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/interface.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/get-interface-state.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/get-locale.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/snap.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/get-snaps.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/invoke-snap.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/invoke-keyring.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/manage-accounts.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/manage-state.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/notify.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/request-snaps.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/update-interface.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/methods.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/index.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/provider.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/global.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/images.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/handlers/cronjob.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/handlers/home-page.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/handlers/keyring.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/handlers/lifecycle.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/handlers/name-lookup.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/handlers/rpc-request.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/handlers/transaction.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/handlers/signature.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/handlers/user-input.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/handlers/index.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/index.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/internals/jsx.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/internals/svg.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/internals/index.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/error-wrappers.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/images.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/index.d.ts","../../../node_modules/@metamask/keyring-api/dist/snap-utils.d.ts","../../../node_modules/@metamask/keyring-api/dist/index.d.ts","../../message-manager/dist/types/AbstractMessageManager.d.ts","../../controller-utils/dist/types/types.d.ts","../../controller-utils/dist/types/constants.d.ts","../../../node_modules/@metamask/eth-query/index.d.ts","../../../node_modules/@types/bn.js/index.d.ts","../../controller-utils/dist/types/util.d.ts","../../../node_modules/@spruceid/siwe-parser/dist/abnf.d.ts","../../../node_modules/@spruceid/siwe-parser/dist/utils.d.ts","../../../node_modules/@spruceid/siwe-parser/dist/parsers.d.ts","../../controller-utils/dist/types/siwe.d.ts","../../controller-utils/dist/types/index.d.ts","../../message-manager/dist/types/PersonalMessageManager.d.ts","../../message-manager/dist/types/TypedMessageManager.d.ts","../../message-manager/dist/types/EncryptionPublicKeyManager.d.ts","../../message-manager/dist/types/DecryptMessageManager.d.ts","../../message-manager/dist/types/index.d.ts","../../../node_modules/async-mutex/lib/MutexInterface.d.ts","../../../node_modules/async-mutex/lib/Mutex.d.ts","../../../node_modules/async-mutex/lib/SemaphoreInterface.d.ts","../../../node_modules/async-mutex/lib/Semaphore.d.ts","../../../node_modules/async-mutex/lib/withTimeout.d.ts","../../../node_modules/async-mutex/lib/tryAcquire.d.ts","../../../node_modules/async-mutex/lib/errors.d.ts","../../../node_modules/async-mutex/lib/index.d.ts","../../../node_modules/ethereumjs-wallet/dist/hdkey.d.ts","../../../node_modules/ethereumjs-wallet/dist/thirdparty.d.ts","../../../node_modules/ethereumjs-wallet/dist/index.d.ts","../src/constants.ts","../src/KeyringController.ts","../src/index.ts","../../../node_modules/@babel/types/lib/index.d.ts","../../../node_modules/@types/babel__generator/index.d.ts","../../../node_modules/@babel/parser/typings/babel-parser.d.ts","../../../node_modules/@types/babel__template/index.d.ts","../../../node_modules/@types/babel__traverse/index.d.ts","../../../node_modules/@types/babel__core/index.d.ts","../../../node_modules/@types/deep-freeze-strict/index.d.ts","../../../node_modules/@types/eslint/helpers.d.ts","../../../node_modules/@types/estree/index.d.ts","../../../node_modules/@types/json-schema/index.d.ts","../../../node_modules/@types/eslint/index.d.ts","../../../node_modules/@types/graceful-fs/index.d.ts","../../../node_modules/@types/istanbul-lib-coverage/index.d.ts","../../../node_modules/@types/istanbul-lib-report/index.d.ts","../../../node_modules/@types/istanbul-reports/index.d.ts","../../../node_modules/chalk/index.d.ts","../../../node_modules/jest-diff/build/cleanupSemantic.d.ts","../../../node_modules/pretty-format/build/types.d.ts","../../../node_modules/pretty-format/build/index.d.ts","../../../node_modules/jest-diff/build/types.d.ts","../../../node_modules/jest-diff/build/diffLines.d.ts","../../../node_modules/jest-diff/build/printDiffs.d.ts","../../../node_modules/jest-diff/build/index.d.ts","../../../node_modules/jest-matcher-utils/build/index.d.ts","../../../node_modules/@types/jest/index.d.ts","../../../node_modules/@types/jest-when/index.d.ts","../../../node_modules/@types/json5/index.d.ts","../../../node_modules/@types/lodash/common/common.d.ts","../../../node_modules/@types/lodash/common/array.d.ts","../../../node_modules/@types/lodash/common/collection.d.ts","../../../node_modules/@types/lodash/common/date.d.ts","../../../node_modules/@types/lodash/common/function.d.ts","../../../node_modules/@types/lodash/common/lang.d.ts","../../../node_modules/@types/lodash/common/math.d.ts","../../../node_modules/@types/lodash/common/number.d.ts","../../../node_modules/@types/lodash/common/object.d.ts","../../../node_modules/@types/lodash/common/seq.d.ts","../../../node_modules/@types/lodash/common/string.d.ts","../../../node_modules/@types/lodash/common/util.d.ts","../../../node_modules/@types/lodash/index.d.ts","../../../node_modules/@types/minimatch/index.d.ts","../../../node_modules/@types/parse-json/index.d.ts","../../../node_modules/@types/pbkdf2/index.d.ts","../../../node_modules/@types/prettier/index.d.ts","../../../node_modules/@types/punycode/index.d.ts","../../../node_modules/@types/secp256k1/index.d.ts","../../../node_modules/@types/semver/classes/semver.d.ts","../../../node_modules/@types/semver/functions/parse.d.ts","../../../node_modules/@types/semver/functions/valid.d.ts","../../../node_modules/@types/semver/functions/clean.d.ts","../../../node_modules/@types/semver/functions/inc.d.ts","../../../node_modules/@types/semver/functions/diff.d.ts","../../../node_modules/@types/semver/functions/major.d.ts","../../../node_modules/@types/semver/functions/minor.d.ts","../../../node_modules/@types/semver/functions/patch.d.ts","../../../node_modules/@types/semver/functions/prerelease.d.ts","../../../node_modules/@types/semver/functions/compare.d.ts","../../../node_modules/@types/semver/functions/rcompare.d.ts","../../../node_modules/@types/semver/functions/compare-loose.d.ts","../../../node_modules/@types/semver/functions/compare-build.d.ts","../../../node_modules/@types/semver/functions/sort.d.ts","../../../node_modules/@types/semver/functions/rsort.d.ts","../../../node_modules/@types/semver/functions/gt.d.ts","../../../node_modules/@types/semver/functions/lt.d.ts","../../../node_modules/@types/semver/functions/eq.d.ts","../../../node_modules/@types/semver/functions/neq.d.ts","../../../node_modules/@types/semver/functions/gte.d.ts","../../../node_modules/@types/semver/functions/lte.d.ts","../../../node_modules/@types/semver/functions/cmp.d.ts","../../../node_modules/@types/semver/functions/coerce.d.ts","../../../node_modules/@types/semver/classes/comparator.d.ts","../../../node_modules/@types/semver/classes/range.d.ts","../../../node_modules/@types/semver/functions/satisfies.d.ts","../../../node_modules/@types/semver/ranges/max-satisfying.d.ts","../../../node_modules/@types/semver/ranges/min-satisfying.d.ts","../../../node_modules/@types/semver/ranges/to-comparators.d.ts","../../../node_modules/@types/semver/ranges/min-version.d.ts","../../../node_modules/@types/semver/ranges/valid.d.ts","../../../node_modules/@types/semver/ranges/outside.d.ts","../../../node_modules/@types/semver/ranges/gtr.d.ts","../../../node_modules/@types/semver/ranges/ltr.d.ts","../../../node_modules/@types/semver/ranges/intersects.d.ts","../../../node_modules/@types/semver/ranges/simplify.d.ts","../../../node_modules/@types/semver/ranges/subset.d.ts","../../../node_modules/@types/semver/internals/identifiers.d.ts","../../../node_modules/@types/semver/index.d.ts","../../../node_modules/@types/sinonjs__fake-timers/index.d.ts","../../../node_modules/@types/sinon/index.d.ts","../../../node_modules/@types/stack-utils/index.d.ts","../../../node_modules/@types/uuid/index.d.ts","../../../node_modules/@types/yargs-parser/index.d.ts","../../../node_modules/@types/yargs/index.d.ts"],"fileInfos":[{"version":"8730f4bf322026ff5229336391a18bcaa1f94d4f82416c8b2f3954e2ccaae2ba","affectsGlobalScope":true},"dc47c4fa66b9b9890cf076304de2a9c5201e94b740cffdf09f87296d877d71f6","7a387c58583dfca701b6c85e0adaf43fb17d590fb16d5b2dc0a2fbd89f35c467","8a12173c586e95f4433e0c6dc446bc88346be73ffe9ca6eec7aa63c8f3dca7f9","5f4e733ced4e129482ae2186aae29fde948ab7182844c3a5a51dd346182c7b06","4b421cbfb3a38a27c279dec1e9112c3d1da296f77a1a85ddadf7e7a425d45d18","1fc5ab7a764205c68fa10d381b08417795fc73111d6dd16b5b1ed36badb743d9",{"version":"3aafcb693fe5b5c3bd277bd4c3a617b53db474fe498fc5df067c5603b1eebde7","affectsGlobalScope":true},{"version":"adb996790133eb33b33aadb9c09f15c2c575e71fb57a62de8bf74dbf59ec7dfb","affectsGlobalScope":true},{"version":"8cc8c5a3bac513368b0157f3d8b31cfdcfe78b56d3724f30f80ed9715e404af8","affectsGlobalScope":true},{"version":"cdccba9a388c2ee3fd6ad4018c640a471a6c060e96f1232062223063b0a5ac6a","affectsGlobalScope":true},{"version":"c5c05907c02476e4bde6b7e76a79ffcd948aedd14b6a8f56e4674221b0417398","affectsGlobalScope":true},{"version":"5f406584aef28a331c36523df688ca3650288d14f39c5d2e555c95f0d2ff8f6f","affectsGlobalScope":true},{"version":"22f230e544b35349cfb3bd9110b6ef37b41c6d6c43c3314a31bd0d9652fcec72","affectsGlobalScope":true},{"version":"7ea0b55f6b315cf9ac2ad622b0a7813315bb6e97bf4bb3fbf8f8affbca7dc695","affectsGlobalScope":true},{"version":"3013574108c36fd3aaca79764002b3717da09725a36a6fc02eac386593110f93","affectsGlobalScope":true},{"version":"eb26de841c52236d8222f87e9e6a235332e0788af8c87a71e9e210314300410a","affectsGlobalScope":true},{"version":"3be5a1453daa63e031d266bf342f3943603873d890ab8b9ada95e22389389006","affectsGlobalScope":true},{"version":"17bb1fc99591b00515502d264fa55dc8370c45c5298f4a5c2083557dccba5a2a","affectsGlobalScope":true},{"version":"7ce9f0bde3307ca1f944119f6365f2d776d281a393b576a18a2f2893a2d75c98","affectsGlobalScope":true},{"version":"6a6b173e739a6a99629a8594bfb294cc7329bfb7b227f12e1f7c11bc163b8577","affectsGlobalScope":true},{"version":"81cac4cbc92c0c839c70f8ffb94eb61e2d32dc1c3cf6d95844ca099463cf37ea","affectsGlobalScope":true},{"version":"b0124885ef82641903d232172577f2ceb5d3e60aed4da1153bab4221e1f6dd4e","affectsGlobalScope":true},{"version":"0eb85d6c590b0d577919a79e0084fa1744c1beba6fd0d4e951432fa1ede5510a","affectsGlobalScope":true},{"version":"da233fc1c8a377ba9e0bed690a73c290d843c2c3d23a7bd7ec5cd3d7d73ba1e0","affectsGlobalScope":true},{"version":"d154ea5bb7f7f9001ed9153e876b2d5b8f5c2bb9ec02b3ae0d239ec769f1f2ae","affectsGlobalScope":true},{"version":"bb2d3fb05a1d2ffbca947cc7cbc95d23e1d053d6595391bd325deb265a18d36c","affectsGlobalScope":true},{"version":"c80df75850fea5caa2afe43b9949338ce4e2de086f91713e9af1a06f973872b8","affectsGlobalScope":true},{"version":"9d57b2b5d15838ed094aa9ff1299eecef40b190722eb619bac4616657a05f951","affectsGlobalScope":true},{"version":"6c51b5dd26a2c31dbf37f00cfc32b2aa6a92e19c995aefb5b97a3a64f1ac99de","affectsGlobalScope":true},{"version":"6e7997ef61de3132e4d4b2250e75343f487903ddf5370e7ce33cf1b9db9a63ed","affectsGlobalScope":true},{"version":"2ad234885a4240522efccd77de6c7d99eecf9b4de0914adb9a35c0c22433f993","affectsGlobalScope":true},{"version":"5e5e095c4470c8bab227dbbc61374878ecead104c74ab9960d3adcccfee23205","affectsGlobalScope":true},{"version":"09aa50414b80c023553090e2f53827f007a301bc34b0495bfb2c3c08ab9ad1eb","affectsGlobalScope":true},{"version":"d7f680a43f8cd12a6b6122c07c54ba40952b0c8aa140dcfcf32eb9e6cb028596","affectsGlobalScope":true},{"version":"3787b83e297de7c315d55d4a7c546ae28e5f6c0a361b7a1dcec1f1f50a54ef11","affectsGlobalScope":true},{"version":"e7e8e1d368290e9295ef18ca23f405cf40d5456fa9f20db6373a61ca45f75f40","affectsGlobalScope":true},{"version":"faf0221ae0465363c842ce6aa8a0cbda5d9296940a8e26c86e04cc4081eea21e","affectsGlobalScope":true},{"version":"06393d13ea207a1bfe08ec8d7be562549c5e2da8983f2ee074e00002629d1871","affectsGlobalScope":true},{"version":"2768ef564cfc0689a1b76106c421a2909bdff0acbe87da010785adab80efdd5c","affectsGlobalScope":true},{"version":"b248e32ca52e8f5571390a4142558ae4f203ae2f94d5bac38a3084d529ef4e58","affectsGlobalScope":true},{"version":"52d1bb7ab7a3306fd0375c8bff560feed26ed676a5b0457fa8027b563aecb9a4","affectsGlobalScope":true},"70bbfaec021ac4a0c805374225b55d70887f987df8b8dd7711d79464bb7b4385","869089d60b67219f63e6aca810284c89bae1b384b5cbc7ce64e53d82ad223ed5",{"version":"18338b6a4b920ec7d49b4ffafcbf0fa8a86b4bfd432966efd722dab611157cf4","affectsGlobalScope":true},"62a0875a0397b35a2364f1d401c0ce17975dfa4d47bf6844de858ae04da349f9","ee7491d0318d1fafcba97d5b72b450eb52671570f7a4ecd9e8898d40eaae9472","e3e7d217d89b380c1f34395eadc9289542851b0f0a64007dfe1fb7cf7423d24e","fd79909e93b4d50fd0ed9f3d39ddf8ba0653290bac25c295aac49f6befbd081b","345a9cc2945406f53051cd0e9b51f82e1e53929848eab046fdda91ee8aa7da31","9debe2de883da37a914e5e784a7be54c201b8f1d783822ad6f443ff409a5ea21","dee5d5c5440cda1f3668f11809a5503c30db0476ad117dd450f7ba5a45300e8f","f5e396c1424c391078c866d6f84afe0b4d2f7f85a160b9c756cd63b5b1775d93","5caa6f4fff16066d377d4e254f6c34c16540da3809cd66cd626a303bc33c419f","730d055528bdf12c8524870bb33d237991be9084c57634e56e5d8075f6605e02","5b3cd03ae354ea96eff1f74d7c410fe4852e6382227e8b0ecf87ab5e3a5bbcd4","7394959e5a741b185456e1ef5d64599c36c60a323207450991e7a42e08911419",{"version":"056097110efd16869ec118cedb44ecbac9a019576eee808d61304ca6d5cb2cbe","affectsGlobalScope":true},"f51b4042a3ac86f1f707500a9768f88d0b0c1fc3f3e45a73333283dea720cdc6",{"version":"6fb8358e10ed92a7f515b7d79da3904c955a3ffd4e14aa9df6f0ea113041f1cf","affectsGlobalScope":true},"45c831238c6dac21c72da5f335747736a56a3847192bf03c84b958a7e9ec93e2","661a11d16ad2e3543a77c53bcd4017ee9a450f47ab7def3ab493a86eae4d550c",{"version":"8cdc646cec7819581ef343b83855b1bfe4fe674f2c84f4fb8dc90d82fb56bd3a","affectsGlobalScope":true},"a40826e8476694e90da94aa008283a7de50d1dafd37beada623863f1901cb7fb","9dd56225cc2d8cb8fe5ceb0043ff386987637e12fecc6078896058a99deae284","2375ed4b439215aa3b6d0c6fd175c78a4384b30cb43cbadaecbf0a18954c98cb","7693b90b3075deaccafd5efb467bf9f2b747a3075be888652ef73e64396d8628","41231da15bb5e3e806a8395bd15c7befd2ec90f9f4e3c9d0ae1356bccb76dbb0","fccfef201d057cb407fa515311bd608549bab6c7b8adcf8f2df31f5d3b796478",{"version":"ee1ee365d88c4c6c0c0a5a5701d66ebc27ccd0bcfcfaa482c6e2e7fe7b98edf7","affectsGlobalScope":true},"5f20d20b7607174caf1a6da9141aeb9f2142159ae2410ca30c7a0fccd1d19c99",{"version":"464762c6213566d072f1ced5e8e9a954785ec5e53883b7397198abb5ef5b8f71","affectsGlobalScope":true},"6387920dc3e18927335b086deec75bf8e50f879a5e273d32ee7bb7a55ba50572","9bba37424094688c4663c177a1379b229f919b8912889a472f32fdc5f08ddb4d","29a4be13b3a30d3e66667b75c58ec61fb2df8fa0422534fdee3cfb30c5dbf450","83366d901beda79d6eb37aaaf6ca248dcd88946302b2a7d975590783be51e88e","bf268a0aea37ad4ae3b7a9b58559190b6fc01ea16a31e35cd05817a0a60f895a","43ec77c369473e92e2ecebf0554a0fdaa9c256644a6070f28228dfcceec77351",{"version":"d7dad6db394a3d9f7b49755e4b610fbf8ed6eb0c9810ae5f1a119f6b5d76de45","affectsGlobalScope":true},"95ed02bacb4502c985b69742ec82a4576d4ff4a6620ecc91593f611d502ae546","bf755525c4e6f85a970b98c4755d98e8aa1b6dbd83a5d8fcc57d3d497351b936","dd67d2b5e4e8a182a38de8e69fb736945eaa4588e0909c14e01a14bd3cc1fd1e",{"version":"28084e15b63e6211769db2fe646d8bc5c4c6776321e0deffe2d12eefd52cb6b9","affectsGlobalScope":true},{"version":"aed37dabf86c99d6c8508700576ecede86688397bc12523541858705a0c737c2","affectsGlobalScope":true},"cc6ef5733d4ea6d2e06310a32dffd2c16418b467c5033d49cecc4f3a25de7497","94768454c3348b6ebe48e45fbad8c92e2bb7af4a35243edbe2b90823d0bd7f9a","0be79b3ff0f16b6c2f9bc8c4cc7097ea417d8d67f8267f7e1eec8e32b548c2ff","1c61ffa3a71b77363b30d19832c269ef62fba787f5610cac7254728d3b69ab2e","84da3c28344e621fd1d591f2c09e9595292d2b70018da28a553268ac122597d4","269929a24b2816343a178008ac9ae9248304d92a8ba8e233055e0ed6dbe6ef71","6e191fea1db6e9e4fa828259cf489e820ec9170effff57fb081a2f3295db4722","aed943465fbce1efe49ee16b5ea409050f15cd8eaf116f6fadb64ef0772e7d95","70d08483a67bf7050dbedace398ef3fee9f436fcd60517c97c4c1e22e3c6f3e8","c40fdf7b2e18df49ce0568e37f0292c12807a0748be79e272745e7216bed2606",{"version":"e933de8143e1d12dd51d89b398760fd5a9081896be366dad88a922d0b29f3c69","affectsGlobalScope":true},"4e228e78c1e9b0a75c70588d59288f63a6258e8b1fe4a67b0c53fe03461421d9","b38d55d08708c2410a3039687db70b4a5bfa69fc4845617c313b5a10d9c5c637","205d50c24359ead003dc537b9b65d2a64208dfdffe368f403cf9e0357831db9e","1265fddcd0c68be9d2a3b29805d0280484c961264dd95e0b675f7bd91f777e78",{"version":"a05e2d784c9be7051c4ac87a407c66d2106e23490c18c038bbd0712bde7602fd","affectsGlobalScope":true},{"version":"df90b9d0e9980762da8daf8adf6ffa0c853e76bfd269c377be0d07a9ad87acd2","affectsGlobalScope":true},"cf434b5c04792f62d6f4bdd5e2c8673f36e638e910333c172614d5def9b17f98","1d65d4798df9c2df008884035c41d3e67731f29db5ecb64cd7378797c7c53a2f","0faee6b555890a1cb106e2adc5d3ffd89545b1da894d474e9d436596d654998f","c6c01ea1c42508edf11a36d13b70f6e35774f74355ba5d358354d4a77cc67ea1","867f95abf1df444aab146b19847391fc2f922a55f6a970a27ed8226766cee29f",{"version":"ab9b9a36e5284fd8d3bf2f7d5fcbc60052f25f27e4d20954782099282c60d23e","affectsGlobalScope":true},"b0297b09e607bec9698cac7cf55463d6731406efb1161ee4d448293b47397c84","175323e2a79a6076e0bada8a390d535a3ea817158bf1b1f46e31efca9028a0a2","7a10053aadc19335532a4d02756db4865974fd69bea5439ddcc5bfdf062d9476","4967529644e391115ca5592184d4b63980569adf60ee685f968fd59ab1557188","aed9e712a9b168345362e8f3a949f16c99ca1e05d21328f05735dfdbb24414ef","b04fe6922ed3db93afdbd49cdda8576aa75f744592fceea96fb0d5f32158c4f5","ed8d6c8de90fc2a4faaebc28e91f2469928738efd5208fb75ade0fa607e892b7","d7c52b198d680fe65b1a8d1b001f0173ffa2536ca2e7082431d726ce1f6714cd","c07f251e1c4e415a838e5498380b55cfea94f3513229de292d2aa85ae52fc3e9","0ed401424892d6bf294a5374efe512d6951b54a71e5dd0290c55b6d0d915f6f7","b945be6da6a3616ef3a250bfe223362b1c7c6872e775b0c4d82a1bf7a28ff902","beea49237dd7c7110fabf3c7509919c9cb9da841d847c53cac162dc3479e2f87","0f45f8a529c450d8f394106cc622bff79e44a1716e1ac9c3cc68b43f7ecf65ee","c624ce90b04c27ce4f318ba6330d39bde3d4e306f0f497ce78d4bda5ab8e22ca","9b8253aa5cb2c82d505f72afdbf96e83b15cc6b9a6f4fadbbbab46210d5f1977","86a8f52e4b1ac49155e889376bcfa8528a634c90c27fec65aa0e949f77b740c5","aab5dd41c1e2316cc0b42a7dd15684f8582d5a1d16c0516276a2a8a7d0fecd9c","59948226626ee210045296ba1fc6cb0fe748d1ff613204e08e7157ab6862dee7","ec3e54d8b713c170fdc8110a7e4a6a97513a7ab6b05ac9e1100cb064d2bb7349","43beb30ecb39a603fde4376554887310b0699f25f7f39c5c91e3147b51bb3a26","666b77d7f06f49da114b090a399abbfa66d5b6c01a3fd9dc4f063a52ace28507","31997714a93fbc570f52d47d6a8ebfb021a34a68ea9ba58bbb69cdec9565657e","6032e4262822160128e644de3fc4410bcd7517c2f137525fd2623d2bb23cb0d3","8bd5c9b1016629c144fd228983395b9dbf0676a576716bc3d316cab612c33cd5","2ed90bd3925b23aed8f859ffd0e885250be0424ca2b57e9866dabef152e1d6b7","93f6bd17d92dab9db7897e1430a5aeaa03bcf51623156213d8397710367a76ce","3f62b770a42e8c47c7008726f95aa383e69d97e85e680d237b99fcb0ee601dd8","5b84cfe78028c35c3bb89c042f18bf08d09da11e82d275c378ae4d07d8477e6c","8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","25139d6a726e0e19d9fc4fa3197367b4a82ec34a08a5ecf23963e142c202c0f3","e3328bffc8eab74665a4fe9c59d6f12f4c8570c3d858497e241eb37efe17dfcf","29389551e426a46421134b55182d6fcf5b143670998bf81db2619c1228235392","c18f7e16982695bdd04e3e183a327d116185f77f1a37b9b2e849d7d93269cd74","2cfb37011989c21dc70b91d521a2d5a4e0f18507f5f536b5dfe504edb15916e8","bb5e02df7aaec7a4ea642639a9963b24b8d9fd6798351f07d8c58616942fbcbf","299a899cb4d061f5d83843ec453e936e9659b2c435746823f90c40eddaef4745","d5610c0fd12870f644b0f42c1bcc4fa2295ac3e3ca01916bdb42c3bdc4c80c36","2c56a8e249b1f45dbdf973100cd37fe2ea68709573cf1fdf2e3052c593be68d8","3553da417ee7b07e388b13bd12a70a1c03e65a6132ba5427fe68f5b362373e6f","612358502042d351c227ba779fdcf6d875d827e424930e60297c533524e50668","d2b5be376ef162aa0c24a826e7dd2d77671a045c085e16d1c1276db4bdccbac7","c4138d8dcccedaff6621e009cf0a54a7bed2a5ad4c509a3513bccc4f417ef939","ad8747fe978dff3e80f4b12b48d37cc8dff11b61d04c035aefbc982ce21201ce","b154f789fd65298e1ba6cbba6944ea892d564c95f3d3700ed85baf8f80748473","c660265aedd7c5b236e2017e53095cb98da66200eb0e8d023b5bf713c36494e8","0efc36bf5c0daca6217fec7063359ccdab8c3a23bb405d25340fae22cf72d74f","5abff0c87d4f9c89715107042d4c73b68ef7a128759f451c8a0fc450cbaaf660","5a03308fbd1af441065149a84c692931bebc7e7735afc23be8684f4e10d3aa06","c787bf4f8f0abbf815cfbd348be41046f2b8f270be24fe7aa8a8fcdd2b7df8c2","e7a5191c663a3228f30104961d548b372e51c5936c01ffc8eddd262bb98d7d7c","43fdc9abe6f8640fda4cdc55a1ee5f666d3fce554277043df925c383137ddf69","f0b09665c9d52de465687fbd3cfb65111d3ffc59ae00c6f42654150f3db05518","72f8c078d06cff690e24ff2b0e118a9de2833dcebf7c53e762dcb505ddf36a68","9705efb0fd901180de84ca4dd11d86f87fd73f99d6a5660a664c048a7487e385","f9b9d0950fdfb90f57e3f045fe73dce7fa6e7921b37622fc12e64fcd90afbd0f","e61b36e7fde608f8bb4b9c973d81556553a715eaef42a181a16ddd7a28da4ac7","03b8389b222af729eae0fb3c33366dcbb1f5a0102ce319bf1d7d5ee987e59fd0","2bf6be7c04db280fdd9b786764f8650c23f9f4d533791cb25a11b25314b76a55","dbb5fc7edd36bfba95cc4dd564e4458276ced30eed18bc05fbda948b3fda8686","c2b556c7cff0dabce2e31cb373ac61c14d8ebc35f1086dff30b39e9ec5357d0d","f958af01131076e8af55d28c4835a51063226ab488ca8738fdee38aeef7d0d33","9f3797b01e3d83d4e4b875699ae984f380ca86aa0a0c9df43ac5bba1cb1f8b7b","752b15ad1b34887adeaa838fc55f5d4ca399026afd266d4ed4db0e3db02eae4e","778331eaea1093451e50be9844bd2b6937c3bb81b0b1ee700624c9774ecfcf2b","0ca0dfc9f657d0822eca9530c7870b22a1d2a5fc48182bdd4d0e6e88e4ad9c35","5c746f034288e6842dd1589b169dcfcc16c5ce5abbd928889ab67aea4fe0b501","92ce6dbbcc135cffd09a58e19fef34bf351391bec92c40d849e4e9997d475769","99e77d092fed72b6b8578d00c7af004f76e98b30ba99f1947535eb4c04a51676","5e379df3d61561c2ed7789b5995b9ba2143bbba21a905e2381e16efe7d1fa424","f07a137bbe2de7a122c37bfea00e761975fb264c49f18003d398d71b3fb35a5f","fcc8beef29f39f09b1d9c9f99c42f9fed605ab1c28d2a630185f732b9ba53763","b5ef52a9f9083724decc5d060f0b34e3a480deed71c32d55ca16c214eb4cc928","5d3d7938b2d7d0a9f851276741327c2ae4c013e7eb420fc3f7caed3b79c8f37f","14df6b81e50a035e9e391558cf30a0420d03e2eb42c7db9c57f44b818e5d5179","f100912a3785eed4a3d29c12f5910b101af9353454de5ddba9b4d43456c56dd1","446439eacf81a163fd7dfc53b28f80deca3d13b250d67639739aa25aa4491090","98034cd285344125f7165a3bb68246d38ab35fabe7f6d6a7c8f80407d31f548d","06b4a23064991251512df4edc12341d5bc69a17b942da18372312d383c90eee7","0f898802705f9a534b537f1be6c57265080e0abd6993d935554c255e6d56cc1a","745efa7b6e27b7216cccede166a822b56acc41b10a8090966c8cf2c96239cb83","75b22c74010ba649de1a1676a4c4b8b5bb4294fecd05089e2094429b16d7840c","5615ccf831db2ffc82145243081ebdb60ea8e1005ee8f975d1c0c1401a9c894e","38682ed3630bb6ecdace80d5a9adc811fc20a419f1940446e306c3a020d083b9","cc182e6e4f691cd6f7bf7cb491247a4c7818f9f1cb2db1d45c65ff906e3f741b","a50599c08934a62f11657bdbe0dc929ab66da1b1f09974408fd9a33ec1bb8060","5a20e7d6c630b91be15e9b837853173829d00273197481dc8d3e94df61105a71","8d478048d71cc16f806d4b71b252ecb67c7444ccf4f4b09b29a312712184f859","e0eda929c6b9b628cdeb0e54cd3582cb97e64f28aab34612fc1431c545899584","9df4662ca3dbc2522bc115833ee04faa1afbb4e249a85ef4a0a09c621346bd08","b25d9065cf1c1f537a140bbc508e953ed2262f77134574c432d206ff36f4bdbf","1b103313097041aa9cd705a682c652f08613cb5cf8663321061c0902f845e81c","68ccec8662818911d8a12b8ed028bc5729fb4f1d34793c4701265ba60bc73cf4","5f85b8b79dc4d36af672c035b2beb71545de63a5d60bccbeee64c260941672ab","b3d48529ae61dc27d0bfbfa2cb3e0dff8189644bd155bdf5df1e8e14669f7043","40fe4b689225816b31fe5794c0fbf3534568819709e40295ead998a2bc1ab237","f65b5e33b9ad545a1eebbd6afe857314725ad42aaf069913e33f928ab3e4990a","fb6f2a87beb7fb1f4c2b762d0c76a9459fc91f557231569b0ee21399e22aa13d","31c858dc85996fac4b7fa944e1016d5c72f514930a72357ab5001097bf6511c7","3de30a871b3340be8b679c52aa12f90dd1c8c60874517be58968fdbcc4d79445","6fd985bd31eaf77542625306fb0404d32bff978990f0a06428e5f0b9a3b58109","980d21b0081cbf81774083b1e3a46f4bbdcd2b68858df0f66d7fad9c82bc34bc","68cc8d6fcc2f270d7108f02f3ebc59480a54615be3e09a47e14527f349e9d53e","3eb11dbf3489064a47a2e1cf9d261b1f100ef0b3b50ffca6c44dd99d6dd81ac1","b17f3bb7d8333479c7e45e5f3d876761b9bca58f97594eca3f6a944fd825e632","3c1f1236cce6d6e0c4e2c1b4371e6f72d7c14842ecd76a98ed0748ee5730c8f3","6d7f58d5ea72d7834946fd7104a734dc7d40661be8b2e1eaced1ddce3268ebaf","4c26222991e6c97d5a8f541d4f2c67585eda9e8b33cf9f52931b098045236e88","277983d414aa99d78655186c3ee1e1c38c302e336aff1d77b47fcdc39d8273fe","47383b45796d525a4039cd22d2840ac55a1ff03a43d027f7f867ba7314a9cf53","6548773b3abbc18de29176c2141f766d4e437e40596ee480447abf83575445ad","6ddd27af0436ce59dd4c1896e2bfdb2bdb2529847d078b83ce67a144dff05491","816264799aef3fd5a09a3b6c25217d5ec26a9dfc7465eac7d6073bcdc7d88f3f","4df0891b133884cd9ed752d31c7d0ec0a09234e9ed5394abffd3c660761598db","b603b62d3dcd31ef757dc7339b4fa8acdbca318b0fb9ac485f9a1351955615f9","e642bd47b75ad6b53cbf0dfd7ddfa0f120bd10193f0c58ec37d87b59bf604aca","be90b24d2ee6f875ce3aaa482e7c41a54278856b03d04212681c4032df62baf9","78f5ff400b3cb37e7b90eef1ff311253ed31c8cb66505e9828fad099bffde021","372c47090e1131305d163469a895ff2938f33fa73aad988df31cd31743f9efb6","71c67dc6987bdbd5599353f90009ff825dd7db0450ef9a0aee5bb0c574d18512","6f12403b5eca6ae7ca8e3efe3eeb9c683b06ce3e3844ccfd04098d83cd7e4957","282c535df88175d64d9df4550d2fd1176fd940c1c6822f1e7584003237f179d3","c3a4752cf103e4c6034d5bd449c8f9d5e7b352d22a5f8f9a41a8efb11646f9c2","11a9e38611ac3c77c74240c58b6bd64a0032128b29354e999650f1de1e034b1c","4ed103ca6fff9cb244f7c4b86d1eb28ce8069c32db720784329946731badb5bb","d738f282842970e058672663311c6875482ee36607c88b98ffb6604fba99cb2a","ec859cd8226aa623e41bbb47c249a55ee16dc1b8647359585244d57d3a5ed0c7","8891c6e959d253a66434ff5dc9ae46058fb3493e84b4ca39f710ef2d350656b1","c4463cf02535444dcbc3e67ecd29f1972490f74e49957d6fd4282a1013796ba6","0cb0a957ff02de0b25fd0f3f37130ca7f22d1e0dea256569c714c1f73c6791f8","2f5075dc512d51786b1ba3b1696565641dfaae3ac854f5f13d61fa12ef81a47e","ca3353cc82b1981f0d25d71d7432d583a6ef882ccdea82d65fbe49af37be51cb","50679a8e27aacf72f8c40bcab15d7ef5e83494089b4726b83eec4554344d5cdc","45351e0d51780b6f4088277a4457b9879506ee2720a887de232df0f1efcb33d8","6ab2a6257ae7bb05559841100c786c845fe465a90be7b904db9096c2fb14696b","e87de5e2e71fe0513d6fbd5951a5f8e35595243bbb88fe00b6b2d9383f62fe59","ebb4f551ea58b96443365f487e5c49396c4811dc51e2fd5c3c45928e93047278","7cd0fabd9e9ae5a8faabc2f70d6d9ddd89c65719a30917eacdae9049c16e8a16","bb665725dcc2e2406c572a63038791a7118802ebd947c0e76b0eb38ccd99926c","0c58b5a414a48f68bfea86556a22f505bac4ce0e67ddd5e40e387a4641ce2b78","13de2d120d9122bbff92dca664ebd180241a856d23e705598eb259621a838f0f","2d072cf71b05c374b0406736ae1f402f4ebac96fab8e9e4d6a2ea4d147b1c26e","1507881fb12592d860e8b731a79cccd5880a9e3a3fdb71c8eeb08987420f7c8d","63e64a301fdbb7fb0b58e81b282608594b700e1af51d509de949e88e711a70e8","d5c19655468e29f60c871b21e73af8ebc653f736e7123ade916f22c4a5f80ce5","50aa290ee8f3ba75c7a3653613ead6594e2e034a7627b249c9a400858cac79f5","9138338d4fff57ba42d57c7316ad1d055b72b90c9e1acbbfa6cfe16db201802a","d5c19655468e29f60c871b21e73af8ebc653f736e7123ade916f22c4a5f80ce5","863416d62eb7dfa12b63b23c112fd0831aa330816c39d64ca88898ebe5fd62a3","9325a5ce0f09132607e27e599b568e3a67f80ea932f6bbb08bdc1bb7727e11a3","6a8649609161e2794b383ba275b0a6cb4a072dde7b954648f83dc6cdf1bfe4a8","6d3101b183ea67ef606b93fe42127f30b2db5ac3b72c34ca9d6d8b00eb85d0f6","f5d7a36ff056cc314b0f61c89a03c4c36a24183b246e61d958e75e86521304cd","f961ddb0abe88c9815a81132cc9984f0294fd920174fccbdde73d156b8a4ab39","6c951235cbf3c324a20c0e2dfdd013d7b226b0c2a72dbd84925682a8d7199237","aba578ce97acb630b406ffb6ed31302dbd8d2ffcfd671194b1d8704825086e05","3a971ea3e36685b96f24fbd53a94ad8dc061711b84e51fde4cf201f7041e618d","77234d8682b67d78748cb61a63407104dc2c8e3196dcf15a454aae26b42f3ee7","c8be9283a381044a392a0687af5d98d3f51cbada2320b1801a82c948b6e39499","d5cdc145bf5ec321674e31804c075ad408a68c86877ce293970e03634d3709f1","85052c71d72b9b017c88179f57a464d66e22619c7acd7d83b117a79cf1608979","9b6c162d20e2ad4abdcff61a24082564ac59e63092220618162aef6e440c9228","b0874729266d9f7fafb9ff1127fcbad2cf7972b5dcc1fdc104be79266a708bc2","9f9e5bae412fa5909fae636d6733aee27a108cc2ed5b13980611016336774d3c","662fe197bba64bd3f17ee118058cd2d0d2dbe33d7c0c865fd6365d90bfc44e1e","030519c351f800551cac2658038804969ca4584d2c0175a710602ac234ca1340","0278a6939ca83cd040b08ff8c5fc7838b6693ddc52f22526bf158e6b10e0246c","c2d6206e5ba4fd3063b01218c2b3b997afc1cfbeb49fcee991fa8595842ce53d","e9d61a89974e5d8231edab199fe1a5f912a344febc74146611c013435f906de3","813e6dc3098dc7b331ed17bb4a3f96cda83748e989a41c469160ef23776af0f4","3c0913724967da385abf69e4f50dc51d5de5ad7c13d8bedd6746063aab7dd6e0","1fdd00179cddca98c8cfa1c403016f6703a8c34858cfeda33769d86da188ff8d","47ca123995420c8579f010179d3d5e61399ce538fc6386f2438193d48c0013b9","599d6ebec95d21df7ee33d8eb8f0b791ffac4a32026f32cf91fdf417153be473","323a75e01c89a50bb8827d1d624e72c20b0d81d4647a30ee6a695dbb4d76f3b5","d1f010c19eb9c8190bd0859fa3b6f4975543b912b8b85e20bbb0b5bfbdf4d2b3","de4ccc96cef3f97fab148640799abb32a24b567a902a8233913f98481e3131bf",{"version":"801934aa449fe6df584bccdcc5d5b9280295cb7ac84918b6014fc5086e6f9ff6","affectsGlobalScope":true},"6af760fb9ea02dc807c5053d8aee86389c4fce72fbb26af7b9568cac6c4710d5","c62c4ba5e910b4523f7e7adf4a55ec45c2bac99d9d8e9b0fe0c2a800a6f641b9","92131434f876fdd6fcbc40bd54a9d7500c66974362b16bd42641f990468587f4","8cf023c0bd57992fdd2ce6a7030a1874f49c8edc62eaffa9bfffcf18d2a2a1a2","8ea8f3040e38fb50d7dc3653f3b8a0dbb5244e82111576f99ce096bdc0fbf94c","48ed788ad126545a6156fcc37cd3bcf17de18a3e3fe6b6ef62cfb8140d1a45a2","63c271a745f628ffd4bd7ad0a63b021c362c9bd6bf8b18441a7162892395a214","a867ba47f71fe3993cef54246893ff8f01411e12e411d8cf1bd038a448b36404","6a8096993458a3d71229031aa7415974eb5b47b320213e29660adfb519d6a3f4","cb7996a1af5b1d276483cd0c9b9de6540eff021abc90a720511ff4464519a2ff","9df6ec68878d65bc690ea3a33ce3ef5aa8254c36bc5f8346c0c2fd1f3b88a35c","a4fad04c4acc8a4b195cbbccef4c55019104753d547d5c94441643ccc89108a0","0244c23ea642361f7c192c1f0cfff9c12cfa5f51f9b155edd5c0a89fef308d34","ac5da520487547013c3abae0933d6366f51db6df31d1993ddb931ce04b083269","3c69a83bde847af6fc3a53e1bb6b13cd06d38a27a142814b8dacc374f3b93284","5b46f7113f54565e7ffc83f2b474f557a1f54c7e5946769d5be220454656be73","fb58035d39c5759283cb73cfb3548aefe370aa3ad4e81fdb4e46f0979eb7669f","1311c325948b2d5576cebc70b1bf968d3446b4630802bef54120daf04ce1f625","d0b3609e8e7afed0fd0570152255458407e67249b94f6603afdfd68599423f21","17f4c5a1d6eaa87ea27eadcdff9085af3190533d98f799dda79a3af6f9a630ea","3e6f734ddf40e2e99ff7fff9568b7d9720663af9a0632c26a352c8d3270a3f0e","ec13f78303abcf550c5569dfae1446b8ceb89050f68ce04491481e72e8122ae2","a3fc57dbaa7f1efb010399ad4ef4fd9b462aa4e93bf74a9a34b099b97ffcc9cb","ffddd7ec6a450b0cb6f2f73f80de1df963ead312d7c81a8440268f34146ecb87","5d6a36ca0087fd6876df654d1b4192f0e402adfde994ad47e5c065da33692f9c","eb157a09c5f543d98644e2a99a785f9e0e91f054f9fecbf1c3e15831ff5d63a7","edd5530e2b1ccdf65093296e40a8634fcb11ecda3c164c31383a8c34cb04bc9d","9dfaf96d090fe8d96143465d85b4837661ae535143eea9ef99cd20df2e66338e","209d45c27e03c1417c42985252de6c25a2ec23abdc199d88e6139c88b93abd11","0ee5cdba58cfde3012bb9ff2e9edcc4e35a651373a2aa2c83ff9eb7df635419a","540f4dca27ea5a232828b6d91e1b2fce2720bdabaa4c1f3fbf59b672cc58bd8a","ba086b99d545ec6c9ff356989f076b5652ea1b09bcc65b87dfc43a5195a2efcc","c85d9776b36166b928ab1488d9224ebf970d41b0a35f09a3ee0b9bee3e698061","683196f606c5dab1c8c4a24a66d26e00f16f2d4b2a5abe25ebedd37d2954f930","9c3a1b01cba1238fb723ce06b6c163ef6c53be755394406782564d5c42c636b2","6e795e6270d39e918c7a0e62ac73793cda06fcf4b3692ee46583e15f5bf57ab8","0e821ef1eb67fa6144ea4de4277d913f5b1982d7407afd5f93754a8239d41554","5c09195ef359ffa9c6bbdb4fefb101d87ede4b9e9c28213faf5b45d102e4c609","80b4d93a4dcc90a12f6f4bb7c6851a8182ae29e556716d0d80b5c012a5ef554a","2556ef9d1820e0b6bbca6dd65a50ea64f525c4d8247ab50dff44c3f0d14a5643","cbd1c836db190d6e3add07165afc228f04e1f6170e1fe3aa5e6fc24a7e9573a3","9b13881feb958237232586d888a10a39d47cdffe3ee34688ed41888fa7baad94","122fe82cf5af80f0b26832b258b537b7dfe3ec28449c301b259ab10204b50d45","c467dada8fea6d60dff8a8be2675f737cacc76e14e50b72daa0f0710376df84b","9cb80bba611c2dd155a446ce424fe4bb1df2129751bc9416b7e42c055d1ddbff","44f41abb29bf3f4c52270d8119a96c935131e42a9186da15216a76b35e793b4e","043783bebe87efb440183c9ebc8c4fdc1bb92060a5a0f7ce847e30dee7013ac3","e3dc0a97a59dea936b4fb7b1f6f4117b4aac9c86d0cd08b69bab2d0532a8a5e3","5d897601f8a4fe913057019d8211b99b06e3138f625a0cfb601d074f4278271d","cfde5d194dd858ad68f910defaed5b0d28730f8bf38359a9265a93ab29bc7bef","16b21bbe6ad41019c071677877b8fc5dbc8d39a8b0406f020261c5f6f3894be3","f20aae41b169cddcbf3fde8ac380443182c8d7225194e788c404d9e11e6dc75d","87fd9a98cb1e689320ab89adc65e85d140a61260b4f66d12c777f4bd7cae2060","c48566cb13403fca44192b4528e3f2ac993869d39526bd42cd2f2167c0285add","efae20e0c581240c7522e04829da4f0453ca263068596554d4b0e27878c7dfac","3af68ef927788cda7daab34be513fa4508229fdc6e5130d564a0a1ccb3fefafe","bbbd2cbb15a37d5f4dd54ad8c7c537d3df8352117523030fcec7dcbe62a05a58","b50d24ebc117f8805332e7e260e9587f572bb7b2ff0ca1ff6cfafb38015781f3","5cc8b8e18fe7fefab4b3c53a39467b5a0deb4200abae7f063ff0624b9e856c51","8e990781eb0107c25429b1274a31a4f3866a9a46290cce40f354b2a6e71c6c21","8616706e4bd72987bd86c1b4afafa90fa2d4ef2f71708de03a823ab4e9b48e60","b9ce4613536386a98897f1e3d8f61a851ce6cb34dc3c9db4f2ef5f55f007e9e1","77fe56751d7615743937268c72d797fba28309f13ec9079c018b232040fca86a","31b5f53e3d57470830e87f9e03c02d4569ac81d4a758fdda75092f9a3f58beba","d765fbab22fd7003a65ed670100362ec1c90d55a772e6773a774135594e7ea41","1bf86149ef215f258d479695aa35ac89a3d34a6356a6df04e1b5db869289e563","58f4da9e99a4bdbd2f54eeb9303d5b5634b25423d729d44abb3fc55c925495b3","f75cd30f162c2af5e5aca39c01c1a521bfa034fae523793de872815a3468bc08","0cf1123db73dabd86466a462375a6addae52f58d23030c6033f8aadc23539a36","e29cef4158591ed213b1c2cba8988237b1ff369f7a6ecd8cb8ac0302bad1fba8","5307876e4d0021ea01235eb2f7c24671f3d8b37590f4b446cd132a4e1dc9a335","92550acd737790dc60c4c130e6aac78656dd48a8334a4882f40e7f86bdf7a590","3df821880914f8bb3c8107b1107be75c8ddbe2120a2cefabbaf9b65936b5f4dd","20626e4260b7d621745b2e78e883d9de7cc94ec346ef13344dd96eb479813870","078b7043bea0968860374bf4671ed74dd9f6be4e28ab659517d81f74be463c51","68b139ebb9a7f3ee4ded6286d74f978a47968727665120f3bfc560476ce33c4d","56d02c29b2fd39b1b1a1265df291f3f98e6ec3e6119aff9f4cfa44fe888efaa7","2d01884891da6495cb4a2f060e4898209a507e711464c4c1480df85264e863ed","620eb3b3aafe33839ee0f50e2cb237450f066fd88c8367cd15d75d02f7c9146f","6a5a3a7ae4e448668f8986632d2b6adfeebfdc06b0f9256f35c10ec148fa01f0","080b1aa93227952b4dd74b9d2c6e4f6002eb8403533749116a1c53bb9961c02d","874087eec1d457f6e3baf5ac46c42ea200e55040b394fac667aa3a64c49f5f6c","6e8a5b04a18abb192abc89d7219b9c6f633cb3136777ec808673a65f111ca749","6db505486e882a6688c5525cb65f6f06d3c5f16f03f329fbdec01dd379c97f96","d74d2a92b54f95e47d2b76bd5ee516aab7ae93afb79cd34c6681dd29eb09e72a","747e6326a724bc54f799a466a5b5c4978a601a04a063a5bdabe150af2f25b9e2","b57e22e53b56cca7a57bfcfb234aa6a66f9b9e4c07159d7388f94f17a3eaee2c","e47709ec4d1618ef429648cd8ef967aef2005526b34fcbfac33037add347dc71","b81abb3e47fbbb3af41fa75bada89bbcfa4b0feed9a0d6d4b19ed1ce1033b53c","15b330546e9784461058e5fd6e2346bf272140fa6f0cda34e193ae501d8b17b1","4d8ce72fd080bf9a46bdcc274bcbacccedd66d84e203966b197ac25a96932183","73327e6ae34e3f6591877fb75b451cf620cbbd76ee2b678213a9f793633cd0d3","3f1ba2f69944fa346789db7f60d53c9bec00032de0d797967978dea42e77b941","3f5df31539fee4816b97d4e45b4344fbdaf3ca59f6df941f8d780ee441e92cc1","50aaf44eb4d0e086af13729b3471a0a7dce95ea35ebd21c762ba26e203134b2e","3857c1773b8503c3ca45b7bc09ac89c3930c85ce93021054503f73d5d9101b5c","72702bd07fd6fb3ef64aadbcb909103aadfe71ee76e9fdeb11e0c92693cff6cb","f0dd6f7c9783637655478db7d7caf6becd41a79d54482aa59578ce88ab38e9bf",{"version":"cd756ccdabf433dd02b84d755383e489f14b3c1aede0477783aa04830fd5d695","affectsGlobalScope":true},"a4c88dbecdf8ee0c79f5b7c2bf31cd77e593f5d78384e2b674f67d754a549a9e","9cbdff04326da794ba008c0fc977ab062d1fe3fa2e9759654c72ffbe54b64a7c","aa60f8d20d36116fe05edaab24adee3c275209f71b65e272692cf99daf9489e1","150855f967a6490161d5aeed4cc4adf31fcb8f5dbe54b75799c12b8687fc9cc2","cf08b7139adc21b94204e3d4b3daf9946e3462a9e3fdc3e94c87e767e7936e20","47ddb601df40bfa01cebdd06ee8b87d0b72aa1259a4ceba3ad3b5cf68130112a","6b6392704ddb3f50e647dbbb716782bdd0cf8ea9cc134aae256a26223e632b47","afc3ad2a50f7f4de908e26fcf467e09ab8528c0e90f91e602b4865d953839228","df90b0c6b1d81851364c4d97fa23b91a993482bcf4a7bed7c7a24aa41632d494","03c0bc80f67c6f75b02341fbeb9f6ee92c66b90597729377f478885e6ad15a88","11ee9ab699b4619d217c640d917ca198f58066a86bd58c2917197d62aa6601e0","cf9d589d9e73bf32c8e7a6cae6b4a1cf9bef39e5594072533fdce985581a6ddc","959544feb1ca2df29eec6c500f27ea10f4885df245ebd8418fb4b87914614383","6548ab4b57eb9d092471a04513091673345f2fd95d5b876f600402ea8d603ee0","2793e8c6a023d26f78d6777a6d7f20fae3a9a8169863d46d8d54c73071851232","d0f11e830aa1350a31d9c00a0197243e9711e4882947aef53a96c629f405cb10","6610b9f45f1f71d2b1fb67df49cbcabe3f9e668a1ccb7d8328a51407b259ffb3","abbcc437e0792ab2fe08797ceca1ec85a95ec413c51612313b18ab8e75f690f6","e29d76ef1183ac0edf94b4712b6e51730c447c7e773e75ceb44a720b0c9a9fd9","4ee6dc3424998eede9a2a9b114acaaf7969cdda67baf82ba2c9cf88a8eec0ab1","26958d6f77e6db2425ca65df0fbfaba439396ef7f4457f5643fc32e4b62568a6","5d697a4b315cc5bb3042ae869abffd10c3b0d7b182cda0e4c45d8819937e5796","89b040dec8fcfc1de98827e1f4d4977e6ff5d3302c6790e9f10b54b916e1c742","6ee58aa536dabb19b09bc036f1abe83feb51e13d63b23d30b2d0631a2de99b8f","8aceb205dcc6f814ad99635baf1e40b6e01d06d3fe27b72fd766c6d0b8c0c600","299567f84bfedd1468dca2755a829cb19e607a6811673788807dc8921e211bc9","795d9fb85aad92221504db74dd179b506bd189bba0c104426f7e7bb8a66ffee5","1311bc194e0a69fe61031e852c1c0b439e2a2a3d1d5e2d8ff795499b9f283459","4b7ce19369d7e7fae76720c2c6c7f671bf3fa0f7093edb864f1ac358ca7c456c","c972ef44deca1fa8fab465915ffa00f82e126aacf3dfc8979c03b1b066ce5bb6","30285a1011c6d6b52f3ba3abb0a984be8148c05cdefb8eb6eb562335a3991f35","e0de9f50e80fed1cc161b50e8e68dc056e38df75a4ef667a06b1922e372de169","6a8b31be08b212d1fc96de0ddd1ea49f32382ba712fea24c70bb56447f643f82","19ac6d624e4c18de4584db4bbdbc55387dbe3d19b3c134e50346bdf165658a17","54e3798c2801e8f3bc7a825d3d26c6a80ce763e19e6cb0b714594c430ef72332","70b8333214aadaccda8d38435911d3e3a686e503837dfda6b8c3f8c83e05729b","f3815045e126ec1b9d224782805a915ae01876a1c7d1eb9b3e320ffadbd63535","d07557f21b2ad690bfe37864aa28090bd7d01c7152b77938d92d97c8419c7144","b843ea5227a9873512aa1226b546a7e52ea5e922b89461f8b202a2f2a3f0b013","64b4d440f905da272e0568224ef8d62c5cd730755c6d453043f2e606e060ec5a","d6b58d955981bc1742501b792f1ab9f4cba0c4611f28dcf1c99376c1c33c9f9c","f0b9f6d5db82c3d1679f71b187c4451dbc2875ba734ce416a4804ad47390970a","a5c38939c3e22954a7166d80ab931ac6757283737b000f1e6dc924c6f4402b88","31a863da9da2a3edec16665695bdbc3134e853195f82dafec58e98c8e1bb3119","b382a659f417df3606f2fbd2d39a02f0aa81d846cd361e79656e135a7896b779","af21e37363b40696508be1e0f1189664d17bc215ac5e64c05f7eb086a6f2ea72","df470b1c65fc51db9486ced8ff89d19c5fa2cfc5c6b3eb32d6cbab354499801e",{"version":"a6703b8328a763c5148eddf07c5801c4b67de507bc25459532d0c0c6622d11c2","signature":"68260f4ebe8f11c39b1d43d6ea75d76da4e81e2965414db9b3bd5ef2a21cdf0e"},{"version":"17ff6bb89c80df67da92a4c4d32ddccef85ce1fc2c56147161d29f638e2a1a87","signature":"acf1e448964971d594529ec272a231c39326bafde595da38cd0797266d3b446f"},"40d81f5f052d5954b51f4f5ec258a2231cdba79232e823ba93dc6dce2af4a7ff","4489c6a9fde8934733aa7df6f7911461ee6e9e4ad092736bd416f6b2cc20b2c6","2c8e55457aaf4902941dfdba4061935922e8ee6e120539c9801cd7b400fae050","8041cfce439ff29d339742389de04c136e3029d6b1817f07b2d7fcbfb7534990","670a76db379b27c8ff42f1ba927828a22862e2ab0b0908e38b671f0e912cc5ed","9d38964b57191567a14b396422c87488cecd48f405c642daa734159875ee81d9","069bebfee29864e3955378107e243508b163e77ab10de6a5ee03ae06939f0bb9","8c95f96ccd4be0674944077aec1e4f2cccd515ca06d4327562dd017250e7d3fc",{"version":"64d4b35c5456adf258d2cf56c341e203a073253f229ef3208fc0d5020253b241","affectsGlobalScope":true},"ee7d8894904b465b072be0d2e4b45cf6b887cdba16a467645c4e200982ece7ea","f3d8c757e148ad968f0d98697987db363070abada5f503da3c06aefd9d4248c1","bc3cba7b0af2d52e7425299aee518db479d44004eff6fbbd206d1ee7e5ec3fb5","afe73051ff6a03a9565cbd8ebb0e956ee3df5e913ad5c1ded64218aabfa3dcb5","035a5df183489c2e22f3cf59fc1ed2b043d27f357eecc0eb8d8e840059d44245","a4809f4d92317535e6b22b01019437030077a76fec1d93b9881c9ed4738fcc54","5f53fa0bd22096d2a78533f94e02c899143b8f0f9891a46965294ee8b91a9434","0d14fa22c41fdc7277e6f71473b20ebc07f40f00e38875142335d5b63cdfc9d2","d8aab31ba8e618cc3eea10b0945de81cb93b7e8150a013a482332263b9305322","462bccdf75fcafc1ae8c30400c9425e1a4681db5d605d1a0edb4f990a54d8094","5923d8facbac6ecf7c84739a5c701a57af94a6f6648d6229a6c768cf28f0f8cb","7adecb2c3238794c378d336a8182d4c3dd2c4fa6fa1785e2797a3db550edea62","dc12dc0e5aa06f4e1a7692149b78f89116af823b9e1f1e4eae140cd3e0e674e6","1bfc6565b90c8771615cd8cfcf9b36efc0275e5e83ac7d9181307e96eb495161","8a8a96898906f065f296665e411f51010b51372fa260d5373bf9f64356703190","7f82ef88bdb67d9a850dd1c7cd2d690f33e0f0acd208e3c9eba086f3670d4f73",{"version":"ccfd8774cd9b929f63ff7dcf657977eb0652e3547f1fcac1b3a1dc5db22d4d58","affectsGlobalScope":true},"d92dc90fecd2552db74d8dc3c6fb4db9145b2aa0efe2c127236ba035969068d4","96d14f21b7652903852eef49379d04dbda28c16ed36468f8c9fa08f7c14c9538","b8442e9db28157344d1bc5d8a5a256f1692de213f0c0ddeb84359834015a008c","458111fc89d11d2151277c822dfdc1a28fa5b6b2493cf942e37d4cd0a6ee5f22","da2b6356b84a40111aaecb18304ea4e4fcb43d70efb1c13ca7d7a906445ee0d3","187119ff4f9553676a884e296089e131e8cc01691c546273b1d0089c3533ce42","febf0b2de54781102b00f61653b21377390a048fbf5262718c91860d11ff34a6","6f294731b495c65ecf46a5694f0082954b961cf05463bea823f8014098eaffa0","0aaef8cded245bf5036a7a40b65622dd6c4da71f7a35343112edbe112b348a1e","00baffbe8a2f2e4875367479489b5d43b5fc1429ecb4a4cc98cfc3009095f52a","68a0d0c508e1b6d8d23a519a8a0a3303dc5baa4849ca049f21e5bad41945e3fc","3c92b6dfd43cc1c2485d9eba5ff0b74a19bb8725b692773ef1d66dac48cda4bd","b03afe4bec768ae333582915146f48b161e567a81b5ebc31c4d78af089770ac9","df996e25faa505f85aeb294d15ebe61b399cf1d1e49959cdfaf2cc0815c203f9","4f6a12044ee6f458db11964153830abbc499e73d065c51c329ec97407f4b13dd","8841e2aa774b89bd23302dede20663306dc1b9902431ac64b24be8b8d0e3f649","916be7d770b0ae0406be9486ac12eb9825f21514961dd050594c4b250617d5a8","254d9fb8c872d73d34594be8a200fd7311dbfa10a4116bfc465fba408052f2b3","d88a5e779faf033be3d52142a04fbe1cb96009868e3bbdd296b2bc6c59e06c0e","2ccea88888048bbfcacbc9531a5596ea48a3e7dcd0a25f531a81bb717903ba4f","d8f7109e14f20eb735225a62fd3f8366da1a8349e90331cdad57f4b04caf6c5a","cf3d384d082b933d987c4e2fe7bfb8710adfd9dc8155190056ed6695a25a559e","9871b7ee672bc16c78833bdab3052615834b08375cb144e4d2cba74473f4a589","c863198dae89420f3c552b5a03da6ed6d0acfa3807a64772b895db624b0de707","8b03a5e327d7db67112ebbc93b4f744133eda2c1743dbb0a990c61a8007823ef","86c73f2ee1752bac8eeeece234fd05dfcf0637a4fbd8032e4f5f43102faa8eec","42fad1f540271e35ca37cecda12c4ce2eef27f0f5cf0f8dd761d723c744d3159","ff3743a5de32bee10906aff63d1de726f6a7fd6ee2da4b8229054dfa69de2c34","83acd370f7f84f203e71ebba33ba61b7f1291ca027d7f9a662c6307d74e4ac22","1445cec898f90bdd18b2949b9590b3c012f5b7e1804e6e329fb0fe053946d5ec","0e5318ec2275d8da858b541920d9306650ae6ac8012f0e872fe66eb50321a669","cf530297c3fb3a92ec9591dd4fa229d58b5981e45fe6702a0bd2bea53a5e59be","c1f6f7d08d42148ddfe164d36d7aba91f467dbcb3caa715966ff95f55048b3a4","f4e9bf9103191ef3b3612d3ec0044ca4044ca5be27711fe648ada06fad4bcc85","0c1ee27b8f6a00097c2d6d91a21ee4d096ab52c1e28350f6362542b55380059a","7677d5b0db9e020d3017720f853ba18f415219fb3a9597343b1b1012cfd699f7","bc1c6bc119c1784b1a2be6d9c47addec0d83ef0d52c8fbe1f14a51b4dfffc675","52cf2ce99c2a23de70225e252e9822a22b4e0adb82643ab0b710858810e00bf1","770625067bb27a20b9826255a8d47b6b5b0a2d3dfcbd21f89904c731f671ba77","d1ed6765f4d7906a05968fb5cd6d1db8afa14dbe512a4884e8ea5c0f5e142c80","799c0f1b07c092626cf1efd71d459997635911bb5f7fc1196efe449bba87e965","2a184e4462b9914a30b1b5c41cf80c6d3428f17b20d3afb711fff3f0644001fd","9eabde32a3aa5d80de34af2c2206cdc3ee094c6504a8d0c2d6d20c7c179503cc","397c8051b6cfcb48aa22656f0faca2553c5f56187262135162ee79d2b2f6c966","a8ead142e0c87dcd5dc130eba1f8eeed506b08952d905c47621dc2f583b1bff9","a02f10ea5f73130efca046429254a4e3c06b5475baecc8f7b99a0014731be8b3","c2576a4083232b0e2d9bd06875dd43d371dee2e090325a9eac0133fd5650c1cb","4c9a0564bb317349de6a24eb4efea8bb79898fa72ad63a1809165f5bd42970dd","f40ac11d8859092d20f953aae14ba967282c3bb056431a37fced1866ec7a2681","cc11e9e79d4746cc59e0e17473a59d6f104692fd0eeea1bdb2e206eabed83b03","b444a410d34fb5e98aa5ee2b381362044f4884652e8bc8a11c8fe14bbd85518e","c35808c1f5e16d2c571aa65067e3cb95afeff843b259ecfa2fc107a9519b5392","14d5dc055143e941c8743c6a21fa459f961cbc3deedf1bfe47b11587ca4b3ef5","a3ad4e1fc542751005267d50a6298e6765928c0c3a8dce1572f2ba6ca518661c","f237e7c97a3a89f4591afd49ecb3bd8d14f51a1c4adc8fcae3430febedff5eb6","3ffdfbec93b7aed71082af62b8c3e0cc71261cc68d796665faa1e91604fbae8f","662201f943ed45b1ad600d03a90dffe20841e725203ced8b708c91fcd7f9379a","c9ef74c64ed051ea5b958621e7fb853fe3b56e8787c1587aefc6ea988b3c7e79","2462ccfac5f3375794b861abaa81da380f1bbd9401de59ffa43119a0b644253d","34baf65cfee92f110d6653322e2120c2d368ee64b3c7981dff08ed105c4f19b0","7d8ddf0f021c53099e34ee831a06c394d50371816caa98684812f089b4c6b3d4","7d2a0ba1297be385a89b5515b88cd31b4a1eeef5236f710166dc1b36b1741e1b","9d92b037978bb9525bc4b673ebddd443277542e010c0aef019c03a170ccdaa73","ab82804a14454734010dcdcd43f564ff7b0389bee4c5692eec76ff5b30d4cf66","fab58e600970e66547644a44bc9918e3223aa2cbd9e8763cec004b2cfb48827e","bae8d023ef6b23df7da26f51cea44321f95817c190342a36882e93b80d07a960","ae271d475b632ce7b03fea6d9cf6da72439e57a109672671cbc79f54e1386938"],"options":{"composite":true,"declaration":true,"declarationMap":true,"emitDeclarationOnly":true,"esModuleInterop":true,"inlineSources":true,"module":1,"outDir":"./types","rootDir":"../src","sourceMap":true,"strict":true,"target":7},"fileIdsList":[[434],[72,108,109,110,125],[109,110,126,127],[108,109],[108,125,128,131],[108,128,131,132],[129,130,131,133,134],[108,131],[108,125,128,129,130,133],[108,116],[108],[72,108],[60,108],[112,113,114,115,116,117,118,119,120,121,122,123,124],[108,114,115],[108,114,116],[108,135,172,173],[172],[173,174],[108,167],[167,168,169,170,171],[108,139,146,147],[108,139,146,147,167],[108,139,146,147,151],[108,139,146,147,148,150,151],[108,139,146,147,149],[108,139,146,147,152,153,155,156],[145,167],[137,146,147,152,153],[139,145,146],[108,139,146,147,152],[108,139,146,147,150],[108,139,146,147,163],[108,139,146,147,164],[111,136,139,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166],[137],[137,138],[72,108,172,175,184],[175,185],[185,186],[242,243,244,245],[108,125],[247],[135,219,246],[300,309,310,313],[300,309,312],[300,309,311,313],[301,304,305,307],[301,302,303],[304,305,307,308],[301,302,306],[312,318],[300,309,312,318],[309,312,318],[309,312,314,315,316,317],[300,309,310,311,312,313,318],[300,309],[301],[196,219,252],[219,266,268],[219,268,269,290],[196,219,249,251,252,265],[196,219],[250],[253],[196,252],[254,255,264],[263],[251,252,257,263,265,266,267,269,291,292,402],[196,219,403],[219,257,265],[258],[256,259,260,261,262],[196,219,250,252,255],[219,267,401],[190,196],[179],[178],[108,177,179],[179,180,181,182,183],[108,177],[178,219,281,282],[283],[108,177,219,283,285],[108,177,178,219,281,283],[285],[282,283,284,285,286,287,288,289],[108,177,284,287],[282,287],[219,281],[219,270],[270,271],[270,271,272,273],[219],[108,219,293],[219,398],[367],[219,293,367,395,398,399,400],[108,219,274,293],[294,295,296,297,396,397],[190,196,395],[190,196,296],[328],[328,340],[328,329,342,344],[328,340,343],[328,334],[328,333,335],[333,334,335,336],[338,339],[329,330,331,332,337,340,341,342,343,344,345,346],[328,347,348,349,350],[328,347,398],[382],[395],[385,386,387,388,389,390,391,392,393],[219,320],[367,391,398],[320,395,398],[196],[320,321,368,371,381,382,383,394],[196,351,367],[395,398],[319,321],[321],[398],[368],[219,371],[298,299,322,323,324,325,326,327,369,370,372,373,374,375,376,377,378,379,380],[219,373],[298,299,322,323,324,325,326,327,369,370,372,373,374,375,376,377,378,379,398],[219,319,320],[290,381],[219,321],[365],[196,352],[353,354,355,356,357,358,359,360,361,362,363,364],[352,365,366],[199],[196,199],[197,198,199,200,201,202,203,204,205,206,207,208,211,212,213,214,215,216,217,218],[190,196,197],[135,199,205,207],[210],[199,200],[196,214],[108,142],[140,141,144],[140,143],[108,140],[410,411],[434,435,436,437,438],[434,436],[209],[441,442,443],[73,108],[446],[447],[458],[452,457],[461,463,464,465,466,467,468,469,470,471,472,473],[461,462,464,465,466,467,468,469,470,471,472,473],[462,463,464,465,466,467,468,469,470,471,472,473],[461,462,463,465,466,467,468,469,470,471,472,473],[461,462,463,464,466,467,468,469,470,471,472,473],[461,462,463,464,465,467,468,469,470,471,472,473],[461,462,463,464,465,466,468,469,470,471,472,473],[461,462,463,464,465,466,467,469,470,471,472,473],[461,462,463,464,465,466,467,468,470,471,472,473],[461,462,463,464,465,466,467,468,469,471,472,473],[461,462,463,464,465,466,467,468,469,470,472,473],[461,462,463,464,465,466,467,468,469,470,471,473],[461,462,463,464,465,466,467,468,469,470,471,472],[56],[59],[60,65,92],[61,72,73,80,89,100],[61,62,72,80],[63,101],[64,65,73,81],[65,89,97],[66,68,72,80],[67],[68,69],[72],[71,72],[59,72],[72,73,74,89,100],[72,73,74,89],[72,75,80,89,100],[72,73,75,76,80,89,97,100],[75,77,89,97,100],[56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107],[72,78],[79,100,105],[68,72,80,89],[81],[82],[59,83],[84,99,105],[85],[86],[72,87],[87,88,101,103],[60,72,89,90,91],[60,89,91],[89,90],[92],[93],[72,95,96],[95,96],[65,80,89,97],[98],[80,99],[60,75,86,100],[65,101],[89,102],[103],[104],[60,65,72,74,83,89,100,103,105],[89,106],[108,176],[480,519],[480,504,519],[519],[480],[480,505,519],[480,481,482,483,484,485,486,487,488,489,490,491,492,493,494,495,496,497,498,499,500,501,502,503,504,505,506,507,508,509,510,511,512,513,514,515,516,517,518],[505,519],[520],[524],[420],[422],[420,421,422,423,424,425,426],[420,422],[108,430],[108,428,429],[430],[231],[231,232,233,234,235],[220,221,222,223,224,225,226,227,228,229,230],[450,453],[450,453,454,455],[452],[449,456],[451],[189,191,192,193,194,195],[189,190],[191],[190,191],[189,191],[219,236,237,238],[237],[238],[188,237,238,239],[405],[405,406,409,413],[412],[219,407,408],[178,219,274],[219,275],[219,275,278],[275,276,277,278,279,280],[48,49,125,135,187,219,236,240,241,246,403,419,427,430,431],[432],[72,108,219,240],[404],[404,414],[404,415,416,417,418],[135,187,219,236,240,241,403,419]],"referencedMap":[[436,1],[126,2],[128,3],[110,4],[132,5],[133,6],[129,6],[135,7],[130,6],[134,8],[131,9],[117,10],[114,11],[121,12],[115,10],[112,13],[125,14],[119,11],[116,15],[118,16],[174,17],[173,18],[175,19],[170,20],[168,20],[169,20],[172,21],[154,22],[159,23],[148,22],[153,24],[152,25],[150,26],[157,27],[158,22],[160,28],[155,29],[147,30],[161,31],[163,32],[164,33],[165,34],[167,35],[138,36],[139,37],[185,38],[186,39],[187,40],[246,41],[242,42],[243,11],[245,42],[248,43],[247,44],[311,45],[313,46],[312,47],[308,48],[304,49],[305,49],[309,50],[307,51],[314,52],[315,53],[316,54],[318,55],[317,52],[319,56],[310,57],[303,58],[306,49],[268,59],[269,60],[291,61],[266,62],[249,63],[251,64],[250,63],[254,65],[253,66],[265,67],[255,63],[264,68],[403,69],[256,70],[258,71],[259,72],[260,63],[263,73],[262,74],[292,60],[402,75],[252,76],[181,77],[182,77],[179,78],[180,79],[184,80],[183,81],[283,82],[284,83],[287,84],[285,85],[286,86],[290,87],[288,88],[289,89],[282,90],[271,91],[272,92],[274,93],[270,94],[178,12],[399,95],[293,96],[400,97],[401,98],[294,99],[295,94],[296,94],[398,100],[396,101],[297,102],[328,94],[329,103],[330,103],[331,103],[332,103],[341,103],[342,103],[343,104],[345,105],[346,103],[344,106],[333,103],[335,107],[336,108],[334,103],[337,109],[338,103],[339,103],[340,110],[347,111],[351,112],[349,103],[348,103],[350,113],[383,114],[385,94],[386,115],[394,116],[387,94],[388,94],[389,117],[390,94],[392,118],[391,119],[393,120],[395,121],[368,122],[298,115],[299,123],[322,124],[323,125],[324,124],[326,94],[327,126],[369,127],[372,128],[381,129],[374,130],[373,94],[375,94],[376,96],[380,131],[377,126],[378,128],[379,115],[321,132],[382,133],[371,134],[366,135],[353,136],[362,136],[354,136],[355,136],[364,136],[356,136],[357,136],[365,137],[363,136],[358,136],[361,136],[359,136],[360,136],[367,138],[352,120],[197,120],[198,120],[200,139],[201,120],[202,120],[203,140],[199,120],[219,141],[207,142],[208,143],[211,144],[217,145],[218,146],[143,147],[142,11],[145,148],[140,11],[144,149],[141,150],[412,151],[439,152],[435,1],[437,153],[438,1],[408,11],[210,154],[444,155],[445,156],[447,157],[448,158],[459,159],[458,160],[462,161],[463,162],[461,163],[464,164],[465,165],[466,166],[467,167],[468,168],[469,169],[470,170],[471,171],[472,172],[473,173],[56,174],[57,174],[59,175],[60,176],[61,177],[62,178],[63,179],[64,180],[65,181],[66,182],[67,183],[68,184],[69,184],[70,185],[71,186],[72,187],[73,188],[74,189],[75,190],[76,191],[77,192],[108,193],[78,194],[79,195],[80,196],[81,197],[82,198],[83,199],[84,200],[85,201],[86,202],[87,203],[88,204],[89,205],[91,206],[90,207],[92,208],[93,209],[95,210],[96,211],[97,212],[98,213],[99,214],[100,215],[101,216],[102,217],[103,218],[104,219],[105,220],[106,221],[476,11],[177,222],[479,11],[504,223],[505,224],[480,225],[483,225],[502,223],[503,223],[493,223],[492,226],[490,223],[485,223],[498,223],[496,223],[500,223],[484,223],[497,223],[501,223],[486,223],[487,223],[499,223],[481,223],[488,223],[489,223],[491,223],[495,223],[506,227],[494,223],[482,223],[519,228],[513,227],[515,229],[514,227],[507,227],[508,227],[510,227],[512,227],[516,229],[517,229],[509,229],[511,229],[521,230],[525,231],[421,232],[423,233],[427,234],[425,235],[424,235],[428,236],[430,237],[429,238],[227,239],[229,239],[228,239],[226,239],[236,240],[231,241],[222,239],[223,239],[224,239],[225,239],[454,242],[456,243],[455,242],[453,244],[457,245],[452,246],[196,247],[191,248],[192,249],[193,249],[194,250],[195,250],[190,251],[239,252],[238,253],[237,254],[240,255],[406,256],[414,257],[413,258],[409,259],[275,260],[276,261],[277,261],[279,262],[281,263],[280,261],[432,264],[433,265],[404,266],[418,267],[417,267],[415,268],[416,267],[419,269]],"exportedModulesMap":[[436,1],[126,2],[128,3],[110,4],[132,5],[133,6],[129,6],[135,7],[130,6],[134,8],[131,9],[117,10],[114,11],[121,12],[115,10],[112,13],[125,14],[119,11],[116,15],[118,16],[174,17],[173,18],[175,19],[170,20],[168,20],[169,20],[172,21],[154,22],[159,23],[148,22],[153,24],[152,25],[150,26],[157,27],[158,22],[160,28],[155,29],[147,30],[161,31],[163,32],[164,33],[165,34],[167,35],[138,36],[139,37],[185,38],[186,39],[187,40],[246,41],[242,42],[243,11],[245,42],[248,43],[247,44],[311,45],[313,46],[312,47],[308,48],[304,49],[305,49],[309,50],[307,51],[314,52],[315,53],[316,54],[318,55],[317,52],[319,56],[310,57],[303,58],[306,49],[268,59],[269,60],[291,61],[266,62],[249,63],[251,64],[250,63],[254,65],[253,66],[265,67],[255,63],[264,68],[403,69],[256,70],[258,71],[259,72],[260,63],[263,73],[262,74],[292,60],[402,75],[252,76],[181,77],[182,77],[179,78],[180,79],[184,80],[183,81],[283,82],[284,83],[287,84],[285,85],[286,86],[290,87],[288,88],[289,89],[282,90],[271,91],[272,92],[274,93],[270,94],[178,12],[399,95],[293,96],[400,97],[401,98],[294,99],[295,94],[296,94],[398,100],[396,101],[297,102],[328,94],[329,103],[330,103],[331,103],[332,103],[341,103],[342,103],[343,104],[345,105],[346,103],[344,106],[333,103],[335,107],[336,108],[334,103],[337,109],[338,103],[339,103],[340,110],[347,111],[351,112],[349,103],[348,103],[350,113],[383,114],[385,94],[386,115],[394,116],[387,94],[388,94],[389,117],[390,94],[392,118],[391,119],[393,120],[395,121],[368,122],[298,115],[299,123],[322,124],[323,125],[324,124],[326,94],[327,126],[369,127],[372,128],[381,129],[374,130],[373,94],[375,94],[376,96],[380,131],[377,126],[378,128],[379,115],[321,132],[382,133],[371,134],[366,135],[353,136],[362,136],[354,136],[355,136],[364,136],[356,136],[357,136],[365,137],[363,136],[358,136],[361,136],[359,136],[360,136],[367,138],[352,120],[197,120],[198,120],[200,139],[201,120],[202,120],[203,140],[199,120],[219,141],[207,142],[208,143],[211,144],[217,145],[218,146],[143,147],[142,11],[145,148],[140,11],[144,149],[141,150],[412,151],[439,152],[435,1],[437,153],[438,1],[408,11],[210,154],[444,155],[445,156],[447,157],[448,158],[459,159],[458,160],[462,161],[463,162],[461,163],[464,164],[465,165],[466,166],[467,167],[468,168],[469,169],[470,170],[471,171],[472,172],[473,173],[56,174],[57,174],[59,175],[60,176],[61,177],[62,178],[63,179],[64,180],[65,181],[66,182],[67,183],[68,184],[69,184],[70,185],[71,186],[72,187],[73,188],[74,189],[75,190],[76,191],[77,192],[108,193],[78,194],[79,195],[80,196],[81,197],[82,198],[83,199],[84,200],[85,201],[86,202],[87,203],[88,204],[89,205],[91,206],[90,207],[92,208],[93,209],[95,210],[96,211],[97,212],[98,213],[99,214],[100,215],[101,216],[102,217],[103,218],[104,219],[105,220],[106,221],[476,11],[177,222],[479,11],[504,223],[505,224],[480,225],[483,225],[502,223],[503,223],[493,223],[492,226],[490,223],[485,223],[498,223],[496,223],[500,223],[484,223],[497,223],[501,223],[486,223],[487,223],[499,223],[481,223],[488,223],[489,223],[491,223],[495,223],[506,227],[494,223],[482,223],[519,228],[513,227],[515,229],[514,227],[507,227],[508,227],[510,227],[512,227],[516,229],[517,229],[509,229],[511,229],[521,230],[525,231],[421,232],[423,233],[427,234],[425,235],[424,235],[428,236],[430,237],[429,238],[227,239],[229,239],[228,239],[226,239],[236,240],[231,241],[222,239],[223,239],[224,239],[225,239],[454,242],[456,243],[455,242],[453,244],[457,245],[452,246],[196,247],[191,248],[192,249],[193,249],[194,250],[195,250],[190,251],[239,252],[238,253],[237,254],[240,255],[406,256],[414,257],[413,258],[409,259],[275,260],[276,261],[277,261],[279,262],[281,263],[280,261],[432,270],[433,265],[404,266],[418,267],[417,267],[415,268],[416,267],[419,269]],"semanticDiagnosticsPerFile":[436,434,126,109,128,110,127,132,133,129,135,130,134,131,117,114,121,115,112,120,125,122,123,124,119,116,113,118,174,173,175,170,168,169,172,171,154,159,148,153,152,150,157,158,160,155,149,147,146,156,162,161,163,164,165,167,137,138,139,136,151,166,185,186,187,241,407,244,246,242,243,245,248,247,311,313,312,300,308,304,305,309,307,314,315,316,318,317,319,310,303,301,302,306,268,269,291,266,249,251,250,257,254,253,265,255,264,267,403,256,258,259,260,263,261,262,292,402,252,181,182,179,180,184,183,283,284,287,285,286,290,288,289,282,271,273,272,274,270,178,399,293,400,401,294,295,296,398,396,297,397,328,329,330,331,332,341,342,343,345,346,344,333,335,336,334,337,338,339,340,347,351,349,348,350,320,383,385,386,394,387,388,389,390,392,391,393,384,395,368,298,299,322,323,324,325,326,327,369,370,372,381,374,373,375,376,380,377,378,379,321,382,371,366,353,362,354,355,364,356,357,365,363,358,361,359,360,367,352,197,198,200,201,202,203,204,205,206,199,219,207,208,211,212,213,214,215,216,217,218,143,142,145,140,144,141,410,412,411,439,435,437,438,408,210,440,441,444,442,445,446,447,448,459,458,443,460,462,463,461,464,465,466,467,468,469,470,471,472,473,474,209,56,57,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,58,107,75,76,77,108,78,79,80,81,82,83,84,85,86,87,88,89,91,90,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,475,476,477,478,177,176,479,504,505,480,483,502,503,493,492,490,485,498,496,500,484,497,501,486,487,499,481,488,489,491,495,506,494,482,519,518,513,515,514,507,508,510,512,516,517,509,511,521,520,522,523,524,525,421,420,423,422,426,427,425,424,111,449,428,430,429,230,227,229,228,226,236,231,235,232,234,233,222,223,224,220,221,225,450,454,456,455,453,457,452,451,189,196,191,192,193,194,195,190,8,10,9,2,11,12,13,14,15,16,17,18,3,4,22,19,20,21,23,24,25,5,26,27,28,29,6,33,30,31,32,34,7,35,40,41,36,37,38,39,1,42,188,239,238,237,240,406,414,413,405,409,275,276,277,278,279,281,280,432,431,433,404,418,417,415,416,419,47,48,49,50,51,52,43,53,54,55,44,45,46],"latestChangedDtsFile":"./types/index.d.ts"},"version":"4.9.5"} -\ No newline at end of file -diff --git a/dist/types/KeyringController.d.ts.map b/dist/types/KeyringController.d.ts.map -index 310d2853a09b2ce1c4aa6c457ba4450103127868..3715da4a8b1826fed478f8b7c8c3de80dafa9987 100644 ---- a/dist/types/KeyringController.d.ts.map -+++ b/dist/types/KeyringController.d.ts.map -@@ -1 +1 @@ --{"version":3,"file":"KeyringController.d.ts","sourceRoot":"","sources":["../../src/KeyringController.ts"],"names":[],"mappings":"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"} -\ No newline at end of file -+{"version":3,"file":"KeyringController.d.ts","sourceRoot":"","sources":["../../src/KeyringController.ts"],"names":[],"mappings":"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"} -\ No newline at end of file diff --git a/.yarn/patches/@metamask-network-controller-npm-19.0.0-a5e0d1fe14.patch b/.yarn/patches/@metamask-network-controller-npm-19.0.0-a5e0d1fe14.patch deleted file mode 100644 index 91cbc903b5c6..000000000000 --- a/.yarn/patches/@metamask-network-controller-npm-19.0.0-a5e0d1fe14.patch +++ /dev/null @@ -1,24 +0,0 @@ -diff --git a/dist/chunk-4ZD3DTQ7.js b/dist/chunk-4ZD3DTQ7.js -index e172d15715b3cb4f26c42e51f8c7ff7394075bfe..2c32427d0e005c8db2e4aa524609972fd57cba60 100644 ---- a/dist/chunk-4ZD3DTQ7.js -+++ b/dist/chunk-4ZD3DTQ7.js -@@ -341,7 +341,6 @@ var NetworkController = class extends _basecontroller.BaseController { - async initializeProvider() { - _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _ensureAutoManagedNetworkClientRegistryPopulated, ensureAutoManagedNetworkClientRegistryPopulated_fn).call(this); - _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _applyNetworkSelection, applyNetworkSelection_fn).call(this); -- await this.lookupNetwork(); - } - /** - * Refreshes the network meta with EIP-1559 support and the network status -diff --git a/dist/chunk-UG2NYGJD.mjs b/dist/chunk-UG2NYGJD.mjs -index c39eb49a4a1d2b4ddb78aadb4fb03446b1705528..73f4df206f489036ed52831ac685219074bb768b 100644 ---- a/dist/chunk-UG2NYGJD.mjs -+++ b/dist/chunk-UG2NYGJD.mjs -@@ -341,7 +341,6 @@ var NetworkController = class extends BaseController { - async initializeProvider() { - __privateMethod(this, _ensureAutoManagedNetworkClientRegistryPopulated, ensureAutoManagedNetworkClientRegistryPopulated_fn).call(this); - __privateMethod(this, _applyNetworkSelection, applyNetworkSelection_fn).call(this); -- await this.lookupNetwork(); - } - /** - * Refreshes the network meta with EIP-1559 support and the network status diff --git a/.yarn/patches/@metamask-network-controller-npm-20.2.0-98b1a5ae59.patch b/.yarn/patches/@metamask-network-controller-npm-20.2.0-98b1a5ae59.patch new file mode 100644 index 000000000000..63fff98c47e6 --- /dev/null +++ b/.yarn/patches/@metamask-network-controller-npm-20.2.0-98b1a5ae59.patch @@ -0,0 +1,33 @@ +diff --git a/PATCH.txt b/PATCH.txt +new file mode 100644 +index 0000000000000000000000000000000000000000..78b9156dc2b0bf7c33dadf325cb3ec0bfae71ccb +--- /dev/null ++++ b/PATCH.txt +@@ -0,0 +1,3 @@ ++We remove `lookupNetwork` from `initializeProvider` in the network controller to prevent network requests before user onboarding is completed. ++The network lookup is done after onboarding is completed, and when the extension reloads if onboarding has been completed. ++This patch is part of a temporary fix that will be reverted soon to make way for a more permanent solution. https://github.com/MetaMask/metamask-extension/pull/23005 +diff --git a/dist/chunk-BEL2VMHN.js b/dist/chunk-BEL2VMHN.js +index fcf6c5ad51d0db75cf0e3219a569e17437a55486..751447609c924e626c0f442931eb77687b160e42 100644 +--- a/dist/chunk-BEL2VMHN.js ++++ b/dist/chunk-BEL2VMHN.js +@@ -315,7 +315,6 @@ var NetworkController = class extends _basecontroller.BaseController { + */ + async initializeProvider() { + _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _applyNetworkSelection, applyNetworkSelection_fn).call(this, this.state.selectedNetworkClientId); +- await this.lookupNetwork(); + } + /** + * Refreshes the network meta with EIP-1559 support and the network status +diff --git a/dist/chunk-RTMQACMX.mjs b/dist/chunk-RTMQACMX.mjs +index fc6ae58a396aaa062e8d9a8de2cddd5ef073a5a4..2a6f811c10a0ed3fc943f4672b21a5d1c195c7cd 100644 +--- a/dist/chunk-RTMQACMX.mjs ++++ b/dist/chunk-RTMQACMX.mjs +@@ -315,7 +315,6 @@ var NetworkController = class extends BaseController { + */ + async initializeProvider() { + __privateMethod(this, _applyNetworkSelection, applyNetworkSelection_fn).call(this, this.state.selectedNetworkClientId); +- await this.lookupNetwork(); + } + /** + * Refreshes the network meta with EIP-1559 support and the network status diff --git a/.yarn/patches/@metamask-ppom-validator-npm-0.32.0-f677deea54.patch b/.yarn/patches/@metamask-ppom-validator-npm-0.32.0-f677deea54.patch new file mode 100644 index 000000000000..d996c0bdd022 --- /dev/null +++ b/.yarn/patches/@metamask-ppom-validator-npm-0.32.0-f677deea54.patch @@ -0,0 +1,15 @@ +diff --git a/dist/ppom-controller.js b/dist/ppom-controller.js +index 9cf1502efabec00b25ad381bf2001200ccc9f34f..bfe55b6e68989f794deab069e8b80fc8d719ec25 100644 +--- a/dist/ppom-controller.js ++++ b/dist/ppom-controller.js +@@ -203,7 +203,9 @@ async function _PPOMController_initialisePPOM() { + console.error(`Error in deleting files: ${error.message}`); + }); + }, _PPOMController_onNetworkChange = function _PPOMController_onNetworkChange(networkControllerState) { +- const id = (0, util_1.addHexPrefix)(networkControllerState.providerConfig.chainId); ++ const selectedNetworkClient = this.messagingSystem.call('NetworkController:getNetworkClientById', networkControllerState.selectedNetworkClientId); ++ const { chainId } = selectedNetworkClient.configuration; ++ const id = (0, util_1.addHexPrefix)(chainId); + if (id === __classPrivateFieldGet(this, _PPOMController_chainId, "f")) { + return; + } diff --git a/.yarn/patches/@metamask-snaps-controllers-npm-9.4.0-7c3abbbea6.patch b/.yarn/patches/@metamask-snaps-controllers-npm-9.5.0-8b21e3c072.patch similarity index 76% rename from .yarn/patches/@metamask-snaps-controllers-npm-9.4.0-7c3abbbea6.patch rename to .yarn/patches/@metamask-snaps-controllers-npm-9.5.0-8b21e3c072.patch index ec4dfb871c9a..314a33b9f881 100644 --- a/.yarn/patches/@metamask-snaps-controllers-npm-9.4.0-7c3abbbea6.patch +++ b/.yarn/patches/@metamask-snaps-controllers-npm-9.5.0-8b21e3c072.patch @@ -1,5 +1,5 @@ diff --git a/package.json b/package.json -index 9464ffc2614c6c648051df5cb5c9c9b9e651a831..5b1bcb34b8167cf6bbc5b2854b1f196e0fa0204f 100644 +index e738c058c38f8bc5c14bab9644ecced32eefa075..9aba0d03fc642572fcbdc2c791e335df72d70f35 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ diff --git a/.yarn/patches/@metamask-transaction-controller-npm-34.0.0-8bdfa87aaf.patch b/.yarn/patches/@metamask-transaction-controller-npm-34.0.0-8bdfa87aaf.patch deleted file mode 100644 index 4ba6272f67bf..000000000000 --- a/.yarn/patches/@metamask-transaction-controller-npm-34.0.0-8bdfa87aaf.patch +++ /dev/null @@ -1,6174 +0,0 @@ -diff --git a/dist/TransactionController.js b/dist/TransactionController.js -index bdb836a895f9bfdc9225c354b59c0aaab20f80c1..f03dc65ff723256e1d4ed2588199166f0ee6e3cc 100644 ---- a/dist/TransactionController.js -+++ b/dist/TransactionController.js -@@ -4,13 +4,13 @@ - - - --var _chunkS7Q622ISjs = require('./chunk-S7Q622IS.js'); -+var _chunkIVR4NMOFjs = require('./chunk-IVR4NMOF.js'); - require('./chunk-PRUNMTRD.js'); - require('./chunk-74W7X6BE.js'); - require('./chunk-KT6UAKBB.js'); - require('./chunk-SD6CWFDF.js'); - require('./chunk-RXIUMVA5.js'); --require('./chunk-ULD4JC3Q.js'); -+require('./chunk-6DODV6OV.js'); - require('./chunk-7LXE4KHV.js'); - require('./chunk-V72C4MCR.js'); - require('./chunk-QP75SWIQ.js'); -@@ -41,5 +41,5 @@ require('./chunk-Z4BLTVTB.js'); - - - --exports.ApprovalState = _chunkS7Q622ISjs.ApprovalState; exports.CANCEL_RATE = _chunkS7Q622ISjs.CANCEL_RATE; exports.HARDFORK = _chunkS7Q622ISjs.HARDFORK; exports.SPEED_UP_RATE = _chunkS7Q622ISjs.SPEED_UP_RATE; exports.TransactionController = _chunkS7Q622ISjs.TransactionController; -+exports.ApprovalState = _chunkIVR4NMOFjs.ApprovalState; exports.CANCEL_RATE = _chunkIVR4NMOFjs.CANCEL_RATE; exports.HARDFORK = _chunkIVR4NMOFjs.HARDFORK; exports.SPEED_UP_RATE = _chunkIVR4NMOFjs.SPEED_UP_RATE; exports.TransactionController = _chunkIVR4NMOFjs.TransactionController; - //# sourceMappingURL=TransactionController.js.map -\ No newline at end of file -diff --git a/dist/TransactionController.mjs b/dist/TransactionController.mjs -index ac7d4c27a4bca05f03cb791441e9e5d87765bbf2..88ecaa74d9e133c02fe09341460f4dca2dd83865 100644 ---- a/dist/TransactionController.mjs -+++ b/dist/TransactionController.mjs -@@ -4,13 +4,13 @@ import { - HARDFORK, - SPEED_UP_RATE, - TransactionController --} from "./chunk-UKV5HIMT.mjs"; -+} from "./chunk-YQYO6EGF.mjs"; - import "./chunk-6DDVVUJC.mjs"; - import "./chunk-EVL6KODQ.mjs"; - import "./chunk-K4KOSAGM.mjs"; - import "./chunk-KG4UW4K4.mjs"; - import "./chunk-5ZEJT5SN.mjs"; --import "./chunk-6B5BEO3R.mjs"; -+import "./chunk-7M2R5AHC.mjs"; - import "./chunk-FRKQ3Z2L.mjs"; - import "./chunk-5G6OHAXI.mjs"; - import "./chunk-XGRAHX6T.mjs"; -diff --git a/dist/chunk-6DODV6OV.js b/dist/chunk-6DODV6OV.js -new file mode 100644 -index 0000000000000000000000000000000000000000..92a813c417d809304e216783e76b6a67651d4661 ---- /dev/null -+++ b/dist/chunk-6DODV6OV.js -@@ -0,0 +1,390 @@ -+"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } -+ -+ -+var _chunkS6VGOPUYjs = require('./chunk-S6VGOPUY.js'); -+ -+ -+ -+ -+ -+var _chunkZ4BLTVTBjs = require('./chunk-Z4BLTVTB.js'); -+ -+// src/helpers/PendingTransactionTracker.ts -+var _controllerutils = require('@metamask/controller-utils'); -+var _events = require('events'); var _events2 = _interopRequireDefault(_events); -+var _lodash = require('lodash'); -+var DROPPED_BLOCK_COUNT = 3; -+var RECEIPT_STATUS_SUCCESS = "0x1"; -+var RECEIPT_STATUS_FAILURE = "0x0"; -+var MAX_RETRY_BLOCK_DISTANCE = 50; -+var KNOWN_TRANSACTION_ERRORS = [ -+ "replacement transaction underpriced", -+ "known transaction", -+ "gas price too low to replace", -+ "transaction with the same hash was already imported", -+ "gateway timeout", -+ "nonce too low" -+]; -+var log = _chunkS6VGOPUYjs.createModuleLogger.call(void 0, _chunkS6VGOPUYjs.projectLogger, "pending-transactions"); -+var _blockTracker, _droppedBlockCountByHash, _getChainId, _getEthQuery, _getTransactions, _isResubmitEnabled, _listener, _getGlobalLock, _publishTransaction, _running, _beforeCheckPendingTransaction, _beforePublish, _start, start_fn, _onLatestBlock, onLatestBlock_fn, _checkTransactions, checkTransactions_fn, _resubmitTransactions, resubmitTransactions_fn, _isKnownTransactionError, isKnownTransactionError_fn, _resubmitTransaction, resubmitTransaction_fn, _isResubmitDue, isResubmitDue_fn, _checkTransaction, checkTransaction_fn, _onTransactionConfirmed, onTransactionConfirmed_fn, _isTransactionDropped, isTransactionDropped_fn, _isNonceTaken, isNonceTaken_fn, _getPendingTransactions, getPendingTransactions_fn, _warnTransaction, warnTransaction_fn, _failTransaction, failTransaction_fn, _dropTransaction, dropTransaction_fn, _updateTransaction, updateTransaction_fn, _getTransactionReceipt, getTransactionReceipt_fn, _getBlockByHash, getBlockByHash_fn, _getNetworkTransactionCount, getNetworkTransactionCount_fn, _getCurrentChainTransactions, getCurrentChainTransactions_fn; -+var PendingTransactionTracker = class { -+ constructor({ -+ blockTracker, -+ getChainId, -+ getEthQuery, -+ getTransactions, -+ isResubmitEnabled, -+ getGlobalLock, -+ publishTransaction, -+ hooks -+ }) { -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _start); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _onLatestBlock); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _checkTransactions); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _resubmitTransactions); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _isKnownTransactionError); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _resubmitTransaction); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _isResubmitDue); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _checkTransaction); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _onTransactionConfirmed); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _isTransactionDropped); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _isNonceTaken); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _getPendingTransactions); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _warnTransaction); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _failTransaction); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _dropTransaction); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _updateTransaction); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _getTransactionReceipt); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _getBlockByHash); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _getNetworkTransactionCount); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _getCurrentChainTransactions); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _blockTracker, void 0); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _droppedBlockCountByHash, void 0); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _getChainId, void 0); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _getEthQuery, void 0); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _getTransactions, void 0); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _isResubmitEnabled, void 0); -+ // TODO: Replace `any` with type -+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _listener, void 0); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _getGlobalLock, void 0); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _publishTransaction, void 0); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _running, void 0); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _beforeCheckPendingTransaction, void 0); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _beforePublish, void 0); -+ this.startIfPendingTransactions = () => { -+ const pendingTransactions = _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _getPendingTransactions, getPendingTransactions_fn).call(this); -+ if (pendingTransactions.length) { -+ _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _start, start_fn).call(this); -+ } else { -+ this.stop(); -+ } -+ }; -+ this.hub = new (0, _events2.default)(); -+ _chunkZ4BLTVTBjs.__privateSet.call(void 0, this, _blockTracker, blockTracker); -+ _chunkZ4BLTVTBjs.__privateSet.call(void 0, this, _droppedBlockCountByHash, /* @__PURE__ */ new Map()); -+ _chunkZ4BLTVTBjs.__privateSet.call(void 0, this, _getChainId, getChainId); -+ _chunkZ4BLTVTBjs.__privateSet.call(void 0, this, _getEthQuery, getEthQuery); -+ _chunkZ4BLTVTBjs.__privateSet.call(void 0, this, _getTransactions, getTransactions); -+ _chunkZ4BLTVTBjs.__privateSet.call(void 0, this, _isResubmitEnabled, isResubmitEnabled ?? (() => true)); -+ _chunkZ4BLTVTBjs.__privateSet.call(void 0, this, _listener, _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _onLatestBlock, onLatestBlock_fn).bind(this)); -+ _chunkZ4BLTVTBjs.__privateSet.call(void 0, this, _getGlobalLock, getGlobalLock); -+ _chunkZ4BLTVTBjs.__privateSet.call(void 0, this, _publishTransaction, publishTransaction); -+ _chunkZ4BLTVTBjs.__privateSet.call(void 0, this, _running, false); -+ _chunkZ4BLTVTBjs.__privateSet.call(void 0, this, _beforePublish, hooks?.beforePublish ?? (() => true)); -+ _chunkZ4BLTVTBjs.__privateSet.call(void 0, this, _beforeCheckPendingTransaction, hooks?.beforeCheckPendingTransaction ?? (() => true)); -+ } -+ /** -+ * Force checks the network if the given transaction is confirmed and updates it's status. -+ * -+ * @param txMeta - The transaction to check -+ */ -+ async forceCheckTransaction(txMeta) { -+ const releaseLock = await _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _getGlobalLock).call(this); -+ try { -+ await _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _checkTransaction, checkTransaction_fn).call(this, txMeta); -+ } catch (error) { -+ log("Failed to check transaction", error); -+ } finally { -+ releaseLock(); -+ } -+ } -+ stop() { -+ if (!_chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _running)) { -+ return; -+ } -+ _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _blockTracker).removeListener("latest", _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _listener)); -+ _chunkZ4BLTVTBjs.__privateSet.call(void 0, this, _running, false); -+ log("Stopped polling"); -+ } -+}; -+_blockTracker = new WeakMap(); -+_droppedBlockCountByHash = new WeakMap(); -+_getChainId = new WeakMap(); -+_getEthQuery = new WeakMap(); -+_getTransactions = new WeakMap(); -+_isResubmitEnabled = new WeakMap(); -+_listener = new WeakMap(); -+_getGlobalLock = new WeakMap(); -+_publishTransaction = new WeakMap(); -+_running = new WeakMap(); -+_beforeCheckPendingTransaction = new WeakMap(); -+_beforePublish = new WeakMap(); -+_start = new WeakSet(); -+start_fn = function() { -+ if (_chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _running)) { -+ return; -+ } -+ _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _blockTracker).on("latest", _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _listener)); -+ _chunkZ4BLTVTBjs.__privateSet.call(void 0, this, _running, true); -+ log("Started polling"); -+}; -+_onLatestBlock = new WeakSet(); -+onLatestBlock_fn = async function(latestBlockNumber) { -+ const releaseLock = await _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _getGlobalLock).call(this); -+ try { -+ await _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _checkTransactions, checkTransactions_fn).call(this); -+ } catch (error) { -+ log("Failed to check transactions", error); -+ } finally { -+ releaseLock(); -+ } -+ try { -+ await _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _resubmitTransactions, resubmitTransactions_fn).call(this, latestBlockNumber); -+ } catch (error) { -+ log("Failed to resubmit transactions", error); -+ } -+}; -+_checkTransactions = new WeakSet(); -+checkTransactions_fn = async function() { -+ log("Checking transactions"); -+ const pendingTransactions = _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _getPendingTransactions, getPendingTransactions_fn).call(this); -+ if (!pendingTransactions.length) { -+ log("No pending transactions to check"); -+ return; -+ } -+ log("Found pending transactions to check", { -+ count: pendingTransactions.length, -+ ids: pendingTransactions.map((tx) => tx.id) -+ }); -+ await Promise.all( -+ pendingTransactions.map((tx) => _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _checkTransaction, checkTransaction_fn).call(this, tx)) -+ ); -+}; -+_resubmitTransactions = new WeakSet(); -+resubmitTransactions_fn = async function(latestBlockNumber) { -+ if (!_chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _isResubmitEnabled).call(this) || !_chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _running)) { -+ return; -+ } -+ log("Resubmitting transactions"); -+ const pendingTransactions = _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _getPendingTransactions, getPendingTransactions_fn).call(this); -+ if (!pendingTransactions.length) { -+ log("No pending transactions to resubmit"); -+ return; -+ } -+ log("Found pending transactions to resubmit", { -+ count: pendingTransactions.length, -+ ids: pendingTransactions.map((tx) => tx.id) -+ }); -+ for (const txMeta of pendingTransactions) { -+ try { -+ await _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _resubmitTransaction, resubmitTransaction_fn).call(this, txMeta, latestBlockNumber); -+ } catch (error) { -+ const errorMessage = error.value?.message?.toLowerCase() || error.message?.toLowerCase() || String(error); -+ if (_chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _isKnownTransactionError, isKnownTransactionError_fn).call(this, errorMessage)) { -+ log("Ignoring known transaction error", errorMessage); -+ continue; -+ } -+ _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _warnTransaction, warnTransaction_fn).call(this, txMeta, error.message, "There was an error when resubmitting this transaction."); -+ } -+ } -+}; -+_isKnownTransactionError = new WeakSet(); -+isKnownTransactionError_fn = function(errorMessage) { -+ return KNOWN_TRANSACTION_ERRORS.some( -+ (knownError) => errorMessage.includes(knownError) -+ ); -+}; -+_resubmitTransaction = new WeakSet(); -+resubmitTransaction_fn = async function(txMeta, latestBlockNumber) { -+ if (!_chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _isResubmitDue, isResubmitDue_fn).call(this, txMeta, latestBlockNumber)) { -+ return; -+ } -+ const { rawTx } = txMeta; -+ if (!_chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _beforePublish).call(this, txMeta)) { -+ return; -+ } -+ const ethQuery = _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _getEthQuery).call(this, txMeta.networkClientId); -+ await _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _publishTransaction).call(this, ethQuery, rawTx); -+ const retryCount = (txMeta.retryCount ?? 0) + 1; -+ _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _updateTransaction, updateTransaction_fn).call(this, _lodash.merge.call(void 0, {}, txMeta, { retryCount }), "PendingTransactionTracker:transaction-retry - Retry count increased"); -+}; -+_isResubmitDue = new WeakSet(); -+isResubmitDue_fn = function(txMeta, latestBlockNumber) { -+ const txMetaWithFirstRetryBlockNumber = _lodash.cloneDeep.call(void 0, txMeta); -+ if (!txMetaWithFirstRetryBlockNumber.firstRetryBlockNumber) { -+ txMetaWithFirstRetryBlockNumber.firstRetryBlockNumber = latestBlockNumber; -+ _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _updateTransaction, updateTransaction_fn).call(this, txMetaWithFirstRetryBlockNumber, "PendingTransactionTracker:#isResubmitDue - First retry block number set"); -+ } -+ const { firstRetryBlockNumber } = txMetaWithFirstRetryBlockNumber; -+ const blocksSinceFirstRetry = Number.parseInt(latestBlockNumber, 16) - Number.parseInt(firstRetryBlockNumber, 16); -+ const retryCount = txMeta.retryCount || 0; -+ const requiredBlocksSinceFirstRetry = Math.min( -+ MAX_RETRY_BLOCK_DISTANCE, -+ Math.pow(2, retryCount) -+ ); -+ return blocksSinceFirstRetry >= requiredBlocksSinceFirstRetry; -+}; -+_checkTransaction = new WeakSet(); -+checkTransaction_fn = async function(txMeta) { -+ const { hash, id } = txMeta; -+ if (!hash && _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _beforeCheckPendingTransaction).call(this, txMeta)) { -+ const error = new Error( -+ "We had an error while submitting this transaction, please try again." -+ ); -+ error.name = "NoTxHashError"; -+ _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _failTransaction, failTransaction_fn).call(this, txMeta, error); -+ return; -+ } -+ if (_chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _isNonceTaken, isNonceTaken_fn).call(this, txMeta)) { -+ log("Nonce already taken", id); -+ _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _dropTransaction, dropTransaction_fn).call(this, txMeta); -+ return; -+ } -+ try { -+ const receipt = await _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _getTransactionReceipt, getTransactionReceipt_fn).call(this, hash); -+ const isSuccess = receipt?.status === RECEIPT_STATUS_SUCCESS; -+ const isFailure = receipt?.status === RECEIPT_STATUS_FAILURE; -+ if (isFailure) { -+ log("Transaction receipt has failed status"); -+ _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _failTransaction, failTransaction_fn).call(this, txMeta, new Error("Transaction dropped or replaced")); -+ return; -+ } -+ const { blockNumber, blockHash } = receipt || {}; -+ if (isSuccess && blockNumber && blockHash) { -+ await _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _onTransactionConfirmed, onTransactionConfirmed_fn).call(this, txMeta, { -+ ...receipt, -+ blockNumber, -+ blockHash -+ }); -+ return; -+ } -+ } catch (error) { -+ log("Failed to check transaction", id, error); -+ _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _warnTransaction, warnTransaction_fn).call(this, txMeta, error.message, "There was a problem loading this transaction."); -+ return; -+ } -+ if (await _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _isTransactionDropped, isTransactionDropped_fn).call(this, txMeta)) { -+ _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _dropTransaction, dropTransaction_fn).call(this, txMeta); -+ } -+}; -+_onTransactionConfirmed = new WeakSet(); -+onTransactionConfirmed_fn = async function(txMeta, receipt) { -+ const { id } = txMeta; -+ const { blockHash } = receipt; -+ log("Transaction confirmed", id); -+ const { baseFeePerGas, timestamp: blockTimestamp } = await _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _getBlockByHash, getBlockByHash_fn).call(this, blockHash, false); -+ const updatedTxMeta = _lodash.cloneDeep.call(void 0, txMeta); -+ updatedTxMeta.baseFeePerGas = baseFeePerGas; -+ updatedTxMeta.blockTimestamp = blockTimestamp; -+ updatedTxMeta.status = "confirmed" /* confirmed */; -+ updatedTxMeta.txParams = { -+ ...updatedTxMeta.txParams, -+ gasUsed: receipt.gasUsed -+ }; -+ updatedTxMeta.txReceipt = receipt; -+ updatedTxMeta.verifiedOnBlockchain = true; -+ _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _updateTransaction, updateTransaction_fn).call(this, updatedTxMeta, "PendingTransactionTracker:#onTransactionConfirmed - Transaction confirmed"); -+ this.hub.emit("transaction-confirmed", updatedTxMeta); -+}; -+_isTransactionDropped = new WeakSet(); -+isTransactionDropped_fn = async function(txMeta) { -+ const { -+ hash, -+ id, -+ txParams: { nonce, from } -+ } = txMeta; -+ if (!nonce || !hash) { -+ return false; -+ } -+ const networkNextNonceHex = await _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _getNetworkTransactionCount, getNetworkTransactionCount_fn).call(this, from); -+ const networkNextNonceNumber = parseInt(networkNextNonceHex, 16); -+ const nonceNumber = parseInt(nonce, 16); -+ if (nonceNumber >= networkNextNonceNumber) { -+ return false; -+ } -+ let droppedBlockCount = _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _droppedBlockCountByHash).get(hash); -+ if (droppedBlockCount === void 0) { -+ droppedBlockCount = 0; -+ _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _droppedBlockCountByHash).set(hash, droppedBlockCount); -+ } -+ if (droppedBlockCount < DROPPED_BLOCK_COUNT) { -+ log("Incrementing dropped block count", { id, droppedBlockCount }); -+ _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _droppedBlockCountByHash).set(hash, droppedBlockCount + 1); -+ return false; -+ } -+ log("Hit dropped block count", id); -+ _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _droppedBlockCountByHash).delete(hash); -+ return true; -+}; -+_isNonceTaken = new WeakSet(); -+isNonceTaken_fn = function(txMeta) { -+ const { id, txParams } = txMeta; -+ return _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _getCurrentChainTransactions, getCurrentChainTransactions_fn).call(this).some( -+ (tx) => tx.id !== id && tx.txParams.from === txParams.from && tx.status === "confirmed" /* confirmed */ && tx.txParams.nonce === txParams.nonce && tx.type !== "incoming" /* incoming */ -+ ); -+}; -+_getPendingTransactions = new WeakSet(); -+getPendingTransactions_fn = function() { -+ return _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _getCurrentChainTransactions, getCurrentChainTransactions_fn).call(this).filter( -+ (tx) => tx.status === "submitted" /* submitted */ && !tx.verifiedOnBlockchain && !tx.isUserOperation -+ ); -+}; -+_warnTransaction = new WeakSet(); -+warnTransaction_fn = function(txMeta, error, message) { -+ _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _updateTransaction, updateTransaction_fn).call(this, { -+ ...txMeta, -+ warning: { error, message } -+ }, "PendingTransactionTracker:#warnTransaction - Warning added"); -+}; -+_failTransaction = new WeakSet(); -+failTransaction_fn = function(txMeta, error) { -+ log("Transaction failed", txMeta.id, error); -+ this.hub.emit("transaction-failed", txMeta, error); -+}; -+_dropTransaction = new WeakSet(); -+dropTransaction_fn = function(txMeta) { -+ log("Transaction dropped", txMeta.id); -+ this.hub.emit("transaction-dropped", txMeta); -+}; -+_updateTransaction = new WeakSet(); -+updateTransaction_fn = function(txMeta, note) { -+ this.hub.emit("transaction-updated", txMeta, note); -+}; -+_getTransactionReceipt = new WeakSet(); -+getTransactionReceipt_fn = async function(txHash) { -+ return await _controllerutils.query.call(void 0, _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _getEthQuery).call(this), "getTransactionReceipt", [txHash]); -+}; -+_getBlockByHash = new WeakSet(); -+getBlockByHash_fn = async function(blockHash, includeTransactionDetails) { -+ return await _controllerutils.query.call(void 0, _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _getEthQuery).call(this), "getBlockByHash", [ -+ blockHash, -+ includeTransactionDetails -+ ]); -+}; -+_getNetworkTransactionCount = new WeakSet(); -+getNetworkTransactionCount_fn = async function(address) { -+ return await _controllerutils.query.call(void 0, _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _getEthQuery).call(this), "getTransactionCount", [address]); -+}; -+_getCurrentChainTransactions = new WeakSet(); -+getCurrentChainTransactions_fn = function() { -+ const currentChainId = _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _getChainId).call(this); -+ return _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _getTransactions).call(this).filter( -+ (tx) => tx.chainId === currentChainId -+ ); -+}; -+ -+ -+ -+exports.PendingTransactionTracker = PendingTransactionTracker; -+//# sourceMappingURL=chunk-6DODV6OV.js.map -\ No newline at end of file -diff --git a/dist/chunk-6DODV6OV.js.map b/dist/chunk-6DODV6OV.js.map -new file mode 100644 -index 0000000000000000000000000000000000000000..c982d3394489271e672f68247654349d554c702a ---- /dev/null -+++ b/dist/chunk-6DODV6OV.js.map -@@ -0,0 +1 @@ -+{"version":3,"sources":["../src/helpers/PendingTransactionTracker.ts"],"names":[],"mappings":"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","sourcesContent":["import { query } from '@metamask/controller-utils';\nimport type EthQuery from '@metamask/eth-query';\nimport type {\n BlockTracker,\n NetworkClientId,\n} from '@metamask/network-controller';\nimport EventEmitter from 'events';\nimport { cloneDeep, merge } from 'lodash';\n\nimport { createModuleLogger, projectLogger } from '../logger';\nimport type { TransactionMeta, TransactionReceipt } from '../types';\nimport { TransactionStatus, TransactionType } from '../types';\n\n/**\n * We wait this many blocks before emitting a 'transaction-dropped' event\n * This is because we could be talking to a node that is out of sync\n */\nconst DROPPED_BLOCK_COUNT = 3;\n\nconst RECEIPT_STATUS_SUCCESS = '0x1';\nconst RECEIPT_STATUS_FAILURE = '0x0';\nconst MAX_RETRY_BLOCK_DISTANCE = 50;\n\nconst KNOWN_TRANSACTION_ERRORS = [\n 'replacement transaction underpriced',\n 'known transaction',\n 'gas price too low to replace',\n 'transaction with the same hash was already imported',\n 'gateway timeout',\n 'nonce too low',\n];\n\nconst log = createModuleLogger(projectLogger, 'pending-transactions');\n\ntype SuccessfulTransactionReceipt = TransactionReceipt & {\n blockNumber: string;\n blockHash: string;\n};\n\ntype Events = {\n 'transaction-confirmed': [txMeta: TransactionMeta];\n 'transaction-dropped': [txMeta: TransactionMeta];\n 'transaction-failed': [txMeta: TransactionMeta, error: Error];\n 'transaction-updated': [txMeta: TransactionMeta, note: string];\n};\n\n// This interface was created before this ESLint rule was added.\n// Convert to a `type` in a future major version.\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\nexport interface PendingTransactionTrackerEventEmitter extends EventEmitter {\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n on(\n eventName: T,\n listener: (...args: Events[T]) => void,\n ): this;\n\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n emit(eventName: T, ...args: Events[T]): boolean;\n}\n\nexport class PendingTransactionTracker {\n hub: PendingTransactionTrackerEventEmitter;\n\n #blockTracker: BlockTracker;\n\n #droppedBlockCountByHash: Map;\n\n #getChainId: () => string;\n\n #getEthQuery: (networkClientId?: NetworkClientId) => EthQuery;\n\n #getTransactions: () => TransactionMeta[];\n\n #isResubmitEnabled: () => boolean;\n\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n #listener: any;\n\n #getGlobalLock: () => Promise<() => void>;\n\n #publishTransaction: (ethQuery: EthQuery, rawTx: string) => Promise;\n\n #running: boolean;\n\n #beforeCheckPendingTransaction: (transactionMeta: TransactionMeta) => boolean;\n\n #beforePublish: (transactionMeta: TransactionMeta) => boolean;\n\n constructor({\n blockTracker,\n getChainId,\n getEthQuery,\n getTransactions,\n isResubmitEnabled,\n getGlobalLock,\n publishTransaction,\n hooks,\n }: {\n blockTracker: BlockTracker;\n getChainId: () => string;\n getEthQuery: (networkClientId?: NetworkClientId) => EthQuery;\n getTransactions: () => TransactionMeta[];\n isResubmitEnabled?: () => boolean;\n getGlobalLock: () => Promise<() => void>;\n publishTransaction: (ethQuery: EthQuery, rawTx: string) => Promise;\n hooks?: {\n beforeCheckPendingTransaction?: (\n transactionMeta: TransactionMeta,\n ) => boolean;\n beforePublish?: (transactionMeta: TransactionMeta) => boolean;\n };\n }) {\n this.hub = new EventEmitter() as PendingTransactionTrackerEventEmitter;\n\n this.#blockTracker = blockTracker;\n this.#droppedBlockCountByHash = new Map();\n this.#getChainId = getChainId;\n this.#getEthQuery = getEthQuery;\n this.#getTransactions = getTransactions;\n this.#isResubmitEnabled = isResubmitEnabled ?? (() => true);\n this.#listener = this.#onLatestBlock.bind(this);\n this.#getGlobalLock = getGlobalLock;\n this.#publishTransaction = publishTransaction;\n this.#running = false;\n this.#beforePublish = hooks?.beforePublish ?? (() => true);\n this.#beforeCheckPendingTransaction =\n hooks?.beforeCheckPendingTransaction ?? (() => true);\n }\n\n startIfPendingTransactions = () => {\n const pendingTransactions = this.#getPendingTransactions();\n\n if (pendingTransactions.length) {\n this.#start();\n } else {\n this.stop();\n }\n };\n\n /**\n * Force checks the network if the given transaction is confirmed and updates it's status.\n *\n * @param txMeta - The transaction to check\n */\n async forceCheckTransaction(txMeta: TransactionMeta) {\n const releaseLock = await this.#getGlobalLock();\n\n try {\n await this.#checkTransaction(txMeta);\n } catch (error) {\n /* istanbul ignore next */\n log('Failed to check transaction', error);\n } finally {\n releaseLock();\n }\n }\n\n #start() {\n if (this.#running) {\n return;\n }\n\n this.#blockTracker.on('latest', this.#listener);\n this.#running = true;\n\n log('Started polling');\n }\n\n stop() {\n if (!this.#running) {\n return;\n }\n\n this.#blockTracker.removeListener('latest', this.#listener);\n this.#running = false;\n\n log('Stopped polling');\n }\n\n async #onLatestBlock(latestBlockNumber: string) {\n const releaseLock = await this.#getGlobalLock();\n\n try {\n await this.#checkTransactions();\n } catch (error) {\n /* istanbul ignore next */\n log('Failed to check transactions', error);\n } finally {\n releaseLock();\n }\n\n try {\n await this.#resubmitTransactions(latestBlockNumber);\n } catch (error) {\n /* istanbul ignore next */\n log('Failed to resubmit transactions', error);\n }\n }\n\n async #checkTransactions() {\n log('Checking transactions');\n\n const pendingTransactions = this.#getPendingTransactions();\n\n if (!pendingTransactions.length) {\n log('No pending transactions to check');\n return;\n }\n\n log('Found pending transactions to check', {\n count: pendingTransactions.length,\n ids: pendingTransactions.map((tx) => tx.id),\n });\n\n await Promise.all(\n pendingTransactions.map((tx) => this.#checkTransaction(tx)),\n );\n }\n\n async #resubmitTransactions(latestBlockNumber: string) {\n if (!this.#isResubmitEnabled() || !this.#running) {\n return;\n }\n\n log('Resubmitting transactions');\n\n const pendingTransactions = this.#getPendingTransactions();\n\n if (!pendingTransactions.length) {\n log('No pending transactions to resubmit');\n return;\n }\n\n log('Found pending transactions to resubmit', {\n count: pendingTransactions.length,\n ids: pendingTransactions.map((tx) => tx.id),\n });\n\n for (const txMeta of pendingTransactions) {\n try {\n await this.#resubmitTransaction(txMeta, latestBlockNumber);\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (error: any) {\n /* istanbul ignore next */\n const errorMessage =\n error.value?.message?.toLowerCase() ||\n error.message?.toLowerCase() ||\n String(error);\n\n if (this.#isKnownTransactionError(errorMessage)) {\n log('Ignoring known transaction error', errorMessage);\n continue;\n }\n\n this.#warnTransaction(\n txMeta,\n error.message,\n 'There was an error when resubmitting this transaction.',\n );\n }\n }\n }\n\n #isKnownTransactionError(errorMessage: string) {\n return KNOWN_TRANSACTION_ERRORS.some((knownError) =>\n errorMessage.includes(knownError),\n );\n }\n\n async #resubmitTransaction(\n txMeta: TransactionMeta,\n latestBlockNumber: string,\n ) {\n if (!this.#isResubmitDue(txMeta, latestBlockNumber)) {\n return;\n }\n\n const { rawTx } = txMeta;\n\n if (!this.#beforePublish(txMeta)) {\n return;\n }\n\n const ethQuery = this.#getEthQuery(txMeta.networkClientId);\n await this.#publishTransaction(ethQuery, rawTx as string);\n\n const retryCount = (txMeta.retryCount ?? 0) + 1;\n\n this.#updateTransaction(\n merge({}, txMeta, { retryCount }),\n 'PendingTransactionTracker:transaction-retry - Retry count increased',\n );\n }\n\n #isResubmitDue(txMeta: TransactionMeta, latestBlockNumber: string): boolean {\n const txMetaWithFirstRetryBlockNumber = cloneDeep(txMeta);\n\n if (!txMetaWithFirstRetryBlockNumber.firstRetryBlockNumber) {\n txMetaWithFirstRetryBlockNumber.firstRetryBlockNumber = latestBlockNumber;\n\n this.#updateTransaction(\n txMetaWithFirstRetryBlockNumber,\n 'PendingTransactionTracker:#isResubmitDue - First retry block number set',\n );\n }\n\n const { firstRetryBlockNumber } = txMetaWithFirstRetryBlockNumber;\n\n const blocksSinceFirstRetry =\n Number.parseInt(latestBlockNumber, 16) -\n Number.parseInt(firstRetryBlockNumber, 16);\n\n const retryCount = txMeta.retryCount || 0;\n\n // Exponential backoff to limit retries at publishing\n // Capped at ~15 minutes between retries\n const requiredBlocksSinceFirstRetry = Math.min(\n MAX_RETRY_BLOCK_DISTANCE,\n Math.pow(2, retryCount),\n );\n\n return blocksSinceFirstRetry >= requiredBlocksSinceFirstRetry;\n }\n\n async #checkTransaction(txMeta: TransactionMeta) {\n const { hash, id } = txMeta;\n\n if (!hash && this.#beforeCheckPendingTransaction(txMeta)) {\n const error = new Error(\n 'We had an error while submitting this transaction, please try again.',\n );\n\n error.name = 'NoTxHashError';\n\n this.#failTransaction(txMeta, error);\n\n return;\n }\n\n if (this.#isNonceTaken(txMeta)) {\n log('Nonce already taken', id);\n this.#dropTransaction(txMeta);\n return;\n }\n\n try {\n const receipt = await this.#getTransactionReceipt(hash);\n const isSuccess = receipt?.status === RECEIPT_STATUS_SUCCESS;\n const isFailure = receipt?.status === RECEIPT_STATUS_FAILURE;\n\n if (isFailure) {\n log('Transaction receipt has failed status');\n\n this.#failTransaction(\n txMeta,\n new Error('Transaction dropped or replaced'),\n );\n\n return;\n }\n\n const { blockNumber, blockHash } = receipt || {};\n\n if (isSuccess && blockNumber && blockHash) {\n await this.#onTransactionConfirmed(txMeta, {\n ...receipt,\n blockNumber,\n blockHash,\n });\n\n return;\n }\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (error: any) {\n log('Failed to check transaction', id, error);\n\n this.#warnTransaction(\n txMeta,\n error.message,\n 'There was a problem loading this transaction.',\n );\n\n return;\n }\n\n if (await this.#isTransactionDropped(txMeta)) {\n this.#dropTransaction(txMeta);\n }\n }\n\n async #onTransactionConfirmed(\n txMeta: TransactionMeta,\n receipt: SuccessfulTransactionReceipt,\n ) {\n const { id } = txMeta;\n const { blockHash } = receipt;\n\n log('Transaction confirmed', id);\n\n const { baseFeePerGas, timestamp: blockTimestamp } =\n await this.#getBlockByHash(blockHash, false);\n\n const updatedTxMeta = cloneDeep(txMeta);\n updatedTxMeta.baseFeePerGas = baseFeePerGas;\n updatedTxMeta.blockTimestamp = blockTimestamp;\n updatedTxMeta.status = TransactionStatus.confirmed;\n updatedTxMeta.txParams = {\n ...updatedTxMeta.txParams,\n gasUsed: receipt.gasUsed,\n };\n updatedTxMeta.txReceipt = receipt;\n updatedTxMeta.verifiedOnBlockchain = true;\n\n this.#updateTransaction(\n updatedTxMeta,\n 'PendingTransactionTracker:#onTransactionConfirmed - Transaction confirmed',\n );\n\n this.hub.emit('transaction-confirmed', updatedTxMeta);\n }\n\n async #isTransactionDropped(txMeta: TransactionMeta) {\n const {\n hash,\n id,\n txParams: { nonce, from },\n } = txMeta;\n\n /* istanbul ignore next */\n if (!nonce || !hash) {\n return false;\n }\n\n const networkNextNonceHex = await this.#getNetworkTransactionCount(from);\n const networkNextNonceNumber = parseInt(networkNextNonceHex, 16);\n const nonceNumber = parseInt(nonce, 16);\n\n if (nonceNumber >= networkNextNonceNumber) {\n return false;\n }\n\n let droppedBlockCount = this.#droppedBlockCountByHash.get(hash);\n\n if (droppedBlockCount === undefined) {\n droppedBlockCount = 0;\n this.#droppedBlockCountByHash.set(hash, droppedBlockCount);\n }\n\n if (droppedBlockCount < DROPPED_BLOCK_COUNT) {\n log('Incrementing dropped block count', { id, droppedBlockCount });\n this.#droppedBlockCountByHash.set(hash, droppedBlockCount + 1);\n return false;\n }\n\n log('Hit dropped block count', id);\n\n this.#droppedBlockCountByHash.delete(hash);\n return true;\n }\n\n #isNonceTaken(txMeta: TransactionMeta): boolean {\n const { id, txParams } = txMeta;\n\n return this.#getCurrentChainTransactions().some(\n (tx) =>\n tx.id !== id &&\n tx.txParams.from === txParams.from &&\n tx.status === TransactionStatus.confirmed &&\n tx.txParams.nonce === txParams.nonce &&\n tx.type !== TransactionType.incoming,\n );\n }\n\n #getPendingTransactions(): TransactionMeta[] {\n return this.#getCurrentChainTransactions().filter(\n (tx) =>\n tx.status === TransactionStatus.submitted &&\n !tx.verifiedOnBlockchain &&\n !tx.isUserOperation,\n );\n }\n\n #warnTransaction(txMeta: TransactionMeta, error: string, message: string) {\n this.#updateTransaction(\n {\n ...txMeta,\n warning: { error, message },\n },\n 'PendingTransactionTracker:#warnTransaction - Warning added',\n );\n }\n\n #failTransaction(txMeta: TransactionMeta, error: Error) {\n log('Transaction failed', txMeta.id, error);\n this.hub.emit('transaction-failed', txMeta, error);\n }\n\n #dropTransaction(txMeta: TransactionMeta) {\n log('Transaction dropped', txMeta.id);\n this.hub.emit('transaction-dropped', txMeta);\n }\n\n #updateTransaction(txMeta: TransactionMeta, note: string) {\n this.hub.emit('transaction-updated', txMeta, note);\n }\n\n async #getTransactionReceipt(\n txHash?: string,\n ): Promise {\n return await query(this.#getEthQuery(), 'getTransactionReceipt', [txHash]);\n }\n\n async #getBlockByHash(\n blockHash: string,\n includeTransactionDetails: boolean,\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ): Promise {\n return await query(this.#getEthQuery(), 'getBlockByHash', [\n blockHash,\n includeTransactionDetails,\n ]);\n }\n\n async #getNetworkTransactionCount(address: string): Promise {\n return await query(this.#getEthQuery(), 'getTransactionCount', [address]);\n }\n\n #getCurrentChainTransactions(): TransactionMeta[] {\n const currentChainId = this.#getChainId();\n\n return this.#getTransactions().filter(\n (tx) => tx.chainId === currentChainId,\n );\n }\n}\n"]} -\ No newline at end of file -diff --git a/dist/chunk-7M2R5AHC.mjs b/dist/chunk-7M2R5AHC.mjs -new file mode 100644 -index 0000000000000000000000000000000000000000..3b137fe1e719807e859ca036c76ffff8c234f964 ---- /dev/null -+++ b/dist/chunk-7M2R5AHC.mjs -@@ -0,0 +1,390 @@ -+import { -+ createModuleLogger, -+ projectLogger -+} from "./chunk-UQQWZT6C.mjs"; -+import { -+ __privateAdd, -+ __privateGet, -+ __privateMethod, -+ __privateSet -+} from "./chunk-XUI43LEZ.mjs"; -+ -+// src/helpers/PendingTransactionTracker.ts -+import { query } from "@metamask/controller-utils"; -+import EventEmitter from "events"; -+import { cloneDeep, merge } from "lodash"; -+var DROPPED_BLOCK_COUNT = 3; -+var RECEIPT_STATUS_SUCCESS = "0x1"; -+var RECEIPT_STATUS_FAILURE = "0x0"; -+var MAX_RETRY_BLOCK_DISTANCE = 50; -+var KNOWN_TRANSACTION_ERRORS = [ -+ "replacement transaction underpriced", -+ "known transaction", -+ "gas price too low to replace", -+ "transaction with the same hash was already imported", -+ "gateway timeout", -+ "nonce too low" -+]; -+var log = createModuleLogger(projectLogger, "pending-transactions"); -+var _blockTracker, _droppedBlockCountByHash, _getChainId, _getEthQuery, _getTransactions, _isResubmitEnabled, _listener, _getGlobalLock, _publishTransaction, _running, _beforeCheckPendingTransaction, _beforePublish, _start, start_fn, _onLatestBlock, onLatestBlock_fn, _checkTransactions, checkTransactions_fn, _resubmitTransactions, resubmitTransactions_fn, _isKnownTransactionError, isKnownTransactionError_fn, _resubmitTransaction, resubmitTransaction_fn, _isResubmitDue, isResubmitDue_fn, _checkTransaction, checkTransaction_fn, _onTransactionConfirmed, onTransactionConfirmed_fn, _isTransactionDropped, isTransactionDropped_fn, _isNonceTaken, isNonceTaken_fn, _getPendingTransactions, getPendingTransactions_fn, _warnTransaction, warnTransaction_fn, _failTransaction, failTransaction_fn, _dropTransaction, dropTransaction_fn, _updateTransaction, updateTransaction_fn, _getTransactionReceipt, getTransactionReceipt_fn, _getBlockByHash, getBlockByHash_fn, _getNetworkTransactionCount, getNetworkTransactionCount_fn, _getCurrentChainTransactions, getCurrentChainTransactions_fn; -+var PendingTransactionTracker = class { -+ constructor({ -+ blockTracker, -+ getChainId, -+ getEthQuery, -+ getTransactions, -+ isResubmitEnabled, -+ getGlobalLock, -+ publishTransaction, -+ hooks -+ }) { -+ __privateAdd(this, _start); -+ __privateAdd(this, _onLatestBlock); -+ __privateAdd(this, _checkTransactions); -+ __privateAdd(this, _resubmitTransactions); -+ __privateAdd(this, _isKnownTransactionError); -+ __privateAdd(this, _resubmitTransaction); -+ __privateAdd(this, _isResubmitDue); -+ __privateAdd(this, _checkTransaction); -+ __privateAdd(this, _onTransactionConfirmed); -+ __privateAdd(this, _isTransactionDropped); -+ __privateAdd(this, _isNonceTaken); -+ __privateAdd(this, _getPendingTransactions); -+ __privateAdd(this, _warnTransaction); -+ __privateAdd(this, _failTransaction); -+ __privateAdd(this, _dropTransaction); -+ __privateAdd(this, _updateTransaction); -+ __privateAdd(this, _getTransactionReceipt); -+ __privateAdd(this, _getBlockByHash); -+ __privateAdd(this, _getNetworkTransactionCount); -+ __privateAdd(this, _getCurrentChainTransactions); -+ __privateAdd(this, _blockTracker, void 0); -+ __privateAdd(this, _droppedBlockCountByHash, void 0); -+ __privateAdd(this, _getChainId, void 0); -+ __privateAdd(this, _getEthQuery, void 0); -+ __privateAdd(this, _getTransactions, void 0); -+ __privateAdd(this, _isResubmitEnabled, void 0); -+ // TODO: Replace `any` with type -+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -+ __privateAdd(this, _listener, void 0); -+ __privateAdd(this, _getGlobalLock, void 0); -+ __privateAdd(this, _publishTransaction, void 0); -+ __privateAdd(this, _running, void 0); -+ __privateAdd(this, _beforeCheckPendingTransaction, void 0); -+ __privateAdd(this, _beforePublish, void 0); -+ this.startIfPendingTransactions = () => { -+ const pendingTransactions = __privateMethod(this, _getPendingTransactions, getPendingTransactions_fn).call(this); -+ if (pendingTransactions.length) { -+ __privateMethod(this, _start, start_fn).call(this); -+ } else { -+ this.stop(); -+ } -+ }; -+ this.hub = new EventEmitter(); -+ __privateSet(this, _blockTracker, blockTracker); -+ __privateSet(this, _droppedBlockCountByHash, /* @__PURE__ */ new Map()); -+ __privateSet(this, _getChainId, getChainId); -+ __privateSet(this, _getEthQuery, getEthQuery); -+ __privateSet(this, _getTransactions, getTransactions); -+ __privateSet(this, _isResubmitEnabled, isResubmitEnabled ?? (() => true)); -+ __privateSet(this, _listener, __privateMethod(this, _onLatestBlock, onLatestBlock_fn).bind(this)); -+ __privateSet(this, _getGlobalLock, getGlobalLock); -+ __privateSet(this, _publishTransaction, publishTransaction); -+ __privateSet(this, _running, false); -+ __privateSet(this, _beforePublish, hooks?.beforePublish ?? (() => true)); -+ __privateSet(this, _beforeCheckPendingTransaction, hooks?.beforeCheckPendingTransaction ?? (() => true)); -+ } -+ /** -+ * Force checks the network if the given transaction is confirmed and updates it's status. -+ * -+ * @param txMeta - The transaction to check -+ */ -+ async forceCheckTransaction(txMeta) { -+ const releaseLock = await __privateGet(this, _getGlobalLock).call(this); -+ try { -+ await __privateMethod(this, _checkTransaction, checkTransaction_fn).call(this, txMeta); -+ } catch (error) { -+ log("Failed to check transaction", error); -+ } finally { -+ releaseLock(); -+ } -+ } -+ stop() { -+ if (!__privateGet(this, _running)) { -+ return; -+ } -+ __privateGet(this, _blockTracker).removeListener("latest", __privateGet(this, _listener)); -+ __privateSet(this, _running, false); -+ log("Stopped polling"); -+ } -+}; -+_blockTracker = new WeakMap(); -+_droppedBlockCountByHash = new WeakMap(); -+_getChainId = new WeakMap(); -+_getEthQuery = new WeakMap(); -+_getTransactions = new WeakMap(); -+_isResubmitEnabled = new WeakMap(); -+_listener = new WeakMap(); -+_getGlobalLock = new WeakMap(); -+_publishTransaction = new WeakMap(); -+_running = new WeakMap(); -+_beforeCheckPendingTransaction = new WeakMap(); -+_beforePublish = new WeakMap(); -+_start = new WeakSet(); -+start_fn = function() { -+ if (__privateGet(this, _running)) { -+ return; -+ } -+ __privateGet(this, _blockTracker).on("latest", __privateGet(this, _listener)); -+ __privateSet(this, _running, true); -+ log("Started polling"); -+}; -+_onLatestBlock = new WeakSet(); -+onLatestBlock_fn = async function(latestBlockNumber) { -+ const releaseLock = await __privateGet(this, _getGlobalLock).call(this); -+ try { -+ await __privateMethod(this, _checkTransactions, checkTransactions_fn).call(this); -+ } catch (error) { -+ log("Failed to check transactions", error); -+ } finally { -+ releaseLock(); -+ } -+ try { -+ await __privateMethod(this, _resubmitTransactions, resubmitTransactions_fn).call(this, latestBlockNumber); -+ } catch (error) { -+ log("Failed to resubmit transactions", error); -+ } -+}; -+_checkTransactions = new WeakSet(); -+checkTransactions_fn = async function() { -+ log("Checking transactions"); -+ const pendingTransactions = __privateMethod(this, _getPendingTransactions, getPendingTransactions_fn).call(this); -+ if (!pendingTransactions.length) { -+ log("No pending transactions to check"); -+ return; -+ } -+ log("Found pending transactions to check", { -+ count: pendingTransactions.length, -+ ids: pendingTransactions.map((tx) => tx.id) -+ }); -+ await Promise.all( -+ pendingTransactions.map((tx) => __privateMethod(this, _checkTransaction, checkTransaction_fn).call(this, tx)) -+ ); -+}; -+_resubmitTransactions = new WeakSet(); -+resubmitTransactions_fn = async function(latestBlockNumber) { -+ if (!__privateGet(this, _isResubmitEnabled).call(this) || !__privateGet(this, _running)) { -+ return; -+ } -+ log("Resubmitting transactions"); -+ const pendingTransactions = __privateMethod(this, _getPendingTransactions, getPendingTransactions_fn).call(this); -+ if (!pendingTransactions.length) { -+ log("No pending transactions to resubmit"); -+ return; -+ } -+ log("Found pending transactions to resubmit", { -+ count: pendingTransactions.length, -+ ids: pendingTransactions.map((tx) => tx.id) -+ }); -+ for (const txMeta of pendingTransactions) { -+ try { -+ await __privateMethod(this, _resubmitTransaction, resubmitTransaction_fn).call(this, txMeta, latestBlockNumber); -+ } catch (error) { -+ const errorMessage = error.value?.message?.toLowerCase() || error.message?.toLowerCase() || String(error); -+ if (__privateMethod(this, _isKnownTransactionError, isKnownTransactionError_fn).call(this, errorMessage)) { -+ log("Ignoring known transaction error", errorMessage); -+ continue; -+ } -+ __privateMethod(this, _warnTransaction, warnTransaction_fn).call(this, txMeta, error.message, "There was an error when resubmitting this transaction."); -+ } -+ } -+}; -+_isKnownTransactionError = new WeakSet(); -+isKnownTransactionError_fn = function(errorMessage) { -+ return KNOWN_TRANSACTION_ERRORS.some( -+ (knownError) => errorMessage.includes(knownError) -+ ); -+}; -+_resubmitTransaction = new WeakSet(); -+resubmitTransaction_fn = async function(txMeta, latestBlockNumber) { -+ if (!__privateMethod(this, _isResubmitDue, isResubmitDue_fn).call(this, txMeta, latestBlockNumber)) { -+ return; -+ } -+ const { rawTx } = txMeta; -+ if (!__privateGet(this, _beforePublish).call(this, txMeta)) { -+ return; -+ } -+ const ethQuery = __privateGet(this, _getEthQuery).call(this, txMeta.networkClientId); -+ await __privateGet(this, _publishTransaction).call(this, ethQuery, rawTx); -+ const retryCount = (txMeta.retryCount ?? 0) + 1; -+ __privateMethod(this, _updateTransaction, updateTransaction_fn).call(this, merge({}, txMeta, { retryCount }), "PendingTransactionTracker:transaction-retry - Retry count increased"); -+}; -+_isResubmitDue = new WeakSet(); -+isResubmitDue_fn = function(txMeta, latestBlockNumber) { -+ const txMetaWithFirstRetryBlockNumber = cloneDeep(txMeta); -+ if (!txMetaWithFirstRetryBlockNumber.firstRetryBlockNumber) { -+ txMetaWithFirstRetryBlockNumber.firstRetryBlockNumber = latestBlockNumber; -+ __privateMethod(this, _updateTransaction, updateTransaction_fn).call(this, txMetaWithFirstRetryBlockNumber, "PendingTransactionTracker:#isResubmitDue - First retry block number set"); -+ } -+ const { firstRetryBlockNumber } = txMetaWithFirstRetryBlockNumber; -+ const blocksSinceFirstRetry = Number.parseInt(latestBlockNumber, 16) - Number.parseInt(firstRetryBlockNumber, 16); -+ const retryCount = txMeta.retryCount || 0; -+ const requiredBlocksSinceFirstRetry = Math.min( -+ MAX_RETRY_BLOCK_DISTANCE, -+ Math.pow(2, retryCount) -+ ); -+ return blocksSinceFirstRetry >= requiredBlocksSinceFirstRetry; -+}; -+_checkTransaction = new WeakSet(); -+checkTransaction_fn = async function(txMeta) { -+ const { hash, id } = txMeta; -+ if (!hash && __privateGet(this, _beforeCheckPendingTransaction).call(this, txMeta)) { -+ const error = new Error( -+ "We had an error while submitting this transaction, please try again." -+ ); -+ error.name = "NoTxHashError"; -+ __privateMethod(this, _failTransaction, failTransaction_fn).call(this, txMeta, error); -+ return; -+ } -+ if (__privateMethod(this, _isNonceTaken, isNonceTaken_fn).call(this, txMeta)) { -+ log("Nonce already taken", id); -+ __privateMethod(this, _dropTransaction, dropTransaction_fn).call(this, txMeta); -+ return; -+ } -+ try { -+ const receipt = await __privateMethod(this, _getTransactionReceipt, getTransactionReceipt_fn).call(this, hash); -+ const isSuccess = receipt?.status === RECEIPT_STATUS_SUCCESS; -+ const isFailure = receipt?.status === RECEIPT_STATUS_FAILURE; -+ if (isFailure) { -+ log("Transaction receipt has failed status"); -+ __privateMethod(this, _failTransaction, failTransaction_fn).call(this, txMeta, new Error("Transaction dropped or replaced")); -+ return; -+ } -+ const { blockNumber, blockHash } = receipt || {}; -+ if (isSuccess && blockNumber && blockHash) { -+ await __privateMethod(this, _onTransactionConfirmed, onTransactionConfirmed_fn).call(this, txMeta, { -+ ...receipt, -+ blockNumber, -+ blockHash -+ }); -+ return; -+ } -+ } catch (error) { -+ log("Failed to check transaction", id, error); -+ __privateMethod(this, _warnTransaction, warnTransaction_fn).call(this, txMeta, error.message, "There was a problem loading this transaction."); -+ return; -+ } -+ if (await __privateMethod(this, _isTransactionDropped, isTransactionDropped_fn).call(this, txMeta)) { -+ __privateMethod(this, _dropTransaction, dropTransaction_fn).call(this, txMeta); -+ } -+}; -+_onTransactionConfirmed = new WeakSet(); -+onTransactionConfirmed_fn = async function(txMeta, receipt) { -+ const { id } = txMeta; -+ const { blockHash } = receipt; -+ log("Transaction confirmed", id); -+ const { baseFeePerGas, timestamp: blockTimestamp } = await __privateMethod(this, _getBlockByHash, getBlockByHash_fn).call(this, blockHash, false); -+ const updatedTxMeta = cloneDeep(txMeta); -+ updatedTxMeta.baseFeePerGas = baseFeePerGas; -+ updatedTxMeta.blockTimestamp = blockTimestamp; -+ updatedTxMeta.status = "confirmed" /* confirmed */; -+ updatedTxMeta.txParams = { -+ ...updatedTxMeta.txParams, -+ gasUsed: receipt.gasUsed -+ }; -+ updatedTxMeta.txReceipt = receipt; -+ updatedTxMeta.verifiedOnBlockchain = true; -+ __privateMethod(this, _updateTransaction, updateTransaction_fn).call(this, updatedTxMeta, "PendingTransactionTracker:#onTransactionConfirmed - Transaction confirmed"); -+ this.hub.emit("transaction-confirmed", updatedTxMeta); -+}; -+_isTransactionDropped = new WeakSet(); -+isTransactionDropped_fn = async function(txMeta) { -+ const { -+ hash, -+ id, -+ txParams: { nonce, from } -+ } = txMeta; -+ if (!nonce || !hash) { -+ return false; -+ } -+ const networkNextNonceHex = await __privateMethod(this, _getNetworkTransactionCount, getNetworkTransactionCount_fn).call(this, from); -+ const networkNextNonceNumber = parseInt(networkNextNonceHex, 16); -+ const nonceNumber = parseInt(nonce, 16); -+ if (nonceNumber >= networkNextNonceNumber) { -+ return false; -+ } -+ let droppedBlockCount = __privateGet(this, _droppedBlockCountByHash).get(hash); -+ if (droppedBlockCount === void 0) { -+ droppedBlockCount = 0; -+ __privateGet(this, _droppedBlockCountByHash).set(hash, droppedBlockCount); -+ } -+ if (droppedBlockCount < DROPPED_BLOCK_COUNT) { -+ log("Incrementing dropped block count", { id, droppedBlockCount }); -+ __privateGet(this, _droppedBlockCountByHash).set(hash, droppedBlockCount + 1); -+ return false; -+ } -+ log("Hit dropped block count", id); -+ __privateGet(this, _droppedBlockCountByHash).delete(hash); -+ return true; -+}; -+_isNonceTaken = new WeakSet(); -+isNonceTaken_fn = function(txMeta) { -+ const { id, txParams } = txMeta; -+ return __privateMethod(this, _getCurrentChainTransactions, getCurrentChainTransactions_fn).call(this).some( -+ (tx) => tx.id !== id && tx.txParams.from === txParams.from && tx.status === "confirmed" /* confirmed */ && tx.txParams.nonce === txParams.nonce && tx.type !== "incoming" /* incoming */ -+ ); -+}; -+_getPendingTransactions = new WeakSet(); -+getPendingTransactions_fn = function() { -+ return __privateMethod(this, _getCurrentChainTransactions, getCurrentChainTransactions_fn).call(this).filter( -+ (tx) => tx.status === "submitted" /* submitted */ && !tx.verifiedOnBlockchain && !tx.isUserOperation -+ ); -+}; -+_warnTransaction = new WeakSet(); -+warnTransaction_fn = function(txMeta, error, message) { -+ __privateMethod(this, _updateTransaction, updateTransaction_fn).call(this, { -+ ...txMeta, -+ warning: { error, message } -+ }, "PendingTransactionTracker:#warnTransaction - Warning added"); -+}; -+_failTransaction = new WeakSet(); -+failTransaction_fn = function(txMeta, error) { -+ log("Transaction failed", txMeta.id, error); -+ this.hub.emit("transaction-failed", txMeta, error); -+}; -+_dropTransaction = new WeakSet(); -+dropTransaction_fn = function(txMeta) { -+ log("Transaction dropped", txMeta.id); -+ this.hub.emit("transaction-dropped", txMeta); -+}; -+_updateTransaction = new WeakSet(); -+updateTransaction_fn = function(txMeta, note) { -+ this.hub.emit("transaction-updated", txMeta, note); -+}; -+_getTransactionReceipt = new WeakSet(); -+getTransactionReceipt_fn = async function(txHash) { -+ return await query(__privateGet(this, _getEthQuery).call(this), "getTransactionReceipt", [txHash]); -+}; -+_getBlockByHash = new WeakSet(); -+getBlockByHash_fn = async function(blockHash, includeTransactionDetails) { -+ return await query(__privateGet(this, _getEthQuery).call(this), "getBlockByHash", [ -+ blockHash, -+ includeTransactionDetails -+ ]); -+}; -+_getNetworkTransactionCount = new WeakSet(); -+getNetworkTransactionCount_fn = async function(address) { -+ return await query(__privateGet(this, _getEthQuery).call(this), "getTransactionCount", [address]); -+}; -+_getCurrentChainTransactions = new WeakSet(); -+getCurrentChainTransactions_fn = function() { -+ const currentChainId = __privateGet(this, _getChainId).call(this); -+ return __privateGet(this, _getTransactions).call(this).filter( -+ (tx) => tx.chainId === currentChainId -+ ); -+}; -+ -+export { -+ PendingTransactionTracker -+}; -+//# sourceMappingURL=chunk-7M2R5AHC.mjs.map -\ No newline at end of file -diff --git a/dist/chunk-7M2R5AHC.mjs.map b/dist/chunk-7M2R5AHC.mjs.map -new file mode 100644 -index 0000000000000000000000000000000000000000..bbde7a01b69392362c7c39c97ef83bd8ebefd4a1 ---- /dev/null -+++ b/dist/chunk-7M2R5AHC.mjs.map -@@ -0,0 +1 @@ -+{"version":3,"sources":["../src/helpers/PendingTransactionTracker.ts"],"sourcesContent":["import { query } from '@metamask/controller-utils';\nimport type EthQuery from '@metamask/eth-query';\nimport type {\n BlockTracker,\n NetworkClientId,\n} from '@metamask/network-controller';\nimport EventEmitter from 'events';\nimport { cloneDeep, merge } from 'lodash';\n\nimport { createModuleLogger, projectLogger } from '../logger';\nimport type { TransactionMeta, TransactionReceipt } from '../types';\nimport { TransactionStatus, TransactionType } from '../types';\n\n/**\n * We wait this many blocks before emitting a 'transaction-dropped' event\n * This is because we could be talking to a node that is out of sync\n */\nconst DROPPED_BLOCK_COUNT = 3;\n\nconst RECEIPT_STATUS_SUCCESS = '0x1';\nconst RECEIPT_STATUS_FAILURE = '0x0';\nconst MAX_RETRY_BLOCK_DISTANCE = 50;\n\nconst KNOWN_TRANSACTION_ERRORS = [\n 'replacement transaction underpriced',\n 'known transaction',\n 'gas price too low to replace',\n 'transaction with the same hash was already imported',\n 'gateway timeout',\n 'nonce too low',\n];\n\nconst log = createModuleLogger(projectLogger, 'pending-transactions');\n\ntype SuccessfulTransactionReceipt = TransactionReceipt & {\n blockNumber: string;\n blockHash: string;\n};\n\ntype Events = {\n 'transaction-confirmed': [txMeta: TransactionMeta];\n 'transaction-dropped': [txMeta: TransactionMeta];\n 'transaction-failed': [txMeta: TransactionMeta, error: Error];\n 'transaction-updated': [txMeta: TransactionMeta, note: string];\n};\n\n// This interface was created before this ESLint rule was added.\n// Convert to a `type` in a future major version.\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\nexport interface PendingTransactionTrackerEventEmitter extends EventEmitter {\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n on(\n eventName: T,\n listener: (...args: Events[T]) => void,\n ): this;\n\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/naming-convention\n emit(eventName: T, ...args: Events[T]): boolean;\n}\n\nexport class PendingTransactionTracker {\n hub: PendingTransactionTrackerEventEmitter;\n\n #blockTracker: BlockTracker;\n\n #droppedBlockCountByHash: Map;\n\n #getChainId: () => string;\n\n #getEthQuery: (networkClientId?: NetworkClientId) => EthQuery;\n\n #getTransactions: () => TransactionMeta[];\n\n #isResubmitEnabled: () => boolean;\n\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n #listener: any;\n\n #getGlobalLock: () => Promise<() => void>;\n\n #publishTransaction: (ethQuery: EthQuery, rawTx: string) => Promise;\n\n #running: boolean;\n\n #beforeCheckPendingTransaction: (transactionMeta: TransactionMeta) => boolean;\n\n #beforePublish: (transactionMeta: TransactionMeta) => boolean;\n\n constructor({\n blockTracker,\n getChainId,\n getEthQuery,\n getTransactions,\n isResubmitEnabled,\n getGlobalLock,\n publishTransaction,\n hooks,\n }: {\n blockTracker: BlockTracker;\n getChainId: () => string;\n getEthQuery: (networkClientId?: NetworkClientId) => EthQuery;\n getTransactions: () => TransactionMeta[];\n isResubmitEnabled?: () => boolean;\n getGlobalLock: () => Promise<() => void>;\n publishTransaction: (ethQuery: EthQuery, rawTx: string) => Promise;\n hooks?: {\n beforeCheckPendingTransaction?: (\n transactionMeta: TransactionMeta,\n ) => boolean;\n beforePublish?: (transactionMeta: TransactionMeta) => boolean;\n };\n }) {\n this.hub = new EventEmitter() as PendingTransactionTrackerEventEmitter;\n\n this.#blockTracker = blockTracker;\n this.#droppedBlockCountByHash = new Map();\n this.#getChainId = getChainId;\n this.#getEthQuery = getEthQuery;\n this.#getTransactions = getTransactions;\n this.#isResubmitEnabled = isResubmitEnabled ?? (() => true);\n this.#listener = this.#onLatestBlock.bind(this);\n this.#getGlobalLock = getGlobalLock;\n this.#publishTransaction = publishTransaction;\n this.#running = false;\n this.#beforePublish = hooks?.beforePublish ?? (() => true);\n this.#beforeCheckPendingTransaction =\n hooks?.beforeCheckPendingTransaction ?? (() => true);\n }\n\n startIfPendingTransactions = () => {\n const pendingTransactions = this.#getPendingTransactions();\n\n if (pendingTransactions.length) {\n this.#start();\n } else {\n this.stop();\n }\n };\n\n /**\n * Force checks the network if the given transaction is confirmed and updates it's status.\n *\n * @param txMeta - The transaction to check\n */\n async forceCheckTransaction(txMeta: TransactionMeta) {\n const releaseLock = await this.#getGlobalLock();\n\n try {\n await this.#checkTransaction(txMeta);\n } catch (error) {\n /* istanbul ignore next */\n log('Failed to check transaction', error);\n } finally {\n releaseLock();\n }\n }\n\n #start() {\n if (this.#running) {\n return;\n }\n\n this.#blockTracker.on('latest', this.#listener);\n this.#running = true;\n\n log('Started polling');\n }\n\n stop() {\n if (!this.#running) {\n return;\n }\n\n this.#blockTracker.removeListener('latest', this.#listener);\n this.#running = false;\n\n log('Stopped polling');\n }\n\n async #onLatestBlock(latestBlockNumber: string) {\n const releaseLock = await this.#getGlobalLock();\n\n try {\n await this.#checkTransactions();\n } catch (error) {\n /* istanbul ignore next */\n log('Failed to check transactions', error);\n } finally {\n releaseLock();\n }\n\n try {\n await this.#resubmitTransactions(latestBlockNumber);\n } catch (error) {\n /* istanbul ignore next */\n log('Failed to resubmit transactions', error);\n }\n }\n\n async #checkTransactions() {\n log('Checking transactions');\n\n const pendingTransactions = this.#getPendingTransactions();\n\n if (!pendingTransactions.length) {\n log('No pending transactions to check');\n return;\n }\n\n log('Found pending transactions to check', {\n count: pendingTransactions.length,\n ids: pendingTransactions.map((tx) => tx.id),\n });\n\n await Promise.all(\n pendingTransactions.map((tx) => this.#checkTransaction(tx)),\n );\n }\n\n async #resubmitTransactions(latestBlockNumber: string) {\n if (!this.#isResubmitEnabled() || !this.#running) {\n return;\n }\n\n log('Resubmitting transactions');\n\n const pendingTransactions = this.#getPendingTransactions();\n\n if (!pendingTransactions.length) {\n log('No pending transactions to resubmit');\n return;\n }\n\n log('Found pending transactions to resubmit', {\n count: pendingTransactions.length,\n ids: pendingTransactions.map((tx) => tx.id),\n });\n\n for (const txMeta of pendingTransactions) {\n try {\n await this.#resubmitTransaction(txMeta, latestBlockNumber);\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (error: any) {\n /* istanbul ignore next */\n const errorMessage =\n error.value?.message?.toLowerCase() ||\n error.message?.toLowerCase() ||\n String(error);\n\n if (this.#isKnownTransactionError(errorMessage)) {\n log('Ignoring known transaction error', errorMessage);\n continue;\n }\n\n this.#warnTransaction(\n txMeta,\n error.message,\n 'There was an error when resubmitting this transaction.',\n );\n }\n }\n }\n\n #isKnownTransactionError(errorMessage: string) {\n return KNOWN_TRANSACTION_ERRORS.some((knownError) =>\n errorMessage.includes(knownError),\n );\n }\n\n async #resubmitTransaction(\n txMeta: TransactionMeta,\n latestBlockNumber: string,\n ) {\n if (!this.#isResubmitDue(txMeta, latestBlockNumber)) {\n return;\n }\n\n const { rawTx } = txMeta;\n\n if (!this.#beforePublish(txMeta)) {\n return;\n }\n\n const ethQuery = this.#getEthQuery(txMeta.networkClientId);\n await this.#publishTransaction(ethQuery, rawTx as string);\n\n const retryCount = (txMeta.retryCount ?? 0) + 1;\n\n this.#updateTransaction(\n merge({}, txMeta, { retryCount }),\n 'PendingTransactionTracker:transaction-retry - Retry count increased',\n );\n }\n\n #isResubmitDue(txMeta: TransactionMeta, latestBlockNumber: string): boolean {\n const txMetaWithFirstRetryBlockNumber = cloneDeep(txMeta);\n\n if (!txMetaWithFirstRetryBlockNumber.firstRetryBlockNumber) {\n txMetaWithFirstRetryBlockNumber.firstRetryBlockNumber = latestBlockNumber;\n\n this.#updateTransaction(\n txMetaWithFirstRetryBlockNumber,\n 'PendingTransactionTracker:#isResubmitDue - First retry block number set',\n );\n }\n\n const { firstRetryBlockNumber } = txMetaWithFirstRetryBlockNumber;\n\n const blocksSinceFirstRetry =\n Number.parseInt(latestBlockNumber, 16) -\n Number.parseInt(firstRetryBlockNumber, 16);\n\n const retryCount = txMeta.retryCount || 0;\n\n // Exponential backoff to limit retries at publishing\n // Capped at ~15 minutes between retries\n const requiredBlocksSinceFirstRetry = Math.min(\n MAX_RETRY_BLOCK_DISTANCE,\n Math.pow(2, retryCount),\n );\n\n return blocksSinceFirstRetry >= requiredBlocksSinceFirstRetry;\n }\n\n async #checkTransaction(txMeta: TransactionMeta) {\n const { hash, id } = txMeta;\n\n if (!hash && this.#beforeCheckPendingTransaction(txMeta)) {\n const error = new Error(\n 'We had an error while submitting this transaction, please try again.',\n );\n\n error.name = 'NoTxHashError';\n\n this.#failTransaction(txMeta, error);\n\n return;\n }\n\n if (this.#isNonceTaken(txMeta)) {\n log('Nonce already taken', id);\n this.#dropTransaction(txMeta);\n return;\n }\n\n try {\n const receipt = await this.#getTransactionReceipt(hash);\n const isSuccess = receipt?.status === RECEIPT_STATUS_SUCCESS;\n const isFailure = receipt?.status === RECEIPT_STATUS_FAILURE;\n\n if (isFailure) {\n log('Transaction receipt has failed status');\n\n this.#failTransaction(\n txMeta,\n new Error('Transaction dropped or replaced'),\n );\n\n return;\n }\n\n const { blockNumber, blockHash } = receipt || {};\n\n if (isSuccess && blockNumber && blockHash) {\n await this.#onTransactionConfirmed(txMeta, {\n ...receipt,\n blockNumber,\n blockHash,\n });\n\n return;\n }\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (error: any) {\n log('Failed to check transaction', id, error);\n\n this.#warnTransaction(\n txMeta,\n error.message,\n 'There was a problem loading this transaction.',\n );\n\n return;\n }\n\n if (await this.#isTransactionDropped(txMeta)) {\n this.#dropTransaction(txMeta);\n }\n }\n\n async #onTransactionConfirmed(\n txMeta: TransactionMeta,\n receipt: SuccessfulTransactionReceipt,\n ) {\n const { id } = txMeta;\n const { blockHash } = receipt;\n\n log('Transaction confirmed', id);\n\n const { baseFeePerGas, timestamp: blockTimestamp } =\n await this.#getBlockByHash(blockHash, false);\n\n const updatedTxMeta = cloneDeep(txMeta);\n updatedTxMeta.baseFeePerGas = baseFeePerGas;\n updatedTxMeta.blockTimestamp = blockTimestamp;\n updatedTxMeta.status = TransactionStatus.confirmed;\n updatedTxMeta.txParams = {\n ...updatedTxMeta.txParams,\n gasUsed: receipt.gasUsed,\n };\n updatedTxMeta.txReceipt = receipt;\n updatedTxMeta.verifiedOnBlockchain = true;\n\n this.#updateTransaction(\n updatedTxMeta,\n 'PendingTransactionTracker:#onTransactionConfirmed - Transaction confirmed',\n );\n\n this.hub.emit('transaction-confirmed', updatedTxMeta);\n }\n\n async #isTransactionDropped(txMeta: TransactionMeta) {\n const {\n hash,\n id,\n txParams: { nonce, from },\n } = txMeta;\n\n /* istanbul ignore next */\n if (!nonce || !hash) {\n return false;\n }\n\n const networkNextNonceHex = await this.#getNetworkTransactionCount(from);\n const networkNextNonceNumber = parseInt(networkNextNonceHex, 16);\n const nonceNumber = parseInt(nonce, 16);\n\n if (nonceNumber >= networkNextNonceNumber) {\n return false;\n }\n\n let droppedBlockCount = this.#droppedBlockCountByHash.get(hash);\n\n if (droppedBlockCount === undefined) {\n droppedBlockCount = 0;\n this.#droppedBlockCountByHash.set(hash, droppedBlockCount);\n }\n\n if (droppedBlockCount < DROPPED_BLOCK_COUNT) {\n log('Incrementing dropped block count', { id, droppedBlockCount });\n this.#droppedBlockCountByHash.set(hash, droppedBlockCount + 1);\n return false;\n }\n\n log('Hit dropped block count', id);\n\n this.#droppedBlockCountByHash.delete(hash);\n return true;\n }\n\n #isNonceTaken(txMeta: TransactionMeta): boolean {\n const { id, txParams } = txMeta;\n\n return this.#getCurrentChainTransactions().some(\n (tx) =>\n tx.id !== id &&\n tx.txParams.from === txParams.from &&\n tx.status === TransactionStatus.confirmed &&\n tx.txParams.nonce === txParams.nonce &&\n tx.type !== TransactionType.incoming,\n );\n }\n\n #getPendingTransactions(): TransactionMeta[] {\n return this.#getCurrentChainTransactions().filter(\n (tx) =>\n tx.status === TransactionStatus.submitted &&\n !tx.verifiedOnBlockchain &&\n !tx.isUserOperation,\n );\n }\n\n #warnTransaction(txMeta: TransactionMeta, error: string, message: string) {\n this.#updateTransaction(\n {\n ...txMeta,\n warning: { error, message },\n },\n 'PendingTransactionTracker:#warnTransaction - Warning added',\n );\n }\n\n #failTransaction(txMeta: TransactionMeta, error: Error) {\n log('Transaction failed', txMeta.id, error);\n this.hub.emit('transaction-failed', txMeta, error);\n }\n\n #dropTransaction(txMeta: TransactionMeta) {\n log('Transaction dropped', txMeta.id);\n this.hub.emit('transaction-dropped', txMeta);\n }\n\n #updateTransaction(txMeta: TransactionMeta, note: string) {\n this.hub.emit('transaction-updated', txMeta, note);\n }\n\n async #getTransactionReceipt(\n txHash?: string,\n ): Promise {\n return await query(this.#getEthQuery(), 'getTransactionReceipt', [txHash]);\n }\n\n async #getBlockByHash(\n blockHash: string,\n includeTransactionDetails: boolean,\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ): Promise {\n return await query(this.#getEthQuery(), 'getBlockByHash', [\n blockHash,\n includeTransactionDetails,\n ]);\n }\n\n async #getNetworkTransactionCount(address: string): Promise {\n return await query(this.#getEthQuery(), 'getTransactionCount', [address]);\n }\n\n #getCurrentChainTransactions(): TransactionMeta[] {\n const currentChainId = this.#getChainId();\n\n return this.#getTransactions().filter(\n (tx) => tx.chainId === currentChainId,\n );\n }\n}\n"],"mappings":"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","names":[]} -\ No newline at end of file -diff --git a/dist/chunk-IVR4NMOF.js b/dist/chunk-IVR4NMOF.js -new file mode 100644 -index 0000000000000000000000000000000000000000..2266127ef5aead422d1ccf8f1a53734f81499d96 ---- /dev/null -+++ b/dist/chunk-IVR4NMOF.js -@@ -0,0 +1,2556 @@ -+"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } -+ -+ -+var _chunkPRUNMTRDjs = require('./chunk-PRUNMTRD.js'); -+ -+ -+var _chunk74W7X6BEjs = require('./chunk-74W7X6BE.js'); -+ -+ -+var _chunkSD6CWFDFjs = require('./chunk-SD6CWFDF.js'); -+ -+ -+ -+var _chunkRXIUMVA5js = require('./chunk-RXIUMVA5.js'); -+ -+ -+var _chunk6DODV6OVjs = require('./chunk-6DODV6OV.js'); -+ -+ -+var _chunk7LXE4KHVjs = require('./chunk-7LXE4KHV.js'); -+ -+ -+ -+ -+var _chunkV72C4MCRjs = require('./chunk-V72C4MCR.js'); -+ -+ -+ -+var _chunkQP75SWIQjs = require('./chunk-QP75SWIQ.js'); -+ -+ -+var _chunkNYKRCWBGjs = require('./chunk-NYKRCWBG.js'); -+ -+ -+var _chunkWR5F34OWjs = require('./chunk-WR5F34OW.js'); -+ -+ -+var _chunkTJMQEH57js = require('./chunk-TJMQEH57.js'); -+ -+ -+var _chunk2EU6346Vjs = require('./chunk-2EU6346V.js'); -+ -+ -+ -+var _chunk2XKEAKQGjs = require('./chunk-2XKEAKQG.js'); -+ -+ -+var _chunkRHDPOIS4js = require('./chunk-RHDPOIS4.js'); -+ -+ -+var _chunk6OLJWLKKjs = require('./chunk-6OLJWLKK.js'); -+ -+ -+var _chunk7NMV2NPMjs = require('./chunk-7NMV2NPM.js'); -+ -+ -+var _chunkARZHJFVGjs = require('./chunk-ARZHJFVG.js'); -+ -+ -+var _chunkQTKXIDGEjs = require('./chunk-QTKXIDGE.js'); -+ -+ -+var _chunkC3WC4OJ3js = require('./chunk-C3WC4OJ3.js'); -+ -+ -+ -+var _chunkQH2H4W3Njs = require('./chunk-QH2H4W3N.js'); -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+var _chunkOZ6UB42Cjs = require('./chunk-OZ6UB42C.js'); -+ -+ -+var _chunk76FONEDAjs = require('./chunk-76FONEDA.js'); -+ -+ -+var _chunkS6VGOPUYjs = require('./chunk-S6VGOPUY.js'); -+ -+ -+ -+ -+ -+var _chunkZ4BLTVTBjs = require('./chunk-Z4BLTVTB.js'); -+ -+// src/TransactionController.ts -+var _common = require('@ethereumjs/common'); -+var _tx = require('@ethereumjs/tx'); -+var _util = require('@ethereumjs/util'); -+var _basecontroller = require('@metamask/base-controller'); -+ -+ -+ -+ -+ -+ -+var _controllerutils = require('@metamask/controller-utils'); -+var _ethquery = require('@metamask/eth-query'); var _ethquery2 = _interopRequireDefault(_ethquery); -+var _networkcontroller = require('@metamask/network-controller'); -+var _noncetracker = require('@metamask/nonce-tracker'); -+var _rpcerrors = require('@metamask/rpc-errors'); -+var _utils = require('@metamask/utils'); -+var _asyncmutex = require('async-mutex'); -+var _ethmethodregistry = require('eth-method-registry'); -+var _events = require('events'); -+var _lodash = require('lodash'); -+var _uuid = require('uuid'); -+var metadata = { -+ transactions: { -+ persist: true, -+ anonymous: false -+ }, -+ methodData: { -+ persist: true, -+ anonymous: false -+ }, -+ lastFetchedBlockNumbers: { -+ persist: true, -+ anonymous: false -+ } -+}; -+var HARDFORK = _common.Hardfork.London; -+var CANCEL_RATE = 1.1; -+var SPEED_UP_RATE = 1.1; -+var controllerName = "TransactionController"; -+var ApprovalState = /* @__PURE__ */ ((ApprovalState2) => { -+ ApprovalState2["Approved"] = "approved"; -+ ApprovalState2["NotApproved"] = "not-approved"; -+ ApprovalState2["SkippedViaBeforePublishHook"] = "skipped-via-before-publish-hook"; -+ return ApprovalState2; -+})(ApprovalState || {}); -+function getDefaultTransactionControllerState() { -+ return { -+ methodData: {}, -+ transactions: [], -+ lastFetchedBlockNumbers: {} -+ }; -+} -+var _internalEvents, _incomingTransactionOptions, _pendingTransactionOptions, _transactionHistoryLimit, _isSimulationEnabled, _testGasFeeFlows, _multichainTrackingHelper, _createNonceTracker, createNonceTracker_fn, _createIncomingTransactionHelper, createIncomingTransactionHelper_fn, _createPendingTransactionTracker, createPendingTransactionTracker_fn, _checkForPendingTransactionAndStartPolling, _stopAllTracking, stopAllTracking_fn, _removeIncomingTransactionHelperListeners, removeIncomingTransactionHelperListeners_fn, _addIncomingTransactionHelperListeners, addIncomingTransactionHelperListeners_fn, _removePendingTransactionTrackerListeners, removePendingTransactionTrackerListeners_fn, _addPendingTransactionTrackerListeners, addPendingTransactionTrackerListeners_fn, _getNonceTrackerPendingTransactions, getNonceTrackerPendingTransactions_fn, _getGasFeeFlows, getGasFeeFlows_fn, _getLayer1GasFeeFlows, getLayer1GasFeeFlows_fn, _updateTransactionInternal, updateTransactionInternal_fn, _checkIfTransactionParamsUpdated, checkIfTransactionParamsUpdated_fn, _onTransactionParamsUpdated, onTransactionParamsUpdated_fn, _updateSimulationData, updateSimulationData_fn, _onGasFeePollerTransactionUpdate, onGasFeePollerTransactionUpdate_fn, _getNetworkClientId, getNetworkClientId_fn, _getGlobalNetworkClientId, getGlobalNetworkClientId_fn, _getGlobalChainId, getGlobalChainId_fn, _isCustomNetwork, isCustomNetwork_fn, _getSelectedAccount, getSelectedAccount_fn; -+var TransactionController = class extends _basecontroller.BaseController { -+ /** -+ * Constructs a TransactionController. -+ * -+ * @param options - The controller options. -+ * @param options.blockTracker - The block tracker used to poll for new blocks data. -+ * @param options.disableHistory - Whether to disable storing history in transaction metadata. -+ * @param options.disableSendFlowHistory - Explicitly disable transaction metadata history. -+ * @param options.disableSwaps - Whether to disable additional processing on swaps transactions. -+ * @param options.getCurrentAccountEIP1559Compatibility - Whether or not the account supports EIP-1559. -+ * @param options.getCurrentNetworkEIP1559Compatibility - Whether or not the network supports EIP-1559. -+ * @param options.getExternalPendingTransactions - Callback to retrieve pending transactions from external sources. -+ * @param options.getGasFeeEstimates - Callback to retrieve gas fee estimates. -+ * @param options.getNetworkClientRegistry - Gets the network client registry. -+ * @param options.getNetworkState - Gets the state of the network controller. -+ * @param options.getPermittedAccounts - Get accounts that a given origin has permissions for. -+ * @param options.getSavedGasFees - Gets the saved gas fee config. -+ * @param options.incomingTransactions - Configuration options for incoming transaction support. -+ * @param options.isMultichainEnabled - Enable multichain support. -+ * @param options.isSimulationEnabled - Whether new transactions will be automatically simulated. -+ * @param options.messenger - The controller messenger. -+ * @param options.onNetworkStateChange - Allows subscribing to network controller state changes. -+ * @param options.pendingTransactions - Configuration options for pending transaction support. -+ * @param options.provider - The provider used to create the underlying EthQuery instance. -+ * @param options.securityProviderRequest - A function for verifying a transaction, whether it is malicious or not. -+ * @param options.sign - Function used to sign transactions. -+ * @param options.state - Initial state to set on this controller. -+ * @param options.testGasFeeFlows - Whether to use the test gas fee flow. -+ * @param options.transactionHistoryLimit - Transaction history limit. -+ * @param options.hooks - The controller hooks. -+ */ -+ constructor({ -+ blockTracker, -+ disableHistory, -+ disableSendFlowHistory, -+ disableSwaps, -+ getCurrentAccountEIP1559Compatibility, -+ getCurrentNetworkEIP1559Compatibility, -+ getExternalPendingTransactions, -+ getGasFeeEstimates, -+ getNetworkClientRegistry, -+ getNetworkState, -+ getPermittedAccounts, -+ getSavedGasFees, -+ incomingTransactions = {}, -+ isMultichainEnabled = false, -+ isSimulationEnabled, -+ messenger, -+ onNetworkStateChange, -+ pendingTransactions = {}, -+ provider, -+ securityProviderRequest, -+ sign, -+ state, -+ testGasFeeFlows, -+ transactionHistoryLimit = 40, -+ hooks -+ }) { -+ super({ -+ name: controllerName, -+ metadata, -+ messenger, -+ state: { -+ ...getDefaultTransactionControllerState(), -+ ...state -+ } -+ }); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _createNonceTracker); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _createIncomingTransactionHelper); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _createPendingTransactionTracker); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _stopAllTracking); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _removeIncomingTransactionHelperListeners); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _addIncomingTransactionHelperListeners); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _removePendingTransactionTrackerListeners); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _addPendingTransactionTrackerListeners); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _getNonceTrackerPendingTransactions); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _getGasFeeFlows); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _getLayer1GasFeeFlows); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _updateTransactionInternal); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _checkIfTransactionParamsUpdated); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _onTransactionParamsUpdated); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _updateSimulationData); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _onGasFeePollerTransactionUpdate); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _getNetworkClientId); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _getGlobalNetworkClientId); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _getGlobalChainId); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _isCustomNetwork); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _getSelectedAccount); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _internalEvents, new (0, _events.EventEmitter)()); -+ this.approvingTransactionIds = /* @__PURE__ */ new Set(); -+ this.mutex = new (0, _asyncmutex.Mutex)(); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _incomingTransactionOptions, void 0); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _pendingTransactionOptions, void 0); -+ this.signAbortCallbacks = /* @__PURE__ */ new Map(); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _transactionHistoryLimit, void 0); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _isSimulationEnabled, void 0); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _testGasFeeFlows, void 0); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _multichainTrackingHelper, void 0); -+ _chunkZ4BLTVTBjs.__privateAdd.call(void 0, this, _checkForPendingTransactionAndStartPolling, () => { -+ this.pendingTransactionTracker.startIfPendingTransactions(); -+ _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _multichainTrackingHelper).checkForPendingTransactionAndStartPolling(); -+ }); -+ this.messagingSystem = messenger; -+ this.getNetworkState = getNetworkState; -+ this.isSendFlowHistoryDisabled = disableSendFlowHistory ?? false; -+ this.isHistoryDisabled = disableHistory ?? false; -+ this.isSwapsDisabled = disableSwaps ?? false; -+ _chunkZ4BLTVTBjs.__privateSet.call(void 0, this, _isSimulationEnabled, isSimulationEnabled ?? (() => true)); -+ this.registry = new (0, _ethmethodregistry.MethodRegistry)({ provider }); -+ this.getSavedGasFees = getSavedGasFees ?? ((_chainId) => void 0); -+ this.getCurrentAccountEIP1559Compatibility = getCurrentAccountEIP1559Compatibility ?? (() => Promise.resolve(true)); -+ this.getCurrentNetworkEIP1559Compatibility = getCurrentNetworkEIP1559Compatibility; -+ this.getGasFeeEstimates = getGasFeeEstimates || (() => Promise.resolve({})); -+ this.getPermittedAccounts = getPermittedAccounts; -+ this.getExternalPendingTransactions = getExternalPendingTransactions ?? (() => []); -+ this.securityProviderRequest = securityProviderRequest; -+ _chunkZ4BLTVTBjs.__privateSet.call(void 0, this, _incomingTransactionOptions, incomingTransactions); -+ _chunkZ4BLTVTBjs.__privateSet.call(void 0, this, _pendingTransactionOptions, pendingTransactions); -+ _chunkZ4BLTVTBjs.__privateSet.call(void 0, this, _transactionHistoryLimit, transactionHistoryLimit); -+ this.sign = sign; -+ _chunkZ4BLTVTBjs.__privateSet.call(void 0, this, _testGasFeeFlows, testGasFeeFlows === true); -+ this.afterSign = hooks?.afterSign ?? (() => true); -+ this.beforeCheckPendingTransaction = hooks?.beforeCheckPendingTransaction ?? /* istanbul ignore next */ -+ (() => true); -+ this.beforePublish = hooks?.beforePublish ?? (() => true); -+ this.getAdditionalSignArguments = hooks?.getAdditionalSignArguments ?? (() => []); -+ this.publish = hooks?.publish ?? (() => Promise.resolve({ transactionHash: void 0 })); -+ this.nonceTracker = _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _createNonceTracker, createNonceTracker_fn).call(this, { -+ provider, -+ blockTracker -+ }); -+ const findNetworkClientIdByChainId = (chainId) => { -+ return this.messagingSystem.call( -+ `NetworkController:findNetworkClientIdByChainId`, -+ chainId -+ ); -+ }; -+ _chunkZ4BLTVTBjs.__privateSet.call(void 0, this, _multichainTrackingHelper, new (0, _chunk6OLJWLKKjs.MultichainTrackingHelper)({ -+ isMultichainEnabled, -+ provider, -+ nonceTracker: this.nonceTracker, -+ incomingTransactionOptions: incomingTransactions, -+ findNetworkClientIdByChainId, -+ getNetworkClientById: (networkClientId) => { -+ return this.messagingSystem.call( -+ `NetworkController:getNetworkClientById`, -+ networkClientId -+ ); -+ }, -+ getNetworkClientRegistry, -+ removeIncomingTransactionHelperListeners: _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _removeIncomingTransactionHelperListeners, removeIncomingTransactionHelperListeners_fn).bind(this), -+ removePendingTransactionTrackerListeners: _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _removePendingTransactionTrackerListeners, removePendingTransactionTrackerListeners_fn).bind(this), -+ createNonceTracker: _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _createNonceTracker, createNonceTracker_fn).bind(this), -+ createIncomingTransactionHelper: _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _createIncomingTransactionHelper, createIncomingTransactionHelper_fn).bind(this), -+ createPendingTransactionTracker: _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _createPendingTransactionTracker, createPendingTransactionTracker_fn).bind(this), -+ onNetworkStateChange: (listener) => { -+ this.messagingSystem.subscribe( -+ "NetworkController:stateChange", -+ listener -+ ); -+ } -+ })); -+ _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _multichainTrackingHelper).initialize(); -+ const etherscanRemoteTransactionSource = new (0, _chunk7NMV2NPMjs.EtherscanRemoteTransactionSource)({ -+ includeTokenTransfers: incomingTransactions.includeTokenTransfers -+ }); -+ this.incomingTransactionHelper = _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _createIncomingTransactionHelper, createIncomingTransactionHelper_fn).call(this, { -+ blockTracker, -+ etherscanRemoteTransactionSource -+ }); -+ this.pendingTransactionTracker = _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _createPendingTransactionTracker, createPendingTransactionTracker_fn).call(this, { -+ provider, -+ blockTracker -+ }); -+ this.gasFeeFlows = _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _getGasFeeFlows, getGasFeeFlows_fn).call(this); -+ this.layer1GasFeeFlows = _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _getLayer1GasFeeFlows, getLayer1GasFeeFlows_fn).call(this); -+ const gasFeePoller = new (0, _chunk2EU6346Vjs.GasFeePoller)({ -+ findNetworkClientIdByChainId, -+ gasFeeFlows: this.gasFeeFlows, -+ getGasFeeControllerEstimates: this.getGasFeeEstimates, -+ getProvider: (chainId, networkClientId) => _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _multichainTrackingHelper).getProvider({ -+ networkClientId, -+ chainId -+ }), -+ getTransactions: () => this.state.transactions, -+ layer1GasFeeFlows: this.layer1GasFeeFlows, -+ onStateChange: (listener) => { -+ this.messagingSystem.subscribe( -+ "TransactionController:stateChange", -+ listener -+ ); -+ } -+ }); -+ gasFeePoller.hub.on( -+ "transaction-updated", -+ _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _onGasFeePollerTransactionUpdate, onGasFeePollerTransactionUpdate_fn).bind(this) -+ ); -+ this.messagingSystem.subscribe( -+ "TransactionController:stateChange", -+ _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _checkForPendingTransactionAndStartPolling) -+ ); -+ onNetworkStateChange(() => { -+ _chunkS6VGOPUYjs.projectLogger.call(void 0, "Detected network change", this.getChainId()); -+ this.pendingTransactionTracker.startIfPendingTransactions(); -+ this.onBootCleanup(); -+ }); -+ this.onBootCleanup(); -+ _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _checkForPendingTransactionAndStartPolling).call(this); -+ } -+ failTransaction(transactionMeta, error, actionId) { -+ let newTransactionMeta; -+ try { -+ newTransactionMeta = _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _updateTransactionInternal, updateTransactionInternal_fn).call(this, { -+ transactionId: transactionMeta.id, -+ note: "TransactionController#failTransaction - Add error message and set status to failed", -+ skipValidation: true -+ }, (draftTransactionMeta) => { -+ draftTransactionMeta.status = "failed" /* failed */; -+ draftTransactionMeta.error = _chunkOZ6UB42Cjs.normalizeTxError.call(void 0, error); -+ }); -+ } catch (err) { -+ _chunkS6VGOPUYjs.projectLogger.call(void 0, "Failed to mark transaction as failed", err); -+ newTransactionMeta = { -+ ...transactionMeta, -+ status: "failed" /* failed */, -+ error: _chunkOZ6UB42Cjs.normalizeTxError.call(void 0, error) -+ }; -+ } -+ this.messagingSystem.publish(`${controllerName}:transactionFailed`, { -+ actionId, -+ error: error.message, -+ transactionMeta: newTransactionMeta -+ }); -+ this.onTransactionStatusChange(newTransactionMeta); -+ this.messagingSystem.publish( -+ `${controllerName}:transactionFinished`, -+ newTransactionMeta -+ ); -+ _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _internalEvents).emit( -+ `${transactionMeta.id}:finished`, -+ newTransactionMeta -+ ); -+ } -+ async registryLookup(fourBytePrefix) { -+ const registryMethod = await this.registry.lookup(fourBytePrefix); -+ if (!registryMethod) { -+ return { -+ registryMethod: "", -+ parsedRegistryMethod: { name: void 0, args: void 0 } -+ }; -+ } -+ const parsedRegistryMethod = this.registry.parse(registryMethod); -+ return { registryMethod, parsedRegistryMethod }; -+ } -+ /** -+ * Stops polling and removes listeners to prepare the controller for garbage collection. -+ */ -+ destroy() { -+ _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _stopAllTracking, stopAllTracking_fn).call(this); -+ } -+ /** -+ * Handle new method data request. -+ * -+ * @param fourBytePrefix - The method prefix. -+ * @returns The method data object corresponding to the given signature prefix. -+ */ -+ async handleMethodData(fourBytePrefix) { -+ const releaseLock = await this.mutex.acquire(); -+ try { -+ const { methodData } = this.state; -+ const knownMethod = Object.keys(methodData).find( -+ (knownFourBytePrefix) => fourBytePrefix === knownFourBytePrefix -+ ); -+ if (knownMethod) { -+ return methodData[fourBytePrefix]; -+ } -+ const registry = await this.registryLookup(fourBytePrefix); -+ this.update((state) => { -+ state.methodData[fourBytePrefix] = registry; -+ }); -+ return registry; -+ } finally { -+ releaseLock(); -+ } -+ } -+ /** -+ * Add a new unapproved transaction to state. Parameters will be validated, a -+ * unique transaction id will be generated, and gas and gasPrice will be calculated -+ * if not provided. If A `:unapproved` hub event will be emitted once added. -+ * -+ * @param txParams - Standard parameters for an Ethereum transaction. -+ * @param opts - Additional options to control how the transaction is added. -+ * @param opts.actionId - Unique ID to prevent duplicate requests. -+ * @param opts.deviceConfirmedOn - An enum to indicate what device confirmed the transaction. -+ * @param opts.method - RPC method that requested the transaction. -+ * @param opts.origin - The origin of the transaction request, such as a dApp hostname. -+ * @param opts.requireApproval - Whether the transaction requires approval by the user, defaults to true unless explicitly disabled. -+ * @param opts.securityAlertResponse - Response from security validator. -+ * @param opts.sendFlowHistory - The sendFlowHistory entries to add. -+ * @param opts.type - Type of transaction to add, such as 'cancel' or 'swap'. -+ * @param opts.swaps - Options for swaps transactions. -+ * @param opts.swaps.hasApproveTx - Whether the transaction has an approval transaction. -+ * @param opts.swaps.meta - Metadata for swap transaction. -+ * @param opts.networkClientId - The id of the network client for this transaction. -+ * @returns Object containing a promise resolving to the transaction hash if approved. -+ */ -+ async addTransaction(txParams, { -+ actionId, -+ deviceConfirmedOn, -+ method, -+ origin, -+ requireApproval, -+ securityAlertResponse, -+ sendFlowHistory, -+ swaps = {}, -+ type, -+ networkClientId: requestNetworkClientId -+ } = {}) { -+ _chunkS6VGOPUYjs.projectLogger.call(void 0, "Adding transaction", txParams); -+ txParams = _chunkOZ6UB42Cjs.normalizeTransactionParams.call(void 0, txParams); -+ if (requestNetworkClientId && !_chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _multichainTrackingHelper).has(requestNetworkClientId)) { -+ throw new Error( -+ "The networkClientId for this transaction could not be found" -+ ); -+ } -+ const networkClientId = requestNetworkClientId ?? _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _getGlobalNetworkClientId, getGlobalNetworkClientId_fn).call(this); -+ const isEIP1559Compatible = await this.getEIP1559Compatibility( -+ networkClientId -+ ); -+ _chunkRXIUMVA5js.validateTxParams.call(void 0, txParams, isEIP1559Compatible); -+ if (origin) { -+ await _chunkRXIUMVA5js.validateTransactionOrigin.call(void 0, -+ await this.getPermittedAccounts(origin), -+ _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _getSelectedAccount, getSelectedAccount_fn).call(this).address, -+ txParams.from, -+ origin -+ ); -+ } -+ const dappSuggestedGasFees = this.generateDappSuggestedGasFees( -+ txParams, -+ origin -+ ); -+ const chainId = this.getChainId(networkClientId); -+ const ethQuery = _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _multichainTrackingHelper).getEthQuery({ -+ networkClientId, -+ chainId -+ }); -+ const transactionType = type ?? (await _chunkSD6CWFDFjs.determineTransactionType.call(void 0, txParams, ethQuery)).type; -+ const existingTransactionMeta = this.getTransactionWithActionId(actionId); -+ let addedTransactionMeta = existingTransactionMeta ? _lodash.cloneDeep.call(void 0, existingTransactionMeta) : { -+ // Add actionId to txMeta to check if same actionId is seen again -+ actionId, -+ chainId, -+ dappSuggestedGasFees, -+ deviceConfirmedOn, -+ id: _uuid.v1.call(void 0, ), -+ origin, -+ securityAlertResponse, -+ status: "unapproved" /* unapproved */, -+ time: Date.now(), -+ txParams, -+ userEditedGasLimit: false, -+ verifiedOnBlockchain: false, -+ type: transactionType, -+ networkClientId -+ }; -+ await this.updateGasProperties(addedTransactionMeta); -+ if (!existingTransactionMeta) { -+ if (method && this.securityProviderRequest) { -+ const securityProviderResponse = await this.securityProviderRequest( -+ addedTransactionMeta, -+ method -+ ); -+ addedTransactionMeta.securityProviderResponse = securityProviderResponse; -+ } -+ if (!this.isSendFlowHistoryDisabled) { -+ addedTransactionMeta.sendFlowHistory = sendFlowHistory ?? []; -+ } -+ if (!this.isHistoryDisabled) { -+ addedTransactionMeta = _chunkQP75SWIQjs.addInitialHistorySnapshot.call(void 0, addedTransactionMeta); -+ } -+ addedTransactionMeta = _chunkQH2H4W3Njs.updateSwapsTransaction.call(void 0, -+ addedTransactionMeta, -+ transactionType, -+ swaps, -+ { -+ isSwapsDisabled: this.isSwapsDisabled, -+ cancelTransaction: this.cancelTransaction.bind(this), -+ messenger: this.messagingSystem -+ } -+ ); -+ this.addMetadata(addedTransactionMeta); -+ if (requireApproval !== false) { -+ _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _updateSimulationData, updateSimulationData_fn).call(this, addedTransactionMeta); -+ } else { -+ _chunkS6VGOPUYjs.projectLogger.call(void 0, "Skipping simulation as approval not required"); -+ } -+ this.messagingSystem.publish( -+ `${controllerName}:unapprovedTransactionAdded`, -+ addedTransactionMeta -+ ); -+ } -+ return { -+ result: this.processApproval(addedTransactionMeta, { -+ isExisting: Boolean(existingTransactionMeta), -+ requireApproval, -+ actionId -+ }), -+ transactionMeta: addedTransactionMeta -+ }; -+ } -+ startIncomingTransactionPolling(networkClientIds = []) { -+ if (networkClientIds.length === 0) { -+ this.incomingTransactionHelper.start(); -+ return; -+ } -+ _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _multichainTrackingHelper).startIncomingTransactionPolling( -+ networkClientIds -+ ); -+ } -+ stopIncomingTransactionPolling(networkClientIds = []) { -+ if (networkClientIds.length === 0) { -+ this.incomingTransactionHelper.stop(); -+ return; -+ } -+ _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _multichainTrackingHelper).stopIncomingTransactionPolling( -+ networkClientIds -+ ); -+ } -+ stopAllIncomingTransactionPolling() { -+ this.incomingTransactionHelper.stop(); -+ _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _multichainTrackingHelper).stopAllIncomingTransactionPolling(); -+ } -+ async updateIncomingTransactions(networkClientIds = []) { -+ if (networkClientIds.length === 0) { -+ await this.incomingTransactionHelper.update(); -+ return; -+ } -+ await _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _multichainTrackingHelper).updateIncomingTransactions( -+ networkClientIds -+ ); -+ } -+ /** -+ * Attempts to cancel a transaction based on its ID by setting its status to "rejected" -+ * and emitting a `:finished` hub event. -+ * -+ * @param transactionId - The ID of the transaction to cancel. -+ * @param gasValues - The gas values to use for the cancellation transaction. -+ * @param options - The options for the cancellation transaction. -+ * @param options.actionId - Unique ID to prevent duplicate requests. -+ * @param options.estimatedBaseFee - The estimated base fee of the transaction. -+ */ -+ async stopTransaction(transactionId, gasValues, { -+ estimatedBaseFee, -+ actionId -+ } = {}) { -+ if (this.getTransactionWithActionId(actionId)) { -+ return; -+ } -+ if (gasValues) { -+ gasValues = _chunkOZ6UB42Cjs.normalizeGasFeeValues.call(void 0, gasValues); -+ _chunkOZ6UB42Cjs.validateGasValues.call(void 0, gasValues); -+ } -+ _chunkS6VGOPUYjs.projectLogger.call(void 0, "Creating cancel transaction", transactionId, gasValues); -+ const transactionMeta = this.getTransaction(transactionId); -+ if (!transactionMeta) { -+ return; -+ } -+ if (!this.sign) { -+ throw new Error("No sign method defined."); -+ } -+ const minGasPrice = _chunkOZ6UB42Cjs.getIncreasedPriceFromExisting.call(void 0, -+ transactionMeta.txParams.gasPrice, -+ CANCEL_RATE -+ ); -+ const gasPriceFromValues = _chunkOZ6UB42Cjs.isGasPriceValue.call(void 0, gasValues) && gasValues.gasPrice; -+ const newGasPrice = gasPriceFromValues && _chunkOZ6UB42Cjs.validateMinimumIncrease.call(void 0, gasPriceFromValues, minGasPrice) || minGasPrice; -+ const existingMaxFeePerGas = transactionMeta.txParams?.maxFeePerGas; -+ const minMaxFeePerGas = _chunkOZ6UB42Cjs.getIncreasedPriceFromExisting.call(void 0, -+ existingMaxFeePerGas, -+ CANCEL_RATE -+ ); -+ const maxFeePerGasValues = _chunkOZ6UB42Cjs.isFeeMarketEIP1559Values.call(void 0, gasValues) && gasValues.maxFeePerGas; -+ const newMaxFeePerGas = maxFeePerGasValues && _chunkOZ6UB42Cjs.validateMinimumIncrease.call(void 0, maxFeePerGasValues, minMaxFeePerGas) || existingMaxFeePerGas && minMaxFeePerGas; -+ const existingMaxPriorityFeePerGas = transactionMeta.txParams?.maxPriorityFeePerGas; -+ const minMaxPriorityFeePerGas = _chunkOZ6UB42Cjs.getIncreasedPriceFromExisting.call(void 0, -+ existingMaxPriorityFeePerGas, -+ CANCEL_RATE -+ ); -+ const maxPriorityFeePerGasValues = _chunkOZ6UB42Cjs.isFeeMarketEIP1559Values.call(void 0, gasValues) && gasValues.maxPriorityFeePerGas; -+ const newMaxPriorityFeePerGas = maxPriorityFeePerGasValues && _chunkOZ6UB42Cjs.validateMinimumIncrease.call(void 0, -+ maxPriorityFeePerGasValues, -+ minMaxPriorityFeePerGas -+ ) || existingMaxPriorityFeePerGas && minMaxPriorityFeePerGas; -+ const newTxParams = newMaxFeePerGas && newMaxPriorityFeePerGas ? { -+ from: transactionMeta.txParams.from, -+ gasLimit: transactionMeta.txParams.gas, -+ maxFeePerGas: newMaxFeePerGas, -+ maxPriorityFeePerGas: newMaxPriorityFeePerGas, -+ type: "0x2" /* feeMarket */, -+ nonce: transactionMeta.txParams.nonce, -+ to: transactionMeta.txParams.from, -+ value: "0x0" -+ } : { -+ from: transactionMeta.txParams.from, -+ gasLimit: transactionMeta.txParams.gas, -+ gasPrice: newGasPrice, -+ nonce: transactionMeta.txParams.nonce, -+ to: transactionMeta.txParams.from, -+ value: "0x0" -+ }; -+ const unsignedEthTx = this.prepareUnsignedEthTx( -+ transactionMeta.chainId, -+ newTxParams -+ ); -+ const signedTx = await this.sign( -+ unsignedEthTx, -+ transactionMeta.txParams.from -+ ); -+ const rawTx = _util.bufferToHex.call(void 0, signedTx.serialize()); -+ const newFee = newTxParams.maxFeePerGas ?? newTxParams.gasPrice; -+ const oldFee = newTxParams.maxFeePerGas ? transactionMeta.txParams.maxFeePerGas : transactionMeta.txParams.gasPrice; -+ _chunkS6VGOPUYjs.projectLogger.call(void 0, "Submitting cancel transaction", { -+ oldFee, -+ newFee, -+ txParams: newTxParams -+ }); -+ const ethQuery = _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _multichainTrackingHelper).getEthQuery({ -+ networkClientId: transactionMeta.networkClientId, -+ chainId: transactionMeta.chainId -+ }); -+ const hash = await this.publishTransactionForRetry( -+ ethQuery, -+ rawTx, -+ transactionMeta -+ ); -+ const cancelTransactionMeta = { -+ actionId, -+ chainId: transactionMeta.chainId, -+ networkClientId: transactionMeta.networkClientId, -+ estimatedBaseFee, -+ hash, -+ id: _uuid.v1.call(void 0, ), -+ originalGasEstimate: transactionMeta.txParams.gas, -+ rawTx, -+ status: "submitted" /* submitted */, -+ time: Date.now(), -+ type: "cancel" /* cancel */, -+ txParams: newTxParams -+ }; -+ this.addMetadata(cancelTransactionMeta); -+ this.messagingSystem.publish(`${controllerName}:transactionApproved`, { -+ transactionMeta: cancelTransactionMeta, -+ actionId -+ }); -+ this.messagingSystem.publish(`${controllerName}:transactionSubmitted`, { -+ transactionMeta: cancelTransactionMeta, -+ actionId -+ }); -+ this.messagingSystem.publish( -+ `${controllerName}:transactionFinished`, -+ cancelTransactionMeta -+ ); -+ _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _internalEvents).emit( -+ `${transactionMeta.id}:finished`, -+ cancelTransactionMeta -+ ); -+ } -+ /** -+ * Attempts to speed up a transaction increasing transaction gasPrice by ten percent. -+ * -+ * @param transactionId - The ID of the transaction to speed up. -+ * @param gasValues - The gas values to use for the speed up transaction. -+ * @param options - The options for the speed up transaction. -+ * @param options.actionId - Unique ID to prevent duplicate requests -+ * @param options.estimatedBaseFee - The estimated base fee of the transaction. -+ */ -+ async speedUpTransaction(transactionId, gasValues, { -+ actionId, -+ estimatedBaseFee -+ } = {}) { -+ if (this.getTransactionWithActionId(actionId)) { -+ return; -+ } -+ if (gasValues) { -+ gasValues = _chunkOZ6UB42Cjs.normalizeGasFeeValues.call(void 0, gasValues); -+ _chunkOZ6UB42Cjs.validateGasValues.call(void 0, gasValues); -+ } -+ _chunkS6VGOPUYjs.projectLogger.call(void 0, "Creating speed up transaction", transactionId, gasValues); -+ const transactionMeta = this.getTransaction(transactionId); -+ if (!transactionMeta) { -+ return; -+ } -+ if (!this.sign) { -+ throw new Error("No sign method defined."); -+ } -+ const minGasPrice = _chunkOZ6UB42Cjs.getIncreasedPriceFromExisting.call(void 0, -+ transactionMeta.txParams.gasPrice, -+ SPEED_UP_RATE -+ ); -+ const gasPriceFromValues = _chunkOZ6UB42Cjs.isGasPriceValue.call(void 0, gasValues) && gasValues.gasPrice; -+ const newGasPrice = gasPriceFromValues && _chunkOZ6UB42Cjs.validateMinimumIncrease.call(void 0, gasPriceFromValues, minGasPrice) || minGasPrice; -+ const existingMaxFeePerGas = transactionMeta.txParams?.maxFeePerGas; -+ const minMaxFeePerGas = _chunkOZ6UB42Cjs.getIncreasedPriceFromExisting.call(void 0, -+ existingMaxFeePerGas, -+ SPEED_UP_RATE -+ ); -+ const maxFeePerGasValues = _chunkOZ6UB42Cjs.isFeeMarketEIP1559Values.call(void 0, gasValues) && gasValues.maxFeePerGas; -+ const newMaxFeePerGas = maxFeePerGasValues && _chunkOZ6UB42Cjs.validateMinimumIncrease.call(void 0, maxFeePerGasValues, minMaxFeePerGas) || existingMaxFeePerGas && minMaxFeePerGas; -+ const existingMaxPriorityFeePerGas = transactionMeta.txParams?.maxPriorityFeePerGas; -+ const minMaxPriorityFeePerGas = _chunkOZ6UB42Cjs.getIncreasedPriceFromExisting.call(void 0, -+ existingMaxPriorityFeePerGas, -+ SPEED_UP_RATE -+ ); -+ const maxPriorityFeePerGasValues = _chunkOZ6UB42Cjs.isFeeMarketEIP1559Values.call(void 0, gasValues) && gasValues.maxPriorityFeePerGas; -+ const newMaxPriorityFeePerGas = maxPriorityFeePerGasValues && _chunkOZ6UB42Cjs.validateMinimumIncrease.call(void 0, -+ maxPriorityFeePerGasValues, -+ minMaxPriorityFeePerGas -+ ) || existingMaxPriorityFeePerGas && minMaxPriorityFeePerGas; -+ const txParams = newMaxFeePerGas && newMaxPriorityFeePerGas ? { -+ ...transactionMeta.txParams, -+ gasLimit: transactionMeta.txParams.gas, -+ maxFeePerGas: newMaxFeePerGas, -+ maxPriorityFeePerGas: newMaxPriorityFeePerGas, -+ type: "0x2" /* feeMarket */ -+ } : { -+ ...transactionMeta.txParams, -+ gasLimit: transactionMeta.txParams.gas, -+ gasPrice: newGasPrice -+ }; -+ const unsignedEthTx = this.prepareUnsignedEthTx( -+ transactionMeta.chainId, -+ txParams -+ ); -+ const signedTx = await this.sign( -+ unsignedEthTx, -+ transactionMeta.txParams.from -+ ); -+ const transactionMetaWithRsv = this.updateTransactionMetaRSV( -+ transactionMeta, -+ signedTx -+ ); -+ const rawTx = _util.bufferToHex.call(void 0, signedTx.serialize()); -+ const newFee = txParams.maxFeePerGas ?? txParams.gasPrice; -+ const oldFee = txParams.maxFeePerGas ? transactionMetaWithRsv.txParams.maxFeePerGas : transactionMetaWithRsv.txParams.gasPrice; -+ _chunkS6VGOPUYjs.projectLogger.call(void 0, "Submitting speed up transaction", { oldFee, newFee, txParams }); -+ const ethQuery = _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _multichainTrackingHelper).getEthQuery({ -+ networkClientId: transactionMeta.networkClientId, -+ chainId: transactionMeta.chainId -+ }); -+ const hash = await this.publishTransactionForRetry( -+ ethQuery, -+ rawTx, -+ transactionMeta -+ ); -+ const baseTransactionMeta = { -+ ...transactionMetaWithRsv, -+ estimatedBaseFee, -+ id: _uuid.v1.call(void 0, ), -+ time: Date.now(), -+ hash, -+ actionId, -+ originalGasEstimate: transactionMeta.txParams.gas, -+ type: "retry" /* retry */, -+ originalType: transactionMeta.type -+ }; -+ const newTransactionMeta = newMaxFeePerGas && newMaxPriorityFeePerGas ? { -+ ...baseTransactionMeta, -+ txParams: { -+ ...transactionMeta.txParams, -+ maxFeePerGas: newMaxFeePerGas, -+ maxPriorityFeePerGas: newMaxPriorityFeePerGas -+ } -+ } : { -+ ...baseTransactionMeta, -+ txParams: { -+ ...transactionMeta.txParams, -+ gasPrice: newGasPrice -+ } -+ }; -+ this.addMetadata(newTransactionMeta); -+ this.messagingSystem.publish(`${controllerName}:transactionApproved`, { -+ transactionMeta: newTransactionMeta, -+ actionId -+ }); -+ this.messagingSystem.publish(`${controllerName}:transactionSubmitted`, { -+ transactionMeta: newTransactionMeta, -+ actionId -+ }); -+ this.messagingSystem.publish( -+ `${controllerName}:speedupTransactionAdded`, -+ newTransactionMeta -+ ); -+ } -+ /** -+ * Estimates required gas for a given transaction. -+ * -+ * @param transaction - The transaction to estimate gas for. -+ * @param networkClientId - The network client id to use for the estimate. -+ * @returns The gas and gas price. -+ */ -+ async estimateGas(transaction, networkClientId) { -+ const ethQuery = _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _multichainTrackingHelper).getEthQuery({ -+ networkClientId -+ }); -+ const { estimatedGas, simulationFails } = await _chunkV72C4MCRjs.estimateGas.call(void 0, -+ transaction, -+ ethQuery -+ ); -+ return { gas: estimatedGas, simulationFails }; -+ } -+ /** -+ * Estimates required gas for a given transaction and add additional gas buffer with the given multiplier. -+ * -+ * @param transaction - The transaction params to estimate gas for. -+ * @param multiplier - The multiplier to use for the gas buffer. -+ * @param networkClientId - The network client id to use for the estimate. -+ */ -+ async estimateGasBuffered(transaction, multiplier, networkClientId) { -+ const ethQuery = _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _multichainTrackingHelper).getEthQuery({ -+ networkClientId -+ }); -+ const { blockGasLimit, estimatedGas, simulationFails } = await _chunkV72C4MCRjs.estimateGas.call(void 0, -+ transaction, -+ ethQuery -+ ); -+ const gas = _chunkV72C4MCRjs.addGasBuffer.call(void 0, estimatedGas, blockGasLimit, multiplier); -+ return { -+ gas, -+ simulationFails -+ }; -+ } -+ /** -+ * Updates an existing transaction in state. -+ * -+ * @param transactionMeta - The new transaction to store in state. -+ * @param note - A note or update reason to include in the transaction history. -+ */ -+ updateTransaction(transactionMeta, note) { -+ const { id: transactionId } = transactionMeta; -+ _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _updateTransactionInternal, updateTransactionInternal_fn).call(this, { transactionId, note }, () => ({ -+ ...transactionMeta -+ })); -+ } -+ /** -+ * Update the security alert response for a transaction. -+ * -+ * @param transactionId - ID of the transaction. -+ * @param securityAlertResponse - The new security alert response for the transaction. -+ */ -+ updateSecurityAlertResponse(transactionId, securityAlertResponse) { -+ if (!securityAlertResponse) { -+ throw new Error( -+ "updateSecurityAlertResponse: securityAlertResponse should not be null" -+ ); -+ } -+ const transactionMeta = this.getTransaction(transactionId); -+ if (!transactionMeta) { -+ throw new Error( -+ `Cannot update security alert response as no transaction metadata found` -+ ); -+ } -+ const updatedTransactionMeta = { -+ ...transactionMeta, -+ securityAlertResponse -+ }; -+ this.updateTransaction( -+ updatedTransactionMeta, -+ `${controllerName}:updatesecurityAlertResponse - securityAlertResponse updated` -+ ); -+ } -+ /** -+ * Removes all transactions from state, optionally based on the current network. -+ * -+ * @param ignoreNetwork - Determines whether to wipe all transactions, or just those on the -+ * current network. If `true`, all transactions are wiped. -+ * @param address - If specified, only transactions originating from this address will be -+ * wiped on current network. -+ */ -+ wipeTransactions(ignoreNetwork, address) { -+ if (ignoreNetwork && !address) { -+ this.update((state) => { -+ state.transactions = []; -+ }); -+ return; -+ } -+ const currentChainId = this.getChainId(); -+ const newTransactions = this.state.transactions.filter( -+ ({ chainId, txParams }) => { -+ const isMatchingNetwork = ignoreNetwork || chainId === currentChainId; -+ if (!isMatchingNetwork) { -+ return true; -+ } -+ const isMatchingAddress = !address || txParams.from?.toLowerCase() === address.toLowerCase(); -+ return !isMatchingAddress; -+ } -+ ); -+ this.update((state) => { -+ state.transactions = this.trimTransactionsForState(newTransactions); -+ }); -+ } -+ /** -+ * Adds external provided transaction to state as confirmed transaction. -+ * -+ * @param transactionMeta - TransactionMeta to add transactions. -+ * @param transactionReceipt - TransactionReceipt of the external transaction. -+ * @param baseFeePerGas - Base fee per gas of the external transaction. -+ */ -+ async confirmExternalTransaction(transactionMeta, transactionReceipt, baseFeePerGas) { -+ const newTransactionMeta = this.addExternalTransaction(transactionMeta); -+ try { -+ const transactionId = newTransactionMeta.id; -+ const updatedTransactionMeta = { -+ ...newTransactionMeta, -+ status: "confirmed" /* confirmed */, -+ txReceipt: transactionReceipt -+ }; -+ if (baseFeePerGas) { -+ updatedTransactionMeta.baseFeePerGas = baseFeePerGas; -+ } -+ this.markNonceDuplicatesDropped(transactionId); -+ this.updateTransaction( -+ updatedTransactionMeta, -+ `${controllerName}:confirmExternalTransaction - Add external transaction` -+ ); -+ this.onTransactionStatusChange(updatedTransactionMeta); -+ this.updatePostBalance(updatedTransactionMeta); -+ this.messagingSystem.publish( -+ `${controllerName}:transactionConfirmed`, -+ updatedTransactionMeta -+ ); -+ } catch (error) { -+ console.error("Failed to confirm external transaction", error); -+ } -+ } -+ /** -+ * Append new send flow history to a transaction. -+ * -+ * @param transactionID - The ID of the transaction to update. -+ * @param currentSendFlowHistoryLength - The length of the current sendFlowHistory array. -+ * @param sendFlowHistoryToAdd - The sendFlowHistory entries to add. -+ * @returns The updated transactionMeta. -+ */ -+ updateTransactionSendFlowHistory(transactionID, currentSendFlowHistoryLength, sendFlowHistoryToAdd) { -+ if (this.isSendFlowHistoryDisabled) { -+ throw new Error( -+ "Send flow history is disabled for the current transaction controller" -+ ); -+ } -+ const transactionMeta = this.getTransaction(transactionID); -+ if (!transactionMeta) { -+ throw new Error( -+ `Cannot update send flow history as no transaction metadata found` -+ ); -+ } -+ _chunkOZ6UB42Cjs.validateIfTransactionUnapproved.call(void 0, -+ transactionMeta, -+ "updateTransactionSendFlowHistory" -+ ); -+ const sendFlowHistory = transactionMeta.sendFlowHistory ?? []; -+ if (currentSendFlowHistoryLength === sendFlowHistory.length) { -+ const updatedTransactionMeta = { -+ ...transactionMeta, -+ sendFlowHistory: [...sendFlowHistory, ...sendFlowHistoryToAdd] -+ }; -+ this.updateTransaction( -+ updatedTransactionMeta, -+ `${controllerName}:updateTransactionSendFlowHistory - sendFlowHistory updated` -+ ); -+ } -+ return this.getTransaction(transactionID); -+ } -+ /** -+ * Update the gas values of a transaction. -+ * -+ * @param transactionId - The ID of the transaction to update. -+ * @param gasValues - Gas values to update. -+ * @param gasValues.gas - Same as transaction.gasLimit. -+ * @param gasValues.gasLimit - Maxmimum number of units of gas to use for this transaction. -+ * @param gasValues.gasPrice - Price per gas for legacy transactions. -+ * @param gasValues.maxPriorityFeePerGas - Maximum amount per gas to give to validator as incentive. -+ * @param gasValues.maxFeePerGas - Maximum amount per gas to pay for the transaction, including the priority fee. -+ * @param gasValues.estimateUsed - Which estimate level was used. -+ * @param gasValues.estimateSuggested - Which estimate level that the API suggested. -+ * @param gasValues.defaultGasEstimates - The default estimate for gas. -+ * @param gasValues.originalGasEstimate - Original estimate for gas. -+ * @param gasValues.userEditedGasLimit - The gas limit supplied by user. -+ * @param gasValues.userFeeLevel - Estimate level user selected. -+ * @returns The updated transactionMeta. -+ */ -+ updateTransactionGasFees(transactionId, { -+ defaultGasEstimates, -+ estimateUsed, -+ estimateSuggested, -+ gas, -+ gasLimit, -+ gasPrice, -+ maxPriorityFeePerGas, -+ maxFeePerGas, -+ originalGasEstimate, -+ userEditedGasLimit, -+ userFeeLevel -+ }) { -+ const transactionMeta = this.getTransaction(transactionId); -+ if (!transactionMeta) { -+ throw new Error( -+ `Cannot update transaction as no transaction metadata found` -+ ); -+ } -+ _chunkOZ6UB42Cjs.validateIfTransactionUnapproved.call(void 0, -+ transactionMeta, -+ "updateTransactionGasFees" -+ ); -+ let transactionGasFees = { -+ txParams: { -+ gas, -+ gasLimit, -+ gasPrice, -+ maxPriorityFeePerGas, -+ maxFeePerGas -+ }, -+ defaultGasEstimates, -+ estimateUsed, -+ estimateSuggested, -+ originalGasEstimate, -+ userEditedGasLimit, -+ userFeeLevel -+ // TODO: Replace `any` with type -+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -+ }; -+ transactionGasFees.txParams = _lodash.pickBy.call(void 0, transactionGasFees.txParams); -+ transactionGasFees = _lodash.pickBy.call(void 0, transactionGasFees); -+ const updatedMeta = _lodash.merge.call(void 0, {}, transactionMeta, transactionGasFees); -+ this.updateTransaction( -+ updatedMeta, -+ `${controllerName}:updateTransactionGasFees - gas values updated` -+ ); -+ return this.getTransaction(transactionId); -+ } -+ /** -+ * Update the previous gas values of a transaction. -+ * -+ * @param transactionId - The ID of the transaction to update. -+ * @param previousGas - Previous gas values to update. -+ * @param previousGas.gasLimit - Maxmimum number of units of gas to use for this transaction. -+ * @param previousGas.maxFeePerGas - Maximum amount per gas to pay for the transaction, including the priority fee. -+ * @param previousGas.maxPriorityFeePerGas - Maximum amount per gas to give to validator as incentive. -+ * @returns The updated transactionMeta. -+ */ -+ updatePreviousGasParams(transactionId, { -+ gasLimit, -+ maxFeePerGas, -+ maxPriorityFeePerGas -+ }) { -+ const transactionMeta = this.getTransaction(transactionId); -+ if (!transactionMeta) { -+ throw new Error( -+ `Cannot update transaction as no transaction metadata found` -+ ); -+ } -+ _chunkOZ6UB42Cjs.validateIfTransactionUnapproved.call(void 0, transactionMeta, "updatePreviousGasParams"); -+ const transactionPreviousGas = { -+ previousGas: { -+ gasLimit, -+ maxFeePerGas, -+ maxPriorityFeePerGas -+ } -+ // TODO: Replace `any` with type -+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -+ }; -+ transactionPreviousGas.previousGas = _lodash.pickBy.call(void 0, -+ transactionPreviousGas.previousGas -+ ); -+ const updatedMeta = _lodash.merge.call(void 0, {}, transactionMeta, transactionPreviousGas); -+ this.updateTransaction( -+ updatedMeta, -+ `${controllerName}:updatePreviousGasParams - Previous gas values updated` -+ ); -+ return this.getTransaction(transactionId); -+ } -+ async getNonceLock(address, networkClientId) { -+ return _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _multichainTrackingHelper).getNonceLock( -+ address, -+ networkClientId -+ ); -+ } -+ /** -+ * Updates the editable parameters of a transaction. -+ * -+ * @param txId - The ID of the transaction to update. -+ * @param params - The editable parameters to update. -+ * @param params.data - Data to pass with the transaction. -+ * @param params.gas - Maximum number of units of gas to use for the transaction. -+ * @param params.gasPrice - Price per gas for legacy transactions. -+ * @param params.from - Address to send the transaction from. -+ * @param params.to - Address to send the transaction to. -+ * @param params.value - Value associated with the transaction. -+ * @returns The updated transaction metadata. -+ */ -+ async updateEditableParams(txId, { -+ data, -+ gas, -+ gasPrice, -+ from, -+ to, -+ value -+ }) { -+ const transactionMeta = this.getTransaction(txId); -+ if (!transactionMeta) { -+ throw new Error( -+ `Cannot update editable params as no transaction metadata found` -+ ); -+ } -+ _chunkOZ6UB42Cjs.validateIfTransactionUnapproved.call(void 0, transactionMeta, "updateEditableParams"); -+ const editableParams = { -+ txParams: { -+ data, -+ from, -+ to, -+ value, -+ gas, -+ gasPrice -+ } -+ }; -+ editableParams.txParams = _lodash.pickBy.call(void 0, -+ editableParams.txParams -+ ); -+ const updatedTransaction = _lodash.merge.call(void 0, {}, transactionMeta, editableParams); -+ const provider = _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _multichainTrackingHelper).getProvider({ -+ chainId: transactionMeta.chainId, -+ networkClientId: transactionMeta.networkClientId -+ }); -+ const ethQuery = new (0, _ethquery2.default)(provider); -+ const { type } = await _chunkSD6CWFDFjs.determineTransactionType.call(void 0, -+ updatedTransaction.txParams, -+ ethQuery -+ ); -+ updatedTransaction.type = type; -+ await _chunk2XKEAKQGjs.updateTransactionLayer1GasFee.call(void 0, { -+ layer1GasFeeFlows: this.layer1GasFeeFlows, -+ provider, -+ transactionMeta: updatedTransaction -+ }); -+ this.updateTransaction( -+ updatedTransaction, -+ `Update Editable Params for ${txId}` -+ ); -+ return this.getTransaction(txId); -+ } -+ /** -+ * Signs and returns the raw transaction data for provided transaction params list. -+ * -+ * @param listOfTxParams - The list of transaction params to approve. -+ * @param opts - Options bag. -+ * @param opts.hasNonce - Whether the transactions already have a nonce. -+ * @returns The raw transactions. -+ */ -+ async approveTransactionsWithSameNonce(listOfTxParams = [], { hasNonce } = {}) { -+ _chunkS6VGOPUYjs.projectLogger.call(void 0, "Approving transactions with same nonce", { -+ transactions: listOfTxParams -+ }); -+ if (listOfTxParams.length === 0) { -+ return ""; -+ } -+ const initialTx = listOfTxParams[0]; -+ const common = this.getCommonConfiguration(initialTx.chainId); -+ let networkClientId; -+ try { -+ networkClientId = this.messagingSystem.call( -+ `NetworkController:findNetworkClientIdByChainId`, -+ initialTx.chainId -+ ); -+ } catch (err) { -+ _chunkS6VGOPUYjs.projectLogger.call(void 0, "failed to find networkClientId from chainId", err); -+ } -+ const initialTxAsEthTx = _tx.TransactionFactory.fromTxData(initialTx, { -+ common -+ }); -+ const initialTxAsSerializedHex = _util.bufferToHex.call(void 0, initialTxAsEthTx.serialize()); -+ if (this.approvingTransactionIds.has(initialTxAsSerializedHex)) { -+ return ""; -+ } -+ this.approvingTransactionIds.add(initialTxAsSerializedHex); -+ let rawTransactions, nonceLock; -+ try { -+ const fromAddress = initialTx.from; -+ const requiresNonce = hasNonce !== true; -+ nonceLock = requiresNonce ? await this.getNonceLock(fromAddress, networkClientId) : void 0; -+ const nonce = nonceLock ? _utils.add0x.call(void 0, nonceLock.nextNonce.toString(16)) : initialTx.nonce; -+ if (nonceLock) { -+ _chunkS6VGOPUYjs.projectLogger.call(void 0, "Using nonce from nonce tracker", nonce, nonceLock.nonceDetails); -+ } -+ rawTransactions = await Promise.all( -+ listOfTxParams.map((txParams) => { -+ txParams.nonce = nonce; -+ return this.signExternalTransaction(txParams.chainId, txParams); -+ }) -+ ); -+ } catch (err) { -+ _chunkS6VGOPUYjs.projectLogger.call(void 0, "Error while signing transactions with same nonce", err); -+ throw err; -+ } finally { -+ nonceLock?.releaseLock(); -+ this.approvingTransactionIds.delete(initialTxAsSerializedHex); -+ } -+ return rawTransactions; -+ } -+ /** -+ * Update a custodial transaction. -+ * -+ * @param transactionId - The ID of the transaction to update. -+ * @param options - The custodial transaction options to update. -+ * @param options.errorMessage - The error message to be assigned in case transaction status update to failed. -+ * @param options.hash - The new hash value to be assigned. -+ * @param options.status - The new status value to be assigned. -+ */ -+ updateCustodialTransaction(transactionId, { -+ errorMessage, -+ hash, -+ status -+ }) { -+ const transactionMeta = this.getTransaction(transactionId); -+ if (!transactionMeta) { -+ throw new Error( -+ `Cannot update custodial transaction as no transaction metadata found` -+ ); -+ } -+ if (!transactionMeta.custodyId) { -+ throw new Error("Transaction must be a custodian transaction"); -+ } -+ if (status && ![ -+ "submitted" /* submitted */, -+ "signed" /* signed */, -+ "failed" /* failed */ -+ ].includes(status)) { -+ throw new Error( -+ `Cannot update custodial transaction with status: ${status}` -+ ); -+ } -+ const updatedTransactionMeta = _lodash.merge.call(void 0, -+ {}, -+ transactionMeta, -+ _lodash.pickBy.call(void 0, { hash, status }) -+ ); -+ if (updatedTransactionMeta.status === "submitted" /* submitted */) { -+ updatedTransactionMeta.submittedTime = (/* @__PURE__ */ new Date()).getTime(); -+ } -+ if (updatedTransactionMeta.status === "failed" /* failed */) { -+ updatedTransactionMeta.error = _chunkOZ6UB42Cjs.normalizeTxError.call(void 0, new Error(errorMessage)); -+ } -+ this.updateTransaction( -+ updatedTransactionMeta, -+ `${controllerName}:updateCustodialTransaction - Custodial transaction updated` -+ ); -+ if (["submitted" /* submitted */, "failed" /* failed */].includes( -+ status -+ )) { -+ this.messagingSystem.publish( -+ `${controllerName}:transactionFinished`, -+ updatedTransactionMeta -+ ); -+ _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _internalEvents).emit( -+ `${updatedTransactionMeta.id}:finished`, -+ updatedTransactionMeta -+ ); -+ } -+ } -+ /** -+ * Search transaction metadata for matching entries. -+ * -+ * @param opts - Options bag. -+ * @param opts.searchCriteria - An object containing values or functions for transaction properties to filter transactions with. -+ * @param opts.initialList - The transactions to search. Defaults to the current state. -+ * @param opts.filterToCurrentNetwork - Whether to filter the results to the current network. Defaults to true. -+ * @param opts.limit - The maximum number of transactions to return. No limit by default. -+ * @returns An array of transactions matching the provided options. -+ */ -+ getTransactions({ -+ searchCriteria = {}, -+ initialList, -+ filterToCurrentNetwork = true, -+ limit -+ } = {}) { -+ const chainId = this.getChainId(); -+ const predicateMethods = _lodash.mapValues.call(void 0, searchCriteria, (predicate) => { -+ return typeof predicate === "function" ? predicate : ( -+ // TODO: Replace `any` with type -+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -+ (v) => v === predicate -+ ); -+ }); -+ const transactionsToFilter = initialList ?? this.state.transactions; -+ const filteredTransactions = _lodash.sortBy.call(void 0, -+ _lodash.pickBy.call(void 0, transactionsToFilter, (transaction) => { -+ if (filterToCurrentNetwork && transaction.chainId !== chainId) { -+ return false; -+ } -+ for (const [key, predicate] of Object.entries(predicateMethods)) { -+ if (key in transaction.txParams) { -+ if (predicate(transaction.txParams[key]) === false) { -+ return false; -+ } -+ } else if (predicate(transaction[key]) === false) { -+ return false; -+ } -+ } -+ return true; -+ }), -+ "time" -+ ); -+ if (limit !== void 0) { -+ const nonces = /* @__PURE__ */ new Set(); -+ const txs = []; -+ for (let i = filteredTransactions.length - 1; i > -1; i--) { -+ const txMeta = filteredTransactions[i]; -+ const { nonce } = txMeta.txParams; -+ if (!nonces.has(nonce)) { -+ if (nonces.size < limit) { -+ nonces.add(nonce); -+ } else { -+ continue; -+ } -+ } -+ txs.unshift(txMeta); -+ } -+ return txs; -+ } -+ return filteredTransactions; -+ } -+ async estimateGasFee({ -+ transactionParams, -+ chainId, -+ networkClientId: requestNetworkClientId -+ }) { -+ const networkClientId = _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _getNetworkClientId, getNetworkClientId_fn).call(this, { -+ networkClientId: requestNetworkClientId, -+ chainId -+ }); -+ const transactionMeta = { -+ txParams: transactionParams, -+ chainId, -+ networkClientId -+ }; -+ const gasFeeFlow = _chunk76FONEDAjs.getGasFeeFlow.call(void 0, -+ transactionMeta, -+ this.gasFeeFlows -+ ); -+ const ethQuery = _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _multichainTrackingHelper).getEthQuery({ -+ networkClientId, -+ chainId -+ }); -+ const gasFeeControllerData = await this.getGasFeeEstimates({ -+ networkClientId -+ }); -+ return gasFeeFlow.getGasFees({ -+ ethQuery, -+ gasFeeControllerData, -+ transactionMeta -+ }); -+ } -+ /** -+ * Determine the layer 1 gas fee for the given transaction parameters. -+ * -+ * @param request - The request object. -+ * @param request.transactionParams - The transaction parameters to estimate the layer 1 gas fee for. -+ * @param request.chainId - The ID of the chain where the transaction will be executed. -+ * @param request.networkClientId - The ID of a specific network client to process the transaction. -+ */ -+ async getLayer1GasFee({ -+ transactionParams, -+ chainId, -+ networkClientId -+ }) { -+ const provider = _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _multichainTrackingHelper).getProvider({ -+ networkClientId, -+ chainId -+ }); -+ return await _chunk2XKEAKQGjs.getTransactionLayer1GasFee.call(void 0, { -+ layer1GasFeeFlows: this.layer1GasFeeFlows, -+ provider, -+ transactionMeta: { -+ txParams: transactionParams, -+ chainId -+ } -+ }); -+ } -+ async signExternalTransaction(chainId, transactionParams) { -+ if (!this.sign) { -+ throw new Error("No sign method defined."); -+ } -+ const normalizedTransactionParams = _chunkOZ6UB42Cjs.normalizeTransactionParams.call(void 0, transactionParams); -+ const type = _chunkOZ6UB42Cjs.isEIP1559Transaction.call(void 0, normalizedTransactionParams) ? "0x2" /* feeMarket */ : "0x0" /* legacy */; -+ const updatedTransactionParams = { -+ ...normalizedTransactionParams, -+ type, -+ gasLimit: normalizedTransactionParams.gas, -+ chainId -+ }; -+ const { from } = updatedTransactionParams; -+ const common = this.getCommonConfiguration(chainId); -+ const unsignedTransaction = _tx.TransactionFactory.fromTxData( -+ updatedTransactionParams, -+ { common } -+ ); -+ const signedTransaction = await this.sign(unsignedTransaction, from); -+ const rawTransaction = _util.bufferToHex.call(void 0, signedTransaction.serialize()); -+ return rawTransaction; -+ } -+ /** -+ * Removes unapproved transactions from state. -+ */ -+ clearUnapprovedTransactions() { -+ const transactions = this.state.transactions.filter( -+ ({ status }) => status !== "unapproved" /* unapproved */ -+ ); -+ this.update((state) => { -+ state.transactions = this.trimTransactionsForState(transactions); -+ }); -+ } -+ /** -+ * Stop the signing process for a specific transaction. -+ * Throws an error causing the transaction status to be set to failed. -+ * @param transactionId - The ID of the transaction to stop signing. -+ */ -+ abortTransactionSigning(transactionId) { -+ const transactionMeta = this.getTransaction(transactionId); -+ if (!transactionMeta) { -+ throw new Error(`Cannot abort signing as no transaction metadata found`); -+ } -+ const abortCallback = this.signAbortCallbacks.get(transactionId); -+ if (!abortCallback) { -+ throw new Error( -+ `Cannot abort signing as transaction is not waiting for signing` -+ ); -+ } -+ abortCallback(); -+ this.signAbortCallbacks.delete(transactionId); -+ } -+ addMetadata(transactionMeta) { -+ this.update((state) => { -+ state.transactions = this.trimTransactionsForState([ -+ ...state.transactions, -+ transactionMeta -+ ]); -+ }); -+ } -+ async updateGasProperties(transactionMeta) { -+ const isEIP1559Compatible = await this.getEIP1559Compatibility(transactionMeta.networkClientId) && transactionMeta.txParams.type !== "0x0" /* legacy */; -+ const { networkClientId, chainId } = transactionMeta; -+ const isCustomNetwork = _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _isCustomNetwork, isCustomNetwork_fn).call(this, networkClientId); -+ const ethQuery = _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _multichainTrackingHelper).getEthQuery({ -+ networkClientId, -+ chainId -+ }); -+ const provider = _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _multichainTrackingHelper).getProvider({ -+ networkClientId, -+ chainId -+ }); -+ await _chunkV72C4MCRjs.updateGas.call(void 0, { -+ ethQuery, -+ chainId, -+ isCustomNetwork, -+ txMeta: transactionMeta -+ }); -+ await _chunkC3WC4OJ3js.updateGasFees.call(void 0, { -+ eip1559: isEIP1559Compatible, -+ ethQuery, -+ gasFeeFlows: this.gasFeeFlows, -+ getGasFeeEstimates: this.getGasFeeEstimates, -+ getSavedGasFees: this.getSavedGasFees.bind(this), -+ txMeta: transactionMeta -+ }); -+ await _chunk2XKEAKQGjs.updateTransactionLayer1GasFee.call(void 0, { -+ layer1GasFeeFlows: this.layer1GasFeeFlows, -+ provider, -+ transactionMeta -+ }); -+ } -+ onBootCleanup() { -+ this.clearUnapprovedTransactions(); -+ this.failIncompleteTransactions(); -+ } -+ failIncompleteTransactions() { -+ const incompleteTransactions = this.state.transactions.filter( -+ (transaction) => ["approved" /* approved */, "signed" /* signed */].includes( -+ transaction.status -+ ) -+ ); -+ for (const transactionMeta of incompleteTransactions) { -+ this.failTransaction( -+ transactionMeta, -+ new Error("Transaction incomplete at startup") -+ ); -+ } -+ } -+ async processApproval(transactionMeta, { -+ isExisting = false, -+ requireApproval, -+ shouldShowRequest = true, -+ actionId -+ }) { -+ const transactionId = transactionMeta.id; -+ let resultCallbacks; -+ const { meta, isCompleted } = this.isTransactionCompleted(transactionId); -+ const finishedPromise = isCompleted ? Promise.resolve(meta) : this.waitForTransactionFinished(transactionId); -+ if (meta && !isExisting && !isCompleted) { -+ try { -+ if (requireApproval !== false) { -+ const acceptResult = await this.requestApproval(transactionMeta, { -+ shouldShowRequest -+ }); -+ resultCallbacks = acceptResult.resultCallbacks; -+ const approvalValue = acceptResult.value; -+ const updatedTransaction = approvalValue?.txMeta; -+ if (updatedTransaction) { -+ _chunkS6VGOPUYjs.projectLogger.call(void 0, "Updating transaction with approval data", { -+ customNonce: updatedTransaction.customNonceValue, -+ params: updatedTransaction.txParams -+ }); -+ this.updateTransaction( -+ updatedTransaction, -+ "TransactionController#processApproval - Updated with approval data" -+ ); -+ } -+ } -+ const { isCompleted: isTxCompleted } = this.isTransactionCompleted(transactionId); -+ if (!isTxCompleted) { -+ const approvalResult = await this.approveTransaction(transactionId); -+ if (approvalResult === "skipped-via-before-publish-hook" /* SkippedViaBeforePublishHook */ && resultCallbacks) { -+ resultCallbacks.success(); -+ } -+ const updatedTransactionMeta = this.getTransaction( -+ transactionId -+ ); -+ this.messagingSystem.publish( -+ `${controllerName}:transactionApproved`, -+ { -+ transactionMeta: updatedTransactionMeta, -+ actionId -+ } -+ ); -+ } -+ } catch (error) { -+ const { isCompleted: isTxCompleted } = this.isTransactionCompleted(transactionId); -+ if (!isTxCompleted) { -+ if (error?.code === _rpcerrors.errorCodes.provider.userRejectedRequest) { -+ this.cancelTransaction(transactionId, actionId); -+ throw _rpcerrors.providerErrors.userRejectedRequest( -+ "MetaMask Tx Signature: User denied transaction signature." -+ ); -+ } else { -+ this.failTransaction(meta, error, actionId); -+ } -+ } -+ } -+ } -+ const finalMeta = await finishedPromise; -+ switch (finalMeta?.status) { -+ case "failed" /* failed */: -+ resultCallbacks?.error(finalMeta.error); -+ throw _rpcerrors.rpcErrors.internal(finalMeta.error.message); -+ case "submitted" /* submitted */: -+ resultCallbacks?.success(); -+ return finalMeta.hash; -+ default: -+ const internalError = _rpcerrors.rpcErrors.internal( -+ `MetaMask Tx Signature: Unknown problem: ${JSON.stringify( -+ finalMeta || transactionId -+ )}` -+ ); -+ resultCallbacks?.error(internalError); -+ throw internalError; -+ } -+ } -+ /** -+ * Approves a transaction and updates it's status in state. If this is not a -+ * retry transaction, a nonce will be generated. The transaction is signed -+ * using the sign configuration property, then published to the blockchain. -+ * A `:finished` hub event is fired after success or failure. -+ * -+ * @param transactionId - The ID of the transaction to approve. -+ */ -+ async approveTransaction(transactionId) { -+ const cleanupTasks = new Array(); -+ cleanupTasks.push(await this.mutex.acquire()); -+ let transactionMeta = this.getTransactionOrThrow(transactionId); -+ try { -+ if (!this.sign) { -+ this.failTransaction( -+ transactionMeta, -+ new Error("No sign method defined.") -+ ); -+ return "not-approved" /* NotApproved */; -+ } else if (!transactionMeta.chainId) { -+ this.failTransaction(transactionMeta, new Error("No chainId defined.")); -+ return "not-approved" /* NotApproved */; -+ } -+ if (this.approvingTransactionIds.has(transactionId)) { -+ _chunkS6VGOPUYjs.projectLogger.call(void 0, "Skipping approval as signing in progress", transactionId); -+ return "not-approved" /* NotApproved */; -+ } -+ this.approvingTransactionIds.add(transactionId); -+ cleanupTasks.push( -+ () => this.approvingTransactionIds.delete(transactionId) -+ ); -+ const [nonce, releaseNonce] = await _chunkPRUNMTRDjs.getNextNonce.call(void 0, -+ transactionMeta, -+ (address) => _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _multichainTrackingHelper).getNonceLock( -+ address, -+ transactionMeta.networkClientId -+ ) -+ ); -+ releaseNonce && cleanupTasks.push(releaseNonce); -+ transactionMeta = _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _updateTransactionInternal, updateTransactionInternal_fn).call(this, { -+ transactionId, -+ note: "TransactionController#approveTransaction - Transaction approved" -+ }, (draftTxMeta) => { -+ const { txParams, chainId } = draftTxMeta; -+ draftTxMeta.status = "approved" /* approved */; -+ draftTxMeta.txParams = { -+ ...txParams, -+ nonce, -+ chainId, -+ gasLimit: txParams.gas, -+ ..._chunkOZ6UB42Cjs.isEIP1559Transaction.call(void 0, txParams) && { -+ type: "0x2" /* feeMarket */ -+ } -+ }; -+ }); -+ this.onTransactionStatusChange(transactionMeta); -+ const rawTx = await this.signTransaction( -+ transactionMeta, -+ transactionMeta.txParams -+ ); -+ if (!this.beforePublish(transactionMeta)) { -+ _chunkS6VGOPUYjs.projectLogger.call(void 0, "Skipping publishing transaction based on hook"); -+ this.messagingSystem.publish( -+ `${controllerName}:transactionPublishingSkipped`, -+ transactionMeta -+ ); -+ return "skipped-via-before-publish-hook" /* SkippedViaBeforePublishHook */; -+ } -+ if (!rawTx) { -+ return "not-approved" /* NotApproved */; -+ } -+ const ethQuery = _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _multichainTrackingHelper).getEthQuery({ -+ networkClientId: transactionMeta.networkClientId, -+ chainId: transactionMeta.chainId -+ }); -+ let preTxBalance; -+ const shouldUpdatePreTxBalance = transactionMeta.type === "swap" /* swap */; -+ if (shouldUpdatePreTxBalance) { -+ _chunkS6VGOPUYjs.projectLogger.call(void 0, "Determining pre-transaction balance"); -+ preTxBalance = await _controllerutils.query.call(void 0, ethQuery, "getBalance", [ -+ transactionMeta.txParams.from -+ ]); -+ } -+ _chunkS6VGOPUYjs.projectLogger.call(void 0, "Publishing transaction", transactionMeta.txParams); -+ let { transactionHash: hash } = await this.publish( -+ transactionMeta, -+ rawTx -+ ); -+ if (hash === void 0) { -+ hash = await this.publishTransaction(ethQuery, rawTx); -+ } -+ _chunkS6VGOPUYjs.projectLogger.call(void 0, "Publish successful", hash); -+ transactionMeta = _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _updateTransactionInternal, updateTransactionInternal_fn).call(this, { -+ transactionId, -+ note: "TransactionController#approveTransaction - Transaction submitted" -+ }, (draftTxMeta) => { -+ draftTxMeta.hash = hash; -+ draftTxMeta.status = "submitted" /* submitted */; -+ draftTxMeta.submittedTime = (/* @__PURE__ */ new Date()).getTime(); -+ if (shouldUpdatePreTxBalance) { -+ draftTxMeta.preTxBalance = preTxBalance; -+ _chunkS6VGOPUYjs.projectLogger.call(void 0, "Updated pre-transaction balance", preTxBalance); -+ } -+ }); -+ this.messagingSystem.publish(`${controllerName}:transactionSubmitted`, { -+ transactionMeta -+ }); -+ this.messagingSystem.publish( -+ `${controllerName}:transactionFinished`, -+ transactionMeta -+ ); -+ _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _internalEvents).emit(`${transactionId}:finished`, transactionMeta); -+ this.onTransactionStatusChange(transactionMeta); -+ return "approved" /* Approved */; -+ } catch (error) { -+ this.failTransaction(transactionMeta, error); -+ return "not-approved" /* NotApproved */; -+ } finally { -+ cleanupTasks.forEach((task) => task()); -+ } -+ } -+ async publishTransaction(ethQuery, rawTransaction) { -+ return await _controllerutils.query.call(void 0, ethQuery, "sendRawTransaction", [rawTransaction]); -+ } -+ /** -+ * Cancels a transaction based on its ID by setting its status to "rejected" -+ * and emitting a `:finished` hub event. -+ * -+ * @param transactionId - The ID of the transaction to cancel. -+ * @param actionId - The actionId passed from UI -+ */ -+ cancelTransaction(transactionId, actionId) { -+ const transactionMeta = this.state.transactions.find( -+ ({ id }) => id === transactionId -+ ); -+ if (!transactionMeta) { -+ return; -+ } -+ this.update((state) => { -+ const transactions = state.transactions.filter( -+ ({ id }) => id !== transactionId -+ ); -+ state.transactions = this.trimTransactionsForState(transactions); -+ }); -+ const updatedTransactionMeta = { -+ ...transactionMeta, -+ status: "rejected" /* rejected */ -+ }; -+ this.messagingSystem.publish( -+ `${controllerName}:transactionFinished`, -+ updatedTransactionMeta -+ ); -+ _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _internalEvents).emit( -+ // TODO: Either fix this lint violation or explain why it's necessary to ignore. -+ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions -+ `${transactionMeta.id}:finished`, -+ updatedTransactionMeta -+ ); -+ this.messagingSystem.publish(`${controllerName}:transactionRejected`, { -+ transactionMeta: updatedTransactionMeta, -+ actionId -+ }); -+ this.onTransactionStatusChange(updatedTransactionMeta); -+ } -+ /** -+ * Trim the amount of transactions that are set on the state. Checks -+ * if the length of the tx history is longer then desired persistence -+ * limit and then if it is removes the oldest confirmed or rejected tx. -+ * Pending or unapproved transactions will not be removed by this -+ * operation. For safety of presenting a fully functional transaction UI -+ * representation, this function will not break apart transactions with the -+ * same nonce, created on the same day, per network. Not accounting for -+ * transactions of the same nonce, same day and network combo can result in -+ * confusing or broken experiences in the UI. -+ * -+ * @param transactions - The transactions to be applied to the state. -+ * @returns The trimmed list of transactions. -+ */ -+ trimTransactionsForState(transactions) { -+ const nonceNetworkSet = /* @__PURE__ */ new Set(); -+ const txsToKeep = [...transactions].sort((a, b) => a.time > b.time ? -1 : 1).filter((tx) => { -+ const { chainId, status, txParams, time } = tx; -+ if (txParams) { -+ const key = `${String(txParams.nonce)}-${_controllerutils.convertHexToDecimal.call(void 0, -+ chainId -+ )}-${new Date(time).toDateString()}`; -+ if (nonceNetworkSet.has(key)) { -+ return true; -+ } else if (nonceNetworkSet.size < _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _transactionHistoryLimit) || !this.isFinalState(status)) { -+ nonceNetworkSet.add(key); -+ return true; -+ } -+ } -+ return false; -+ }); -+ txsToKeep.reverse(); -+ return txsToKeep; -+ } -+ /** -+ * Determines if the transaction is in a final state. -+ * -+ * @param status - The transaction status. -+ * @returns Whether the transaction is in a final state. -+ */ -+ isFinalState(status) { -+ return status === "rejected" /* rejected */ || status === "confirmed" /* confirmed */ || status === "failed" /* failed */; -+ } -+ /** -+ * Whether the transaction has at least completed all local processing. -+ * -+ * @param status - The transaction status. -+ * @returns Whether the transaction is in a final state. -+ */ -+ isLocalFinalState(status) { -+ return [ -+ "confirmed" /* confirmed */, -+ "failed" /* failed */, -+ "rejected" /* rejected */, -+ "submitted" /* submitted */ -+ ].includes(status); -+ } -+ async requestApproval(txMeta, { shouldShowRequest }) { -+ const id = this.getApprovalId(txMeta); -+ const { origin } = txMeta; -+ const type = _controllerutils.ApprovalType.Transaction; -+ const requestData = { txId: txMeta.id }; -+ return await this.messagingSystem.call( -+ "ApprovalController:addRequest", -+ { -+ id, -+ origin: origin || _controllerutils.ORIGIN_METAMASK, -+ type, -+ requestData, -+ expectsResult: true -+ }, -+ shouldShowRequest -+ ); -+ } -+ getTransaction(transactionId) { -+ const { transactions } = this.state; -+ return transactions.find(({ id }) => id === transactionId); -+ } -+ getTransactionOrThrow(transactionId, errorMessagePrefix = "TransactionController") { -+ const txMeta = this.getTransaction(transactionId); -+ if (!txMeta) { -+ throw new Error( -+ `${errorMessagePrefix}: No transaction found with id ${transactionId}` -+ ); -+ } -+ return txMeta; -+ } -+ getApprovalId(txMeta) { -+ return String(txMeta.id); -+ } -+ isTransactionCompleted(transactionId) { -+ const transaction = this.getTransaction(transactionId); -+ if (!transaction) { -+ return { meta: void 0, isCompleted: false }; -+ } -+ const isCompleted = this.isLocalFinalState(transaction.status); -+ return { meta: transaction, isCompleted }; -+ } -+ getChainId(networkClientId) { -+ const globalChainId = _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _getGlobalChainId, getGlobalChainId_fn).call(this); -+ const globalNetworkClientId = _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _getGlobalNetworkClientId, getGlobalNetworkClientId_fn).call(this); -+ if (!networkClientId || networkClientId === globalNetworkClientId) { -+ return globalChainId; -+ } -+ return this.messagingSystem.call( -+ `NetworkController:getNetworkClientById`, -+ networkClientId -+ ).configuration.chainId; -+ } -+ prepareUnsignedEthTx(chainId, txParams) { -+ return _tx.TransactionFactory.fromTxData(txParams, { -+ freeze: false, -+ common: this.getCommonConfiguration(chainId) -+ }); -+ } -+ /** -+ * `@ethereumjs/tx` uses `@ethereumjs/common` as a configuration tool for -+ * specifying which chain, network, hardfork and EIPs to support for -+ * a transaction. By referencing this configuration, and analyzing the fields -+ * specified in txParams, @ethereumjs/tx is able to determine which EIP-2718 -+ * transaction type to use. -+ * -+ * @param chainId - The chainId to use for the configuration. -+ * @returns common configuration object -+ */ -+ getCommonConfiguration(chainId) { -+ const customChainParams = { -+ chainId: parseInt(chainId, 16), -+ defaultHardfork: HARDFORK -+ }; -+ return _common.Common.custom(customChainParams); -+ } -+ onIncomingTransactions({ -+ added, -+ updated -+ }) { -+ this.update((state) => { -+ const { transactions: currentTransactions } = state; -+ const updatedTransactions = [ -+ ...added, -+ ...currentTransactions.map((originalTransaction) => { -+ const updatedTransaction = updated.find( -+ ({ hash }) => hash === originalTransaction.hash -+ ); -+ return updatedTransaction ?? originalTransaction; -+ }) -+ ]; -+ state.transactions = this.trimTransactionsForState(updatedTransactions); -+ }); -+ } -+ onUpdatedLastFetchedBlockNumbers({ -+ lastFetchedBlockNumbers, -+ blockNumber -+ }) { -+ this.update((state) => { -+ state.lastFetchedBlockNumbers = lastFetchedBlockNumbers; -+ }); -+ this.messagingSystem.publish( -+ `${controllerName}:incomingTransactionBlockReceived`, -+ blockNumber -+ ); -+ } -+ generateDappSuggestedGasFees(txParams, origin) { -+ if (!origin || origin === _controllerutils.ORIGIN_METAMASK) { -+ return void 0; -+ } -+ const { gasPrice, maxFeePerGas, maxPriorityFeePerGas, gas } = txParams; -+ if (gasPrice === void 0 && maxFeePerGas === void 0 && maxPriorityFeePerGas === void 0 && gas === void 0) { -+ return void 0; -+ } -+ const dappSuggestedGasFees = {}; -+ if (gasPrice !== void 0) { -+ dappSuggestedGasFees.gasPrice = gasPrice; -+ } else if (maxFeePerGas !== void 0 || maxPriorityFeePerGas !== void 0) { -+ dappSuggestedGasFees.maxFeePerGas = maxFeePerGas; -+ dappSuggestedGasFees.maxPriorityFeePerGas = maxPriorityFeePerGas; -+ } -+ if (gas !== void 0) { -+ dappSuggestedGasFees.gas = gas; -+ } -+ return dappSuggestedGasFees; -+ } -+ /** -+ * Validates and adds external provided transaction to state. -+ * -+ * @param transactionMeta - Nominated external transaction to be added to state. -+ * @returns The new transaction. -+ */ -+ addExternalTransaction(transactionMeta) { -+ const { chainId } = transactionMeta; -+ const { transactions } = this.state; -+ const fromAddress = transactionMeta?.txParams?.from; -+ const sameFromAndNetworkTransactions = transactions.filter( -+ (transaction) => transaction.txParams.from === fromAddress && transaction.chainId === chainId -+ ); -+ const confirmedTxs = sameFromAndNetworkTransactions.filter( -+ (transaction) => transaction.status === "confirmed" /* confirmed */ -+ ); -+ const pendingTxs = sameFromAndNetworkTransactions.filter( -+ (transaction) => transaction.status === "submitted" /* submitted */ -+ ); -+ _chunk7LXE4KHVjs.validateConfirmedExternalTransaction.call(void 0, -+ transactionMeta, -+ confirmedTxs, -+ pendingTxs -+ ); -+ const newTransactionMeta = (transactionMeta.history ?? []).length === 0 && !this.isHistoryDisabled ? _chunkQP75SWIQjs.addInitialHistorySnapshot.call(void 0, transactionMeta) : transactionMeta; -+ this.update((state) => { -+ state.transactions = this.trimTransactionsForState([ -+ ...state.transactions, -+ newTransactionMeta -+ ]); -+ }); -+ return newTransactionMeta; -+ } -+ /** -+ * Sets other txMeta statuses to dropped if the txMeta that has been confirmed has other transactions -+ * in the transactions have the same nonce. -+ * -+ * @param transactionId - Used to identify original transaction. -+ */ -+ markNonceDuplicatesDropped(transactionId) { -+ const transactionMeta = this.getTransaction(transactionId); -+ if (!transactionMeta) { -+ return; -+ } -+ const nonce = transactionMeta.txParams?.nonce; -+ const from = transactionMeta.txParams?.from; -+ const { chainId } = transactionMeta; -+ const sameNonceTransactions = this.state.transactions.filter( -+ (transaction) => transaction.id !== transactionId && transaction.txParams.from === from && transaction.txParams.nonce === nonce && transaction.chainId === chainId && transaction.type !== "incoming" /* incoming */ -+ ); -+ const sameNonceTransactionIds = sameNonceTransactions.map( -+ (transaction) => transaction.id -+ ); -+ if (sameNonceTransactions.length === 0) { -+ return; -+ } -+ this.update((state) => { -+ for (const transaction of state.transactions) { -+ if (sameNonceTransactionIds.includes(transaction.id)) { -+ transaction.replacedBy = transactionMeta?.hash; -+ transaction.replacedById = transactionMeta?.id; -+ } -+ } -+ }); -+ for (const transaction of this.state.transactions) { -+ if (sameNonceTransactionIds.includes(transaction.id) && transaction.status !== "failed" /* failed */) { -+ this.setTransactionStatusDropped(transaction); -+ } -+ } -+ } -+ /** -+ * Method to set transaction status to dropped. -+ * -+ * @param transactionMeta - TransactionMeta of transaction to be marked as dropped. -+ */ -+ setTransactionStatusDropped(transactionMeta) { -+ const updatedTransactionMeta = { -+ ...transactionMeta, -+ status: "dropped" /* dropped */ -+ }; -+ this.messagingSystem.publish(`${controllerName}:transactionDropped`, { -+ transactionMeta: updatedTransactionMeta -+ }); -+ this.updateTransaction( -+ updatedTransactionMeta, -+ "TransactionController#setTransactionStatusDropped - Transaction dropped" -+ ); -+ this.onTransactionStatusChange(updatedTransactionMeta); -+ } -+ /** -+ * Get transaction with provided actionId. -+ * -+ * @param actionId - Unique ID to prevent duplicate requests -+ * @returns the filtered transaction -+ */ -+ getTransactionWithActionId(actionId) { -+ return this.state.transactions.find( -+ (transaction) => actionId && transaction.actionId === actionId -+ ); -+ } -+ async waitForTransactionFinished(transactionId) { -+ return new Promise((resolve) => { -+ _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _internalEvents).once(`${transactionId}:finished`, (txMeta) => { -+ resolve(txMeta); -+ }); -+ }); -+ } -+ /** -+ * Updates the r, s, and v properties of a TransactionMeta object -+ * with values from a signed transaction. -+ * -+ * @param transactionMeta - The TransactionMeta object to update. -+ * @param signedTx - The encompassing type for all transaction types containing r, s, and v values. -+ * @returns The updated TransactionMeta object. -+ */ -+ updateTransactionMetaRSV(transactionMeta, signedTx) { -+ const transactionMetaWithRsv = _lodash.cloneDeep.call(void 0, transactionMeta); -+ for (const key of ["r", "s", "v"]) { -+ const value = signedTx[key]; -+ if (value === void 0 || value === null) { -+ continue; -+ } -+ transactionMetaWithRsv[key] = _utils.add0x.call(void 0, value.toString(16)); -+ } -+ return transactionMetaWithRsv; -+ } -+ async getEIP1559Compatibility(networkClientId) { -+ const currentNetworkIsEIP1559Compatible = await this.getCurrentNetworkEIP1559Compatibility(networkClientId); -+ const currentAccountIsEIP1559Compatible = await this.getCurrentAccountEIP1559Compatibility(); -+ return currentNetworkIsEIP1559Compatible && currentAccountIsEIP1559Compatible; -+ } -+ async signTransaction(transactionMeta, txParams) { -+ _chunkS6VGOPUYjs.projectLogger.call(void 0, "Signing transaction", txParams); -+ const unsignedEthTx = this.prepareUnsignedEthTx( -+ transactionMeta.chainId, -+ txParams -+ ); -+ this.approvingTransactionIds.add(transactionMeta.id); -+ const signedTx = await new Promise((resolve, reject) => { -+ this.sign?.( -+ unsignedEthTx, -+ txParams.from, -+ ...this.getAdditionalSignArguments(transactionMeta) -+ ).then(resolve, reject); -+ this.signAbortCallbacks.set( -+ transactionMeta.id, -+ () => reject(new Error("Signing aborted by user")) -+ ); -+ }); -+ this.signAbortCallbacks.delete(transactionMeta.id); -+ if (!signedTx) { -+ _chunkS6VGOPUYjs.projectLogger.call(void 0, "Skipping signed status as no signed transaction"); -+ return void 0; -+ } -+ const transactionMetaFromHook = _lodash.cloneDeep.call(void 0, transactionMeta); -+ if (!this.afterSign(transactionMetaFromHook, signedTx)) { -+ this.updateTransaction( -+ transactionMetaFromHook, -+ "TransactionController#signTransaction - Update after sign" -+ ); -+ _chunkS6VGOPUYjs.projectLogger.call(void 0, "Skipping signed status based on hook"); -+ return void 0; -+ } -+ const transactionMetaWithRsv = { -+ ...this.updateTransactionMetaRSV(transactionMetaFromHook, signedTx), -+ status: "signed" /* signed */ -+ }; -+ this.updateTransaction( -+ transactionMetaWithRsv, -+ "TransactionController#approveTransaction - Transaction signed" -+ ); -+ this.onTransactionStatusChange(transactionMetaWithRsv); -+ const rawTx = _util.bufferToHex.call(void 0, signedTx.serialize()); -+ const transactionMetaWithRawTx = _lodash.merge.call(void 0, {}, transactionMetaWithRsv, { -+ rawTx -+ }); -+ this.updateTransaction( -+ transactionMetaWithRawTx, -+ "TransactionController#approveTransaction - RawTransaction added" -+ ); -+ return rawTx; -+ } -+ onTransactionStatusChange(transactionMeta) { -+ this.messagingSystem.publish(`${controllerName}:transactionStatusUpdated`, { -+ transactionMeta -+ }); -+ } -+ getNonceTrackerTransactions(status, address, chainId = this.getChainId()) { -+ return _chunkPRUNMTRDjs.getAndFormatTransactionsForNonceTracker.call(void 0, -+ chainId, -+ address, -+ status, -+ this.state.transactions -+ ); -+ } -+ onConfirmedTransaction(transactionMeta) { -+ _chunkS6VGOPUYjs.projectLogger.call(void 0, "Processing confirmed transaction", transactionMeta.id); -+ this.markNonceDuplicatesDropped(transactionMeta.id); -+ this.messagingSystem.publish( -+ `${controllerName}:transactionConfirmed`, -+ transactionMeta -+ ); -+ this.onTransactionStatusChange(transactionMeta); -+ this.updatePostBalance(transactionMeta); -+ } -+ async updatePostBalance(transactionMeta) { -+ try { -+ if (transactionMeta.type !== "swap" /* swap */) { -+ return; -+ } -+ const ethQuery = _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _multichainTrackingHelper).getEthQuery({ -+ networkClientId: transactionMeta.networkClientId, -+ chainId: transactionMeta.chainId -+ }); -+ const { updatedTransactionMeta, approvalTransactionMeta } = await _chunkQH2H4W3Njs.updatePostTransactionBalance.call(void 0, transactionMeta, { -+ ethQuery, -+ getTransaction: this.getTransaction.bind(this), -+ updateTransaction: this.updateTransaction.bind(this) -+ }); -+ this.messagingSystem.publish( -+ `${controllerName}:postTransactionBalanceUpdated`, -+ { -+ transactionMeta: updatedTransactionMeta, -+ approvalTransactionMeta -+ } -+ ); -+ } catch (error) { -+ _chunkS6VGOPUYjs.projectLogger.call(void 0, "Error while updating post transaction balance", error); -+ } -+ } -+ async publishTransactionForRetry(ethQuery, rawTx, transactionMeta) { -+ try { -+ const hash = await this.publishTransaction(ethQuery, rawTx); -+ return hash; -+ } catch (error) { -+ if (this.isTransactionAlreadyConfirmedError(error)) { -+ await this.pendingTransactionTracker.forceCheckTransaction( -+ transactionMeta -+ ); -+ throw new Error("Previous transaction is already confirmed"); -+ } -+ throw error; -+ } -+ } -+ /** -+ * Ensures that error is a nonce issue -+ * -+ * @param error - The error to check -+ * @returns Whether or not the error is a nonce issue -+ */ -+ // TODO: Replace `any` with type -+ // Some networks are returning original error in the data field -+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -+ isTransactionAlreadyConfirmedError(error) { -+ return error?.message?.includes("nonce too low") || error?.data?.message?.includes("nonce too low"); -+ } -+}; -+_internalEvents = new WeakMap(); -+_incomingTransactionOptions = new WeakMap(); -+_pendingTransactionOptions = new WeakMap(); -+_transactionHistoryLimit = new WeakMap(); -+_isSimulationEnabled = new WeakMap(); -+_testGasFeeFlows = new WeakMap(); -+_multichainTrackingHelper = new WeakMap(); -+_createNonceTracker = new WeakSet(); -+createNonceTracker_fn = function({ -+ provider, -+ blockTracker, -+ chainId -+}) { -+ return new (0, _noncetracker.NonceTracker)({ -+ // TODO: Fix types -+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -+ provider, -+ // @ts-expect-error TODO: Fix types -+ blockTracker, -+ getPendingTransactions: _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _getNonceTrackerPendingTransactions, getNonceTrackerPendingTransactions_fn).bind( -+ this, -+ chainId -+ ), -+ getConfirmedTransactions: this.getNonceTrackerTransactions.bind( -+ this, -+ "confirmed" /* confirmed */ -+ ) -+ }); -+}; -+_createIncomingTransactionHelper = new WeakSet(); -+createIncomingTransactionHelper_fn = function({ -+ blockTracker, -+ etherscanRemoteTransactionSource, -+ chainId -+}) { -+ const incomingTransactionHelper = new (0, _chunkRHDPOIS4js.IncomingTransactionHelper)({ -+ blockTracker, -+ getCurrentAccount: () => _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _getSelectedAccount, getSelectedAccount_fn).call(this), -+ getLastFetchedBlockNumbers: () => this.state.lastFetchedBlockNumbers, -+ getChainId: chainId ? () => chainId : this.getChainId.bind(this), -+ isEnabled: _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _incomingTransactionOptions).isEnabled, -+ queryEntireHistory: _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _incomingTransactionOptions).queryEntireHistory, -+ remoteTransactionSource: etherscanRemoteTransactionSource, -+ transactionLimit: _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _transactionHistoryLimit), -+ updateTransactions: _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _incomingTransactionOptions).updateTransactions -+ }); -+ _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _addIncomingTransactionHelperListeners, addIncomingTransactionHelperListeners_fn).call(this, incomingTransactionHelper); -+ return incomingTransactionHelper; -+}; -+_createPendingTransactionTracker = new WeakSet(); -+createPendingTransactionTracker_fn = function({ -+ provider, -+ blockTracker, -+ chainId -+}) { -+ const ethQuery = new (0, _ethquery2.default)(provider); -+ const getChainId = chainId ? () => chainId : this.getChainId.bind(this); -+ const pendingTransactionTracker = new (0, _chunk6DODV6OVjs.PendingTransactionTracker)({ -+ blockTracker, -+ getChainId, -+ getEthQuery: () => ethQuery, -+ getTransactions: () => this.state.transactions, -+ isResubmitEnabled: _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _pendingTransactionOptions).isResubmitEnabled, -+ getGlobalLock: () => _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _multichainTrackingHelper).acquireNonceLockForChainIdKey({ -+ chainId: getChainId() -+ }), -+ publishTransaction: this.publishTransaction.bind(this), -+ hooks: { -+ beforeCheckPendingTransaction: this.beforeCheckPendingTransaction.bind(this), -+ beforePublish: this.beforePublish.bind(this) -+ } -+ }); -+ _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _addPendingTransactionTrackerListeners, addPendingTransactionTrackerListeners_fn).call(this, pendingTransactionTracker); -+ return pendingTransactionTracker; -+}; -+_checkForPendingTransactionAndStartPolling = new WeakMap(); -+_stopAllTracking = new WeakSet(); -+stopAllTracking_fn = function() { -+ this.pendingTransactionTracker.stop(); -+ _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _removePendingTransactionTrackerListeners, removePendingTransactionTrackerListeners_fn).call(this, this.pendingTransactionTracker); -+ this.incomingTransactionHelper.stop(); -+ _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _removeIncomingTransactionHelperListeners, removeIncomingTransactionHelperListeners_fn).call(this, this.incomingTransactionHelper); -+ _chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _multichainTrackingHelper).stopAllTracking(); -+}; -+_removeIncomingTransactionHelperListeners = new WeakSet(); -+removeIncomingTransactionHelperListeners_fn = function(incomingTransactionHelper) { -+ incomingTransactionHelper.hub.removeAllListeners("transactions"); -+ incomingTransactionHelper.hub.removeAllListeners( -+ "updatedLastFetchedBlockNumbers" -+ ); -+}; -+_addIncomingTransactionHelperListeners = new WeakSet(); -+addIncomingTransactionHelperListeners_fn = function(incomingTransactionHelper) { -+ incomingTransactionHelper.hub.on( -+ "transactions", -+ this.onIncomingTransactions.bind(this) -+ ); -+ incomingTransactionHelper.hub.on( -+ "updatedLastFetchedBlockNumbers", -+ this.onUpdatedLastFetchedBlockNumbers.bind(this) -+ ); -+}; -+_removePendingTransactionTrackerListeners = new WeakSet(); -+removePendingTransactionTrackerListeners_fn = function(pendingTransactionTracker) { -+ pendingTransactionTracker.hub.removeAllListeners("transaction-confirmed"); -+ pendingTransactionTracker.hub.removeAllListeners("transaction-dropped"); -+ pendingTransactionTracker.hub.removeAllListeners("transaction-failed"); -+ pendingTransactionTracker.hub.removeAllListeners("transaction-updated"); -+}; -+_addPendingTransactionTrackerListeners = new WeakSet(); -+addPendingTransactionTrackerListeners_fn = function(pendingTransactionTracker) { -+ pendingTransactionTracker.hub.on( -+ "transaction-confirmed", -+ this.onConfirmedTransaction.bind(this) -+ ); -+ pendingTransactionTracker.hub.on( -+ "transaction-dropped", -+ this.setTransactionStatusDropped.bind(this) -+ ); -+ pendingTransactionTracker.hub.on( -+ "transaction-failed", -+ this.failTransaction.bind(this) -+ ); -+ pendingTransactionTracker.hub.on( -+ "transaction-updated", -+ this.updateTransaction.bind(this) -+ ); -+}; -+_getNonceTrackerPendingTransactions = new WeakSet(); -+getNonceTrackerPendingTransactions_fn = function(chainId, address) { -+ const standardPendingTransactions = this.getNonceTrackerTransactions( -+ "submitted" /* submitted */, -+ address, -+ chainId -+ ); -+ const externalPendingTransactions = this.getExternalPendingTransactions( -+ address, -+ chainId -+ ); -+ return [...standardPendingTransactions, ...externalPendingTransactions]; -+}; -+_getGasFeeFlows = new WeakSet(); -+getGasFeeFlows_fn = function() { -+ if (_chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _testGasFeeFlows)) { -+ return [new (0, _chunkTJMQEH57js.TestGasFeeFlow)()]; -+ } -+ return [new (0, _chunkARZHJFVGjs.LineaGasFeeFlow)(), new (0, _chunkQTKXIDGEjs.DefaultGasFeeFlow)()]; -+}; -+_getLayer1GasFeeFlows = new WeakSet(); -+getLayer1GasFeeFlows_fn = function() { -+ return [new (0, _chunkNYKRCWBGjs.OptimismLayer1GasFeeFlow)(), new (0, _chunkWR5F34OWjs.ScrollLayer1GasFeeFlow)()]; -+}; -+_updateTransactionInternal = new WeakSet(); -+updateTransactionInternal_fn = function({ -+ transactionId, -+ note, -+ skipHistory, -+ skipValidation -+}, callback) { -+ let updatedTransactionParams = []; -+ this.update((state) => { -+ const index = state.transactions.findIndex( -+ ({ id }) => id === transactionId -+ ); -+ let transactionMeta2 = state.transactions[index]; -+ transactionMeta2 = callback(transactionMeta2) ?? transactionMeta2; -+ if (skipValidation !== true) { -+ transactionMeta2.txParams = _chunkOZ6UB42Cjs.normalizeTransactionParams.call(void 0, -+ transactionMeta2.txParams -+ ); -+ _chunkRXIUMVA5js.validateTxParams.call(void 0, transactionMeta2.txParams); -+ } -+ updatedTransactionParams = _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _checkIfTransactionParamsUpdated, checkIfTransactionParamsUpdated_fn).call(this, transactionMeta2); -+ const shouldSkipHistory = this.isHistoryDisabled || skipHistory; -+ if (!shouldSkipHistory) { -+ transactionMeta2 = _chunkQP75SWIQjs.updateTransactionHistory.call(void 0, -+ transactionMeta2, -+ note ?? "Transaction updated" -+ ); -+ } -+ state.transactions[index] = transactionMeta2; -+ }); -+ const transactionMeta = this.getTransaction( -+ transactionId -+ ); -+ if (updatedTransactionParams.length > 0) { -+ _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _onTransactionParamsUpdated, onTransactionParamsUpdated_fn).call(this, transactionMeta, updatedTransactionParams); -+ } -+ return transactionMeta; -+}; -+_checkIfTransactionParamsUpdated = new WeakSet(); -+checkIfTransactionParamsUpdated_fn = function(newTransactionMeta) { -+ const { id: transactionId, txParams: newParams } = newTransactionMeta; -+ const originalParams = this.getTransaction(transactionId)?.txParams; -+ if (!originalParams || _lodash.isEqual.call(void 0, originalParams, newParams)) { -+ return []; -+ } -+ const params = Object.keys(newParams); -+ const updatedProperties = params.filter( -+ (param) => newParams[param] !== originalParams[param] -+ ); -+ _chunkS6VGOPUYjs.projectLogger.call(void 0, -+ "Transaction parameters have been updated", -+ transactionId, -+ updatedProperties, -+ originalParams, -+ newParams -+ ); -+ return updatedProperties; -+}; -+_onTransactionParamsUpdated = new WeakSet(); -+onTransactionParamsUpdated_fn = function(transactionMeta, updatedParams) { -+ if (["to", "value", "data"].some( -+ (param) => updatedParams.includes(param) -+ )) { -+ _chunkS6VGOPUYjs.projectLogger.call(void 0, "Updating simulation data due to transaction parameter update"); -+ _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _updateSimulationData, updateSimulationData_fn).call(this, transactionMeta); -+ } -+}; -+_updateSimulationData = new WeakSet(); -+updateSimulationData_fn = async function(transactionMeta) { -+ const { id: transactionId, chainId, txParams } = transactionMeta; -+ const { from, to, value, data } = txParams; -+ let simulationData = { -+ error: { -+ code: "disabled" /* Disabled */, -+ message: "Simulation disabled" -+ }, -+ tokenBalanceChanges: [] -+ }; -+ if (_chunkZ4BLTVTBjs.__privateGet.call(void 0, this, _isSimulationEnabled).call(this)) { -+ _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _updateTransactionInternal, updateTransactionInternal_fn).call(this, { transactionId, skipHistory: true }, (txMeta) => { -+ txMeta.simulationData = void 0; -+ }); -+ simulationData = await _chunk74W7X6BEjs.getSimulationData.call(void 0, { -+ chainId, -+ from, -+ to, -+ value, -+ data -+ }); -+ } -+ const finalTransactionMeta = this.getTransaction(transactionId); -+ if (!finalTransactionMeta) { -+ _chunkS6VGOPUYjs.projectLogger.call(void 0, -+ "Cannot update simulation data as transaction not found", -+ transactionId, -+ simulationData -+ ); -+ return; -+ } -+ _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _updateTransactionInternal, updateTransactionInternal_fn).call(this, { -+ transactionId, -+ note: "TransactionController#updateSimulationData - Update simulation data" -+ }, (txMeta) => { -+ txMeta.simulationData = simulationData; -+ }); -+ _chunkS6VGOPUYjs.projectLogger.call(void 0, "Updated simulation data", transactionId, simulationData); -+}; -+_onGasFeePollerTransactionUpdate = new WeakSet(); -+onGasFeePollerTransactionUpdate_fn = function({ -+ transactionId, -+ gasFeeEstimates, -+ gasFeeEstimatesLoaded, -+ layer1GasFee -+}) { -+ _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _updateTransactionInternal, updateTransactionInternal_fn).call(this, { transactionId, skipHistory: true }, (txMeta) => { -+ if (gasFeeEstimates) { -+ txMeta.gasFeeEstimates = gasFeeEstimates; -+ } -+ if (gasFeeEstimatesLoaded !== void 0) { -+ txMeta.gasFeeEstimatesLoaded = gasFeeEstimatesLoaded; -+ } -+ if (layer1GasFee) { -+ txMeta.layer1GasFee = layer1GasFee; -+ } -+ }); -+}; -+_getNetworkClientId = new WeakSet(); -+getNetworkClientId_fn = function({ -+ networkClientId: requestNetworkClientId, -+ chainId -+}) { -+ const globalChainId = _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _getGlobalChainId, getGlobalChainId_fn).call(this); -+ const globalNetworkClientId = _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _getGlobalNetworkClientId, getGlobalNetworkClientId_fn).call(this); -+ if (requestNetworkClientId) { -+ return requestNetworkClientId; -+ } -+ if (!chainId || chainId === globalChainId) { -+ return globalNetworkClientId; -+ } -+ return this.messagingSystem.call( -+ `NetworkController:findNetworkClientIdByChainId`, -+ chainId -+ ); -+}; -+_getGlobalNetworkClientId = new WeakSet(); -+getGlobalNetworkClientId_fn = function() { -+ return this.getNetworkState().selectedNetworkClientId; -+}; -+_getGlobalChainId = new WeakSet(); -+getGlobalChainId_fn = function() { -+ return this.messagingSystem.call( -+ `NetworkController:getNetworkClientById`, -+ this.getNetworkState().selectedNetworkClientId -+ ).configuration.chainId; -+}; -+_isCustomNetwork = new WeakSet(); -+isCustomNetwork_fn = function(networkClientId) { -+ const globalNetworkClientId = _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _getGlobalNetworkClientId, getGlobalNetworkClientId_fn).call(this); -+ if (!networkClientId || networkClientId === globalNetworkClientId) { -+ return !_controllerutils.isInfuraNetworkType.call(void 0, -+ this.getNetworkState().selectedNetworkClientId -+ ); -+ } -+ return this.messagingSystem.call( -+ `NetworkController:getNetworkClientById`, -+ networkClientId -+ ).configuration.type === _networkcontroller.NetworkClientType.Custom; -+}; -+_getSelectedAccount = new WeakSet(); -+getSelectedAccount_fn = function() { -+ return this.messagingSystem.call("AccountsController:getSelectedAccount"); -+}; -+ -+ -+ -+ -+ -+ -+ -+exports.HARDFORK = HARDFORK; exports.CANCEL_RATE = CANCEL_RATE; exports.SPEED_UP_RATE = SPEED_UP_RATE; exports.ApprovalState = ApprovalState; exports.TransactionController = TransactionController; -+//# sourceMappingURL=chunk-IVR4NMOF.js.map -\ No newline at end of file -diff --git a/dist/chunk-IVR4NMOF.js.map b/dist/chunk-IVR4NMOF.js.map -new file mode 100644 -index 0000000000000000000000000000000000000000..e06df9082a1bf650bafaf9b2a3602a8c597cf6ed ---- /dev/null -+++ b/dist/chunk-IVR4NMOF.js.map -@@ -0,0 +1 @@ -+{"version":3,"sources":["../src/TransactionController.ts"],"names":["ApprovalState","transactionMeta"],"mappings":"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","sourcesContent":["import { Hardfork, Common, type ChainConfig } from '@ethereumjs/common';\nimport type { TypedTransaction } from '@ethereumjs/tx';\nimport { TransactionFactory } from '@ethereumjs/tx';\nimport { bufferToHex } from '@ethereumjs/util';\nimport type { AccountsControllerGetSelectedAccountAction } from '@metamask/accounts-controller';\nimport type {\n AcceptResultCallbacks,\n AddApprovalRequest,\n AddResult,\n} from '@metamask/approval-controller';\nimport type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n RestrictedControllerMessenger,\n} from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport {\n query,\n ApprovalType,\n ORIGIN_METAMASK,\n convertHexToDecimal,\n isInfuraNetworkType,\n} from '@metamask/controller-utils';\nimport EthQuery from '@metamask/eth-query';\nimport type {\n FetchGasFeeEstimateOptions,\n GasFeeState,\n} from '@metamask/gas-fee-controller';\nimport type {\n BlockTracker,\n NetworkClientId,\n NetworkController,\n NetworkControllerStateChangeEvent,\n NetworkState,\n Provider,\n NetworkControllerFindNetworkClientIdByChainIdAction,\n NetworkControllerGetNetworkClientByIdAction,\n} from '@metamask/network-controller';\nimport { NetworkClientType } from '@metamask/network-controller';\nimport type {\n NonceLock,\n Transaction as NonceTrackerTransaction,\n} from '@metamask/nonce-tracker';\nimport { NonceTracker } from '@metamask/nonce-tracker';\nimport { errorCodes, rpcErrors, providerErrors } from '@metamask/rpc-errors';\nimport type { Hex } from '@metamask/utils';\nimport { add0x } from '@metamask/utils';\nimport { Mutex } from 'async-mutex';\nimport { MethodRegistry } from 'eth-method-registry';\nimport { EventEmitter } from 'events';\nimport { cloneDeep, mapValues, merge, pickBy, sortBy, isEqual } from 'lodash';\nimport { v1 as random } from 'uuid';\n\nimport { DefaultGasFeeFlow } from './gas-flows/DefaultGasFeeFlow';\nimport { LineaGasFeeFlow } from './gas-flows/LineaGasFeeFlow';\nimport { OptimismLayer1GasFeeFlow } from './gas-flows/OptimismLayer1GasFeeFlow';\nimport { ScrollLayer1GasFeeFlow } from './gas-flows/ScrollLayer1GasFeeFlow';\nimport { TestGasFeeFlow } from './gas-flows/TestGasFeeFlow';\nimport { EtherscanRemoteTransactionSource } from './helpers/EtherscanRemoteTransactionSource';\nimport { GasFeePoller } from './helpers/GasFeePoller';\nimport type { IncomingTransactionOptions } from './helpers/IncomingTransactionHelper';\nimport { IncomingTransactionHelper } from './helpers/IncomingTransactionHelper';\nimport { MultichainTrackingHelper } from './helpers/MultichainTrackingHelper';\nimport { PendingTransactionTracker } from './helpers/PendingTransactionTracker';\nimport { projectLogger as log } from './logger';\nimport type {\n DappSuggestedGasFees,\n Layer1GasFeeFlow,\n SavedGasFees,\n SecurityProviderRequest,\n SendFlowHistoryEntry,\n TransactionParams,\n TransactionMeta,\n TransactionReceipt,\n WalletDevice,\n SecurityAlertResponse,\n GasFeeFlow,\n SimulationData,\n GasFeeEstimates,\n GasFeeFlowResponse,\n} from './types';\nimport {\n TransactionEnvelopeType,\n TransactionType,\n TransactionStatus,\n SimulationErrorCode,\n} from './types';\nimport { validateConfirmedExternalTransaction } from './utils/external-transactions';\nimport { addGasBuffer, estimateGas, updateGas } from './utils/gas';\nimport { updateGasFees } from './utils/gas-fees';\nimport { getGasFeeFlow } from './utils/gas-flow';\nimport {\n addInitialHistorySnapshot,\n updateTransactionHistory,\n} from './utils/history';\nimport {\n getTransactionLayer1GasFee,\n updateTransactionLayer1GasFee,\n} from './utils/layer1-gas-fee-flow';\nimport {\n getAndFormatTransactionsForNonceTracker,\n getNextNonce,\n} from './utils/nonce';\nimport { getSimulationData } from './utils/simulation';\nimport {\n updatePostTransactionBalance,\n updateSwapsTransaction,\n} from './utils/swaps';\nimport { determineTransactionType } from './utils/transaction-type';\nimport {\n getIncreasedPriceFromExisting,\n normalizeTransactionParams,\n isEIP1559Transaction,\n isFeeMarketEIP1559Values,\n isGasPriceValue,\n validateGasValues,\n validateIfTransactionUnapproved,\n validateMinimumIncrease,\n normalizeTxError,\n normalizeGasFeeValues,\n} from './utils/utils';\nimport {\n validateTransactionOrigin,\n validateTxParams,\n} from './utils/validation';\n\n/**\n * Metadata for the TransactionController state, describing how to \"anonymize\"\n * the state and which parts should be persisted.\n */\nconst metadata = {\n transactions: {\n persist: true,\n anonymous: false,\n },\n methodData: {\n persist: true,\n anonymous: false,\n },\n lastFetchedBlockNumbers: {\n persist: true,\n anonymous: false,\n },\n};\n\nexport const HARDFORK = Hardfork.London;\n\n/**\n * Object with new transaction's meta and a promise resolving to the\n * transaction hash if successful.\n *\n * @property result - Promise resolving to a new transaction hash\n * @property transactionMeta - Meta information about this new transaction\n */\n// This interface was created before this ESLint rule was added.\n// Convert to a `type` in a future major version.\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\nexport interface Result {\n result: Promise;\n transactionMeta: TransactionMeta;\n}\n\n// This interface was created before this ESLint rule was added.\n// Convert to a `type` in a future major version.\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\nexport interface GasPriceValue {\n gasPrice: string;\n}\n\n// This interface was created before this ESLint rule was added.\n// Convert to a `type` in a future major version.\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\nexport interface FeeMarketEIP1559Values {\n maxFeePerGas: string;\n maxPriorityFeePerGas: string;\n}\n\n/**\n * Method data registry object\n *\n * @property registryMethod - Registry method raw string\n * @property parsedRegistryMethod - Registry method object, containing name and method arguments\n */\nexport type MethodData = {\n registryMethod: string;\n parsedRegistryMethod:\n | {\n name: string;\n args: { type: string }[];\n }\n | {\n // We're using `any` instead of `undefined` for compatibility with `Json`\n // TODO: Correct this type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n name?: any;\n // We're using `any` instead of `undefined` for compatibility with `Json`\n // TODO: Correct this type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n args?: any;\n };\n};\n\n/**\n * Transaction controller state\n *\n * @property transactions - A list of TransactionMeta objects\n * @property methodData - Object containing all known method data information\n * @property lastFetchedBlockNumbers - Last fetched block numbers.\n */\nexport type TransactionControllerState = {\n transactions: TransactionMeta[];\n methodData: Record;\n lastFetchedBlockNumbers: { [key: string]: number };\n};\n\n/**\n * Multiplier used to determine a transaction's increased gas fee during cancellation\n */\nexport const CANCEL_RATE = 1.1;\n\n/**\n * Multiplier used to determine a transaction's increased gas fee during speed up\n */\nexport const SPEED_UP_RATE = 1.1;\n\n/**\n * Represents the `TransactionController:getState` action.\n */\nexport type TransactionControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n TransactionControllerState\n>;\n\n/**\n * The internal actions available to the TransactionController.\n */\nexport type TransactionControllerActions = TransactionControllerGetStateAction;\n\n/**\n * Configuration options for the PendingTransactionTracker\n *\n * @property isResubmitEnabled - Whether transaction publishing is automatically retried.\n */\nexport type PendingTransactionOptions = {\n isResubmitEnabled?: () => boolean;\n};\n\n/**\n * TransactionController constructor options.\n *\n * @property blockTracker - The block tracker used to poll for new blocks data.\n * @property disableHistory - Whether to disable storing history in transaction metadata.\n * @property disableSendFlowHistory - Explicitly disable transaction metadata history.\n * @property disableSwaps - Whether to disable additional processing on swaps transactions.\n * @property getCurrentAccountEIP1559Compatibility - Whether or not the account supports EIP-1559.\n * @property getCurrentNetworkEIP1559Compatibility - Whether or not the network supports EIP-1559.\n * @property getExternalPendingTransactions - Callback to retrieve pending transactions from external sources.\n * @property getGasFeeEstimates - Callback to retrieve gas fee estimates.\n * @property getNetworkClientRegistry - Gets the network client registry.\n * @property getNetworkState - Gets the state of the network controller.\n * @property getPermittedAccounts - Get accounts that a given origin has permissions for.\n * @property getSavedGasFees - Gets the saved gas fee config.\n * @property getSelectedAddress - Gets the address of the currently selected account.\n * @property incomingTransactions - Configuration options for incoming transaction support.\n * @property isMultichainEnabled - Enable multichain support.\n * @property isSimulationEnabled - Whether new transactions will be automatically simulated.\n * @property messenger - The controller messenger.\n * @property onNetworkStateChange - Allows subscribing to network controller state changes.\n * @property pendingTransactions - Configuration options for pending transaction support.\n * @property provider - The provider used to create the underlying EthQuery instance.\n * @property securityProviderRequest - A function for verifying a transaction, whether it is malicious or not.\n * @property sign - Function used to sign transactions.\n * @property state - Initial state to set on this controller.\n * @property transactionHistoryLimit - Transaction history limit.\n * @property hooks - The controller hooks.\n * @property hooks.afterSign - Additional logic to execute after signing a transaction. Return false to not change the status to signed.\n * @property hooks.beforeCheckPendingTransaction - Additional logic to execute before checking pending transactions. Return false to prevent the broadcast of the transaction.\n * @property hooks.beforePublish - Additional logic to execute before publishing a transaction. Return false to prevent the broadcast of the transaction.\n * @property hooks.getAdditionalSignArguments - Returns additional arguments required to sign a transaction.\n * @property hooks.publish - Alternate logic to publish a transaction.\n */\nexport type TransactionControllerOptions = {\n blockTracker: BlockTracker;\n disableHistory: boolean;\n disableSendFlowHistory: boolean;\n disableSwaps: boolean;\n getCurrentAccountEIP1559Compatibility?: () => Promise;\n getCurrentNetworkEIP1559Compatibility: () => Promise;\n getExternalPendingTransactions?: (\n address: string,\n chainId?: string,\n ) => NonceTrackerTransaction[];\n getGasFeeEstimates?: (\n options: FetchGasFeeEstimateOptions,\n ) => Promise;\n getNetworkClientRegistry: NetworkController['getNetworkClientRegistry'];\n getNetworkState: () => NetworkState;\n getPermittedAccounts: (origin?: string) => Promise;\n getSavedGasFees?: (chainId: Hex) => SavedGasFees | undefined;\n incomingTransactions?: IncomingTransactionOptions;\n isMultichainEnabled: boolean;\n isSimulationEnabled?: () => boolean;\n messenger: TransactionControllerMessenger;\n onNetworkStateChange: (listener: (state: NetworkState) => void) => void;\n pendingTransactions?: PendingTransactionOptions;\n provider: Provider;\n securityProviderRequest?: SecurityProviderRequest;\n sign?: (\n transaction: TypedTransaction,\n from: string,\n transactionMeta?: TransactionMeta,\n ) => Promise;\n state?: Partial;\n testGasFeeFlows?: boolean;\n transactionHistoryLimit: number;\n hooks: {\n afterSign?: (\n transactionMeta: TransactionMeta,\n signedTx: TypedTransaction,\n ) => boolean;\n beforeCheckPendingTransaction?: (\n transactionMeta: TransactionMeta,\n ) => boolean;\n beforePublish?: (transactionMeta: TransactionMeta) => boolean;\n getAdditionalSignArguments?: (\n transactionMeta: TransactionMeta,\n ) => (TransactionMeta | undefined)[];\n publish?: (\n transactionMeta: TransactionMeta,\n ) => Promise<{ transactionHash: string }>;\n };\n};\n\n/**\n * The name of the {@link TransactionController}.\n */\nconst controllerName = 'TransactionController';\n\n/**\n * The external actions available to the {@link TransactionController}.\n */\nexport type AllowedActions =\n | AddApprovalRequest\n | NetworkControllerFindNetworkClientIdByChainIdAction\n | NetworkControllerGetNetworkClientByIdAction\n | AccountsControllerGetSelectedAccountAction;\n\n/**\n * The external events available to the {@link TransactionController}.\n */\nexport type AllowedEvents = NetworkControllerStateChangeEvent;\n\n/**\n * Represents the `TransactionController:stateChange` event.\n */\nexport type TransactionControllerStateChangeEvent = ControllerStateChangeEvent<\n typeof controllerName,\n TransactionControllerState\n>;\n\n/**\n * Represents the `TransactionController:incomingTransactionBlockReceived` event.\n */\nexport type TransactionControllerIncomingTransactionBlockReceivedEvent = {\n type: `${typeof controllerName}:incomingTransactionBlockReceived`;\n payload: [blockNumber: number];\n};\n\n/**\n * Represents the `TransactionController:postTransactionBalanceUpdated` event.\n */\nexport type TransactionControllerPostTransactionBalanceUpdatedEvent = {\n type: `${typeof controllerName}:postTransactionBalanceUpdated`;\n payload: [\n {\n transactionMeta: TransactionMeta;\n approvalTransactionMeta?: TransactionMeta;\n },\n ];\n};\n\n/**\n * Represents the `TransactionController:speedUpTransactionAdded` event.\n */\nexport type TransactionControllerSpeedupTransactionAddedEvent = {\n type: `${typeof controllerName}:speedupTransactionAdded`;\n payload: [transactionMeta: TransactionMeta];\n};\n\n/**\n * Represents the `TransactionController:transactionApproved` event.\n */\nexport type TransactionControllerTransactionApprovedEvent = {\n type: `${typeof controllerName}:transactionApproved`;\n payload: [\n {\n transactionMeta: TransactionMeta;\n actionId?: string;\n },\n ];\n};\n\n/**\n * Represents the `TransactionController:transactionConfirmed` event.\n */\nexport type TransactionControllerTransactionConfirmedEvent = {\n type: `${typeof controllerName}:transactionConfirmed`;\n payload: [transactionMeta: TransactionMeta];\n};\n\n/**\n * Represents the `TransactionController:transactionDropped` event.\n */\nexport type TransactionControllerTransactionDroppedEvent = {\n type: `${typeof controllerName}:transactionDropped`;\n payload: [{ transactionMeta: TransactionMeta }];\n};\n\n/**\n * Represents the `TransactionController:transactionFailed` event.\n */\nexport type TransactionControllerTransactionFailedEvent = {\n type: `${typeof controllerName}:transactionFailed`;\n payload: [\n {\n actionId?: string;\n error: string;\n transactionMeta: TransactionMeta;\n },\n ];\n};\n\n/**\n * Represents the `TransactionController:transactionFinished` event.\n */\nexport type TransactionControllerTransactionFinishedEvent = {\n type: `${typeof controllerName}:transactionFinished`;\n payload: [transactionMeta: TransactionMeta];\n};\n\n/**\n * Represents the `TransactionController:transactionNewSwapApproval` event.\n */\nexport type TransactionControllerTransactionNewSwapApprovalEvent = {\n type: `${typeof controllerName}:transactionNewSwapApproval`;\n payload: [{ transactionMeta: TransactionMeta }];\n};\n\n/**\n * Represents the `TransactionController:transactionNewSwap` event.\n */\nexport type TransactionControllerTransactionNewSwapEvent = {\n type: `${typeof controllerName}:transactionNewSwap`;\n payload: [{ transactionMeta: TransactionMeta }];\n};\n\n/**\n * Represents the `TransactionController:transactionNewSwapApproval` event.\n */\nexport type TransactionControllerTransactionNewSwapAndSendEvent = {\n type: `${typeof controllerName}:transactionNewSwapAndSend`;\n payload: [{ transactionMeta: TransactionMeta }];\n};\n\n/**\n * Represents the `TransactionController:transactionPublishingSkipped` event.\n */\nexport type TransactionControllerTransactionPublishingSkipped = {\n type: `${typeof controllerName}:transactionPublishingSkipped`;\n payload: [transactionMeta: TransactionMeta];\n};\n\n/**\n * Represents the `TransactionController:transactionRejected` event.\n */\nexport type TransactionControllerTransactionRejectedEvent = {\n type: `${typeof controllerName}:transactionRejected`;\n payload: [\n {\n transactionMeta: TransactionMeta;\n actionId?: string;\n },\n ];\n};\n\n/**\n * Represents the `TransactionController:transactionStatusUpdated` event.\n */\nexport type TransactionControllerTransactionStatusUpdatedEvent = {\n type: `${typeof controllerName}:transactionStatusUpdated`;\n payload: [\n {\n transactionMeta: TransactionMeta;\n },\n ];\n};\n\n/**\n * Represents the `TransactionController:transactionSubmitted` event.\n */\nexport type TransactionControllerTransactionSubmittedEvent = {\n type: `${typeof controllerName}:transactionSubmitted`;\n payload: [\n {\n transactionMeta: TransactionMeta;\n actionId?: string;\n },\n ];\n};\n\n/**\n * Represents the `TransactionController:unapprovedTransactionAdded` event.\n */\nexport type TransactionControllerUnapprovedTransactionAddedEvent = {\n type: `${typeof controllerName}:unapprovedTransactionAdded`;\n payload: [transactionMeta: TransactionMeta];\n};\n\n/**\n * The internal events available to the {@link TransactionController}.\n */\nexport type TransactionControllerEvents =\n | TransactionControllerIncomingTransactionBlockReceivedEvent\n | TransactionControllerPostTransactionBalanceUpdatedEvent\n | TransactionControllerSpeedupTransactionAddedEvent\n | TransactionControllerStateChangeEvent\n | TransactionControllerTransactionApprovedEvent\n | TransactionControllerTransactionConfirmedEvent\n | TransactionControllerTransactionDroppedEvent\n | TransactionControllerTransactionFailedEvent\n | TransactionControllerTransactionFinishedEvent\n | TransactionControllerTransactionNewSwapApprovalEvent\n | TransactionControllerTransactionNewSwapEvent\n | TransactionControllerTransactionNewSwapAndSendEvent\n | TransactionControllerTransactionPublishingSkipped\n | TransactionControllerTransactionRejectedEvent\n | TransactionControllerTransactionStatusUpdatedEvent\n | TransactionControllerTransactionSubmittedEvent\n | TransactionControllerUnapprovedTransactionAddedEvent;\n\n/**\n * The messenger of the {@link TransactionController}.\n */\nexport type TransactionControllerMessenger = RestrictedControllerMessenger<\n typeof controllerName,\n TransactionControllerActions | AllowedActions,\n TransactionControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\n/**\n * Possible states of the approve transaction step.\n */\nexport enum ApprovalState {\n Approved = 'approved',\n NotApproved = 'not-approved',\n SkippedViaBeforePublishHook = 'skipped-via-before-publish-hook',\n}\n\n/**\n * Get the default TransactionsController state.\n *\n * @returns The default TransactionsController state.\n */\nfunction getDefaultTransactionControllerState(): TransactionControllerState {\n return {\n methodData: {},\n transactions: [],\n lastFetchedBlockNumbers: {},\n };\n}\n\n/**\n * Controller responsible for submitting and managing transactions.\n */\nexport class TransactionController extends BaseController<\n typeof controllerName,\n TransactionControllerState,\n TransactionControllerMessenger\n> {\n #internalEvents = new EventEmitter();\n\n private readonly isHistoryDisabled: boolean;\n\n private readonly isSwapsDisabled: boolean;\n\n private readonly isSendFlowHistoryDisabled: boolean;\n\n private readonly approvingTransactionIds: Set = new Set();\n\n private readonly nonceTracker: NonceTracker;\n\n private readonly registry: MethodRegistry;\n\n private readonly mutex = new Mutex();\n\n private readonly gasFeeFlows: GasFeeFlow[];\n\n private readonly getSavedGasFees: (chainId: Hex) => SavedGasFees | undefined;\n\n private readonly getNetworkState: () => NetworkState;\n\n private readonly getCurrentAccountEIP1559Compatibility: () => Promise;\n\n private readonly getCurrentNetworkEIP1559Compatibility: (\n networkClientId?: NetworkClientId,\n ) => Promise;\n\n private readonly getGasFeeEstimates: (\n options: FetchGasFeeEstimateOptions,\n ) => Promise;\n\n private readonly getPermittedAccounts: (origin?: string) => Promise;\n\n private readonly getExternalPendingTransactions: (\n address: string,\n chainId?: string,\n ) => NonceTrackerTransaction[];\n\n private readonly layer1GasFeeFlows: Layer1GasFeeFlow[];\n\n readonly #incomingTransactionOptions: IncomingTransactionOptions;\n\n private readonly incomingTransactionHelper: IncomingTransactionHelper;\n\n private readonly securityProviderRequest?: SecurityProviderRequest;\n\n readonly #pendingTransactionOptions: PendingTransactionOptions;\n\n private readonly pendingTransactionTracker: PendingTransactionTracker;\n\n private readonly signAbortCallbacks: Map void> = new Map();\n\n #transactionHistoryLimit: number;\n\n #isSimulationEnabled: () => boolean;\n\n #testGasFeeFlows: boolean;\n\n private readonly afterSign: (\n transactionMeta: TransactionMeta,\n signedTx: TypedTransaction,\n ) => boolean;\n\n private readonly beforeCheckPendingTransaction: (\n transactionMeta: TransactionMeta,\n ) => boolean;\n\n private readonly beforePublish: (transactionMeta: TransactionMeta) => boolean;\n\n private readonly publish: (\n transactionMeta: TransactionMeta,\n rawTx: string,\n ) => Promise<{ transactionHash?: string }>;\n\n private readonly getAdditionalSignArguments: (\n transactionMeta: TransactionMeta,\n ) => (TransactionMeta | undefined)[];\n\n private failTransaction(\n transactionMeta: TransactionMeta,\n error: Error,\n actionId?: string,\n ) {\n let newTransactionMeta: TransactionMeta;\n\n try {\n newTransactionMeta = this.#updateTransactionInternal(\n {\n transactionId: transactionMeta.id,\n note: 'TransactionController#failTransaction - Add error message and set status to failed',\n skipValidation: true,\n },\n (draftTransactionMeta) => {\n draftTransactionMeta.status = TransactionStatus.failed;\n\n (\n draftTransactionMeta as TransactionMeta & {\n status: TransactionStatus.failed;\n }\n ).error = normalizeTxError(error);\n },\n );\n } catch (err: unknown) {\n log('Failed to mark transaction as failed', err);\n\n newTransactionMeta = {\n ...transactionMeta,\n status: TransactionStatus.failed,\n error: normalizeTxError(error),\n };\n }\n\n this.messagingSystem.publish(`${controllerName}:transactionFailed`, {\n actionId,\n error: error.message,\n transactionMeta: newTransactionMeta,\n });\n\n this.onTransactionStatusChange(newTransactionMeta);\n\n this.messagingSystem.publish(\n `${controllerName}:transactionFinished`,\n newTransactionMeta,\n );\n\n this.#internalEvents.emit(\n `${transactionMeta.id}:finished`,\n newTransactionMeta,\n );\n }\n\n private async registryLookup(fourBytePrefix: string): Promise {\n const registryMethod = await this.registry.lookup(fourBytePrefix);\n if (!registryMethod) {\n return {\n registryMethod: '',\n parsedRegistryMethod: { name: undefined, args: undefined },\n };\n }\n const parsedRegistryMethod = this.registry.parse(registryMethod);\n return { registryMethod, parsedRegistryMethod };\n }\n\n #multichainTrackingHelper: MultichainTrackingHelper;\n\n /**\n * Method used to sign transactions\n */\n sign?: (\n transaction: TypedTransaction,\n from: string,\n transactionMeta?: TransactionMeta,\n ) => Promise;\n\n /**\n * Constructs a TransactionController.\n *\n * @param options - The controller options.\n * @param options.blockTracker - The block tracker used to poll for new blocks data.\n * @param options.disableHistory - Whether to disable storing history in transaction metadata.\n * @param options.disableSendFlowHistory - Explicitly disable transaction metadata history.\n * @param options.disableSwaps - Whether to disable additional processing on swaps transactions.\n * @param options.getCurrentAccountEIP1559Compatibility - Whether or not the account supports EIP-1559.\n * @param options.getCurrentNetworkEIP1559Compatibility - Whether or not the network supports EIP-1559.\n * @param options.getExternalPendingTransactions - Callback to retrieve pending transactions from external sources.\n * @param options.getGasFeeEstimates - Callback to retrieve gas fee estimates.\n * @param options.getNetworkClientRegistry - Gets the network client registry.\n * @param options.getNetworkState - Gets the state of the network controller.\n * @param options.getPermittedAccounts - Get accounts that a given origin has permissions for.\n * @param options.getSavedGasFees - Gets the saved gas fee config.\n * @param options.incomingTransactions - Configuration options for incoming transaction support.\n * @param options.isMultichainEnabled - Enable multichain support.\n * @param options.isSimulationEnabled - Whether new transactions will be automatically simulated.\n * @param options.messenger - The controller messenger.\n * @param options.onNetworkStateChange - Allows subscribing to network controller state changes.\n * @param options.pendingTransactions - Configuration options for pending transaction support.\n * @param options.provider - The provider used to create the underlying EthQuery instance.\n * @param options.securityProviderRequest - A function for verifying a transaction, whether it is malicious or not.\n * @param options.sign - Function used to sign transactions.\n * @param options.state - Initial state to set on this controller.\n * @param options.testGasFeeFlows - Whether to use the test gas fee flow.\n * @param options.transactionHistoryLimit - Transaction history limit.\n * @param options.hooks - The controller hooks.\n */\n constructor({\n blockTracker,\n disableHistory,\n disableSendFlowHistory,\n disableSwaps,\n getCurrentAccountEIP1559Compatibility,\n getCurrentNetworkEIP1559Compatibility,\n getExternalPendingTransactions,\n getGasFeeEstimates,\n getNetworkClientRegistry,\n getNetworkState,\n getPermittedAccounts,\n getSavedGasFees,\n incomingTransactions = {},\n isMultichainEnabled = false,\n isSimulationEnabled,\n messenger,\n onNetworkStateChange,\n pendingTransactions = {},\n provider,\n securityProviderRequest,\n sign,\n state,\n testGasFeeFlows,\n transactionHistoryLimit = 40,\n hooks,\n }: TransactionControllerOptions) {\n super({\n name: controllerName,\n metadata,\n messenger,\n state: {\n ...getDefaultTransactionControllerState(),\n ...state,\n },\n });\n\n this.messagingSystem = messenger;\n this.getNetworkState = getNetworkState;\n this.isSendFlowHistoryDisabled = disableSendFlowHistory ?? false;\n this.isHistoryDisabled = disableHistory ?? false;\n this.isSwapsDisabled = disableSwaps ?? false;\n this.#isSimulationEnabled = isSimulationEnabled ?? (() => true);\n // @ts-expect-error the type in eth-method-registry is inappropriate and should be changed\n this.registry = new MethodRegistry({ provider });\n this.getSavedGasFees = getSavedGasFees ?? ((_chainId) => undefined);\n this.getCurrentAccountEIP1559Compatibility =\n getCurrentAccountEIP1559Compatibility ?? (() => Promise.resolve(true));\n this.getCurrentNetworkEIP1559Compatibility =\n getCurrentNetworkEIP1559Compatibility;\n this.getGasFeeEstimates =\n getGasFeeEstimates || (() => Promise.resolve({} as GasFeeState));\n this.getPermittedAccounts = getPermittedAccounts;\n this.getExternalPendingTransactions =\n getExternalPendingTransactions ?? (() => []);\n this.securityProviderRequest = securityProviderRequest;\n this.#incomingTransactionOptions = incomingTransactions;\n this.#pendingTransactionOptions = pendingTransactions;\n this.#transactionHistoryLimit = transactionHistoryLimit;\n this.sign = sign;\n this.#testGasFeeFlows = testGasFeeFlows === true;\n\n this.afterSign = hooks?.afterSign ?? (() => true);\n this.beforeCheckPendingTransaction =\n hooks?.beforeCheckPendingTransaction ??\n /* istanbul ignore next */\n (() => true);\n this.beforePublish = hooks?.beforePublish ?? (() => true);\n this.getAdditionalSignArguments =\n hooks?.getAdditionalSignArguments ?? (() => []);\n this.publish =\n hooks?.publish ?? (() => Promise.resolve({ transactionHash: undefined }));\n\n this.nonceTracker = this.#createNonceTracker({\n provider,\n blockTracker,\n });\n\n const findNetworkClientIdByChainId = (chainId: Hex) => {\n return this.messagingSystem.call(\n `NetworkController:findNetworkClientIdByChainId`,\n chainId,\n );\n };\n\n this.#multichainTrackingHelper = new MultichainTrackingHelper({\n isMultichainEnabled,\n provider,\n nonceTracker: this.nonceTracker,\n incomingTransactionOptions: incomingTransactions,\n findNetworkClientIdByChainId,\n getNetworkClientById: ((networkClientId: NetworkClientId) => {\n return this.messagingSystem.call(\n `NetworkController:getNetworkClientById`,\n networkClientId,\n );\n }) as NetworkController['getNetworkClientById'],\n getNetworkClientRegistry,\n removeIncomingTransactionHelperListeners:\n this.#removeIncomingTransactionHelperListeners.bind(this),\n removePendingTransactionTrackerListeners:\n this.#removePendingTransactionTrackerListeners.bind(this),\n createNonceTracker: this.#createNonceTracker.bind(this),\n createIncomingTransactionHelper:\n this.#createIncomingTransactionHelper.bind(this),\n createPendingTransactionTracker:\n this.#createPendingTransactionTracker.bind(this),\n onNetworkStateChange: (listener) => {\n this.messagingSystem.subscribe(\n 'NetworkController:stateChange',\n listener,\n );\n },\n });\n this.#multichainTrackingHelper.initialize();\n\n const etherscanRemoteTransactionSource =\n new EtherscanRemoteTransactionSource({\n includeTokenTransfers: incomingTransactions.includeTokenTransfers,\n });\n\n this.incomingTransactionHelper = this.#createIncomingTransactionHelper({\n blockTracker,\n etherscanRemoteTransactionSource,\n });\n\n this.pendingTransactionTracker = this.#createPendingTransactionTracker({\n provider,\n blockTracker,\n });\n\n this.gasFeeFlows = this.#getGasFeeFlows();\n this.layer1GasFeeFlows = this.#getLayer1GasFeeFlows();\n\n const gasFeePoller = new GasFeePoller({\n findNetworkClientIdByChainId,\n gasFeeFlows: this.gasFeeFlows,\n getGasFeeControllerEstimates: this.getGasFeeEstimates,\n getProvider: (chainId, networkClientId) =>\n this.#multichainTrackingHelper.getProvider({\n networkClientId,\n chainId,\n }),\n getTransactions: () => this.state.transactions,\n layer1GasFeeFlows: this.layer1GasFeeFlows,\n onStateChange: (listener) => {\n this.messagingSystem.subscribe(\n 'TransactionController:stateChange',\n listener,\n );\n },\n });\n\n gasFeePoller.hub.on(\n 'transaction-updated',\n this.#onGasFeePollerTransactionUpdate.bind(this),\n );\n\n // when transactionsController state changes\n // check for pending transactions and start polling if there are any\n this.messagingSystem.subscribe(\n 'TransactionController:stateChange',\n this.#checkForPendingTransactionAndStartPolling,\n );\n\n // TODO once v2 is merged make sure this only runs when\n // selectedNetworkClientId changes\n onNetworkStateChange(() => {\n log('Detected network change', this.getChainId());\n this.pendingTransactionTracker.startIfPendingTransactions();\n this.onBootCleanup();\n });\n\n this.onBootCleanup();\n this.#checkForPendingTransactionAndStartPolling();\n }\n\n /**\n * Stops polling and removes listeners to prepare the controller for garbage collection.\n */\n destroy() {\n this.#stopAllTracking();\n }\n\n /**\n * Handle new method data request.\n *\n * @param fourBytePrefix - The method prefix.\n * @returns The method data object corresponding to the given signature prefix.\n */\n async handleMethodData(fourBytePrefix: string): Promise {\n const releaseLock = await this.mutex.acquire();\n try {\n const { methodData } = this.state;\n const knownMethod = Object.keys(methodData).find(\n (knownFourBytePrefix) => fourBytePrefix === knownFourBytePrefix,\n );\n if (knownMethod) {\n return methodData[fourBytePrefix];\n }\n const registry = await this.registryLookup(fourBytePrefix);\n this.update((state) => {\n state.methodData[fourBytePrefix] = registry;\n });\n return registry;\n } finally {\n releaseLock();\n }\n }\n\n /**\n * Add a new unapproved transaction to state. Parameters will be validated, a\n * unique transaction id will be generated, and gas and gasPrice will be calculated\n * if not provided. If A `:unapproved` hub event will be emitted once added.\n *\n * @param txParams - Standard parameters for an Ethereum transaction.\n * @param opts - Additional options to control how the transaction is added.\n * @param opts.actionId - Unique ID to prevent duplicate requests.\n * @param opts.deviceConfirmedOn - An enum to indicate what device confirmed the transaction.\n * @param opts.method - RPC method that requested the transaction.\n * @param opts.origin - The origin of the transaction request, such as a dApp hostname.\n * @param opts.requireApproval - Whether the transaction requires approval by the user, defaults to true unless explicitly disabled.\n * @param opts.securityAlertResponse - Response from security validator.\n * @param opts.sendFlowHistory - The sendFlowHistory entries to add.\n * @param opts.type - Type of transaction to add, such as 'cancel' or 'swap'.\n * @param opts.swaps - Options for swaps transactions.\n * @param opts.swaps.hasApproveTx - Whether the transaction has an approval transaction.\n * @param opts.swaps.meta - Metadata for swap transaction.\n * @param opts.networkClientId - The id of the network client for this transaction.\n * @returns Object containing a promise resolving to the transaction hash if approved.\n */\n async addTransaction(\n txParams: TransactionParams,\n {\n actionId,\n deviceConfirmedOn,\n method,\n origin,\n requireApproval,\n securityAlertResponse,\n sendFlowHistory,\n swaps = {},\n type,\n networkClientId: requestNetworkClientId,\n }: {\n actionId?: string;\n deviceConfirmedOn?: WalletDevice;\n method?: string;\n origin?: string;\n requireApproval?: boolean | undefined;\n securityAlertResponse?: SecurityAlertResponse;\n sendFlowHistory?: SendFlowHistoryEntry[];\n swaps?: {\n hasApproveTx?: boolean;\n meta?: Partial;\n };\n type?: TransactionType;\n networkClientId?: NetworkClientId;\n } = {},\n ): Promise {\n log('Adding transaction', txParams);\n\n txParams = normalizeTransactionParams(txParams);\n if (\n requestNetworkClientId &&\n !this.#multichainTrackingHelper.has(requestNetworkClientId)\n ) {\n throw new Error(\n 'The networkClientId for this transaction could not be found',\n );\n }\n\n const networkClientId =\n requestNetworkClientId ?? this.#getGlobalNetworkClientId();\n\n const isEIP1559Compatible = await this.getEIP1559Compatibility(\n networkClientId,\n );\n\n validateTxParams(txParams, isEIP1559Compatible);\n\n if (origin) {\n await validateTransactionOrigin(\n await this.getPermittedAccounts(origin),\n this.#getSelectedAccount().address,\n txParams.from,\n origin,\n );\n }\n\n const dappSuggestedGasFees = this.generateDappSuggestedGasFees(\n txParams,\n origin,\n );\n\n const chainId = this.getChainId(networkClientId);\n const ethQuery = this.#multichainTrackingHelper.getEthQuery({\n networkClientId,\n chainId,\n });\n\n const transactionType =\n type ?? (await determineTransactionType(txParams, ethQuery)).type;\n\n const existingTransactionMeta = this.getTransactionWithActionId(actionId);\n\n // If a request to add a transaction with the same actionId is submitted again, a new transaction will not be created for it.\n let addedTransactionMeta = existingTransactionMeta\n ? cloneDeep(existingTransactionMeta)\n : {\n // Add actionId to txMeta to check if same actionId is seen again\n actionId,\n chainId,\n dappSuggestedGasFees,\n deviceConfirmedOn,\n id: random(),\n origin,\n securityAlertResponse,\n status: TransactionStatus.unapproved as const,\n time: Date.now(),\n txParams,\n userEditedGasLimit: false,\n verifiedOnBlockchain: false,\n type: transactionType,\n networkClientId,\n };\n\n await this.updateGasProperties(addedTransactionMeta);\n\n // Checks if a transaction already exists with a given actionId\n if (!existingTransactionMeta) {\n // Set security provider response\n if (method && this.securityProviderRequest) {\n const securityProviderResponse = await this.securityProviderRequest(\n addedTransactionMeta,\n method,\n );\n addedTransactionMeta.securityProviderResponse =\n securityProviderResponse;\n }\n\n if (!this.isSendFlowHistoryDisabled) {\n addedTransactionMeta.sendFlowHistory = sendFlowHistory ?? [];\n }\n // Initial history push\n if (!this.isHistoryDisabled) {\n addedTransactionMeta = addInitialHistorySnapshot(addedTransactionMeta);\n }\n\n addedTransactionMeta = updateSwapsTransaction(\n addedTransactionMeta,\n transactionType,\n swaps,\n {\n isSwapsDisabled: this.isSwapsDisabled,\n cancelTransaction: this.cancelTransaction.bind(this),\n messenger: this.messagingSystem,\n },\n );\n\n this.addMetadata(addedTransactionMeta);\n\n if (requireApproval !== false) {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.#updateSimulationData(addedTransactionMeta);\n } else {\n log('Skipping simulation as approval not required');\n }\n\n this.messagingSystem.publish(\n `${controllerName}:unapprovedTransactionAdded`,\n addedTransactionMeta,\n );\n }\n\n return {\n result: this.processApproval(addedTransactionMeta, {\n isExisting: Boolean(existingTransactionMeta),\n requireApproval,\n actionId,\n }),\n transactionMeta: addedTransactionMeta,\n };\n }\n\n startIncomingTransactionPolling(networkClientIds: NetworkClientId[] = []) {\n if (networkClientIds.length === 0) {\n this.incomingTransactionHelper.start();\n return;\n }\n this.#multichainTrackingHelper.startIncomingTransactionPolling(\n networkClientIds,\n );\n }\n\n stopIncomingTransactionPolling(networkClientIds: NetworkClientId[] = []) {\n if (networkClientIds.length === 0) {\n this.incomingTransactionHelper.stop();\n return;\n }\n this.#multichainTrackingHelper.stopIncomingTransactionPolling(\n networkClientIds,\n );\n }\n\n stopAllIncomingTransactionPolling() {\n this.incomingTransactionHelper.stop();\n this.#multichainTrackingHelper.stopAllIncomingTransactionPolling();\n }\n\n async updateIncomingTransactions(networkClientIds: NetworkClientId[] = []) {\n if (networkClientIds.length === 0) {\n await this.incomingTransactionHelper.update();\n return;\n }\n await this.#multichainTrackingHelper.updateIncomingTransactions(\n networkClientIds,\n );\n }\n\n /**\n * Attempts to cancel a transaction based on its ID by setting its status to \"rejected\"\n * and emitting a `:finished` hub event.\n *\n * @param transactionId - The ID of the transaction to cancel.\n * @param gasValues - The gas values to use for the cancellation transaction.\n * @param options - The options for the cancellation transaction.\n * @param options.actionId - Unique ID to prevent duplicate requests.\n * @param options.estimatedBaseFee - The estimated base fee of the transaction.\n */\n async stopTransaction(\n transactionId: string,\n gasValues?: GasPriceValue | FeeMarketEIP1559Values,\n {\n estimatedBaseFee,\n actionId,\n }: { estimatedBaseFee?: string; actionId?: string } = {},\n ) {\n // If transaction is found for same action id, do not create a cancel transaction.\n if (this.getTransactionWithActionId(actionId)) {\n return;\n }\n\n if (gasValues) {\n // Not good practice to reassign a parameter but temporarily avoiding a larger refactor.\n gasValues = normalizeGasFeeValues(gasValues);\n validateGasValues(gasValues);\n }\n\n log('Creating cancel transaction', transactionId, gasValues);\n\n const transactionMeta = this.getTransaction(transactionId);\n if (!transactionMeta) {\n return;\n }\n\n if (!this.sign) {\n throw new Error('No sign method defined.');\n }\n\n // gasPrice (legacy non EIP1559)\n const minGasPrice = getIncreasedPriceFromExisting(\n transactionMeta.txParams.gasPrice,\n CANCEL_RATE,\n );\n\n const gasPriceFromValues = isGasPriceValue(gasValues) && gasValues.gasPrice;\n\n const newGasPrice =\n (gasPriceFromValues &&\n validateMinimumIncrease(gasPriceFromValues, minGasPrice)) ||\n minGasPrice;\n\n // maxFeePerGas (EIP1559)\n const existingMaxFeePerGas = transactionMeta.txParams?.maxFeePerGas;\n const minMaxFeePerGas = getIncreasedPriceFromExisting(\n existingMaxFeePerGas,\n CANCEL_RATE,\n );\n const maxFeePerGasValues =\n isFeeMarketEIP1559Values(gasValues) && gasValues.maxFeePerGas;\n const newMaxFeePerGas =\n (maxFeePerGasValues &&\n validateMinimumIncrease(maxFeePerGasValues, minMaxFeePerGas)) ||\n (existingMaxFeePerGas && minMaxFeePerGas);\n\n // maxPriorityFeePerGas (EIP1559)\n const existingMaxPriorityFeePerGas =\n transactionMeta.txParams?.maxPriorityFeePerGas;\n const minMaxPriorityFeePerGas = getIncreasedPriceFromExisting(\n existingMaxPriorityFeePerGas,\n CANCEL_RATE,\n );\n const maxPriorityFeePerGasValues =\n isFeeMarketEIP1559Values(gasValues) && gasValues.maxPriorityFeePerGas;\n const newMaxPriorityFeePerGas =\n (maxPriorityFeePerGasValues &&\n validateMinimumIncrease(\n maxPriorityFeePerGasValues,\n minMaxPriorityFeePerGas,\n )) ||\n (existingMaxPriorityFeePerGas && minMaxPriorityFeePerGas);\n\n const newTxParams: TransactionParams =\n newMaxFeePerGas && newMaxPriorityFeePerGas\n ? {\n from: transactionMeta.txParams.from,\n gasLimit: transactionMeta.txParams.gas,\n maxFeePerGas: newMaxFeePerGas,\n maxPriorityFeePerGas: newMaxPriorityFeePerGas,\n type: TransactionEnvelopeType.feeMarket,\n nonce: transactionMeta.txParams.nonce,\n to: transactionMeta.txParams.from,\n value: '0x0',\n }\n : {\n from: transactionMeta.txParams.from,\n gasLimit: transactionMeta.txParams.gas,\n gasPrice: newGasPrice,\n nonce: transactionMeta.txParams.nonce,\n to: transactionMeta.txParams.from,\n value: '0x0',\n };\n\n const unsignedEthTx = this.prepareUnsignedEthTx(\n transactionMeta.chainId,\n newTxParams,\n );\n\n const signedTx = await this.sign(\n unsignedEthTx,\n transactionMeta.txParams.from,\n );\n\n const rawTx = bufferToHex(signedTx.serialize());\n\n const newFee = newTxParams.maxFeePerGas ?? newTxParams.gasPrice;\n\n const oldFee = newTxParams.maxFeePerGas\n ? transactionMeta.txParams.maxFeePerGas\n : transactionMeta.txParams.gasPrice;\n\n log('Submitting cancel transaction', {\n oldFee,\n newFee,\n txParams: newTxParams,\n });\n\n const ethQuery = this.#multichainTrackingHelper.getEthQuery({\n networkClientId: transactionMeta.networkClientId,\n chainId: transactionMeta.chainId,\n });\n const hash = await this.publishTransactionForRetry(\n ethQuery,\n rawTx,\n transactionMeta,\n );\n\n const cancelTransactionMeta = {\n actionId,\n chainId: transactionMeta.chainId,\n networkClientId: transactionMeta.networkClientId,\n estimatedBaseFee,\n hash,\n id: random(),\n originalGasEstimate: transactionMeta.txParams.gas,\n rawTx,\n status: TransactionStatus.submitted as const,\n time: Date.now(),\n type: TransactionType.cancel as const,\n txParams: newTxParams,\n };\n\n this.addMetadata(cancelTransactionMeta);\n\n // stopTransaction has no approval request, so we assume the user has already approved the transaction\n this.messagingSystem.publish(`${controllerName}:transactionApproved`, {\n transactionMeta: cancelTransactionMeta,\n actionId,\n });\n this.messagingSystem.publish(`${controllerName}:transactionSubmitted`, {\n transactionMeta: cancelTransactionMeta,\n actionId,\n });\n\n this.messagingSystem.publish(\n `${controllerName}:transactionFinished`,\n cancelTransactionMeta,\n );\n this.#internalEvents.emit(\n `${transactionMeta.id}:finished`,\n cancelTransactionMeta,\n );\n }\n\n /**\n * Attempts to speed up a transaction increasing transaction gasPrice by ten percent.\n *\n * @param transactionId - The ID of the transaction to speed up.\n * @param gasValues - The gas values to use for the speed up transaction.\n * @param options - The options for the speed up transaction.\n * @param options.actionId - Unique ID to prevent duplicate requests\n * @param options.estimatedBaseFee - The estimated base fee of the transaction.\n */\n async speedUpTransaction(\n transactionId: string,\n gasValues?: GasPriceValue | FeeMarketEIP1559Values,\n {\n actionId,\n estimatedBaseFee,\n }: { actionId?: string; estimatedBaseFee?: string } = {},\n ) {\n // If transaction is found for same action id, do not create a new speed up transaction.\n if (this.getTransactionWithActionId(actionId)) {\n return;\n }\n\n if (gasValues) {\n // Not good practice to reassign a parameter but temporarily avoiding a larger refactor.\n gasValues = normalizeGasFeeValues(gasValues);\n validateGasValues(gasValues);\n }\n\n log('Creating speed up transaction', transactionId, gasValues);\n\n const transactionMeta = this.getTransaction(transactionId);\n /* istanbul ignore next */\n if (!transactionMeta) {\n return;\n }\n\n /* istanbul ignore next */\n if (!this.sign) {\n throw new Error('No sign method defined.');\n }\n\n // gasPrice (legacy non EIP1559)\n const minGasPrice = getIncreasedPriceFromExisting(\n transactionMeta.txParams.gasPrice,\n SPEED_UP_RATE,\n );\n\n const gasPriceFromValues = isGasPriceValue(gasValues) && gasValues.gasPrice;\n\n const newGasPrice =\n (gasPriceFromValues &&\n validateMinimumIncrease(gasPriceFromValues, minGasPrice)) ||\n minGasPrice;\n\n // maxFeePerGas (EIP1559)\n const existingMaxFeePerGas = transactionMeta.txParams?.maxFeePerGas;\n const minMaxFeePerGas = getIncreasedPriceFromExisting(\n existingMaxFeePerGas,\n SPEED_UP_RATE,\n );\n const maxFeePerGasValues =\n isFeeMarketEIP1559Values(gasValues) && gasValues.maxFeePerGas;\n const newMaxFeePerGas =\n (maxFeePerGasValues &&\n validateMinimumIncrease(maxFeePerGasValues, minMaxFeePerGas)) ||\n (existingMaxFeePerGas && minMaxFeePerGas);\n\n // maxPriorityFeePerGas (EIP1559)\n const existingMaxPriorityFeePerGas =\n transactionMeta.txParams?.maxPriorityFeePerGas;\n const minMaxPriorityFeePerGas = getIncreasedPriceFromExisting(\n existingMaxPriorityFeePerGas,\n SPEED_UP_RATE,\n );\n const maxPriorityFeePerGasValues =\n isFeeMarketEIP1559Values(gasValues) && gasValues.maxPriorityFeePerGas;\n const newMaxPriorityFeePerGas =\n (maxPriorityFeePerGasValues &&\n validateMinimumIncrease(\n maxPriorityFeePerGasValues,\n minMaxPriorityFeePerGas,\n )) ||\n (existingMaxPriorityFeePerGas && minMaxPriorityFeePerGas);\n\n const txParams: TransactionParams =\n newMaxFeePerGas && newMaxPriorityFeePerGas\n ? {\n ...transactionMeta.txParams,\n gasLimit: transactionMeta.txParams.gas,\n maxFeePerGas: newMaxFeePerGas,\n maxPriorityFeePerGas: newMaxPriorityFeePerGas,\n type: TransactionEnvelopeType.feeMarket,\n }\n : {\n ...transactionMeta.txParams,\n gasLimit: transactionMeta.txParams.gas,\n gasPrice: newGasPrice,\n };\n\n const unsignedEthTx = this.prepareUnsignedEthTx(\n transactionMeta.chainId,\n txParams,\n );\n\n const signedTx = await this.sign(\n unsignedEthTx,\n transactionMeta.txParams.from,\n );\n\n const transactionMetaWithRsv = this.updateTransactionMetaRSV(\n transactionMeta,\n signedTx,\n );\n const rawTx = bufferToHex(signedTx.serialize());\n\n const newFee = txParams.maxFeePerGas ?? txParams.gasPrice;\n\n const oldFee = txParams.maxFeePerGas\n ? transactionMetaWithRsv.txParams.maxFeePerGas\n : transactionMetaWithRsv.txParams.gasPrice;\n\n log('Submitting speed up transaction', { oldFee, newFee, txParams });\n\n const ethQuery = this.#multichainTrackingHelper.getEthQuery({\n networkClientId: transactionMeta.networkClientId,\n chainId: transactionMeta.chainId,\n });\n const hash = await this.publishTransactionForRetry(\n ethQuery,\n rawTx,\n transactionMeta,\n );\n\n const baseTransactionMeta = {\n ...transactionMetaWithRsv,\n estimatedBaseFee,\n id: random(),\n time: Date.now(),\n hash,\n actionId,\n originalGasEstimate: transactionMeta.txParams.gas,\n type: TransactionType.retry as const,\n originalType: transactionMeta.type,\n };\n\n const newTransactionMeta =\n newMaxFeePerGas && newMaxPriorityFeePerGas\n ? {\n ...baseTransactionMeta,\n txParams: {\n ...transactionMeta.txParams,\n maxFeePerGas: newMaxFeePerGas,\n maxPriorityFeePerGas: newMaxPriorityFeePerGas,\n },\n }\n : {\n ...baseTransactionMeta,\n txParams: {\n ...transactionMeta.txParams,\n gasPrice: newGasPrice,\n },\n };\n\n this.addMetadata(newTransactionMeta);\n\n // speedUpTransaction has no approval request, so we assume the user has already approved the transaction\n this.messagingSystem.publish(`${controllerName}:transactionApproved`, {\n transactionMeta: newTransactionMeta,\n actionId,\n });\n\n this.messagingSystem.publish(`${controllerName}:transactionSubmitted`, {\n transactionMeta: newTransactionMeta,\n actionId,\n });\n\n this.messagingSystem.publish(\n `${controllerName}:speedupTransactionAdded`,\n newTransactionMeta,\n );\n }\n\n /**\n * Estimates required gas for a given transaction.\n *\n * @param transaction - The transaction to estimate gas for.\n * @param networkClientId - The network client id to use for the estimate.\n * @returns The gas and gas price.\n */\n async estimateGas(\n transaction: TransactionParams,\n networkClientId?: NetworkClientId,\n ) {\n const ethQuery = this.#multichainTrackingHelper.getEthQuery({\n networkClientId,\n });\n const { estimatedGas, simulationFails } = await estimateGas(\n transaction,\n ethQuery,\n );\n\n return { gas: estimatedGas, simulationFails };\n }\n\n /**\n * Estimates required gas for a given transaction and add additional gas buffer with the given multiplier.\n *\n * @param transaction - The transaction params to estimate gas for.\n * @param multiplier - The multiplier to use for the gas buffer.\n * @param networkClientId - The network client id to use for the estimate.\n */\n async estimateGasBuffered(\n transaction: TransactionParams,\n multiplier: number,\n networkClientId?: NetworkClientId,\n ) {\n const ethQuery = this.#multichainTrackingHelper.getEthQuery({\n networkClientId,\n });\n const { blockGasLimit, estimatedGas, simulationFails } = await estimateGas(\n transaction,\n ethQuery,\n );\n\n const gas = addGasBuffer(estimatedGas, blockGasLimit, multiplier);\n\n return {\n gas,\n simulationFails,\n };\n }\n\n /**\n * Updates an existing transaction in state.\n *\n * @param transactionMeta - The new transaction to store in state.\n * @param note - A note or update reason to include in the transaction history.\n */\n updateTransaction(transactionMeta: TransactionMeta, note: string) {\n const { id: transactionId } = transactionMeta;\n\n this.#updateTransactionInternal({ transactionId, note }, () => ({\n ...transactionMeta,\n }));\n }\n\n /**\n * Update the security alert response for a transaction.\n *\n * @param transactionId - ID of the transaction.\n * @param securityAlertResponse - The new security alert response for the transaction.\n */\n updateSecurityAlertResponse(\n transactionId: string,\n securityAlertResponse: SecurityAlertResponse,\n ) {\n if (!securityAlertResponse) {\n throw new Error(\n 'updateSecurityAlertResponse: securityAlertResponse should not be null',\n );\n }\n const transactionMeta = this.getTransaction(transactionId);\n if (!transactionMeta) {\n throw new Error(\n `Cannot update security alert response as no transaction metadata found`,\n );\n }\n const updatedTransactionMeta = {\n ...transactionMeta,\n securityAlertResponse,\n };\n this.updateTransaction(\n updatedTransactionMeta,\n `${controllerName}:updatesecurityAlertResponse - securityAlertResponse updated`,\n );\n }\n\n /**\n * Removes all transactions from state, optionally based on the current network.\n *\n * @param ignoreNetwork - Determines whether to wipe all transactions, or just those on the\n * current network. If `true`, all transactions are wiped.\n * @param address - If specified, only transactions originating from this address will be\n * wiped on current network.\n */\n wipeTransactions(ignoreNetwork?: boolean, address?: string) {\n /* istanbul ignore next */\n if (ignoreNetwork && !address) {\n this.update((state) => {\n state.transactions = [];\n });\n return;\n }\n const currentChainId = this.getChainId();\n const newTransactions = this.state.transactions.filter(\n ({ chainId, txParams }) => {\n const isMatchingNetwork = ignoreNetwork || chainId === currentChainId;\n\n if (!isMatchingNetwork) {\n return true;\n }\n\n const isMatchingAddress =\n !address || txParams.from?.toLowerCase() === address.toLowerCase();\n\n return !isMatchingAddress;\n },\n );\n\n this.update((state) => {\n state.transactions = this.trimTransactionsForState(newTransactions);\n });\n }\n\n /**\n * Adds external provided transaction to state as confirmed transaction.\n *\n * @param transactionMeta - TransactionMeta to add transactions.\n * @param transactionReceipt - TransactionReceipt of the external transaction.\n * @param baseFeePerGas - Base fee per gas of the external transaction.\n */\n async confirmExternalTransaction(\n transactionMeta: TransactionMeta,\n transactionReceipt: TransactionReceipt,\n baseFeePerGas: Hex,\n ) {\n // Run validation and add external transaction to state.\n const newTransactionMeta = this.addExternalTransaction(transactionMeta);\n\n try {\n const transactionId = newTransactionMeta.id;\n\n // Make sure status is confirmed and define gasUsed as in receipt.\n const updatedTransactionMeta = {\n ...newTransactionMeta,\n status: TransactionStatus.confirmed as const,\n txReceipt: transactionReceipt,\n };\n if (baseFeePerGas) {\n updatedTransactionMeta.baseFeePerGas = baseFeePerGas;\n }\n\n // Update same nonce local transactions as dropped and define replacedBy properties.\n this.markNonceDuplicatesDropped(transactionId);\n\n // Update external provided transaction with updated gas values and confirmed status.\n this.updateTransaction(\n updatedTransactionMeta,\n `${controllerName}:confirmExternalTransaction - Add external transaction`,\n );\n this.onTransactionStatusChange(updatedTransactionMeta);\n\n // Intentional given potential duration of process.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.updatePostBalance(updatedTransactionMeta);\n\n this.messagingSystem.publish(\n `${controllerName}:transactionConfirmed`,\n updatedTransactionMeta,\n );\n } catch (error) {\n console.error('Failed to confirm external transaction', error);\n }\n }\n\n /**\n * Append new send flow history to a transaction.\n *\n * @param transactionID - The ID of the transaction to update.\n * @param currentSendFlowHistoryLength - The length of the current sendFlowHistory array.\n * @param sendFlowHistoryToAdd - The sendFlowHistory entries to add.\n * @returns The updated transactionMeta.\n */\n updateTransactionSendFlowHistory(\n transactionID: string,\n currentSendFlowHistoryLength: number,\n sendFlowHistoryToAdd: SendFlowHistoryEntry[],\n ): TransactionMeta {\n if (this.isSendFlowHistoryDisabled) {\n throw new Error(\n 'Send flow history is disabled for the current transaction controller',\n );\n }\n\n const transactionMeta = this.getTransaction(transactionID);\n\n if (!transactionMeta) {\n throw new Error(\n `Cannot update send flow history as no transaction metadata found`,\n );\n }\n\n validateIfTransactionUnapproved(\n transactionMeta,\n 'updateTransactionSendFlowHistory',\n );\n\n const sendFlowHistory = transactionMeta.sendFlowHistory ?? [];\n if (currentSendFlowHistoryLength === sendFlowHistory.length) {\n const updatedTransactionMeta = {\n ...transactionMeta,\n sendFlowHistory: [...sendFlowHistory, ...sendFlowHistoryToAdd],\n };\n this.updateTransaction(\n updatedTransactionMeta,\n `${controllerName}:updateTransactionSendFlowHistory - sendFlowHistory updated`,\n );\n }\n\n return this.getTransaction(transactionID) as TransactionMeta;\n }\n\n /**\n * Update the gas values of a transaction.\n *\n * @param transactionId - The ID of the transaction to update.\n * @param gasValues - Gas values to update.\n * @param gasValues.gas - Same as transaction.gasLimit.\n * @param gasValues.gasLimit - Maxmimum number of units of gas to use for this transaction.\n * @param gasValues.gasPrice - Price per gas for legacy transactions.\n * @param gasValues.maxPriorityFeePerGas - Maximum amount per gas to give to validator as incentive.\n * @param gasValues.maxFeePerGas - Maximum amount per gas to pay for the transaction, including the priority fee.\n * @param gasValues.estimateUsed - Which estimate level was used.\n * @param gasValues.estimateSuggested - Which estimate level that the API suggested.\n * @param gasValues.defaultGasEstimates - The default estimate for gas.\n * @param gasValues.originalGasEstimate - Original estimate for gas.\n * @param gasValues.userEditedGasLimit - The gas limit supplied by user.\n * @param gasValues.userFeeLevel - Estimate level user selected.\n * @returns The updated transactionMeta.\n */\n updateTransactionGasFees(\n transactionId: string,\n {\n defaultGasEstimates,\n estimateUsed,\n estimateSuggested,\n gas,\n gasLimit,\n gasPrice,\n maxPriorityFeePerGas,\n maxFeePerGas,\n originalGasEstimate,\n userEditedGasLimit,\n userFeeLevel,\n }: {\n defaultGasEstimates?: string;\n estimateUsed?: string;\n estimateSuggested?: string;\n gas?: string;\n gasLimit?: string;\n gasPrice?: string;\n maxPriorityFeePerGas?: string;\n maxFeePerGas?: string;\n originalGasEstimate?: string;\n userEditedGasLimit?: boolean;\n userFeeLevel?: string;\n },\n ): TransactionMeta {\n const transactionMeta = this.getTransaction(transactionId);\n\n if (!transactionMeta) {\n throw new Error(\n `Cannot update transaction as no transaction metadata found`,\n );\n }\n\n validateIfTransactionUnapproved(\n transactionMeta,\n 'updateTransactionGasFees',\n );\n\n let transactionGasFees = {\n txParams: {\n gas,\n gasLimit,\n gasPrice,\n maxPriorityFeePerGas,\n maxFeePerGas,\n },\n defaultGasEstimates,\n estimateUsed,\n estimateSuggested,\n originalGasEstimate,\n userEditedGasLimit,\n userFeeLevel,\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } as any;\n\n // only update what is defined\n transactionGasFees.txParams = pickBy(transactionGasFees.txParams);\n transactionGasFees = pickBy(transactionGasFees);\n\n // merge updated gas values with existing transaction meta\n const updatedMeta = merge({}, transactionMeta, transactionGasFees);\n\n this.updateTransaction(\n updatedMeta,\n `${controllerName}:updateTransactionGasFees - gas values updated`,\n );\n\n return this.getTransaction(transactionId) as TransactionMeta;\n }\n\n /**\n * Update the previous gas values of a transaction.\n *\n * @param transactionId - The ID of the transaction to update.\n * @param previousGas - Previous gas values to update.\n * @param previousGas.gasLimit - Maxmimum number of units of gas to use for this transaction.\n * @param previousGas.maxFeePerGas - Maximum amount per gas to pay for the transaction, including the priority fee.\n * @param previousGas.maxPriorityFeePerGas - Maximum amount per gas to give to validator as incentive.\n * @returns The updated transactionMeta.\n */\n updatePreviousGasParams(\n transactionId: string,\n {\n gasLimit,\n maxFeePerGas,\n maxPriorityFeePerGas,\n }: {\n gasLimit?: string;\n maxFeePerGas?: string;\n maxPriorityFeePerGas?: string;\n },\n ): TransactionMeta {\n const transactionMeta = this.getTransaction(transactionId);\n\n if (!transactionMeta) {\n throw new Error(\n `Cannot update transaction as no transaction metadata found`,\n );\n }\n\n validateIfTransactionUnapproved(transactionMeta, 'updatePreviousGasParams');\n\n const transactionPreviousGas = {\n previousGas: {\n gasLimit,\n maxFeePerGas,\n maxPriorityFeePerGas,\n },\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } as any;\n\n // only update what is defined\n transactionPreviousGas.previousGas = pickBy(\n transactionPreviousGas.previousGas,\n );\n\n // merge updated previous gas values with existing transaction meta\n const updatedMeta = merge({}, transactionMeta, transactionPreviousGas);\n\n this.updateTransaction(\n updatedMeta,\n `${controllerName}:updatePreviousGasParams - Previous gas values updated`,\n );\n\n return this.getTransaction(transactionId) as TransactionMeta;\n }\n\n async getNonceLock(\n address: string,\n networkClientId?: NetworkClientId,\n ): Promise {\n return this.#multichainTrackingHelper.getNonceLock(\n address,\n networkClientId,\n );\n }\n\n /**\n * Updates the editable parameters of a transaction.\n *\n * @param txId - The ID of the transaction to update.\n * @param params - The editable parameters to update.\n * @param params.data - Data to pass with the transaction.\n * @param params.gas - Maximum number of units of gas to use for the transaction.\n * @param params.gasPrice - Price per gas for legacy transactions.\n * @param params.from - Address to send the transaction from.\n * @param params.to - Address to send the transaction to.\n * @param params.value - Value associated with the transaction.\n * @returns The updated transaction metadata.\n */\n async updateEditableParams(\n txId: string,\n {\n data,\n gas,\n gasPrice,\n from,\n to,\n value,\n }: {\n data?: string;\n gas?: string;\n gasPrice?: string;\n from?: string;\n to?: string;\n value?: string;\n },\n ) {\n const transactionMeta = this.getTransaction(txId);\n if (!transactionMeta) {\n throw new Error(\n `Cannot update editable params as no transaction metadata found`,\n );\n }\n\n validateIfTransactionUnapproved(transactionMeta, 'updateEditableParams');\n\n const editableParams = {\n txParams: {\n data,\n from,\n to,\n value,\n gas,\n gasPrice,\n },\n } as Partial;\n\n editableParams.txParams = pickBy(\n editableParams.txParams,\n ) as TransactionParams;\n\n const updatedTransaction = merge({}, transactionMeta, editableParams);\n const provider = this.#multichainTrackingHelper.getProvider({\n chainId: transactionMeta.chainId,\n networkClientId: transactionMeta.networkClientId,\n });\n const ethQuery = new EthQuery(provider);\n const { type } = await determineTransactionType(\n updatedTransaction.txParams,\n ethQuery,\n );\n updatedTransaction.type = type;\n\n await updateTransactionLayer1GasFee({\n layer1GasFeeFlows: this.layer1GasFeeFlows,\n provider,\n transactionMeta: updatedTransaction,\n });\n\n this.updateTransaction(\n updatedTransaction,\n `Update Editable Params for ${txId}`,\n );\n return this.getTransaction(txId);\n }\n\n /**\n * Signs and returns the raw transaction data for provided transaction params list.\n *\n * @param listOfTxParams - The list of transaction params to approve.\n * @param opts - Options bag.\n * @param opts.hasNonce - Whether the transactions already have a nonce.\n * @returns The raw transactions.\n */\n async approveTransactionsWithSameNonce(\n listOfTxParams: (TransactionParams & { chainId: Hex })[] = [],\n { hasNonce }: { hasNonce?: boolean } = {},\n ): Promise {\n log('Approving transactions with same nonce', {\n transactions: listOfTxParams,\n });\n\n if (listOfTxParams.length === 0) {\n return '';\n }\n\n const initialTx = listOfTxParams[0];\n const common = this.getCommonConfiguration(initialTx.chainId);\n\n // We need to ensure we get the nonce using the the NonceTracker on the chain matching\n // the txParams. In this context we only have chainId available to us, but the\n // NonceTrackers are keyed by networkClientId. To workaround this, we attempt to find\n // a networkClientId that matches the chainId. As a fallback, the globally selected\n // network's NonceTracker will be used instead.\n let networkClientId: NetworkClientId | undefined;\n try {\n networkClientId = this.messagingSystem.call(\n `NetworkController:findNetworkClientIdByChainId`,\n initialTx.chainId,\n );\n } catch (err) {\n log('failed to find networkClientId from chainId', err);\n }\n\n const initialTxAsEthTx = TransactionFactory.fromTxData(initialTx, {\n common,\n });\n const initialTxAsSerializedHex = bufferToHex(initialTxAsEthTx.serialize());\n\n if (this.approvingTransactionIds.has(initialTxAsSerializedHex)) {\n return '';\n }\n this.approvingTransactionIds.add(initialTxAsSerializedHex);\n\n let rawTransactions, nonceLock;\n try {\n // TODO: we should add a check to verify that all transactions have the same from address\n const fromAddress = initialTx.from;\n const requiresNonce = hasNonce !== true;\n\n nonceLock = requiresNonce\n ? await this.getNonceLock(fromAddress, networkClientId)\n : undefined;\n\n const nonce = nonceLock\n ? add0x(nonceLock.nextNonce.toString(16))\n : initialTx.nonce;\n\n if (nonceLock) {\n log('Using nonce from nonce tracker', nonce, nonceLock.nonceDetails);\n }\n\n rawTransactions = await Promise.all(\n listOfTxParams.map((txParams) => {\n txParams.nonce = nonce;\n return this.signExternalTransaction(txParams.chainId, txParams);\n }),\n );\n } catch (err) {\n log('Error while signing transactions with same nonce', err);\n // Must set transaction to submitted/failed before releasing lock\n // continue with error chain\n throw err;\n } finally {\n nonceLock?.releaseLock();\n this.approvingTransactionIds.delete(initialTxAsSerializedHex);\n }\n return rawTransactions;\n }\n\n /**\n * Update a custodial transaction.\n *\n * @param transactionId - The ID of the transaction to update.\n * @param options - The custodial transaction options to update.\n * @param options.errorMessage - The error message to be assigned in case transaction status update to failed.\n * @param options.hash - The new hash value to be assigned.\n * @param options.status - The new status value to be assigned.\n */\n updateCustodialTransaction(\n transactionId: string,\n {\n errorMessage,\n hash,\n status,\n }: {\n errorMessage?: string;\n hash?: string;\n status?: TransactionStatus;\n },\n ) {\n const transactionMeta = this.getTransaction(transactionId);\n\n if (!transactionMeta) {\n throw new Error(\n `Cannot update custodial transaction as no transaction metadata found`,\n );\n }\n\n if (!transactionMeta.custodyId) {\n throw new Error('Transaction must be a custodian transaction');\n }\n\n if (\n status &&\n ![\n TransactionStatus.submitted,\n TransactionStatus.signed,\n TransactionStatus.failed,\n ].includes(status)\n ) {\n throw new Error(\n `Cannot update custodial transaction with status: ${status}`,\n );\n }\n\n const updatedTransactionMeta = merge(\n {},\n transactionMeta,\n pickBy({ hash, status }),\n ) as TransactionMeta;\n\n if (updatedTransactionMeta.status === TransactionStatus.submitted) {\n updatedTransactionMeta.submittedTime = new Date().getTime();\n }\n\n if (updatedTransactionMeta.status === TransactionStatus.failed) {\n updatedTransactionMeta.error = normalizeTxError(new Error(errorMessage));\n }\n\n this.updateTransaction(\n updatedTransactionMeta,\n `${controllerName}:updateCustodialTransaction - Custodial transaction updated`,\n );\n\n if (\n [TransactionStatus.submitted, TransactionStatus.failed].includes(\n status as TransactionStatus,\n )\n ) {\n this.messagingSystem.publish(\n `${controllerName}:transactionFinished`,\n updatedTransactionMeta,\n );\n this.#internalEvents.emit(\n `${updatedTransactionMeta.id}:finished`,\n updatedTransactionMeta,\n );\n }\n }\n\n /**\n * Search transaction metadata for matching entries.\n *\n * @param opts - Options bag.\n * @param opts.searchCriteria - An object containing values or functions for transaction properties to filter transactions with.\n * @param opts.initialList - The transactions to search. Defaults to the current state.\n * @param opts.filterToCurrentNetwork - Whether to filter the results to the current network. Defaults to true.\n * @param opts.limit - The maximum number of transactions to return. No limit by default.\n * @returns An array of transactions matching the provided options.\n */\n getTransactions({\n searchCriteria = {},\n initialList,\n filterToCurrentNetwork = true,\n limit,\n }: {\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n searchCriteria?: any;\n initialList?: TransactionMeta[];\n filterToCurrentNetwork?: boolean;\n limit?: number;\n } = {}): TransactionMeta[] {\n const chainId = this.getChainId();\n // searchCriteria is an object that might have values that aren't predicate\n // methods. When providing any other value type (string, number, etc), we\n // consider this shorthand for \"check the value at key for strict equality\n // with the provided value\". To conform this object to be only methods, we\n // mapValues (lodash) such that every value on the object is a method that\n // returns a boolean.\n const predicateMethods = mapValues(searchCriteria, (predicate) => {\n return typeof predicate === 'function'\n ? predicate\n : // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (v: any) => v === predicate;\n });\n\n const transactionsToFilter = initialList ?? this.state.transactions;\n\n // Combine sortBy and pickBy to transform our state object into an array of\n // matching transactions that are sorted by time.\n const filteredTransactions = sortBy(\n pickBy(transactionsToFilter, (transaction) => {\n if (filterToCurrentNetwork && transaction.chainId !== chainId) {\n return false;\n }\n // iterate over the predicateMethods keys to check if the transaction\n // matches the searchCriteria\n for (const [key, predicate] of Object.entries(predicateMethods)) {\n // We return false early as soon as we know that one of the specified\n // search criteria do not match the transaction. This prevents\n // needlessly checking all criteria when we already know the criteria\n // are not fully satisfied. We check both txParams and the base\n // object as predicate keys can be either.\n if (key in transaction.txParams) {\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n if (predicate((transaction.txParams as any)[key]) === false) {\n return false;\n }\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } else if (predicate((transaction as any)[key]) === false) {\n return false;\n }\n }\n\n return true;\n }),\n 'time',\n );\n if (limit !== undefined) {\n // We need to have all transactions of a given nonce in order to display\n // necessary details in the UI. We use the size of this set to determine\n // whether we have reached the limit provided, thus ensuring that all\n // transactions of nonces we include will be sent to the UI.\n const nonces = new Set();\n const txs = [];\n // By default, the transaction list we filter from is sorted by time ASC.\n // To ensure that filtered results prefers the newest transactions we\n // iterate from right to left, inserting transactions into front of a new\n // array. The original order is preserved, but we ensure that newest txs\n // are preferred.\n for (let i = filteredTransactions.length - 1; i > -1; i--) {\n const txMeta = filteredTransactions[i];\n const { nonce } = txMeta.txParams;\n if (!nonces.has(nonce)) {\n if (nonces.size < limit) {\n nonces.add(nonce);\n } else {\n continue;\n }\n }\n // Push transaction into the beginning of our array to ensure the\n // original order is preserved.\n txs.unshift(txMeta);\n }\n return txs;\n }\n return filteredTransactions;\n }\n\n async estimateGasFee({\n transactionParams,\n chainId,\n networkClientId: requestNetworkClientId,\n }: {\n transactionParams: TransactionParams;\n chainId?: Hex;\n networkClientId?: NetworkClientId;\n }): Promise {\n const networkClientId = this.#getNetworkClientId({\n networkClientId: requestNetworkClientId,\n chainId,\n });\n\n const transactionMeta = {\n txParams: transactionParams,\n chainId,\n networkClientId,\n } as TransactionMeta;\n\n // Guaranteed as the default gas fee flow matches all transactions.\n const gasFeeFlow = getGasFeeFlow(\n transactionMeta,\n this.gasFeeFlows,\n ) as GasFeeFlow;\n\n const ethQuery = this.#multichainTrackingHelper.getEthQuery({\n networkClientId,\n chainId,\n });\n\n const gasFeeControllerData = await this.getGasFeeEstimates({\n networkClientId,\n });\n\n return gasFeeFlow.getGasFees({\n ethQuery,\n gasFeeControllerData,\n transactionMeta,\n });\n }\n\n /**\n * Determine the layer 1 gas fee for the given transaction parameters.\n *\n * @param request - The request object.\n * @param request.transactionParams - The transaction parameters to estimate the layer 1 gas fee for.\n * @param request.chainId - The ID of the chain where the transaction will be executed.\n * @param request.networkClientId - The ID of a specific network client to process the transaction.\n */\n async getLayer1GasFee({\n transactionParams,\n chainId,\n networkClientId,\n }: {\n transactionParams: TransactionParams;\n chainId?: Hex;\n networkClientId?: NetworkClientId;\n }): Promise {\n const provider = this.#multichainTrackingHelper.getProvider({\n networkClientId,\n chainId,\n });\n\n return await getTransactionLayer1GasFee({\n layer1GasFeeFlows: this.layer1GasFeeFlows,\n provider,\n transactionMeta: {\n txParams: transactionParams,\n chainId,\n } as TransactionMeta,\n });\n }\n\n private async signExternalTransaction(\n chainId: Hex,\n transactionParams: TransactionParams,\n ): Promise {\n if (!this.sign) {\n throw new Error('No sign method defined.');\n }\n\n const normalizedTransactionParams =\n normalizeTransactionParams(transactionParams);\n const type = isEIP1559Transaction(normalizedTransactionParams)\n ? TransactionEnvelopeType.feeMarket\n : TransactionEnvelopeType.legacy;\n const updatedTransactionParams = {\n ...normalizedTransactionParams,\n type,\n gasLimit: normalizedTransactionParams.gas,\n chainId,\n };\n\n const { from } = updatedTransactionParams;\n const common = this.getCommonConfiguration(chainId);\n const unsignedTransaction = TransactionFactory.fromTxData(\n updatedTransactionParams,\n { common },\n );\n const signedTransaction = await this.sign(unsignedTransaction, from);\n\n const rawTransaction = bufferToHex(signedTransaction.serialize());\n return rawTransaction;\n }\n\n /**\n * Removes unapproved transactions from state.\n */\n clearUnapprovedTransactions() {\n const transactions = this.state.transactions.filter(\n ({ status }) => status !== TransactionStatus.unapproved,\n );\n this.update((state) => {\n state.transactions = this.trimTransactionsForState(transactions);\n });\n }\n\n /**\n * Stop the signing process for a specific transaction.\n * Throws an error causing the transaction status to be set to failed.\n * @param transactionId - The ID of the transaction to stop signing.\n */\n abortTransactionSigning(transactionId: string) {\n const transactionMeta = this.getTransaction(transactionId);\n\n if (!transactionMeta) {\n throw new Error(`Cannot abort signing as no transaction metadata found`);\n }\n\n const abortCallback = this.signAbortCallbacks.get(transactionId);\n\n if (!abortCallback) {\n throw new Error(\n `Cannot abort signing as transaction is not waiting for signing`,\n );\n }\n\n abortCallback();\n\n this.signAbortCallbacks.delete(transactionId);\n }\n\n private addMetadata(transactionMeta: TransactionMeta) {\n this.update((state) => {\n state.transactions = this.trimTransactionsForState([\n ...state.transactions,\n transactionMeta,\n ]);\n });\n }\n\n private async updateGasProperties(transactionMeta: TransactionMeta) {\n const isEIP1559Compatible =\n (await this.getEIP1559Compatibility(transactionMeta.networkClientId)) &&\n transactionMeta.txParams.type !== TransactionEnvelopeType.legacy;\n\n const { networkClientId, chainId } = transactionMeta;\n\n const isCustomNetwork = this.#isCustomNetwork(networkClientId);\n\n const ethQuery = this.#multichainTrackingHelper.getEthQuery({\n networkClientId,\n chainId,\n });\n\n const provider = this.#multichainTrackingHelper.getProvider({\n networkClientId,\n chainId,\n });\n\n await updateGas({\n ethQuery,\n chainId,\n isCustomNetwork,\n txMeta: transactionMeta,\n });\n\n await updateGasFees({\n eip1559: isEIP1559Compatible,\n ethQuery,\n gasFeeFlows: this.gasFeeFlows,\n getGasFeeEstimates: this.getGasFeeEstimates,\n getSavedGasFees: this.getSavedGasFees.bind(this),\n txMeta: transactionMeta,\n });\n\n await updateTransactionLayer1GasFee({\n layer1GasFeeFlows: this.layer1GasFeeFlows,\n provider,\n transactionMeta,\n });\n }\n\n private onBootCleanup() {\n this.clearUnapprovedTransactions();\n this.failIncompleteTransactions();\n }\n\n private failIncompleteTransactions() {\n const incompleteTransactions = this.state.transactions.filter(\n (transaction) =>\n [TransactionStatus.approved, TransactionStatus.signed].includes(\n transaction.status,\n ),\n );\n\n for (const transactionMeta of incompleteTransactions) {\n this.failTransaction(\n transactionMeta,\n new Error('Transaction incomplete at startup'),\n );\n }\n }\n\n private async processApproval(\n transactionMeta: TransactionMeta,\n {\n isExisting = false,\n requireApproval,\n shouldShowRequest = true,\n actionId,\n }: {\n isExisting?: boolean;\n requireApproval?: boolean | undefined;\n shouldShowRequest?: boolean;\n actionId?: string;\n },\n ): Promise {\n const transactionId = transactionMeta.id;\n let resultCallbacks: AcceptResultCallbacks | undefined;\n const { meta, isCompleted } = this.isTransactionCompleted(transactionId);\n const finishedPromise = isCompleted\n ? Promise.resolve(meta)\n : this.waitForTransactionFinished(transactionId);\n\n if (meta && !isExisting && !isCompleted) {\n try {\n if (requireApproval !== false) {\n const acceptResult = await this.requestApproval(transactionMeta, {\n shouldShowRequest,\n });\n resultCallbacks = acceptResult.resultCallbacks;\n\n const approvalValue = acceptResult.value as\n | {\n txMeta?: TransactionMeta;\n }\n | undefined;\n\n const updatedTransaction = approvalValue?.txMeta;\n\n if (updatedTransaction) {\n log('Updating transaction with approval data', {\n customNonce: updatedTransaction.customNonceValue,\n params: updatedTransaction.txParams,\n });\n\n this.updateTransaction(\n updatedTransaction,\n 'TransactionController#processApproval - Updated with approval data',\n );\n }\n }\n\n const { isCompleted: isTxCompleted } =\n this.isTransactionCompleted(transactionId);\n\n if (!isTxCompleted) {\n const approvalResult = await this.approveTransaction(transactionId);\n if (\n approvalResult === ApprovalState.SkippedViaBeforePublishHook &&\n resultCallbacks\n ) {\n resultCallbacks.success();\n }\n const updatedTransactionMeta = this.getTransaction(\n transactionId,\n ) as TransactionMeta;\n this.messagingSystem.publish(\n `${controllerName}:transactionApproved`,\n {\n transactionMeta: updatedTransactionMeta,\n actionId,\n },\n );\n }\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (error: any) {\n const { isCompleted: isTxCompleted } =\n this.isTransactionCompleted(transactionId);\n if (!isTxCompleted) {\n if (error?.code === errorCodes.provider.userRejectedRequest) {\n this.cancelTransaction(transactionId, actionId);\n\n throw providerErrors.userRejectedRequest(\n 'MetaMask Tx Signature: User denied transaction signature.',\n );\n } else {\n this.failTransaction(meta, error, actionId);\n }\n }\n }\n }\n\n const finalMeta = await finishedPromise;\n\n switch (finalMeta?.status) {\n case TransactionStatus.failed:\n resultCallbacks?.error(finalMeta.error);\n throw rpcErrors.internal(finalMeta.error.message);\n\n case TransactionStatus.submitted:\n resultCallbacks?.success();\n return finalMeta.hash as string;\n\n default:\n const internalError = rpcErrors.internal(\n `MetaMask Tx Signature: Unknown problem: ${JSON.stringify(\n finalMeta || transactionId,\n )}`,\n );\n\n resultCallbacks?.error(internalError);\n throw internalError;\n }\n }\n\n /**\n * Approves a transaction and updates it's status in state. If this is not a\n * retry transaction, a nonce will be generated. The transaction is signed\n * using the sign configuration property, then published to the blockchain.\n * A `:finished` hub event is fired after success or failure.\n *\n * @param transactionId - The ID of the transaction to approve.\n */\n private async approveTransaction(transactionId: string) {\n const cleanupTasks = new Array<() => void>();\n cleanupTasks.push(await this.mutex.acquire());\n\n let transactionMeta = this.getTransactionOrThrow(transactionId);\n\n try {\n if (!this.sign) {\n this.failTransaction(\n transactionMeta,\n new Error('No sign method defined.'),\n );\n return ApprovalState.NotApproved;\n } else if (!transactionMeta.chainId) {\n this.failTransaction(transactionMeta, new Error('No chainId defined.'));\n return ApprovalState.NotApproved;\n }\n\n if (this.approvingTransactionIds.has(transactionId)) {\n log('Skipping approval as signing in progress', transactionId);\n return ApprovalState.NotApproved;\n }\n this.approvingTransactionIds.add(transactionId);\n cleanupTasks.push(() =>\n this.approvingTransactionIds.delete(transactionId),\n );\n\n const [nonce, releaseNonce] = await getNextNonce(\n transactionMeta,\n (address: string) =>\n this.#multichainTrackingHelper.getNonceLock(\n address,\n transactionMeta.networkClientId,\n ),\n );\n\n // must set transaction to submitted/failed before releasing lock\n releaseNonce && cleanupTasks.push(releaseNonce);\n\n transactionMeta = this.#updateTransactionInternal(\n {\n transactionId,\n note: 'TransactionController#approveTransaction - Transaction approved',\n },\n (draftTxMeta) => {\n const { txParams, chainId } = draftTxMeta;\n\n draftTxMeta.status = TransactionStatus.approved;\n draftTxMeta.txParams = {\n ...txParams,\n nonce,\n chainId,\n gasLimit: txParams.gas,\n ...(isEIP1559Transaction(txParams) && {\n type: TransactionEnvelopeType.feeMarket,\n }),\n };\n },\n );\n\n this.onTransactionStatusChange(transactionMeta);\n\n const rawTx = await this.signTransaction(\n transactionMeta,\n transactionMeta.txParams,\n );\n\n if (!this.beforePublish(transactionMeta)) {\n log('Skipping publishing transaction based on hook');\n this.messagingSystem.publish(\n `${controllerName}:transactionPublishingSkipped`,\n transactionMeta,\n );\n return ApprovalState.SkippedViaBeforePublishHook;\n }\n\n if (!rawTx) {\n return ApprovalState.NotApproved;\n }\n\n const ethQuery = this.#multichainTrackingHelper.getEthQuery({\n networkClientId: transactionMeta.networkClientId,\n chainId: transactionMeta.chainId,\n });\n\n let preTxBalance: string | undefined;\n const shouldUpdatePreTxBalance =\n transactionMeta.type === TransactionType.swap;\n\n if (shouldUpdatePreTxBalance) {\n log('Determining pre-transaction balance');\n\n preTxBalance = await query(ethQuery, 'getBalance', [\n transactionMeta.txParams.from,\n ]);\n }\n\n log('Publishing transaction', transactionMeta.txParams);\n\n let { transactionHash: hash } = await this.publish(\n transactionMeta,\n rawTx,\n );\n\n if (hash === undefined) {\n hash = await this.publishTransaction(ethQuery, rawTx);\n }\n\n log('Publish successful', hash);\n\n transactionMeta = this.#updateTransactionInternal(\n {\n transactionId,\n note: 'TransactionController#approveTransaction - Transaction submitted',\n },\n (draftTxMeta) => {\n draftTxMeta.hash = hash;\n draftTxMeta.status = TransactionStatus.submitted;\n draftTxMeta.submittedTime = new Date().getTime();\n if (shouldUpdatePreTxBalance) {\n draftTxMeta.preTxBalance = preTxBalance;\n log('Updated pre-transaction balance', preTxBalance);\n }\n },\n );\n\n this.messagingSystem.publish(`${controllerName}:transactionSubmitted`, {\n transactionMeta,\n });\n\n this.messagingSystem.publish(\n `${controllerName}:transactionFinished`,\n transactionMeta,\n );\n this.#internalEvents.emit(`${transactionId}:finished`, transactionMeta);\n\n this.onTransactionStatusChange(transactionMeta);\n return ApprovalState.Approved;\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (error: any) {\n this.failTransaction(transactionMeta, error);\n return ApprovalState.NotApproved;\n } finally {\n cleanupTasks.forEach((task) => task());\n }\n }\n\n private async publishTransaction(\n ethQuery: EthQuery,\n rawTransaction: string,\n ): Promise {\n return await query(ethQuery, 'sendRawTransaction', [rawTransaction]);\n }\n\n /**\n * Cancels a transaction based on its ID by setting its status to \"rejected\"\n * and emitting a `:finished` hub event.\n *\n * @param transactionId - The ID of the transaction to cancel.\n * @param actionId - The actionId passed from UI\n */\n private cancelTransaction(transactionId: string, actionId?: string) {\n const transactionMeta = this.state.transactions.find(\n ({ id }) => id === transactionId,\n );\n if (!transactionMeta) {\n return;\n }\n this.update((state) => {\n const transactions = state.transactions.filter(\n ({ id }) => id !== transactionId,\n );\n state.transactions = this.trimTransactionsForState(transactions);\n });\n const updatedTransactionMeta = {\n ...transactionMeta,\n status: TransactionStatus.rejected as const,\n };\n this.messagingSystem.publish(\n `${controllerName}:transactionFinished`,\n updatedTransactionMeta,\n );\n this.#internalEvents.emit(\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/restrict-template-expressions\n `${transactionMeta.id}:finished`,\n updatedTransactionMeta,\n );\n this.messagingSystem.publish(`${controllerName}:transactionRejected`, {\n transactionMeta: updatedTransactionMeta,\n actionId,\n });\n this.onTransactionStatusChange(updatedTransactionMeta);\n }\n\n /**\n * Trim the amount of transactions that are set on the state. Checks\n * if the length of the tx history is longer then desired persistence\n * limit and then if it is removes the oldest confirmed or rejected tx.\n * Pending or unapproved transactions will not be removed by this\n * operation. For safety of presenting a fully functional transaction UI\n * representation, this function will not break apart transactions with the\n * same nonce, created on the same day, per network. Not accounting for\n * transactions of the same nonce, same day and network combo can result in\n * confusing or broken experiences in the UI.\n *\n * @param transactions - The transactions to be applied to the state.\n * @returns The trimmed list of transactions.\n */\n private trimTransactionsForState(\n transactions: TransactionMeta[],\n ): TransactionMeta[] {\n const nonceNetworkSet = new Set();\n\n const txsToKeep = [...transactions]\n .sort((a, b) => (a.time > b.time ? -1 : 1)) // Descending time order\n .filter((tx) => {\n const { chainId, status, txParams, time } = tx;\n\n if (txParams) {\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/restrict-template-expressions\n const key = `${String(txParams.nonce)}-${convertHexToDecimal(\n chainId,\n )}-${new Date(time).toDateString()}`;\n\n if (nonceNetworkSet.has(key)) {\n return true;\n } else if (\n nonceNetworkSet.size < this.#transactionHistoryLimit ||\n !this.isFinalState(status)\n ) {\n nonceNetworkSet.add(key);\n return true;\n }\n }\n\n return false;\n });\n\n txsToKeep.reverse(); // Ascending time order\n return txsToKeep;\n }\n\n /**\n * Determines if the transaction is in a final state.\n *\n * @param status - The transaction status.\n * @returns Whether the transaction is in a final state.\n */\n private isFinalState(status: TransactionStatus): boolean {\n return (\n status === TransactionStatus.rejected ||\n status === TransactionStatus.confirmed ||\n status === TransactionStatus.failed\n );\n }\n\n /**\n * Whether the transaction has at least completed all local processing.\n *\n * @param status - The transaction status.\n * @returns Whether the transaction is in a final state.\n */\n private isLocalFinalState(status: TransactionStatus): boolean {\n return [\n TransactionStatus.confirmed,\n TransactionStatus.failed,\n TransactionStatus.rejected,\n TransactionStatus.submitted,\n ].includes(status);\n }\n\n private async requestApproval(\n txMeta: TransactionMeta,\n { shouldShowRequest }: { shouldShowRequest: boolean },\n ): Promise {\n const id = this.getApprovalId(txMeta);\n const { origin } = txMeta;\n const type = ApprovalType.Transaction;\n const requestData = { txId: txMeta.id };\n\n return (await this.messagingSystem.call(\n 'ApprovalController:addRequest',\n {\n id,\n origin: origin || ORIGIN_METAMASK,\n type,\n requestData,\n expectsResult: true,\n },\n shouldShowRequest,\n )) as Promise;\n }\n\n private getTransaction(\n transactionId: string,\n ): Readonly | undefined {\n const { transactions } = this.state;\n return transactions.find(({ id }) => id === transactionId);\n }\n\n private getTransactionOrThrow(\n transactionId: string,\n errorMessagePrefix = 'TransactionController',\n ): Readonly {\n const txMeta = this.getTransaction(transactionId);\n if (!txMeta) {\n throw new Error(\n `${errorMessagePrefix}: No transaction found with id ${transactionId}`,\n );\n }\n return txMeta;\n }\n\n private getApprovalId(txMeta: TransactionMeta) {\n return String(txMeta.id);\n }\n\n private isTransactionCompleted(transactionId: string): {\n meta?: TransactionMeta;\n isCompleted: boolean;\n } {\n const transaction = this.getTransaction(transactionId);\n\n if (!transaction) {\n return { meta: undefined, isCompleted: false };\n }\n\n const isCompleted = this.isLocalFinalState(transaction.status);\n\n return { meta: transaction, isCompleted };\n }\n\n private getChainId(networkClientId?: NetworkClientId): Hex {\n const globalChainId = this.#getGlobalChainId();\n const globalNetworkClientId = this.#getGlobalNetworkClientId();\n\n if (!networkClientId || networkClientId === globalNetworkClientId) {\n return globalChainId;\n }\n\n return this.messagingSystem.call(\n `NetworkController:getNetworkClientById`,\n networkClientId,\n ).configuration.chainId;\n }\n\n private prepareUnsignedEthTx(\n chainId: Hex,\n txParams: TransactionParams,\n ): TypedTransaction {\n return TransactionFactory.fromTxData(txParams, {\n freeze: false,\n common: this.getCommonConfiguration(chainId),\n });\n }\n\n /**\n * `@ethereumjs/tx` uses `@ethereumjs/common` as a configuration tool for\n * specifying which chain, network, hardfork and EIPs to support for\n * a transaction. By referencing this configuration, and analyzing the fields\n * specified in txParams, @ethereumjs/tx is able to determine which EIP-2718\n * transaction type to use.\n *\n * @param chainId - The chainId to use for the configuration.\n * @returns common configuration object\n */\n private getCommonConfiguration(chainId: Hex): Common {\n const customChainParams: Partial = {\n chainId: parseInt(chainId, 16),\n defaultHardfork: HARDFORK,\n };\n\n return Common.custom(customChainParams);\n }\n\n private onIncomingTransactions({\n added,\n updated,\n }: {\n added: TransactionMeta[];\n updated: TransactionMeta[];\n }) {\n this.update((state) => {\n const { transactions: currentTransactions } = state;\n const updatedTransactions = [\n ...added,\n ...currentTransactions.map((originalTransaction) => {\n const updatedTransaction = updated.find(\n ({ hash }) => hash === originalTransaction.hash,\n );\n\n return updatedTransaction ?? originalTransaction;\n }),\n ];\n\n state.transactions = this.trimTransactionsForState(updatedTransactions);\n });\n }\n\n private onUpdatedLastFetchedBlockNumbers({\n lastFetchedBlockNumbers,\n blockNumber,\n }: {\n lastFetchedBlockNumbers: {\n [key: string]: number;\n };\n blockNumber: number;\n }) {\n this.update((state) => {\n state.lastFetchedBlockNumbers = lastFetchedBlockNumbers;\n });\n this.messagingSystem.publish(\n `${controllerName}:incomingTransactionBlockReceived`,\n blockNumber,\n );\n }\n\n private generateDappSuggestedGasFees(\n txParams: TransactionParams,\n origin?: string,\n ): DappSuggestedGasFees | undefined {\n if (!origin || origin === ORIGIN_METAMASK) {\n return undefined;\n }\n\n const { gasPrice, maxFeePerGas, maxPriorityFeePerGas, gas } = txParams;\n\n if (\n gasPrice === undefined &&\n maxFeePerGas === undefined &&\n maxPriorityFeePerGas === undefined &&\n gas === undefined\n ) {\n return undefined;\n }\n\n const dappSuggestedGasFees: DappSuggestedGasFees = {};\n\n if (gasPrice !== undefined) {\n dappSuggestedGasFees.gasPrice = gasPrice;\n } else if (\n maxFeePerGas !== undefined ||\n maxPriorityFeePerGas !== undefined\n ) {\n dappSuggestedGasFees.maxFeePerGas = maxFeePerGas;\n dappSuggestedGasFees.maxPriorityFeePerGas = maxPriorityFeePerGas;\n }\n\n if (gas !== undefined) {\n dappSuggestedGasFees.gas = gas;\n }\n\n return dappSuggestedGasFees;\n }\n\n /**\n * Validates and adds external provided transaction to state.\n *\n * @param transactionMeta - Nominated external transaction to be added to state.\n * @returns The new transaction.\n */\n private addExternalTransaction(transactionMeta: TransactionMeta) {\n const { chainId } = transactionMeta;\n const { transactions } = this.state;\n const fromAddress = transactionMeta?.txParams?.from;\n const sameFromAndNetworkTransactions = transactions.filter(\n (transaction) =>\n transaction.txParams.from === fromAddress &&\n transaction.chainId === chainId,\n );\n const confirmedTxs = sameFromAndNetworkTransactions.filter(\n (transaction) => transaction.status === TransactionStatus.confirmed,\n );\n const pendingTxs = sameFromAndNetworkTransactions.filter(\n (transaction) => transaction.status === TransactionStatus.submitted,\n );\n\n validateConfirmedExternalTransaction(\n transactionMeta,\n confirmedTxs,\n pendingTxs,\n );\n\n // Make sure provided external transaction has non empty history array\n const newTransactionMeta =\n (transactionMeta.history ?? []).length === 0 && !this.isHistoryDisabled\n ? addInitialHistorySnapshot(transactionMeta)\n : transactionMeta;\n\n this.update((state) => {\n state.transactions = this.trimTransactionsForState([\n ...state.transactions,\n newTransactionMeta,\n ]);\n });\n\n return newTransactionMeta;\n }\n\n /**\n * Sets other txMeta statuses to dropped if the txMeta that has been confirmed has other transactions\n * in the transactions have the same nonce.\n *\n * @param transactionId - Used to identify original transaction.\n */\n private markNonceDuplicatesDropped(transactionId: string) {\n const transactionMeta = this.getTransaction(transactionId);\n if (!transactionMeta) {\n return;\n }\n const nonce = transactionMeta.txParams?.nonce;\n const from = transactionMeta.txParams?.from;\n const { chainId } = transactionMeta;\n\n const sameNonceTransactions = this.state.transactions.filter(\n (transaction) =>\n transaction.id !== transactionId &&\n transaction.txParams.from === from &&\n transaction.txParams.nonce === nonce &&\n transaction.chainId === chainId &&\n transaction.type !== TransactionType.incoming,\n );\n const sameNonceTransactionIds = sameNonceTransactions.map(\n (transaction) => transaction.id,\n );\n\n if (sameNonceTransactions.length === 0) {\n return;\n }\n\n this.update((state) => {\n for (const transaction of state.transactions) {\n if (sameNonceTransactionIds.includes(transaction.id)) {\n transaction.replacedBy = transactionMeta?.hash;\n transaction.replacedById = transactionMeta?.id;\n }\n }\n });\n\n for (const transaction of this.state.transactions) {\n if (\n sameNonceTransactionIds.includes(transaction.id) &&\n transaction.status !== TransactionStatus.failed\n ) {\n this.setTransactionStatusDropped(transaction);\n }\n }\n }\n\n /**\n * Method to set transaction status to dropped.\n *\n * @param transactionMeta - TransactionMeta of transaction to be marked as dropped.\n */\n private setTransactionStatusDropped(transactionMeta: TransactionMeta) {\n const updatedTransactionMeta = {\n ...transactionMeta,\n status: TransactionStatus.dropped as const,\n };\n this.messagingSystem.publish(`${controllerName}:transactionDropped`, {\n transactionMeta: updatedTransactionMeta,\n });\n this.updateTransaction(\n updatedTransactionMeta,\n 'TransactionController#setTransactionStatusDropped - Transaction dropped',\n );\n this.onTransactionStatusChange(updatedTransactionMeta);\n }\n\n /**\n * Get transaction with provided actionId.\n *\n * @param actionId - Unique ID to prevent duplicate requests\n * @returns the filtered transaction\n */\n private getTransactionWithActionId(actionId?: string) {\n return this.state.transactions.find(\n (transaction) => actionId && transaction.actionId === actionId,\n );\n }\n\n private async waitForTransactionFinished(\n transactionId: string,\n ): Promise {\n return new Promise((resolve) => {\n this.#internalEvents.once(`${transactionId}:finished`, (txMeta) => {\n resolve(txMeta);\n });\n });\n }\n\n /**\n * Updates the r, s, and v properties of a TransactionMeta object\n * with values from a signed transaction.\n *\n * @param transactionMeta - The TransactionMeta object to update.\n * @param signedTx - The encompassing type for all transaction types containing r, s, and v values.\n * @returns The updated TransactionMeta object.\n */\n private updateTransactionMetaRSV(\n transactionMeta: TransactionMeta,\n signedTx: TypedTransaction,\n ): TransactionMeta {\n const transactionMetaWithRsv = cloneDeep(transactionMeta);\n\n for (const key of ['r', 's', 'v'] as const) {\n const value = signedTx[key];\n\n if (value === undefined || value === null) {\n continue;\n }\n\n transactionMetaWithRsv[key] = add0x(value.toString(16));\n }\n\n return transactionMetaWithRsv;\n }\n\n private async getEIP1559Compatibility(networkClientId?: NetworkClientId) {\n const currentNetworkIsEIP1559Compatible =\n await this.getCurrentNetworkEIP1559Compatibility(networkClientId);\n\n const currentAccountIsEIP1559Compatible =\n await this.getCurrentAccountEIP1559Compatibility();\n\n return (\n currentNetworkIsEIP1559Compatible && currentAccountIsEIP1559Compatible\n );\n }\n\n private async signTransaction(\n transactionMeta: TransactionMeta,\n txParams: TransactionParams,\n ): Promise {\n log('Signing transaction', txParams);\n\n const unsignedEthTx = this.prepareUnsignedEthTx(\n transactionMeta.chainId,\n txParams,\n );\n\n this.approvingTransactionIds.add(transactionMeta.id);\n\n const signedTx = await new Promise((resolve, reject) => {\n this.sign?.(\n unsignedEthTx,\n txParams.from,\n ...this.getAdditionalSignArguments(transactionMeta),\n ).then(resolve, reject);\n\n this.signAbortCallbacks.set(transactionMeta.id, () =>\n reject(new Error('Signing aborted by user')),\n );\n });\n\n this.signAbortCallbacks.delete(transactionMeta.id);\n\n if (!signedTx) {\n log('Skipping signed status as no signed transaction');\n return undefined;\n }\n\n const transactionMetaFromHook = cloneDeep(transactionMeta);\n if (!this.afterSign(transactionMetaFromHook, signedTx)) {\n this.updateTransaction(\n transactionMetaFromHook,\n 'TransactionController#signTransaction - Update after sign',\n );\n\n log('Skipping signed status based on hook');\n\n return undefined;\n }\n\n const transactionMetaWithRsv = {\n ...this.updateTransactionMetaRSV(transactionMetaFromHook, signedTx),\n status: TransactionStatus.signed as const,\n };\n\n this.updateTransaction(\n transactionMetaWithRsv,\n 'TransactionController#approveTransaction - Transaction signed',\n );\n\n this.onTransactionStatusChange(transactionMetaWithRsv);\n\n const rawTx = bufferToHex(signedTx.serialize());\n\n const transactionMetaWithRawTx = merge({}, transactionMetaWithRsv, {\n rawTx,\n });\n\n this.updateTransaction(\n transactionMetaWithRawTx,\n 'TransactionController#approveTransaction - RawTransaction added',\n );\n\n return rawTx;\n }\n\n private onTransactionStatusChange(transactionMeta: TransactionMeta) {\n this.messagingSystem.publish(`${controllerName}:transactionStatusUpdated`, {\n transactionMeta,\n });\n }\n\n private getNonceTrackerTransactions(\n status: TransactionStatus,\n address: string,\n chainId: string = this.getChainId(),\n ) {\n return getAndFormatTransactionsForNonceTracker(\n chainId,\n address,\n status,\n this.state.transactions,\n );\n }\n\n private onConfirmedTransaction(transactionMeta: TransactionMeta) {\n log('Processing confirmed transaction', transactionMeta.id);\n\n this.markNonceDuplicatesDropped(transactionMeta.id);\n\n this.messagingSystem.publish(\n `${controllerName}:transactionConfirmed`,\n transactionMeta,\n );\n\n this.onTransactionStatusChange(transactionMeta);\n\n // Intentional given potential duration of process.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.updatePostBalance(transactionMeta);\n }\n\n private async updatePostBalance(transactionMeta: TransactionMeta) {\n try {\n if (transactionMeta.type !== TransactionType.swap) {\n return;\n }\n\n const ethQuery = this.#multichainTrackingHelper.getEthQuery({\n networkClientId: transactionMeta.networkClientId,\n chainId: transactionMeta.chainId,\n });\n const { updatedTransactionMeta, approvalTransactionMeta } =\n await updatePostTransactionBalance(transactionMeta, {\n ethQuery,\n getTransaction: this.getTransaction.bind(this),\n updateTransaction: this.updateTransaction.bind(this),\n });\n\n this.messagingSystem.publish(\n `${controllerName}:postTransactionBalanceUpdated`,\n {\n transactionMeta: updatedTransactionMeta,\n approvalTransactionMeta,\n },\n );\n } catch (error) {\n /* istanbul ignore next */\n log('Error while updating post transaction balance', error);\n }\n }\n\n #createNonceTracker({\n provider,\n blockTracker,\n chainId,\n }: {\n provider: Provider;\n blockTracker: BlockTracker;\n chainId?: Hex;\n }): NonceTracker {\n return new NonceTracker({\n // TODO: Fix types\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n provider: provider as any,\n // @ts-expect-error TODO: Fix types\n blockTracker,\n getPendingTransactions: this.#getNonceTrackerPendingTransactions.bind(\n this,\n chainId,\n ),\n getConfirmedTransactions: this.getNonceTrackerTransactions.bind(\n this,\n TransactionStatus.confirmed,\n ),\n });\n }\n\n #createIncomingTransactionHelper({\n blockTracker,\n etherscanRemoteTransactionSource,\n chainId,\n }: {\n blockTracker: BlockTracker;\n etherscanRemoteTransactionSource: EtherscanRemoteTransactionSource;\n chainId?: Hex;\n }): IncomingTransactionHelper {\n const incomingTransactionHelper = new IncomingTransactionHelper({\n blockTracker,\n getCurrentAccount: () => this.#getSelectedAccount(),\n getLastFetchedBlockNumbers: () => this.state.lastFetchedBlockNumbers,\n getChainId: chainId ? () => chainId : this.getChainId.bind(this),\n isEnabled: this.#incomingTransactionOptions.isEnabled,\n queryEntireHistory: this.#incomingTransactionOptions.queryEntireHistory,\n remoteTransactionSource: etherscanRemoteTransactionSource,\n transactionLimit: this.#transactionHistoryLimit,\n updateTransactions: this.#incomingTransactionOptions.updateTransactions,\n });\n\n this.#addIncomingTransactionHelperListeners(incomingTransactionHelper);\n\n return incomingTransactionHelper;\n }\n\n #createPendingTransactionTracker({\n provider,\n blockTracker,\n chainId,\n }: {\n provider: Provider;\n blockTracker: BlockTracker;\n chainId?: Hex;\n }): PendingTransactionTracker {\n const ethQuery = new EthQuery(provider);\n const getChainId = chainId ? () => chainId : this.getChainId.bind(this);\n\n const pendingTransactionTracker = new PendingTransactionTracker({\n blockTracker,\n getChainId,\n getEthQuery: () => ethQuery,\n getTransactions: () => this.state.transactions,\n isResubmitEnabled: this.#pendingTransactionOptions.isResubmitEnabled,\n getGlobalLock: () =>\n this.#multichainTrackingHelper.acquireNonceLockForChainIdKey({\n chainId: getChainId(),\n }),\n publishTransaction: this.publishTransaction.bind(this),\n hooks: {\n beforeCheckPendingTransaction:\n this.beforeCheckPendingTransaction.bind(this),\n beforePublish: this.beforePublish.bind(this),\n },\n });\n\n this.#addPendingTransactionTrackerListeners(pendingTransactionTracker);\n\n return pendingTransactionTracker;\n }\n\n #checkForPendingTransactionAndStartPolling = () => {\n // PendingTransactionTracker reads state through its getTransactions hook\n this.pendingTransactionTracker.startIfPendingTransactions();\n this.#multichainTrackingHelper.checkForPendingTransactionAndStartPolling();\n };\n\n #stopAllTracking() {\n this.pendingTransactionTracker.stop();\n this.#removePendingTransactionTrackerListeners(\n this.pendingTransactionTracker,\n );\n this.incomingTransactionHelper.stop();\n this.#removeIncomingTransactionHelperListeners(\n this.incomingTransactionHelper,\n );\n\n this.#multichainTrackingHelper.stopAllTracking();\n }\n\n #removeIncomingTransactionHelperListeners(\n incomingTransactionHelper: IncomingTransactionHelper,\n ) {\n incomingTransactionHelper.hub.removeAllListeners('transactions');\n incomingTransactionHelper.hub.removeAllListeners(\n 'updatedLastFetchedBlockNumbers',\n );\n }\n\n #addIncomingTransactionHelperListeners(\n incomingTransactionHelper: IncomingTransactionHelper,\n ) {\n incomingTransactionHelper.hub.on(\n 'transactions',\n this.onIncomingTransactions.bind(this),\n );\n incomingTransactionHelper.hub.on(\n 'updatedLastFetchedBlockNumbers',\n this.onUpdatedLastFetchedBlockNumbers.bind(this),\n );\n }\n\n #removePendingTransactionTrackerListeners(\n pendingTransactionTracker: PendingTransactionTracker,\n ) {\n pendingTransactionTracker.hub.removeAllListeners('transaction-confirmed');\n pendingTransactionTracker.hub.removeAllListeners('transaction-dropped');\n pendingTransactionTracker.hub.removeAllListeners('transaction-failed');\n pendingTransactionTracker.hub.removeAllListeners('transaction-updated');\n }\n\n #addPendingTransactionTrackerListeners(\n pendingTransactionTracker: PendingTransactionTracker,\n ) {\n pendingTransactionTracker.hub.on(\n 'transaction-confirmed',\n this.onConfirmedTransaction.bind(this),\n );\n\n pendingTransactionTracker.hub.on(\n 'transaction-dropped',\n this.setTransactionStatusDropped.bind(this),\n );\n\n pendingTransactionTracker.hub.on(\n 'transaction-failed',\n this.failTransaction.bind(this),\n );\n\n pendingTransactionTracker.hub.on(\n 'transaction-updated',\n this.updateTransaction.bind(this),\n );\n }\n\n #getNonceTrackerPendingTransactions(\n chainId: string | undefined,\n address: string,\n ) {\n const standardPendingTransactions = this.getNonceTrackerTransactions(\n TransactionStatus.submitted,\n address,\n chainId,\n );\n\n const externalPendingTransactions = this.getExternalPendingTransactions(\n address,\n chainId,\n );\n return [...standardPendingTransactions, ...externalPendingTransactions];\n }\n\n private async publishTransactionForRetry(\n ethQuery: EthQuery,\n rawTx: string,\n transactionMeta: TransactionMeta,\n ): Promise {\n try {\n const hash = await this.publishTransaction(ethQuery, rawTx);\n return hash;\n } catch (error: unknown) {\n if (this.isTransactionAlreadyConfirmedError(error as Error)) {\n await this.pendingTransactionTracker.forceCheckTransaction(\n transactionMeta,\n );\n throw new Error('Previous transaction is already confirmed');\n }\n throw error;\n }\n }\n\n /**\n * Ensures that error is a nonce issue\n *\n * @param error - The error to check\n * @returns Whether or not the error is a nonce issue\n */\n // TODO: Replace `any` with type\n // Some networks are returning original error in the data field\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private isTransactionAlreadyConfirmedError(error: any): boolean {\n return (\n error?.message?.includes('nonce too low') ||\n error?.data?.message?.includes('nonce too low')\n );\n }\n\n #getGasFeeFlows(): GasFeeFlow[] {\n if (this.#testGasFeeFlows) {\n return [new TestGasFeeFlow()];\n }\n\n return [new LineaGasFeeFlow(), new DefaultGasFeeFlow()];\n }\n\n #getLayer1GasFeeFlows(): Layer1GasFeeFlow[] {\n return [new OptimismLayer1GasFeeFlow(), new ScrollLayer1GasFeeFlow()];\n }\n\n #updateTransactionInternal(\n {\n transactionId,\n note,\n skipHistory,\n skipValidation,\n }: {\n transactionId: string;\n note?: string;\n skipHistory?: boolean;\n skipValidation?: boolean;\n },\n callback: (transactionMeta: TransactionMeta) => TransactionMeta | void,\n ): Readonly {\n let updatedTransactionParams: (keyof TransactionParams)[] = [];\n\n this.update((state) => {\n const index = state.transactions.findIndex(\n ({ id }) => id === transactionId,\n );\n\n let transactionMeta = state.transactions[index];\n\n // eslint-disable-next-line n/callback-return\n transactionMeta = callback(transactionMeta) ?? transactionMeta;\n\n if (skipValidation !== true) {\n transactionMeta.txParams = normalizeTransactionParams(\n transactionMeta.txParams,\n );\n\n validateTxParams(transactionMeta.txParams);\n }\n\n updatedTransactionParams =\n this.#checkIfTransactionParamsUpdated(transactionMeta);\n\n const shouldSkipHistory = this.isHistoryDisabled || skipHistory;\n\n if (!shouldSkipHistory) {\n transactionMeta = updateTransactionHistory(\n transactionMeta,\n note ?? 'Transaction updated',\n );\n }\n state.transactions[index] = transactionMeta;\n });\n\n const transactionMeta = this.getTransaction(\n transactionId,\n ) as TransactionMeta;\n\n if (updatedTransactionParams.length > 0) {\n this.#onTransactionParamsUpdated(\n transactionMeta,\n updatedTransactionParams,\n );\n }\n\n return transactionMeta;\n }\n\n #checkIfTransactionParamsUpdated(newTransactionMeta: TransactionMeta) {\n const { id: transactionId, txParams: newParams } = newTransactionMeta;\n\n const originalParams = this.getTransaction(transactionId)?.txParams;\n\n if (!originalParams || isEqual(originalParams, newParams)) {\n return [];\n }\n\n const params = Object.keys(newParams) as (keyof TransactionParams)[];\n\n const updatedProperties = params.filter(\n (param) => newParams[param] !== originalParams[param],\n );\n\n log(\n 'Transaction parameters have been updated',\n transactionId,\n updatedProperties,\n originalParams,\n newParams,\n );\n\n return updatedProperties;\n }\n\n #onTransactionParamsUpdated(\n transactionMeta: TransactionMeta,\n updatedParams: (keyof TransactionParams)[],\n ) {\n if (\n (['to', 'value', 'data'] as const).some((param) =>\n updatedParams.includes(param),\n )\n ) {\n log('Updating simulation data due to transaction parameter update');\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.#updateSimulationData(transactionMeta);\n }\n }\n\n async #updateSimulationData(transactionMeta: TransactionMeta) {\n const { id: transactionId, chainId, txParams } = transactionMeta;\n const { from, to, value, data } = txParams;\n\n let simulationData: SimulationData = {\n error: {\n code: SimulationErrorCode.Disabled,\n message: 'Simulation disabled',\n },\n tokenBalanceChanges: [],\n };\n\n if (this.#isSimulationEnabled()) {\n this.#updateTransactionInternal(\n { transactionId, skipHistory: true },\n (txMeta) => {\n txMeta.simulationData = undefined;\n },\n );\n\n simulationData = await getSimulationData({\n chainId,\n from: from as Hex,\n to: to as Hex,\n value: value as Hex,\n data: data as Hex,\n });\n }\n\n const finalTransactionMeta = this.getTransaction(transactionId);\n\n /* istanbul ignore if */\n if (!finalTransactionMeta) {\n log(\n 'Cannot update simulation data as transaction not found',\n transactionId,\n simulationData,\n );\n\n return;\n }\n\n this.#updateTransactionInternal(\n {\n transactionId,\n note: 'TransactionController#updateSimulationData - Update simulation data',\n },\n (txMeta) => {\n txMeta.simulationData = simulationData;\n },\n );\n\n log('Updated simulation data', transactionId, simulationData);\n }\n\n #onGasFeePollerTransactionUpdate({\n transactionId,\n gasFeeEstimates,\n gasFeeEstimatesLoaded,\n layer1GasFee,\n }: {\n transactionId: string;\n gasFeeEstimates?: GasFeeEstimates;\n gasFeeEstimatesLoaded?: boolean;\n layer1GasFee?: Hex;\n }) {\n this.#updateTransactionInternal(\n { transactionId, skipHistory: true },\n (txMeta) => {\n if (gasFeeEstimates) {\n txMeta.gasFeeEstimates = gasFeeEstimates;\n }\n\n if (gasFeeEstimatesLoaded !== undefined) {\n txMeta.gasFeeEstimatesLoaded = gasFeeEstimatesLoaded;\n }\n\n if (layer1GasFee) {\n txMeta.layer1GasFee = layer1GasFee;\n }\n },\n );\n }\n\n #getNetworkClientId({\n networkClientId: requestNetworkClientId,\n chainId,\n }: {\n networkClientId?: NetworkClientId;\n chainId?: Hex;\n }) {\n const globalChainId = this.#getGlobalChainId();\n const globalNetworkClientId = this.#getGlobalNetworkClientId();\n\n if (requestNetworkClientId) {\n return requestNetworkClientId;\n }\n\n if (!chainId || chainId === globalChainId) {\n return globalNetworkClientId;\n }\n\n return this.messagingSystem.call(\n `NetworkController:findNetworkClientIdByChainId`,\n chainId,\n );\n }\n\n #getGlobalNetworkClientId() {\n return this.getNetworkState().selectedNetworkClientId;\n }\n\n #getGlobalChainId() {\n return this.messagingSystem.call(\n `NetworkController:getNetworkClientById`,\n this.getNetworkState().selectedNetworkClientId,\n ).configuration.chainId;\n }\n\n #isCustomNetwork(networkClientId?: NetworkClientId) {\n const globalNetworkClientId = this.#getGlobalNetworkClientId();\n\n if (!networkClientId || networkClientId === globalNetworkClientId) {\n return !isInfuraNetworkType(\n this.getNetworkState().selectedNetworkClientId,\n );\n }\n\n return (\n this.messagingSystem.call(\n `NetworkController:getNetworkClientById`,\n networkClientId,\n ).configuration.type === NetworkClientType.Custom\n );\n }\n\n #getSelectedAccount() {\n return this.messagingSystem.call('AccountsController:getSelectedAccount');\n }\n}\n"]} -\ No newline at end of file -diff --git a/dist/chunk-YQYO6EGF.mjs b/dist/chunk-YQYO6EGF.mjs -new file mode 100644 -index 0000000000000000000000000000000000000000..a055cb8a748cd205a7dab47019d1e1a747c20c05 ---- /dev/null -+++ b/dist/chunk-YQYO6EGF.mjs -@@ -0,0 +1,2556 @@ -+import { -+ getAndFormatTransactionsForNonceTracker, -+ getNextNonce -+} from "./chunk-6DDVVUJC.mjs"; -+import { -+ getSimulationData -+} from "./chunk-EVL6KODQ.mjs"; -+import { -+ determineTransactionType -+} from "./chunk-KG4UW4K4.mjs"; -+import { -+ validateTransactionOrigin, -+ validateTxParams -+} from "./chunk-5ZEJT5SN.mjs"; -+import { -+ PendingTransactionTracker -+} from "./chunk-7M2R5AHC.mjs"; -+import { -+ validateConfirmedExternalTransaction -+} from "./chunk-FRKQ3Z2L.mjs"; -+import { -+ addGasBuffer, -+ estimateGas, -+ updateGas -+} from "./chunk-5G6OHAXI.mjs"; -+import { -+ addInitialHistorySnapshot, -+ updateTransactionHistory -+} from "./chunk-XGRAHX6T.mjs"; -+import { -+ OptimismLayer1GasFeeFlow -+} from "./chunk-VEVVBHP3.mjs"; -+import { -+ ScrollLayer1GasFeeFlow -+} from "./chunk-Z4GV3YQQ.mjs"; -+import { -+ TestGasFeeFlow -+} from "./chunk-FMRLPVFZ.mjs"; -+import { -+ GasFeePoller -+} from "./chunk-SFFTNB2X.mjs"; -+import { -+ getTransactionLayer1GasFee, -+ updateTransactionLayer1GasFee -+} from "./chunk-NOHEXQ7Y.mjs"; -+import { -+ IncomingTransactionHelper -+} from "./chunk-3ZV5YEUV.mjs"; -+import { -+ MultichainTrackingHelper -+} from "./chunk-4V4XIPCI.mjs"; -+import { -+ EtherscanRemoteTransactionSource -+} from "./chunk-EKJXGERC.mjs"; -+import { -+ LineaGasFeeFlow -+} from "./chunk-UHG2LLVV.mjs"; -+import { -+ DefaultGasFeeFlow -+} from "./chunk-H2KZOK3J.mjs"; -+import { -+ updateGasFees -+} from "./chunk-VXNPVIYL.mjs"; -+import { -+ updatePostTransactionBalance, -+ updateSwapsTransaction -+} from "./chunk-GNAL5HC2.mjs"; -+import { -+ getIncreasedPriceFromExisting, -+ isEIP1559Transaction, -+ isFeeMarketEIP1559Values, -+ isGasPriceValue, -+ normalizeGasFeeValues, -+ normalizeTransactionParams, -+ normalizeTxError, -+ validateGasValues, -+ validateIfTransactionUnapproved, -+ validateMinimumIncrease -+} from "./chunk-Q56I5ONX.mjs"; -+import { -+ getGasFeeFlow -+} from "./chunk-JXXTNVU4.mjs"; -+import { -+ projectLogger -+} from "./chunk-UQQWZT6C.mjs"; -+import { -+ __privateAdd, -+ __privateGet, -+ __privateMethod, -+ __privateSet -+} from "./chunk-XUI43LEZ.mjs"; -+ -+// src/TransactionController.ts -+import { Hardfork, Common } from "@ethereumjs/common"; -+import { TransactionFactory } from "@ethereumjs/tx"; -+import { bufferToHex } from "@ethereumjs/util"; -+import { BaseController } from "@metamask/base-controller"; -+import { -+ query, -+ ApprovalType, -+ ORIGIN_METAMASK, -+ convertHexToDecimal, -+ isInfuraNetworkType -+} from "@metamask/controller-utils"; -+import EthQuery from "@metamask/eth-query"; -+import { NetworkClientType } from "@metamask/network-controller"; -+import { NonceTracker } from "@metamask/nonce-tracker"; -+import { errorCodes, rpcErrors, providerErrors } from "@metamask/rpc-errors"; -+import { add0x } from "@metamask/utils"; -+import { Mutex } from "async-mutex"; -+import { MethodRegistry } from "eth-method-registry"; -+import { EventEmitter } from "events"; -+import { cloneDeep, mapValues, merge, pickBy, sortBy, isEqual } from "lodash"; -+import { v1 as random } from "uuid"; -+var metadata = { -+ transactions: { -+ persist: true, -+ anonymous: false -+ }, -+ methodData: { -+ persist: true, -+ anonymous: false -+ }, -+ lastFetchedBlockNumbers: { -+ persist: true, -+ anonymous: false -+ } -+}; -+var HARDFORK = Hardfork.London; -+var CANCEL_RATE = 1.1; -+var SPEED_UP_RATE = 1.1; -+var controllerName = "TransactionController"; -+var ApprovalState = /* @__PURE__ */ ((ApprovalState2) => { -+ ApprovalState2["Approved"] = "approved"; -+ ApprovalState2["NotApproved"] = "not-approved"; -+ ApprovalState2["SkippedViaBeforePublishHook"] = "skipped-via-before-publish-hook"; -+ return ApprovalState2; -+})(ApprovalState || {}); -+function getDefaultTransactionControllerState() { -+ return { -+ methodData: {}, -+ transactions: [], -+ lastFetchedBlockNumbers: {} -+ }; -+} -+var _internalEvents, _incomingTransactionOptions, _pendingTransactionOptions, _transactionHistoryLimit, _isSimulationEnabled, _testGasFeeFlows, _multichainTrackingHelper, _createNonceTracker, createNonceTracker_fn, _createIncomingTransactionHelper, createIncomingTransactionHelper_fn, _createPendingTransactionTracker, createPendingTransactionTracker_fn, _checkForPendingTransactionAndStartPolling, _stopAllTracking, stopAllTracking_fn, _removeIncomingTransactionHelperListeners, removeIncomingTransactionHelperListeners_fn, _addIncomingTransactionHelperListeners, addIncomingTransactionHelperListeners_fn, _removePendingTransactionTrackerListeners, removePendingTransactionTrackerListeners_fn, _addPendingTransactionTrackerListeners, addPendingTransactionTrackerListeners_fn, _getNonceTrackerPendingTransactions, getNonceTrackerPendingTransactions_fn, _getGasFeeFlows, getGasFeeFlows_fn, _getLayer1GasFeeFlows, getLayer1GasFeeFlows_fn, _updateTransactionInternal, updateTransactionInternal_fn, _checkIfTransactionParamsUpdated, checkIfTransactionParamsUpdated_fn, _onTransactionParamsUpdated, onTransactionParamsUpdated_fn, _updateSimulationData, updateSimulationData_fn, _onGasFeePollerTransactionUpdate, onGasFeePollerTransactionUpdate_fn, _getNetworkClientId, getNetworkClientId_fn, _getGlobalNetworkClientId, getGlobalNetworkClientId_fn, _getGlobalChainId, getGlobalChainId_fn, _isCustomNetwork, isCustomNetwork_fn, _getSelectedAccount, getSelectedAccount_fn; -+var TransactionController = class extends BaseController { -+ /** -+ * Constructs a TransactionController. -+ * -+ * @param options - The controller options. -+ * @param options.blockTracker - The block tracker used to poll for new blocks data. -+ * @param options.disableHistory - Whether to disable storing history in transaction metadata. -+ * @param options.disableSendFlowHistory - Explicitly disable transaction metadata history. -+ * @param options.disableSwaps - Whether to disable additional processing on swaps transactions. -+ * @param options.getCurrentAccountEIP1559Compatibility - Whether or not the account supports EIP-1559. -+ * @param options.getCurrentNetworkEIP1559Compatibility - Whether or not the network supports EIP-1559. -+ * @param options.getExternalPendingTransactions - Callback to retrieve pending transactions from external sources. -+ * @param options.getGasFeeEstimates - Callback to retrieve gas fee estimates. -+ * @param options.getNetworkClientRegistry - Gets the network client registry. -+ * @param options.getNetworkState - Gets the state of the network controller. -+ * @param options.getPermittedAccounts - Get accounts that a given origin has permissions for. -+ * @param options.getSavedGasFees - Gets the saved gas fee config. -+ * @param options.incomingTransactions - Configuration options for incoming transaction support. -+ * @param options.isMultichainEnabled - Enable multichain support. -+ * @param options.isSimulationEnabled - Whether new transactions will be automatically simulated. -+ * @param options.messenger - The controller messenger. -+ * @param options.onNetworkStateChange - Allows subscribing to network controller state changes. -+ * @param options.pendingTransactions - Configuration options for pending transaction support. -+ * @param options.provider - The provider used to create the underlying EthQuery instance. -+ * @param options.securityProviderRequest - A function for verifying a transaction, whether it is malicious or not. -+ * @param options.sign - Function used to sign transactions. -+ * @param options.state - Initial state to set on this controller. -+ * @param options.testGasFeeFlows - Whether to use the test gas fee flow. -+ * @param options.transactionHistoryLimit - Transaction history limit. -+ * @param options.hooks - The controller hooks. -+ */ -+ constructor({ -+ blockTracker, -+ disableHistory, -+ disableSendFlowHistory, -+ disableSwaps, -+ getCurrentAccountEIP1559Compatibility, -+ getCurrentNetworkEIP1559Compatibility, -+ getExternalPendingTransactions, -+ getGasFeeEstimates, -+ getNetworkClientRegistry, -+ getNetworkState, -+ getPermittedAccounts, -+ getSavedGasFees, -+ incomingTransactions = {}, -+ isMultichainEnabled = false, -+ isSimulationEnabled, -+ messenger, -+ onNetworkStateChange, -+ pendingTransactions = {}, -+ provider, -+ securityProviderRequest, -+ sign, -+ state, -+ testGasFeeFlows, -+ transactionHistoryLimit = 40, -+ hooks -+ }) { -+ super({ -+ name: controllerName, -+ metadata, -+ messenger, -+ state: { -+ ...getDefaultTransactionControllerState(), -+ ...state -+ } -+ }); -+ __privateAdd(this, _createNonceTracker); -+ __privateAdd(this, _createIncomingTransactionHelper); -+ __privateAdd(this, _createPendingTransactionTracker); -+ __privateAdd(this, _stopAllTracking); -+ __privateAdd(this, _removeIncomingTransactionHelperListeners); -+ __privateAdd(this, _addIncomingTransactionHelperListeners); -+ __privateAdd(this, _removePendingTransactionTrackerListeners); -+ __privateAdd(this, _addPendingTransactionTrackerListeners); -+ __privateAdd(this, _getNonceTrackerPendingTransactions); -+ __privateAdd(this, _getGasFeeFlows); -+ __privateAdd(this, _getLayer1GasFeeFlows); -+ __privateAdd(this, _updateTransactionInternal); -+ __privateAdd(this, _checkIfTransactionParamsUpdated); -+ __privateAdd(this, _onTransactionParamsUpdated); -+ __privateAdd(this, _updateSimulationData); -+ __privateAdd(this, _onGasFeePollerTransactionUpdate); -+ __privateAdd(this, _getNetworkClientId); -+ __privateAdd(this, _getGlobalNetworkClientId); -+ __privateAdd(this, _getGlobalChainId); -+ __privateAdd(this, _isCustomNetwork); -+ __privateAdd(this, _getSelectedAccount); -+ __privateAdd(this, _internalEvents, new EventEmitter()); -+ this.approvingTransactionIds = /* @__PURE__ */ new Set(); -+ this.mutex = new Mutex(); -+ __privateAdd(this, _incomingTransactionOptions, void 0); -+ __privateAdd(this, _pendingTransactionOptions, void 0); -+ this.signAbortCallbacks = /* @__PURE__ */ new Map(); -+ __privateAdd(this, _transactionHistoryLimit, void 0); -+ __privateAdd(this, _isSimulationEnabled, void 0); -+ __privateAdd(this, _testGasFeeFlows, void 0); -+ __privateAdd(this, _multichainTrackingHelper, void 0); -+ __privateAdd(this, _checkForPendingTransactionAndStartPolling, () => { -+ this.pendingTransactionTracker.startIfPendingTransactions(); -+ __privateGet(this, _multichainTrackingHelper).checkForPendingTransactionAndStartPolling(); -+ }); -+ this.messagingSystem = messenger; -+ this.getNetworkState = getNetworkState; -+ this.isSendFlowHistoryDisabled = disableSendFlowHistory ?? false; -+ this.isHistoryDisabled = disableHistory ?? false; -+ this.isSwapsDisabled = disableSwaps ?? false; -+ __privateSet(this, _isSimulationEnabled, isSimulationEnabled ?? (() => true)); -+ this.registry = new MethodRegistry({ provider }); -+ this.getSavedGasFees = getSavedGasFees ?? ((_chainId) => void 0); -+ this.getCurrentAccountEIP1559Compatibility = getCurrentAccountEIP1559Compatibility ?? (() => Promise.resolve(true)); -+ this.getCurrentNetworkEIP1559Compatibility = getCurrentNetworkEIP1559Compatibility; -+ this.getGasFeeEstimates = getGasFeeEstimates || (() => Promise.resolve({})); -+ this.getPermittedAccounts = getPermittedAccounts; -+ this.getExternalPendingTransactions = getExternalPendingTransactions ?? (() => []); -+ this.securityProviderRequest = securityProviderRequest; -+ __privateSet(this, _incomingTransactionOptions, incomingTransactions); -+ __privateSet(this, _pendingTransactionOptions, pendingTransactions); -+ __privateSet(this, _transactionHistoryLimit, transactionHistoryLimit); -+ this.sign = sign; -+ __privateSet(this, _testGasFeeFlows, testGasFeeFlows === true); -+ this.afterSign = hooks?.afterSign ?? (() => true); -+ this.beforeCheckPendingTransaction = hooks?.beforeCheckPendingTransaction ?? /* istanbul ignore next */ -+ (() => true); -+ this.beforePublish = hooks?.beforePublish ?? (() => true); -+ this.getAdditionalSignArguments = hooks?.getAdditionalSignArguments ?? (() => []); -+ this.publish = hooks?.publish ?? (() => Promise.resolve({ transactionHash: void 0 })); -+ this.nonceTracker = __privateMethod(this, _createNonceTracker, createNonceTracker_fn).call(this, { -+ provider, -+ blockTracker -+ }); -+ const findNetworkClientIdByChainId = (chainId) => { -+ return this.messagingSystem.call( -+ `NetworkController:findNetworkClientIdByChainId`, -+ chainId -+ ); -+ }; -+ __privateSet(this, _multichainTrackingHelper, new MultichainTrackingHelper({ -+ isMultichainEnabled, -+ provider, -+ nonceTracker: this.nonceTracker, -+ incomingTransactionOptions: incomingTransactions, -+ findNetworkClientIdByChainId, -+ getNetworkClientById: (networkClientId) => { -+ return this.messagingSystem.call( -+ `NetworkController:getNetworkClientById`, -+ networkClientId -+ ); -+ }, -+ getNetworkClientRegistry, -+ removeIncomingTransactionHelperListeners: __privateMethod(this, _removeIncomingTransactionHelperListeners, removeIncomingTransactionHelperListeners_fn).bind(this), -+ removePendingTransactionTrackerListeners: __privateMethod(this, _removePendingTransactionTrackerListeners, removePendingTransactionTrackerListeners_fn).bind(this), -+ createNonceTracker: __privateMethod(this, _createNonceTracker, createNonceTracker_fn).bind(this), -+ createIncomingTransactionHelper: __privateMethod(this, _createIncomingTransactionHelper, createIncomingTransactionHelper_fn).bind(this), -+ createPendingTransactionTracker: __privateMethod(this, _createPendingTransactionTracker, createPendingTransactionTracker_fn).bind(this), -+ onNetworkStateChange: (listener) => { -+ this.messagingSystem.subscribe( -+ "NetworkController:stateChange", -+ listener -+ ); -+ } -+ })); -+ __privateGet(this, _multichainTrackingHelper).initialize(); -+ const etherscanRemoteTransactionSource = new EtherscanRemoteTransactionSource({ -+ includeTokenTransfers: incomingTransactions.includeTokenTransfers -+ }); -+ this.incomingTransactionHelper = __privateMethod(this, _createIncomingTransactionHelper, createIncomingTransactionHelper_fn).call(this, { -+ blockTracker, -+ etherscanRemoteTransactionSource -+ }); -+ this.pendingTransactionTracker = __privateMethod(this, _createPendingTransactionTracker, createPendingTransactionTracker_fn).call(this, { -+ provider, -+ blockTracker -+ }); -+ this.gasFeeFlows = __privateMethod(this, _getGasFeeFlows, getGasFeeFlows_fn).call(this); -+ this.layer1GasFeeFlows = __privateMethod(this, _getLayer1GasFeeFlows, getLayer1GasFeeFlows_fn).call(this); -+ const gasFeePoller = new GasFeePoller({ -+ findNetworkClientIdByChainId, -+ gasFeeFlows: this.gasFeeFlows, -+ getGasFeeControllerEstimates: this.getGasFeeEstimates, -+ getProvider: (chainId, networkClientId) => __privateGet(this, _multichainTrackingHelper).getProvider({ -+ networkClientId, -+ chainId -+ }), -+ getTransactions: () => this.state.transactions, -+ layer1GasFeeFlows: this.layer1GasFeeFlows, -+ onStateChange: (listener) => { -+ this.messagingSystem.subscribe( -+ "TransactionController:stateChange", -+ listener -+ ); -+ } -+ }); -+ gasFeePoller.hub.on( -+ "transaction-updated", -+ __privateMethod(this, _onGasFeePollerTransactionUpdate, onGasFeePollerTransactionUpdate_fn).bind(this) -+ ); -+ this.messagingSystem.subscribe( -+ "TransactionController:stateChange", -+ __privateGet(this, _checkForPendingTransactionAndStartPolling) -+ ); -+ onNetworkStateChange(() => { -+ projectLogger("Detected network change", this.getChainId()); -+ this.pendingTransactionTracker.startIfPendingTransactions(); -+ this.onBootCleanup(); -+ }); -+ this.onBootCleanup(); -+ __privateGet(this, _checkForPendingTransactionAndStartPolling).call(this); -+ } -+ failTransaction(transactionMeta, error, actionId) { -+ let newTransactionMeta; -+ try { -+ newTransactionMeta = __privateMethod(this, _updateTransactionInternal, updateTransactionInternal_fn).call(this, { -+ transactionId: transactionMeta.id, -+ note: "TransactionController#failTransaction - Add error message and set status to failed", -+ skipValidation: true -+ }, (draftTransactionMeta) => { -+ draftTransactionMeta.status = "failed" /* failed */; -+ draftTransactionMeta.error = normalizeTxError(error); -+ }); -+ } catch (err) { -+ projectLogger("Failed to mark transaction as failed", err); -+ newTransactionMeta = { -+ ...transactionMeta, -+ status: "failed" /* failed */, -+ error: normalizeTxError(error) -+ }; -+ } -+ this.messagingSystem.publish(`${controllerName}:transactionFailed`, { -+ actionId, -+ error: error.message, -+ transactionMeta: newTransactionMeta -+ }); -+ this.onTransactionStatusChange(newTransactionMeta); -+ this.messagingSystem.publish( -+ `${controllerName}:transactionFinished`, -+ newTransactionMeta -+ ); -+ __privateGet(this, _internalEvents).emit( -+ `${transactionMeta.id}:finished`, -+ newTransactionMeta -+ ); -+ } -+ async registryLookup(fourBytePrefix) { -+ const registryMethod = await this.registry.lookup(fourBytePrefix); -+ if (!registryMethod) { -+ return { -+ registryMethod: "", -+ parsedRegistryMethod: { name: void 0, args: void 0 } -+ }; -+ } -+ const parsedRegistryMethod = this.registry.parse(registryMethod); -+ return { registryMethod, parsedRegistryMethod }; -+ } -+ /** -+ * Stops polling and removes listeners to prepare the controller for garbage collection. -+ */ -+ destroy() { -+ __privateMethod(this, _stopAllTracking, stopAllTracking_fn).call(this); -+ } -+ /** -+ * Handle new method data request. -+ * -+ * @param fourBytePrefix - The method prefix. -+ * @returns The method data object corresponding to the given signature prefix. -+ */ -+ async handleMethodData(fourBytePrefix) { -+ const releaseLock = await this.mutex.acquire(); -+ try { -+ const { methodData } = this.state; -+ const knownMethod = Object.keys(methodData).find( -+ (knownFourBytePrefix) => fourBytePrefix === knownFourBytePrefix -+ ); -+ if (knownMethod) { -+ return methodData[fourBytePrefix]; -+ } -+ const registry = await this.registryLookup(fourBytePrefix); -+ this.update((state) => { -+ state.methodData[fourBytePrefix] = registry; -+ }); -+ return registry; -+ } finally { -+ releaseLock(); -+ } -+ } -+ /** -+ * Add a new unapproved transaction to state. Parameters will be validated, a -+ * unique transaction id will be generated, and gas and gasPrice will be calculated -+ * if not provided. If A `:unapproved` hub event will be emitted once added. -+ * -+ * @param txParams - Standard parameters for an Ethereum transaction. -+ * @param opts - Additional options to control how the transaction is added. -+ * @param opts.actionId - Unique ID to prevent duplicate requests. -+ * @param opts.deviceConfirmedOn - An enum to indicate what device confirmed the transaction. -+ * @param opts.method - RPC method that requested the transaction. -+ * @param opts.origin - The origin of the transaction request, such as a dApp hostname. -+ * @param opts.requireApproval - Whether the transaction requires approval by the user, defaults to true unless explicitly disabled. -+ * @param opts.securityAlertResponse - Response from security validator. -+ * @param opts.sendFlowHistory - The sendFlowHistory entries to add. -+ * @param opts.type - Type of transaction to add, such as 'cancel' or 'swap'. -+ * @param opts.swaps - Options for swaps transactions. -+ * @param opts.swaps.hasApproveTx - Whether the transaction has an approval transaction. -+ * @param opts.swaps.meta - Metadata for swap transaction. -+ * @param opts.networkClientId - The id of the network client for this transaction. -+ * @returns Object containing a promise resolving to the transaction hash if approved. -+ */ -+ async addTransaction(txParams, { -+ actionId, -+ deviceConfirmedOn, -+ method, -+ origin, -+ requireApproval, -+ securityAlertResponse, -+ sendFlowHistory, -+ swaps = {}, -+ type, -+ networkClientId: requestNetworkClientId -+ } = {}) { -+ projectLogger("Adding transaction", txParams); -+ txParams = normalizeTransactionParams(txParams); -+ if (requestNetworkClientId && !__privateGet(this, _multichainTrackingHelper).has(requestNetworkClientId)) { -+ throw new Error( -+ "The networkClientId for this transaction could not be found" -+ ); -+ } -+ const networkClientId = requestNetworkClientId ?? __privateMethod(this, _getGlobalNetworkClientId, getGlobalNetworkClientId_fn).call(this); -+ const isEIP1559Compatible = await this.getEIP1559Compatibility( -+ networkClientId -+ ); -+ validateTxParams(txParams, isEIP1559Compatible); -+ if (origin) { -+ await validateTransactionOrigin( -+ await this.getPermittedAccounts(origin), -+ __privateMethod(this, _getSelectedAccount, getSelectedAccount_fn).call(this).address, -+ txParams.from, -+ origin -+ ); -+ } -+ const dappSuggestedGasFees = this.generateDappSuggestedGasFees( -+ txParams, -+ origin -+ ); -+ const chainId = this.getChainId(networkClientId); -+ const ethQuery = __privateGet(this, _multichainTrackingHelper).getEthQuery({ -+ networkClientId, -+ chainId -+ }); -+ const transactionType = type ?? (await determineTransactionType(txParams, ethQuery)).type; -+ const existingTransactionMeta = this.getTransactionWithActionId(actionId); -+ let addedTransactionMeta = existingTransactionMeta ? cloneDeep(existingTransactionMeta) : { -+ // Add actionId to txMeta to check if same actionId is seen again -+ actionId, -+ chainId, -+ dappSuggestedGasFees, -+ deviceConfirmedOn, -+ id: random(), -+ origin, -+ securityAlertResponse, -+ status: "unapproved" /* unapproved */, -+ time: Date.now(), -+ txParams, -+ userEditedGasLimit: false, -+ verifiedOnBlockchain: false, -+ type: transactionType, -+ networkClientId -+ }; -+ await this.updateGasProperties(addedTransactionMeta); -+ if (!existingTransactionMeta) { -+ if (method && this.securityProviderRequest) { -+ const securityProviderResponse = await this.securityProviderRequest( -+ addedTransactionMeta, -+ method -+ ); -+ addedTransactionMeta.securityProviderResponse = securityProviderResponse; -+ } -+ if (!this.isSendFlowHistoryDisabled) { -+ addedTransactionMeta.sendFlowHistory = sendFlowHistory ?? []; -+ } -+ if (!this.isHistoryDisabled) { -+ addedTransactionMeta = addInitialHistorySnapshot(addedTransactionMeta); -+ } -+ addedTransactionMeta = updateSwapsTransaction( -+ addedTransactionMeta, -+ transactionType, -+ swaps, -+ { -+ isSwapsDisabled: this.isSwapsDisabled, -+ cancelTransaction: this.cancelTransaction.bind(this), -+ messenger: this.messagingSystem -+ } -+ ); -+ this.addMetadata(addedTransactionMeta); -+ if (requireApproval !== false) { -+ __privateMethod(this, _updateSimulationData, updateSimulationData_fn).call(this, addedTransactionMeta); -+ } else { -+ projectLogger("Skipping simulation as approval not required"); -+ } -+ this.messagingSystem.publish( -+ `${controllerName}:unapprovedTransactionAdded`, -+ addedTransactionMeta -+ ); -+ } -+ return { -+ result: this.processApproval(addedTransactionMeta, { -+ isExisting: Boolean(existingTransactionMeta), -+ requireApproval, -+ actionId -+ }), -+ transactionMeta: addedTransactionMeta -+ }; -+ } -+ startIncomingTransactionPolling(networkClientIds = []) { -+ if (networkClientIds.length === 0) { -+ this.incomingTransactionHelper.start(); -+ return; -+ } -+ __privateGet(this, _multichainTrackingHelper).startIncomingTransactionPolling( -+ networkClientIds -+ ); -+ } -+ stopIncomingTransactionPolling(networkClientIds = []) { -+ if (networkClientIds.length === 0) { -+ this.incomingTransactionHelper.stop(); -+ return; -+ } -+ __privateGet(this, _multichainTrackingHelper).stopIncomingTransactionPolling( -+ networkClientIds -+ ); -+ } -+ stopAllIncomingTransactionPolling() { -+ this.incomingTransactionHelper.stop(); -+ __privateGet(this, _multichainTrackingHelper).stopAllIncomingTransactionPolling(); -+ } -+ async updateIncomingTransactions(networkClientIds = []) { -+ if (networkClientIds.length === 0) { -+ await this.incomingTransactionHelper.update(); -+ return; -+ } -+ await __privateGet(this, _multichainTrackingHelper).updateIncomingTransactions( -+ networkClientIds -+ ); -+ } -+ /** -+ * Attempts to cancel a transaction based on its ID by setting its status to "rejected" -+ * and emitting a `:finished` hub event. -+ * -+ * @param transactionId - The ID of the transaction to cancel. -+ * @param gasValues - The gas values to use for the cancellation transaction. -+ * @param options - The options for the cancellation transaction. -+ * @param options.actionId - Unique ID to prevent duplicate requests. -+ * @param options.estimatedBaseFee - The estimated base fee of the transaction. -+ */ -+ async stopTransaction(transactionId, gasValues, { -+ estimatedBaseFee, -+ actionId -+ } = {}) { -+ if (this.getTransactionWithActionId(actionId)) { -+ return; -+ } -+ if (gasValues) { -+ gasValues = normalizeGasFeeValues(gasValues); -+ validateGasValues(gasValues); -+ } -+ projectLogger("Creating cancel transaction", transactionId, gasValues); -+ const transactionMeta = this.getTransaction(transactionId); -+ if (!transactionMeta) { -+ return; -+ } -+ if (!this.sign) { -+ throw new Error("No sign method defined."); -+ } -+ const minGasPrice = getIncreasedPriceFromExisting( -+ transactionMeta.txParams.gasPrice, -+ CANCEL_RATE -+ ); -+ const gasPriceFromValues = isGasPriceValue(gasValues) && gasValues.gasPrice; -+ const newGasPrice = gasPriceFromValues && validateMinimumIncrease(gasPriceFromValues, minGasPrice) || minGasPrice; -+ const existingMaxFeePerGas = transactionMeta.txParams?.maxFeePerGas; -+ const minMaxFeePerGas = getIncreasedPriceFromExisting( -+ existingMaxFeePerGas, -+ CANCEL_RATE -+ ); -+ const maxFeePerGasValues = isFeeMarketEIP1559Values(gasValues) && gasValues.maxFeePerGas; -+ const newMaxFeePerGas = maxFeePerGasValues && validateMinimumIncrease(maxFeePerGasValues, minMaxFeePerGas) || existingMaxFeePerGas && minMaxFeePerGas; -+ const existingMaxPriorityFeePerGas = transactionMeta.txParams?.maxPriorityFeePerGas; -+ const minMaxPriorityFeePerGas = getIncreasedPriceFromExisting( -+ existingMaxPriorityFeePerGas, -+ CANCEL_RATE -+ ); -+ const maxPriorityFeePerGasValues = isFeeMarketEIP1559Values(gasValues) && gasValues.maxPriorityFeePerGas; -+ const newMaxPriorityFeePerGas = maxPriorityFeePerGasValues && validateMinimumIncrease( -+ maxPriorityFeePerGasValues, -+ minMaxPriorityFeePerGas -+ ) || existingMaxPriorityFeePerGas && minMaxPriorityFeePerGas; -+ const newTxParams = newMaxFeePerGas && newMaxPriorityFeePerGas ? { -+ from: transactionMeta.txParams.from, -+ gasLimit: transactionMeta.txParams.gas, -+ maxFeePerGas: newMaxFeePerGas, -+ maxPriorityFeePerGas: newMaxPriorityFeePerGas, -+ type: "0x2" /* feeMarket */, -+ nonce: transactionMeta.txParams.nonce, -+ to: transactionMeta.txParams.from, -+ value: "0x0" -+ } : { -+ from: transactionMeta.txParams.from, -+ gasLimit: transactionMeta.txParams.gas, -+ gasPrice: newGasPrice, -+ nonce: transactionMeta.txParams.nonce, -+ to: transactionMeta.txParams.from, -+ value: "0x0" -+ }; -+ const unsignedEthTx = this.prepareUnsignedEthTx( -+ transactionMeta.chainId, -+ newTxParams -+ ); -+ const signedTx = await this.sign( -+ unsignedEthTx, -+ transactionMeta.txParams.from -+ ); -+ const rawTx = bufferToHex(signedTx.serialize()); -+ const newFee = newTxParams.maxFeePerGas ?? newTxParams.gasPrice; -+ const oldFee = newTxParams.maxFeePerGas ? transactionMeta.txParams.maxFeePerGas : transactionMeta.txParams.gasPrice; -+ projectLogger("Submitting cancel transaction", { -+ oldFee, -+ newFee, -+ txParams: newTxParams -+ }); -+ const ethQuery = __privateGet(this, _multichainTrackingHelper).getEthQuery({ -+ networkClientId: transactionMeta.networkClientId, -+ chainId: transactionMeta.chainId -+ }); -+ const hash = await this.publishTransactionForRetry( -+ ethQuery, -+ rawTx, -+ transactionMeta -+ ); -+ const cancelTransactionMeta = { -+ actionId, -+ chainId: transactionMeta.chainId, -+ networkClientId: transactionMeta.networkClientId, -+ estimatedBaseFee, -+ hash, -+ id: random(), -+ originalGasEstimate: transactionMeta.txParams.gas, -+ rawTx, -+ status: "submitted" /* submitted */, -+ time: Date.now(), -+ type: "cancel" /* cancel */, -+ txParams: newTxParams -+ }; -+ this.addMetadata(cancelTransactionMeta); -+ this.messagingSystem.publish(`${controllerName}:transactionApproved`, { -+ transactionMeta: cancelTransactionMeta, -+ actionId -+ }); -+ this.messagingSystem.publish(`${controllerName}:transactionSubmitted`, { -+ transactionMeta: cancelTransactionMeta, -+ actionId -+ }); -+ this.messagingSystem.publish( -+ `${controllerName}:transactionFinished`, -+ cancelTransactionMeta -+ ); -+ __privateGet(this, _internalEvents).emit( -+ `${transactionMeta.id}:finished`, -+ cancelTransactionMeta -+ ); -+ } -+ /** -+ * Attempts to speed up a transaction increasing transaction gasPrice by ten percent. -+ * -+ * @param transactionId - The ID of the transaction to speed up. -+ * @param gasValues - The gas values to use for the speed up transaction. -+ * @param options - The options for the speed up transaction. -+ * @param options.actionId - Unique ID to prevent duplicate requests -+ * @param options.estimatedBaseFee - The estimated base fee of the transaction. -+ */ -+ async speedUpTransaction(transactionId, gasValues, { -+ actionId, -+ estimatedBaseFee -+ } = {}) { -+ if (this.getTransactionWithActionId(actionId)) { -+ return; -+ } -+ if (gasValues) { -+ gasValues = normalizeGasFeeValues(gasValues); -+ validateGasValues(gasValues); -+ } -+ projectLogger("Creating speed up transaction", transactionId, gasValues); -+ const transactionMeta = this.getTransaction(transactionId); -+ if (!transactionMeta) { -+ return; -+ } -+ if (!this.sign) { -+ throw new Error("No sign method defined."); -+ } -+ const minGasPrice = getIncreasedPriceFromExisting( -+ transactionMeta.txParams.gasPrice, -+ SPEED_UP_RATE -+ ); -+ const gasPriceFromValues = isGasPriceValue(gasValues) && gasValues.gasPrice; -+ const newGasPrice = gasPriceFromValues && validateMinimumIncrease(gasPriceFromValues, minGasPrice) || minGasPrice; -+ const existingMaxFeePerGas = transactionMeta.txParams?.maxFeePerGas; -+ const minMaxFeePerGas = getIncreasedPriceFromExisting( -+ existingMaxFeePerGas, -+ SPEED_UP_RATE -+ ); -+ const maxFeePerGasValues = isFeeMarketEIP1559Values(gasValues) && gasValues.maxFeePerGas; -+ const newMaxFeePerGas = maxFeePerGasValues && validateMinimumIncrease(maxFeePerGasValues, minMaxFeePerGas) || existingMaxFeePerGas && minMaxFeePerGas; -+ const existingMaxPriorityFeePerGas = transactionMeta.txParams?.maxPriorityFeePerGas; -+ const minMaxPriorityFeePerGas = getIncreasedPriceFromExisting( -+ existingMaxPriorityFeePerGas, -+ SPEED_UP_RATE -+ ); -+ const maxPriorityFeePerGasValues = isFeeMarketEIP1559Values(gasValues) && gasValues.maxPriorityFeePerGas; -+ const newMaxPriorityFeePerGas = maxPriorityFeePerGasValues && validateMinimumIncrease( -+ maxPriorityFeePerGasValues, -+ minMaxPriorityFeePerGas -+ ) || existingMaxPriorityFeePerGas && minMaxPriorityFeePerGas; -+ const txParams = newMaxFeePerGas && newMaxPriorityFeePerGas ? { -+ ...transactionMeta.txParams, -+ gasLimit: transactionMeta.txParams.gas, -+ maxFeePerGas: newMaxFeePerGas, -+ maxPriorityFeePerGas: newMaxPriorityFeePerGas, -+ type: "0x2" /* feeMarket */ -+ } : { -+ ...transactionMeta.txParams, -+ gasLimit: transactionMeta.txParams.gas, -+ gasPrice: newGasPrice -+ }; -+ const unsignedEthTx = this.prepareUnsignedEthTx( -+ transactionMeta.chainId, -+ txParams -+ ); -+ const signedTx = await this.sign( -+ unsignedEthTx, -+ transactionMeta.txParams.from -+ ); -+ const transactionMetaWithRsv = this.updateTransactionMetaRSV( -+ transactionMeta, -+ signedTx -+ ); -+ const rawTx = bufferToHex(signedTx.serialize()); -+ const newFee = txParams.maxFeePerGas ?? txParams.gasPrice; -+ const oldFee = txParams.maxFeePerGas ? transactionMetaWithRsv.txParams.maxFeePerGas : transactionMetaWithRsv.txParams.gasPrice; -+ projectLogger("Submitting speed up transaction", { oldFee, newFee, txParams }); -+ const ethQuery = __privateGet(this, _multichainTrackingHelper).getEthQuery({ -+ networkClientId: transactionMeta.networkClientId, -+ chainId: transactionMeta.chainId -+ }); -+ const hash = await this.publishTransactionForRetry( -+ ethQuery, -+ rawTx, -+ transactionMeta -+ ); -+ const baseTransactionMeta = { -+ ...transactionMetaWithRsv, -+ estimatedBaseFee, -+ id: random(), -+ time: Date.now(), -+ hash, -+ actionId, -+ originalGasEstimate: transactionMeta.txParams.gas, -+ type: "retry" /* retry */, -+ originalType: transactionMeta.type -+ }; -+ const newTransactionMeta = newMaxFeePerGas && newMaxPriorityFeePerGas ? { -+ ...baseTransactionMeta, -+ txParams: { -+ ...transactionMeta.txParams, -+ maxFeePerGas: newMaxFeePerGas, -+ maxPriorityFeePerGas: newMaxPriorityFeePerGas -+ } -+ } : { -+ ...baseTransactionMeta, -+ txParams: { -+ ...transactionMeta.txParams, -+ gasPrice: newGasPrice -+ } -+ }; -+ this.addMetadata(newTransactionMeta); -+ this.messagingSystem.publish(`${controllerName}:transactionApproved`, { -+ transactionMeta: newTransactionMeta, -+ actionId -+ }); -+ this.messagingSystem.publish(`${controllerName}:transactionSubmitted`, { -+ transactionMeta: newTransactionMeta, -+ actionId -+ }); -+ this.messagingSystem.publish( -+ `${controllerName}:speedupTransactionAdded`, -+ newTransactionMeta -+ ); -+ } -+ /** -+ * Estimates required gas for a given transaction. -+ * -+ * @param transaction - The transaction to estimate gas for. -+ * @param networkClientId - The network client id to use for the estimate. -+ * @returns The gas and gas price. -+ */ -+ async estimateGas(transaction, networkClientId) { -+ const ethQuery = __privateGet(this, _multichainTrackingHelper).getEthQuery({ -+ networkClientId -+ }); -+ const { estimatedGas, simulationFails } = await estimateGas( -+ transaction, -+ ethQuery -+ ); -+ return { gas: estimatedGas, simulationFails }; -+ } -+ /** -+ * Estimates required gas for a given transaction and add additional gas buffer with the given multiplier. -+ * -+ * @param transaction - The transaction params to estimate gas for. -+ * @param multiplier - The multiplier to use for the gas buffer. -+ * @param networkClientId - The network client id to use for the estimate. -+ */ -+ async estimateGasBuffered(transaction, multiplier, networkClientId) { -+ const ethQuery = __privateGet(this, _multichainTrackingHelper).getEthQuery({ -+ networkClientId -+ }); -+ const { blockGasLimit, estimatedGas, simulationFails } = await estimateGas( -+ transaction, -+ ethQuery -+ ); -+ const gas = addGasBuffer(estimatedGas, blockGasLimit, multiplier); -+ return { -+ gas, -+ simulationFails -+ }; -+ } -+ /** -+ * Updates an existing transaction in state. -+ * -+ * @param transactionMeta - The new transaction to store in state. -+ * @param note - A note or update reason to include in the transaction history. -+ */ -+ updateTransaction(transactionMeta, note) { -+ const { id: transactionId } = transactionMeta; -+ __privateMethod(this, _updateTransactionInternal, updateTransactionInternal_fn).call(this, { transactionId, note }, () => ({ -+ ...transactionMeta -+ })); -+ } -+ /** -+ * Update the security alert response for a transaction. -+ * -+ * @param transactionId - ID of the transaction. -+ * @param securityAlertResponse - The new security alert response for the transaction. -+ */ -+ updateSecurityAlertResponse(transactionId, securityAlertResponse) { -+ if (!securityAlertResponse) { -+ throw new Error( -+ "updateSecurityAlertResponse: securityAlertResponse should not be null" -+ ); -+ } -+ const transactionMeta = this.getTransaction(transactionId); -+ if (!transactionMeta) { -+ throw new Error( -+ `Cannot update security alert response as no transaction metadata found` -+ ); -+ } -+ const updatedTransactionMeta = { -+ ...transactionMeta, -+ securityAlertResponse -+ }; -+ this.updateTransaction( -+ updatedTransactionMeta, -+ `${controllerName}:updatesecurityAlertResponse - securityAlertResponse updated` -+ ); -+ } -+ /** -+ * Removes all transactions from state, optionally based on the current network. -+ * -+ * @param ignoreNetwork - Determines whether to wipe all transactions, or just those on the -+ * current network. If `true`, all transactions are wiped. -+ * @param address - If specified, only transactions originating from this address will be -+ * wiped on current network. -+ */ -+ wipeTransactions(ignoreNetwork, address) { -+ if (ignoreNetwork && !address) { -+ this.update((state) => { -+ state.transactions = []; -+ }); -+ return; -+ } -+ const currentChainId = this.getChainId(); -+ const newTransactions = this.state.transactions.filter( -+ ({ chainId, txParams }) => { -+ const isMatchingNetwork = ignoreNetwork || chainId === currentChainId; -+ if (!isMatchingNetwork) { -+ return true; -+ } -+ const isMatchingAddress = !address || txParams.from?.toLowerCase() === address.toLowerCase(); -+ return !isMatchingAddress; -+ } -+ ); -+ this.update((state) => { -+ state.transactions = this.trimTransactionsForState(newTransactions); -+ }); -+ } -+ /** -+ * Adds external provided transaction to state as confirmed transaction. -+ * -+ * @param transactionMeta - TransactionMeta to add transactions. -+ * @param transactionReceipt - TransactionReceipt of the external transaction. -+ * @param baseFeePerGas - Base fee per gas of the external transaction. -+ */ -+ async confirmExternalTransaction(transactionMeta, transactionReceipt, baseFeePerGas) { -+ const newTransactionMeta = this.addExternalTransaction(transactionMeta); -+ try { -+ const transactionId = newTransactionMeta.id; -+ const updatedTransactionMeta = { -+ ...newTransactionMeta, -+ status: "confirmed" /* confirmed */, -+ txReceipt: transactionReceipt -+ }; -+ if (baseFeePerGas) { -+ updatedTransactionMeta.baseFeePerGas = baseFeePerGas; -+ } -+ this.markNonceDuplicatesDropped(transactionId); -+ this.updateTransaction( -+ updatedTransactionMeta, -+ `${controllerName}:confirmExternalTransaction - Add external transaction` -+ ); -+ this.onTransactionStatusChange(updatedTransactionMeta); -+ this.updatePostBalance(updatedTransactionMeta); -+ this.messagingSystem.publish( -+ `${controllerName}:transactionConfirmed`, -+ updatedTransactionMeta -+ ); -+ } catch (error) { -+ console.error("Failed to confirm external transaction", error); -+ } -+ } -+ /** -+ * Append new send flow history to a transaction. -+ * -+ * @param transactionID - The ID of the transaction to update. -+ * @param currentSendFlowHistoryLength - The length of the current sendFlowHistory array. -+ * @param sendFlowHistoryToAdd - The sendFlowHistory entries to add. -+ * @returns The updated transactionMeta. -+ */ -+ updateTransactionSendFlowHistory(transactionID, currentSendFlowHistoryLength, sendFlowHistoryToAdd) { -+ if (this.isSendFlowHistoryDisabled) { -+ throw new Error( -+ "Send flow history is disabled for the current transaction controller" -+ ); -+ } -+ const transactionMeta = this.getTransaction(transactionID); -+ if (!transactionMeta) { -+ throw new Error( -+ `Cannot update send flow history as no transaction metadata found` -+ ); -+ } -+ validateIfTransactionUnapproved( -+ transactionMeta, -+ "updateTransactionSendFlowHistory" -+ ); -+ const sendFlowHistory = transactionMeta.sendFlowHistory ?? []; -+ if (currentSendFlowHistoryLength === sendFlowHistory.length) { -+ const updatedTransactionMeta = { -+ ...transactionMeta, -+ sendFlowHistory: [...sendFlowHistory, ...sendFlowHistoryToAdd] -+ }; -+ this.updateTransaction( -+ updatedTransactionMeta, -+ `${controllerName}:updateTransactionSendFlowHistory - sendFlowHistory updated` -+ ); -+ } -+ return this.getTransaction(transactionID); -+ } -+ /** -+ * Update the gas values of a transaction. -+ * -+ * @param transactionId - The ID of the transaction to update. -+ * @param gasValues - Gas values to update. -+ * @param gasValues.gas - Same as transaction.gasLimit. -+ * @param gasValues.gasLimit - Maxmimum number of units of gas to use for this transaction. -+ * @param gasValues.gasPrice - Price per gas for legacy transactions. -+ * @param gasValues.maxPriorityFeePerGas - Maximum amount per gas to give to validator as incentive. -+ * @param gasValues.maxFeePerGas - Maximum amount per gas to pay for the transaction, including the priority fee. -+ * @param gasValues.estimateUsed - Which estimate level was used. -+ * @param gasValues.estimateSuggested - Which estimate level that the API suggested. -+ * @param gasValues.defaultGasEstimates - The default estimate for gas. -+ * @param gasValues.originalGasEstimate - Original estimate for gas. -+ * @param gasValues.userEditedGasLimit - The gas limit supplied by user. -+ * @param gasValues.userFeeLevel - Estimate level user selected. -+ * @returns The updated transactionMeta. -+ */ -+ updateTransactionGasFees(transactionId, { -+ defaultGasEstimates, -+ estimateUsed, -+ estimateSuggested, -+ gas, -+ gasLimit, -+ gasPrice, -+ maxPriorityFeePerGas, -+ maxFeePerGas, -+ originalGasEstimate, -+ userEditedGasLimit, -+ userFeeLevel -+ }) { -+ const transactionMeta = this.getTransaction(transactionId); -+ if (!transactionMeta) { -+ throw new Error( -+ `Cannot update transaction as no transaction metadata found` -+ ); -+ } -+ validateIfTransactionUnapproved( -+ transactionMeta, -+ "updateTransactionGasFees" -+ ); -+ let transactionGasFees = { -+ txParams: { -+ gas, -+ gasLimit, -+ gasPrice, -+ maxPriorityFeePerGas, -+ maxFeePerGas -+ }, -+ defaultGasEstimates, -+ estimateUsed, -+ estimateSuggested, -+ originalGasEstimate, -+ userEditedGasLimit, -+ userFeeLevel -+ // TODO: Replace `any` with type -+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -+ }; -+ transactionGasFees.txParams = pickBy(transactionGasFees.txParams); -+ transactionGasFees = pickBy(transactionGasFees); -+ const updatedMeta = merge({}, transactionMeta, transactionGasFees); -+ this.updateTransaction( -+ updatedMeta, -+ `${controllerName}:updateTransactionGasFees - gas values updated` -+ ); -+ return this.getTransaction(transactionId); -+ } -+ /** -+ * Update the previous gas values of a transaction. -+ * -+ * @param transactionId - The ID of the transaction to update. -+ * @param previousGas - Previous gas values to update. -+ * @param previousGas.gasLimit - Maxmimum number of units of gas to use for this transaction. -+ * @param previousGas.maxFeePerGas - Maximum amount per gas to pay for the transaction, including the priority fee. -+ * @param previousGas.maxPriorityFeePerGas - Maximum amount per gas to give to validator as incentive. -+ * @returns The updated transactionMeta. -+ */ -+ updatePreviousGasParams(transactionId, { -+ gasLimit, -+ maxFeePerGas, -+ maxPriorityFeePerGas -+ }) { -+ const transactionMeta = this.getTransaction(transactionId); -+ if (!transactionMeta) { -+ throw new Error( -+ `Cannot update transaction as no transaction metadata found` -+ ); -+ } -+ validateIfTransactionUnapproved(transactionMeta, "updatePreviousGasParams"); -+ const transactionPreviousGas = { -+ previousGas: { -+ gasLimit, -+ maxFeePerGas, -+ maxPriorityFeePerGas -+ } -+ // TODO: Replace `any` with type -+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -+ }; -+ transactionPreviousGas.previousGas = pickBy( -+ transactionPreviousGas.previousGas -+ ); -+ const updatedMeta = merge({}, transactionMeta, transactionPreviousGas); -+ this.updateTransaction( -+ updatedMeta, -+ `${controllerName}:updatePreviousGasParams - Previous gas values updated` -+ ); -+ return this.getTransaction(transactionId); -+ } -+ async getNonceLock(address, networkClientId) { -+ return __privateGet(this, _multichainTrackingHelper).getNonceLock( -+ address, -+ networkClientId -+ ); -+ } -+ /** -+ * Updates the editable parameters of a transaction. -+ * -+ * @param txId - The ID of the transaction to update. -+ * @param params - The editable parameters to update. -+ * @param params.data - Data to pass with the transaction. -+ * @param params.gas - Maximum number of units of gas to use for the transaction. -+ * @param params.gasPrice - Price per gas for legacy transactions. -+ * @param params.from - Address to send the transaction from. -+ * @param params.to - Address to send the transaction to. -+ * @param params.value - Value associated with the transaction. -+ * @returns The updated transaction metadata. -+ */ -+ async updateEditableParams(txId, { -+ data, -+ gas, -+ gasPrice, -+ from, -+ to, -+ value -+ }) { -+ const transactionMeta = this.getTransaction(txId); -+ if (!transactionMeta) { -+ throw new Error( -+ `Cannot update editable params as no transaction metadata found` -+ ); -+ } -+ validateIfTransactionUnapproved(transactionMeta, "updateEditableParams"); -+ const editableParams = { -+ txParams: { -+ data, -+ from, -+ to, -+ value, -+ gas, -+ gasPrice -+ } -+ }; -+ editableParams.txParams = pickBy( -+ editableParams.txParams -+ ); -+ const updatedTransaction = merge({}, transactionMeta, editableParams); -+ const provider = __privateGet(this, _multichainTrackingHelper).getProvider({ -+ chainId: transactionMeta.chainId, -+ networkClientId: transactionMeta.networkClientId -+ }); -+ const ethQuery = new EthQuery(provider); -+ const { type } = await determineTransactionType( -+ updatedTransaction.txParams, -+ ethQuery -+ ); -+ updatedTransaction.type = type; -+ await updateTransactionLayer1GasFee({ -+ layer1GasFeeFlows: this.layer1GasFeeFlows, -+ provider, -+ transactionMeta: updatedTransaction -+ }); -+ this.updateTransaction( -+ updatedTransaction, -+ `Update Editable Params for ${txId}` -+ ); -+ return this.getTransaction(txId); -+ } -+ /** -+ * Signs and returns the raw transaction data for provided transaction params list. -+ * -+ * @param listOfTxParams - The list of transaction params to approve. -+ * @param opts - Options bag. -+ * @param opts.hasNonce - Whether the transactions already have a nonce. -+ * @returns The raw transactions. -+ */ -+ async approveTransactionsWithSameNonce(listOfTxParams = [], { hasNonce } = {}) { -+ projectLogger("Approving transactions with same nonce", { -+ transactions: listOfTxParams -+ }); -+ if (listOfTxParams.length === 0) { -+ return ""; -+ } -+ const initialTx = listOfTxParams[0]; -+ const common = this.getCommonConfiguration(initialTx.chainId); -+ let networkClientId; -+ try { -+ networkClientId = this.messagingSystem.call( -+ `NetworkController:findNetworkClientIdByChainId`, -+ initialTx.chainId -+ ); -+ } catch (err) { -+ projectLogger("failed to find networkClientId from chainId", err); -+ } -+ const initialTxAsEthTx = TransactionFactory.fromTxData(initialTx, { -+ common -+ }); -+ const initialTxAsSerializedHex = bufferToHex(initialTxAsEthTx.serialize()); -+ if (this.approvingTransactionIds.has(initialTxAsSerializedHex)) { -+ return ""; -+ } -+ this.approvingTransactionIds.add(initialTxAsSerializedHex); -+ let rawTransactions, nonceLock; -+ try { -+ const fromAddress = initialTx.from; -+ const requiresNonce = hasNonce !== true; -+ nonceLock = requiresNonce ? await this.getNonceLock(fromAddress, networkClientId) : void 0; -+ const nonce = nonceLock ? add0x(nonceLock.nextNonce.toString(16)) : initialTx.nonce; -+ if (nonceLock) { -+ projectLogger("Using nonce from nonce tracker", nonce, nonceLock.nonceDetails); -+ } -+ rawTransactions = await Promise.all( -+ listOfTxParams.map((txParams) => { -+ txParams.nonce = nonce; -+ return this.signExternalTransaction(txParams.chainId, txParams); -+ }) -+ ); -+ } catch (err) { -+ projectLogger("Error while signing transactions with same nonce", err); -+ throw err; -+ } finally { -+ nonceLock?.releaseLock(); -+ this.approvingTransactionIds.delete(initialTxAsSerializedHex); -+ } -+ return rawTransactions; -+ } -+ /** -+ * Update a custodial transaction. -+ * -+ * @param transactionId - The ID of the transaction to update. -+ * @param options - The custodial transaction options to update. -+ * @param options.errorMessage - The error message to be assigned in case transaction status update to failed. -+ * @param options.hash - The new hash value to be assigned. -+ * @param options.status - The new status value to be assigned. -+ */ -+ updateCustodialTransaction(transactionId, { -+ errorMessage, -+ hash, -+ status -+ }) { -+ const transactionMeta = this.getTransaction(transactionId); -+ if (!transactionMeta) { -+ throw new Error( -+ `Cannot update custodial transaction as no transaction metadata found` -+ ); -+ } -+ if (!transactionMeta.custodyId) { -+ throw new Error("Transaction must be a custodian transaction"); -+ } -+ if (status && ![ -+ "submitted" /* submitted */, -+ "signed" /* signed */, -+ "failed" /* failed */ -+ ].includes(status)) { -+ throw new Error( -+ `Cannot update custodial transaction with status: ${status}` -+ ); -+ } -+ const updatedTransactionMeta = merge( -+ {}, -+ transactionMeta, -+ pickBy({ hash, status }) -+ ); -+ if (updatedTransactionMeta.status === "submitted" /* submitted */) { -+ updatedTransactionMeta.submittedTime = (/* @__PURE__ */ new Date()).getTime(); -+ } -+ if (updatedTransactionMeta.status === "failed" /* failed */) { -+ updatedTransactionMeta.error = normalizeTxError(new Error(errorMessage)); -+ } -+ this.updateTransaction( -+ updatedTransactionMeta, -+ `${controllerName}:updateCustodialTransaction - Custodial transaction updated` -+ ); -+ if (["submitted" /* submitted */, "failed" /* failed */].includes( -+ status -+ )) { -+ this.messagingSystem.publish( -+ `${controllerName}:transactionFinished`, -+ updatedTransactionMeta -+ ); -+ __privateGet(this, _internalEvents).emit( -+ `${updatedTransactionMeta.id}:finished`, -+ updatedTransactionMeta -+ ); -+ } -+ } -+ /** -+ * Search transaction metadata for matching entries. -+ * -+ * @param opts - Options bag. -+ * @param opts.searchCriteria - An object containing values or functions for transaction properties to filter transactions with. -+ * @param opts.initialList - The transactions to search. Defaults to the current state. -+ * @param opts.filterToCurrentNetwork - Whether to filter the results to the current network. Defaults to true. -+ * @param opts.limit - The maximum number of transactions to return. No limit by default. -+ * @returns An array of transactions matching the provided options. -+ */ -+ getTransactions({ -+ searchCriteria = {}, -+ initialList, -+ filterToCurrentNetwork = true, -+ limit -+ } = {}) { -+ const chainId = this.getChainId(); -+ const predicateMethods = mapValues(searchCriteria, (predicate) => { -+ return typeof predicate === "function" ? predicate : ( -+ // TODO: Replace `any` with type -+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -+ (v) => v === predicate -+ ); -+ }); -+ const transactionsToFilter = initialList ?? this.state.transactions; -+ const filteredTransactions = sortBy( -+ pickBy(transactionsToFilter, (transaction) => { -+ if (filterToCurrentNetwork && transaction.chainId !== chainId) { -+ return false; -+ } -+ for (const [key, predicate] of Object.entries(predicateMethods)) { -+ if (key in transaction.txParams) { -+ if (predicate(transaction.txParams[key]) === false) { -+ return false; -+ } -+ } else if (predicate(transaction[key]) === false) { -+ return false; -+ } -+ } -+ return true; -+ }), -+ "time" -+ ); -+ if (limit !== void 0) { -+ const nonces = /* @__PURE__ */ new Set(); -+ const txs = []; -+ for (let i = filteredTransactions.length - 1; i > -1; i--) { -+ const txMeta = filteredTransactions[i]; -+ const { nonce } = txMeta.txParams; -+ if (!nonces.has(nonce)) { -+ if (nonces.size < limit) { -+ nonces.add(nonce); -+ } else { -+ continue; -+ } -+ } -+ txs.unshift(txMeta); -+ } -+ return txs; -+ } -+ return filteredTransactions; -+ } -+ async estimateGasFee({ -+ transactionParams, -+ chainId, -+ networkClientId: requestNetworkClientId -+ }) { -+ const networkClientId = __privateMethod(this, _getNetworkClientId, getNetworkClientId_fn).call(this, { -+ networkClientId: requestNetworkClientId, -+ chainId -+ }); -+ const transactionMeta = { -+ txParams: transactionParams, -+ chainId, -+ networkClientId -+ }; -+ const gasFeeFlow = getGasFeeFlow( -+ transactionMeta, -+ this.gasFeeFlows -+ ); -+ const ethQuery = __privateGet(this, _multichainTrackingHelper).getEthQuery({ -+ networkClientId, -+ chainId -+ }); -+ const gasFeeControllerData = await this.getGasFeeEstimates({ -+ networkClientId -+ }); -+ return gasFeeFlow.getGasFees({ -+ ethQuery, -+ gasFeeControllerData, -+ transactionMeta -+ }); -+ } -+ /** -+ * Determine the layer 1 gas fee for the given transaction parameters. -+ * -+ * @param request - The request object. -+ * @param request.transactionParams - The transaction parameters to estimate the layer 1 gas fee for. -+ * @param request.chainId - The ID of the chain where the transaction will be executed. -+ * @param request.networkClientId - The ID of a specific network client to process the transaction. -+ */ -+ async getLayer1GasFee({ -+ transactionParams, -+ chainId, -+ networkClientId -+ }) { -+ const provider = __privateGet(this, _multichainTrackingHelper).getProvider({ -+ networkClientId, -+ chainId -+ }); -+ return await getTransactionLayer1GasFee({ -+ layer1GasFeeFlows: this.layer1GasFeeFlows, -+ provider, -+ transactionMeta: { -+ txParams: transactionParams, -+ chainId -+ } -+ }); -+ } -+ async signExternalTransaction(chainId, transactionParams) { -+ if (!this.sign) { -+ throw new Error("No sign method defined."); -+ } -+ const normalizedTransactionParams = normalizeTransactionParams(transactionParams); -+ const type = isEIP1559Transaction(normalizedTransactionParams) ? "0x2" /* feeMarket */ : "0x0" /* legacy */; -+ const updatedTransactionParams = { -+ ...normalizedTransactionParams, -+ type, -+ gasLimit: normalizedTransactionParams.gas, -+ chainId -+ }; -+ const { from } = updatedTransactionParams; -+ const common = this.getCommonConfiguration(chainId); -+ const unsignedTransaction = TransactionFactory.fromTxData( -+ updatedTransactionParams, -+ { common } -+ ); -+ const signedTransaction = await this.sign(unsignedTransaction, from); -+ const rawTransaction = bufferToHex(signedTransaction.serialize()); -+ return rawTransaction; -+ } -+ /** -+ * Removes unapproved transactions from state. -+ */ -+ clearUnapprovedTransactions() { -+ const transactions = this.state.transactions.filter( -+ ({ status }) => status !== "unapproved" /* unapproved */ -+ ); -+ this.update((state) => { -+ state.transactions = this.trimTransactionsForState(transactions); -+ }); -+ } -+ /** -+ * Stop the signing process for a specific transaction. -+ * Throws an error causing the transaction status to be set to failed. -+ * @param transactionId - The ID of the transaction to stop signing. -+ */ -+ abortTransactionSigning(transactionId) { -+ const transactionMeta = this.getTransaction(transactionId); -+ if (!transactionMeta) { -+ throw new Error(`Cannot abort signing as no transaction metadata found`); -+ } -+ const abortCallback = this.signAbortCallbacks.get(transactionId); -+ if (!abortCallback) { -+ throw new Error( -+ `Cannot abort signing as transaction is not waiting for signing` -+ ); -+ } -+ abortCallback(); -+ this.signAbortCallbacks.delete(transactionId); -+ } -+ addMetadata(transactionMeta) { -+ this.update((state) => { -+ state.transactions = this.trimTransactionsForState([ -+ ...state.transactions, -+ transactionMeta -+ ]); -+ }); -+ } -+ async updateGasProperties(transactionMeta) { -+ const isEIP1559Compatible = await this.getEIP1559Compatibility(transactionMeta.networkClientId) && transactionMeta.txParams.type !== "0x0" /* legacy */; -+ const { networkClientId, chainId } = transactionMeta; -+ const isCustomNetwork = __privateMethod(this, _isCustomNetwork, isCustomNetwork_fn).call(this, networkClientId); -+ const ethQuery = __privateGet(this, _multichainTrackingHelper).getEthQuery({ -+ networkClientId, -+ chainId -+ }); -+ const provider = __privateGet(this, _multichainTrackingHelper).getProvider({ -+ networkClientId, -+ chainId -+ }); -+ await updateGas({ -+ ethQuery, -+ chainId, -+ isCustomNetwork, -+ txMeta: transactionMeta -+ }); -+ await updateGasFees({ -+ eip1559: isEIP1559Compatible, -+ ethQuery, -+ gasFeeFlows: this.gasFeeFlows, -+ getGasFeeEstimates: this.getGasFeeEstimates, -+ getSavedGasFees: this.getSavedGasFees.bind(this), -+ txMeta: transactionMeta -+ }); -+ await updateTransactionLayer1GasFee({ -+ layer1GasFeeFlows: this.layer1GasFeeFlows, -+ provider, -+ transactionMeta -+ }); -+ } -+ onBootCleanup() { -+ this.clearUnapprovedTransactions(); -+ this.failIncompleteTransactions(); -+ } -+ failIncompleteTransactions() { -+ const incompleteTransactions = this.state.transactions.filter( -+ (transaction) => ["approved" /* approved */, "signed" /* signed */].includes( -+ transaction.status -+ ) -+ ); -+ for (const transactionMeta of incompleteTransactions) { -+ this.failTransaction( -+ transactionMeta, -+ new Error("Transaction incomplete at startup") -+ ); -+ } -+ } -+ async processApproval(transactionMeta, { -+ isExisting = false, -+ requireApproval, -+ shouldShowRequest = true, -+ actionId -+ }) { -+ const transactionId = transactionMeta.id; -+ let resultCallbacks; -+ const { meta, isCompleted } = this.isTransactionCompleted(transactionId); -+ const finishedPromise = isCompleted ? Promise.resolve(meta) : this.waitForTransactionFinished(transactionId); -+ if (meta && !isExisting && !isCompleted) { -+ try { -+ if (requireApproval !== false) { -+ const acceptResult = await this.requestApproval(transactionMeta, { -+ shouldShowRequest -+ }); -+ resultCallbacks = acceptResult.resultCallbacks; -+ const approvalValue = acceptResult.value; -+ const updatedTransaction = approvalValue?.txMeta; -+ if (updatedTransaction) { -+ projectLogger("Updating transaction with approval data", { -+ customNonce: updatedTransaction.customNonceValue, -+ params: updatedTransaction.txParams -+ }); -+ this.updateTransaction( -+ updatedTransaction, -+ "TransactionController#processApproval - Updated with approval data" -+ ); -+ } -+ } -+ const { isCompleted: isTxCompleted } = this.isTransactionCompleted(transactionId); -+ if (!isTxCompleted) { -+ const approvalResult = await this.approveTransaction(transactionId); -+ if (approvalResult === "skipped-via-before-publish-hook" /* SkippedViaBeforePublishHook */ && resultCallbacks) { -+ resultCallbacks.success(); -+ } -+ const updatedTransactionMeta = this.getTransaction( -+ transactionId -+ ); -+ this.messagingSystem.publish( -+ `${controllerName}:transactionApproved`, -+ { -+ transactionMeta: updatedTransactionMeta, -+ actionId -+ } -+ ); -+ } -+ } catch (error) { -+ const { isCompleted: isTxCompleted } = this.isTransactionCompleted(transactionId); -+ if (!isTxCompleted) { -+ if (error?.code === errorCodes.provider.userRejectedRequest) { -+ this.cancelTransaction(transactionId, actionId); -+ throw providerErrors.userRejectedRequest( -+ "MetaMask Tx Signature: User denied transaction signature." -+ ); -+ } else { -+ this.failTransaction(meta, error, actionId); -+ } -+ } -+ } -+ } -+ const finalMeta = await finishedPromise; -+ switch (finalMeta?.status) { -+ case "failed" /* failed */: -+ resultCallbacks?.error(finalMeta.error); -+ throw rpcErrors.internal(finalMeta.error.message); -+ case "submitted" /* submitted */: -+ resultCallbacks?.success(); -+ return finalMeta.hash; -+ default: -+ const internalError = rpcErrors.internal( -+ `MetaMask Tx Signature: Unknown problem: ${JSON.stringify( -+ finalMeta || transactionId -+ )}` -+ ); -+ resultCallbacks?.error(internalError); -+ throw internalError; -+ } -+ } -+ /** -+ * Approves a transaction and updates it's status in state. If this is not a -+ * retry transaction, a nonce will be generated. The transaction is signed -+ * using the sign configuration property, then published to the blockchain. -+ * A `:finished` hub event is fired after success or failure. -+ * -+ * @param transactionId - The ID of the transaction to approve. -+ */ -+ async approveTransaction(transactionId) { -+ const cleanupTasks = new Array(); -+ cleanupTasks.push(await this.mutex.acquire()); -+ let transactionMeta = this.getTransactionOrThrow(transactionId); -+ try { -+ if (!this.sign) { -+ this.failTransaction( -+ transactionMeta, -+ new Error("No sign method defined.") -+ ); -+ return "not-approved" /* NotApproved */; -+ } else if (!transactionMeta.chainId) { -+ this.failTransaction(transactionMeta, new Error("No chainId defined.")); -+ return "not-approved" /* NotApproved */; -+ } -+ if (this.approvingTransactionIds.has(transactionId)) { -+ projectLogger("Skipping approval as signing in progress", transactionId); -+ return "not-approved" /* NotApproved */; -+ } -+ this.approvingTransactionIds.add(transactionId); -+ cleanupTasks.push( -+ () => this.approvingTransactionIds.delete(transactionId) -+ ); -+ const [nonce, releaseNonce] = await getNextNonce( -+ transactionMeta, -+ (address) => __privateGet(this, _multichainTrackingHelper).getNonceLock( -+ address, -+ transactionMeta.networkClientId -+ ) -+ ); -+ releaseNonce && cleanupTasks.push(releaseNonce); -+ transactionMeta = __privateMethod(this, _updateTransactionInternal, updateTransactionInternal_fn).call(this, { -+ transactionId, -+ note: "TransactionController#approveTransaction - Transaction approved" -+ }, (draftTxMeta) => { -+ const { txParams, chainId } = draftTxMeta; -+ draftTxMeta.status = "approved" /* approved */; -+ draftTxMeta.txParams = { -+ ...txParams, -+ nonce, -+ chainId, -+ gasLimit: txParams.gas, -+ ...isEIP1559Transaction(txParams) && { -+ type: "0x2" /* feeMarket */ -+ } -+ }; -+ }); -+ this.onTransactionStatusChange(transactionMeta); -+ const rawTx = await this.signTransaction( -+ transactionMeta, -+ transactionMeta.txParams -+ ); -+ if (!this.beforePublish(transactionMeta)) { -+ projectLogger("Skipping publishing transaction based on hook"); -+ this.messagingSystem.publish( -+ `${controllerName}:transactionPublishingSkipped`, -+ transactionMeta -+ ); -+ return "skipped-via-before-publish-hook" /* SkippedViaBeforePublishHook */; -+ } -+ if (!rawTx) { -+ return "not-approved" /* NotApproved */; -+ } -+ const ethQuery = __privateGet(this, _multichainTrackingHelper).getEthQuery({ -+ networkClientId: transactionMeta.networkClientId, -+ chainId: transactionMeta.chainId -+ }); -+ let preTxBalance; -+ const shouldUpdatePreTxBalance = transactionMeta.type === "swap" /* swap */; -+ if (shouldUpdatePreTxBalance) { -+ projectLogger("Determining pre-transaction balance"); -+ preTxBalance = await query(ethQuery, "getBalance", [ -+ transactionMeta.txParams.from -+ ]); -+ } -+ projectLogger("Publishing transaction", transactionMeta.txParams); -+ let { transactionHash: hash } = await this.publish( -+ transactionMeta, -+ rawTx -+ ); -+ if (hash === void 0) { -+ hash = await this.publishTransaction(ethQuery, rawTx); -+ } -+ projectLogger("Publish successful", hash); -+ transactionMeta = __privateMethod(this, _updateTransactionInternal, updateTransactionInternal_fn).call(this, { -+ transactionId, -+ note: "TransactionController#approveTransaction - Transaction submitted" -+ }, (draftTxMeta) => { -+ draftTxMeta.hash = hash; -+ draftTxMeta.status = "submitted" /* submitted */; -+ draftTxMeta.submittedTime = (/* @__PURE__ */ new Date()).getTime(); -+ if (shouldUpdatePreTxBalance) { -+ draftTxMeta.preTxBalance = preTxBalance; -+ projectLogger("Updated pre-transaction balance", preTxBalance); -+ } -+ }); -+ this.messagingSystem.publish(`${controllerName}:transactionSubmitted`, { -+ transactionMeta -+ }); -+ this.messagingSystem.publish( -+ `${controllerName}:transactionFinished`, -+ transactionMeta -+ ); -+ __privateGet(this, _internalEvents).emit(`${transactionId}:finished`, transactionMeta); -+ this.onTransactionStatusChange(transactionMeta); -+ return "approved" /* Approved */; -+ } catch (error) { -+ this.failTransaction(transactionMeta, error); -+ return "not-approved" /* NotApproved */; -+ } finally { -+ cleanupTasks.forEach((task) => task()); -+ } -+ } -+ async publishTransaction(ethQuery, rawTransaction) { -+ return await query(ethQuery, "sendRawTransaction", [rawTransaction]); -+ } -+ /** -+ * Cancels a transaction based on its ID by setting its status to "rejected" -+ * and emitting a `:finished` hub event. -+ * -+ * @param transactionId - The ID of the transaction to cancel. -+ * @param actionId - The actionId passed from UI -+ */ -+ cancelTransaction(transactionId, actionId) { -+ const transactionMeta = this.state.transactions.find( -+ ({ id }) => id === transactionId -+ ); -+ if (!transactionMeta) { -+ return; -+ } -+ this.update((state) => { -+ const transactions = state.transactions.filter( -+ ({ id }) => id !== transactionId -+ ); -+ state.transactions = this.trimTransactionsForState(transactions); -+ }); -+ const updatedTransactionMeta = { -+ ...transactionMeta, -+ status: "rejected" /* rejected */ -+ }; -+ this.messagingSystem.publish( -+ `${controllerName}:transactionFinished`, -+ updatedTransactionMeta -+ ); -+ __privateGet(this, _internalEvents).emit( -+ // TODO: Either fix this lint violation or explain why it's necessary to ignore. -+ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions -+ `${transactionMeta.id}:finished`, -+ updatedTransactionMeta -+ ); -+ this.messagingSystem.publish(`${controllerName}:transactionRejected`, { -+ transactionMeta: updatedTransactionMeta, -+ actionId -+ }); -+ this.onTransactionStatusChange(updatedTransactionMeta); -+ } -+ /** -+ * Trim the amount of transactions that are set on the state. Checks -+ * if the length of the tx history is longer then desired persistence -+ * limit and then if it is removes the oldest confirmed or rejected tx. -+ * Pending or unapproved transactions will not be removed by this -+ * operation. For safety of presenting a fully functional transaction UI -+ * representation, this function will not break apart transactions with the -+ * same nonce, created on the same day, per network. Not accounting for -+ * transactions of the same nonce, same day and network combo can result in -+ * confusing or broken experiences in the UI. -+ * -+ * @param transactions - The transactions to be applied to the state. -+ * @returns The trimmed list of transactions. -+ */ -+ trimTransactionsForState(transactions) { -+ const nonceNetworkSet = /* @__PURE__ */ new Set(); -+ const txsToKeep = [...transactions].sort((a, b) => a.time > b.time ? -1 : 1).filter((tx) => { -+ const { chainId, status, txParams, time } = tx; -+ if (txParams) { -+ const key = `${String(txParams.nonce)}-${convertHexToDecimal( -+ chainId -+ )}-${new Date(time).toDateString()}`; -+ if (nonceNetworkSet.has(key)) { -+ return true; -+ } else if (nonceNetworkSet.size < __privateGet(this, _transactionHistoryLimit) || !this.isFinalState(status)) { -+ nonceNetworkSet.add(key); -+ return true; -+ } -+ } -+ return false; -+ }); -+ txsToKeep.reverse(); -+ return txsToKeep; -+ } -+ /** -+ * Determines if the transaction is in a final state. -+ * -+ * @param status - The transaction status. -+ * @returns Whether the transaction is in a final state. -+ */ -+ isFinalState(status) { -+ return status === "rejected" /* rejected */ || status === "confirmed" /* confirmed */ || status === "failed" /* failed */; -+ } -+ /** -+ * Whether the transaction has at least completed all local processing. -+ * -+ * @param status - The transaction status. -+ * @returns Whether the transaction is in a final state. -+ */ -+ isLocalFinalState(status) { -+ return [ -+ "confirmed" /* confirmed */, -+ "failed" /* failed */, -+ "rejected" /* rejected */, -+ "submitted" /* submitted */ -+ ].includes(status); -+ } -+ async requestApproval(txMeta, { shouldShowRequest }) { -+ const id = this.getApprovalId(txMeta); -+ const { origin } = txMeta; -+ const type = ApprovalType.Transaction; -+ const requestData = { txId: txMeta.id }; -+ return await this.messagingSystem.call( -+ "ApprovalController:addRequest", -+ { -+ id, -+ origin: origin || ORIGIN_METAMASK, -+ type, -+ requestData, -+ expectsResult: true -+ }, -+ shouldShowRequest -+ ); -+ } -+ getTransaction(transactionId) { -+ const { transactions } = this.state; -+ return transactions.find(({ id }) => id === transactionId); -+ } -+ getTransactionOrThrow(transactionId, errorMessagePrefix = "TransactionController") { -+ const txMeta = this.getTransaction(transactionId); -+ if (!txMeta) { -+ throw new Error( -+ `${errorMessagePrefix}: No transaction found with id ${transactionId}` -+ ); -+ } -+ return txMeta; -+ } -+ getApprovalId(txMeta) { -+ return String(txMeta.id); -+ } -+ isTransactionCompleted(transactionId) { -+ const transaction = this.getTransaction(transactionId); -+ if (!transaction) { -+ return { meta: void 0, isCompleted: false }; -+ } -+ const isCompleted = this.isLocalFinalState(transaction.status); -+ return { meta: transaction, isCompleted }; -+ } -+ getChainId(networkClientId) { -+ const globalChainId = __privateMethod(this, _getGlobalChainId, getGlobalChainId_fn).call(this); -+ const globalNetworkClientId = __privateMethod(this, _getGlobalNetworkClientId, getGlobalNetworkClientId_fn).call(this); -+ if (!networkClientId || networkClientId === globalNetworkClientId) { -+ return globalChainId; -+ } -+ return this.messagingSystem.call( -+ `NetworkController:getNetworkClientById`, -+ networkClientId -+ ).configuration.chainId; -+ } -+ prepareUnsignedEthTx(chainId, txParams) { -+ return TransactionFactory.fromTxData(txParams, { -+ freeze: false, -+ common: this.getCommonConfiguration(chainId) -+ }); -+ } -+ /** -+ * `@ethereumjs/tx` uses `@ethereumjs/common` as a configuration tool for -+ * specifying which chain, network, hardfork and EIPs to support for -+ * a transaction. By referencing this configuration, and analyzing the fields -+ * specified in txParams, @ethereumjs/tx is able to determine which EIP-2718 -+ * transaction type to use. -+ * -+ * @param chainId - The chainId to use for the configuration. -+ * @returns common configuration object -+ */ -+ getCommonConfiguration(chainId) { -+ const customChainParams = { -+ chainId: parseInt(chainId, 16), -+ defaultHardfork: HARDFORK -+ }; -+ return Common.custom(customChainParams); -+ } -+ onIncomingTransactions({ -+ added, -+ updated -+ }) { -+ this.update((state) => { -+ const { transactions: currentTransactions } = state; -+ const updatedTransactions = [ -+ ...added, -+ ...currentTransactions.map((originalTransaction) => { -+ const updatedTransaction = updated.find( -+ ({ hash }) => hash === originalTransaction.hash -+ ); -+ return updatedTransaction ?? originalTransaction; -+ }) -+ ]; -+ state.transactions = this.trimTransactionsForState(updatedTransactions); -+ }); -+ } -+ onUpdatedLastFetchedBlockNumbers({ -+ lastFetchedBlockNumbers, -+ blockNumber -+ }) { -+ this.update((state) => { -+ state.lastFetchedBlockNumbers = lastFetchedBlockNumbers; -+ }); -+ this.messagingSystem.publish( -+ `${controllerName}:incomingTransactionBlockReceived`, -+ blockNumber -+ ); -+ } -+ generateDappSuggestedGasFees(txParams, origin) { -+ if (!origin || origin === ORIGIN_METAMASK) { -+ return void 0; -+ } -+ const { gasPrice, maxFeePerGas, maxPriorityFeePerGas, gas } = txParams; -+ if (gasPrice === void 0 && maxFeePerGas === void 0 && maxPriorityFeePerGas === void 0 && gas === void 0) { -+ return void 0; -+ } -+ const dappSuggestedGasFees = {}; -+ if (gasPrice !== void 0) { -+ dappSuggestedGasFees.gasPrice = gasPrice; -+ } else if (maxFeePerGas !== void 0 || maxPriorityFeePerGas !== void 0) { -+ dappSuggestedGasFees.maxFeePerGas = maxFeePerGas; -+ dappSuggestedGasFees.maxPriorityFeePerGas = maxPriorityFeePerGas; -+ } -+ if (gas !== void 0) { -+ dappSuggestedGasFees.gas = gas; -+ } -+ return dappSuggestedGasFees; -+ } -+ /** -+ * Validates and adds external provided transaction to state. -+ * -+ * @param transactionMeta - Nominated external transaction to be added to state. -+ * @returns The new transaction. -+ */ -+ addExternalTransaction(transactionMeta) { -+ const { chainId } = transactionMeta; -+ const { transactions } = this.state; -+ const fromAddress = transactionMeta?.txParams?.from; -+ const sameFromAndNetworkTransactions = transactions.filter( -+ (transaction) => transaction.txParams.from === fromAddress && transaction.chainId === chainId -+ ); -+ const confirmedTxs = sameFromAndNetworkTransactions.filter( -+ (transaction) => transaction.status === "confirmed" /* confirmed */ -+ ); -+ const pendingTxs = sameFromAndNetworkTransactions.filter( -+ (transaction) => transaction.status === "submitted" /* submitted */ -+ ); -+ validateConfirmedExternalTransaction( -+ transactionMeta, -+ confirmedTxs, -+ pendingTxs -+ ); -+ const newTransactionMeta = (transactionMeta.history ?? []).length === 0 && !this.isHistoryDisabled ? addInitialHistorySnapshot(transactionMeta) : transactionMeta; -+ this.update((state) => { -+ state.transactions = this.trimTransactionsForState([ -+ ...state.transactions, -+ newTransactionMeta -+ ]); -+ }); -+ return newTransactionMeta; -+ } -+ /** -+ * Sets other txMeta statuses to dropped if the txMeta that has been confirmed has other transactions -+ * in the transactions have the same nonce. -+ * -+ * @param transactionId - Used to identify original transaction. -+ */ -+ markNonceDuplicatesDropped(transactionId) { -+ const transactionMeta = this.getTransaction(transactionId); -+ if (!transactionMeta) { -+ return; -+ } -+ const nonce = transactionMeta.txParams?.nonce; -+ const from = transactionMeta.txParams?.from; -+ const { chainId } = transactionMeta; -+ const sameNonceTransactions = this.state.transactions.filter( -+ (transaction) => transaction.id !== transactionId && transaction.txParams.from === from && transaction.txParams.nonce === nonce && transaction.chainId === chainId && transaction.type !== "incoming" /* incoming */ -+ ); -+ const sameNonceTransactionIds = sameNonceTransactions.map( -+ (transaction) => transaction.id -+ ); -+ if (sameNonceTransactions.length === 0) { -+ return; -+ } -+ this.update((state) => { -+ for (const transaction of state.transactions) { -+ if (sameNonceTransactionIds.includes(transaction.id)) { -+ transaction.replacedBy = transactionMeta?.hash; -+ transaction.replacedById = transactionMeta?.id; -+ } -+ } -+ }); -+ for (const transaction of this.state.transactions) { -+ if (sameNonceTransactionIds.includes(transaction.id) && transaction.status !== "failed" /* failed */) { -+ this.setTransactionStatusDropped(transaction); -+ } -+ } -+ } -+ /** -+ * Method to set transaction status to dropped. -+ * -+ * @param transactionMeta - TransactionMeta of transaction to be marked as dropped. -+ */ -+ setTransactionStatusDropped(transactionMeta) { -+ const updatedTransactionMeta = { -+ ...transactionMeta, -+ status: "dropped" /* dropped */ -+ }; -+ this.messagingSystem.publish(`${controllerName}:transactionDropped`, { -+ transactionMeta: updatedTransactionMeta -+ }); -+ this.updateTransaction( -+ updatedTransactionMeta, -+ "TransactionController#setTransactionStatusDropped - Transaction dropped" -+ ); -+ this.onTransactionStatusChange(updatedTransactionMeta); -+ } -+ /** -+ * Get transaction with provided actionId. -+ * -+ * @param actionId - Unique ID to prevent duplicate requests -+ * @returns the filtered transaction -+ */ -+ getTransactionWithActionId(actionId) { -+ return this.state.transactions.find( -+ (transaction) => actionId && transaction.actionId === actionId -+ ); -+ } -+ async waitForTransactionFinished(transactionId) { -+ return new Promise((resolve) => { -+ __privateGet(this, _internalEvents).once(`${transactionId}:finished`, (txMeta) => { -+ resolve(txMeta); -+ }); -+ }); -+ } -+ /** -+ * Updates the r, s, and v properties of a TransactionMeta object -+ * with values from a signed transaction. -+ * -+ * @param transactionMeta - The TransactionMeta object to update. -+ * @param signedTx - The encompassing type for all transaction types containing r, s, and v values. -+ * @returns The updated TransactionMeta object. -+ */ -+ updateTransactionMetaRSV(transactionMeta, signedTx) { -+ const transactionMetaWithRsv = cloneDeep(transactionMeta); -+ for (const key of ["r", "s", "v"]) { -+ const value = signedTx[key]; -+ if (value === void 0 || value === null) { -+ continue; -+ } -+ transactionMetaWithRsv[key] = add0x(value.toString(16)); -+ } -+ return transactionMetaWithRsv; -+ } -+ async getEIP1559Compatibility(networkClientId) { -+ const currentNetworkIsEIP1559Compatible = await this.getCurrentNetworkEIP1559Compatibility(networkClientId); -+ const currentAccountIsEIP1559Compatible = await this.getCurrentAccountEIP1559Compatibility(); -+ return currentNetworkIsEIP1559Compatible && currentAccountIsEIP1559Compatible; -+ } -+ async signTransaction(transactionMeta, txParams) { -+ projectLogger("Signing transaction", txParams); -+ const unsignedEthTx = this.prepareUnsignedEthTx( -+ transactionMeta.chainId, -+ txParams -+ ); -+ this.approvingTransactionIds.add(transactionMeta.id); -+ const signedTx = await new Promise((resolve, reject) => { -+ this.sign?.( -+ unsignedEthTx, -+ txParams.from, -+ ...this.getAdditionalSignArguments(transactionMeta) -+ ).then(resolve, reject); -+ this.signAbortCallbacks.set( -+ transactionMeta.id, -+ () => reject(new Error("Signing aborted by user")) -+ ); -+ }); -+ this.signAbortCallbacks.delete(transactionMeta.id); -+ if (!signedTx) { -+ projectLogger("Skipping signed status as no signed transaction"); -+ return void 0; -+ } -+ const transactionMetaFromHook = cloneDeep(transactionMeta); -+ if (!this.afterSign(transactionMetaFromHook, signedTx)) { -+ this.updateTransaction( -+ transactionMetaFromHook, -+ "TransactionController#signTransaction - Update after sign" -+ ); -+ projectLogger("Skipping signed status based on hook"); -+ return void 0; -+ } -+ const transactionMetaWithRsv = { -+ ...this.updateTransactionMetaRSV(transactionMetaFromHook, signedTx), -+ status: "signed" /* signed */ -+ }; -+ this.updateTransaction( -+ transactionMetaWithRsv, -+ "TransactionController#approveTransaction - Transaction signed" -+ ); -+ this.onTransactionStatusChange(transactionMetaWithRsv); -+ const rawTx = bufferToHex(signedTx.serialize()); -+ const transactionMetaWithRawTx = merge({}, transactionMetaWithRsv, { -+ rawTx -+ }); -+ this.updateTransaction( -+ transactionMetaWithRawTx, -+ "TransactionController#approveTransaction - RawTransaction added" -+ ); -+ return rawTx; -+ } -+ onTransactionStatusChange(transactionMeta) { -+ this.messagingSystem.publish(`${controllerName}:transactionStatusUpdated`, { -+ transactionMeta -+ }); -+ } -+ getNonceTrackerTransactions(status, address, chainId = this.getChainId()) { -+ return getAndFormatTransactionsForNonceTracker( -+ chainId, -+ address, -+ status, -+ this.state.transactions -+ ); -+ } -+ onConfirmedTransaction(transactionMeta) { -+ projectLogger("Processing confirmed transaction", transactionMeta.id); -+ this.markNonceDuplicatesDropped(transactionMeta.id); -+ this.messagingSystem.publish( -+ `${controllerName}:transactionConfirmed`, -+ transactionMeta -+ ); -+ this.onTransactionStatusChange(transactionMeta); -+ this.updatePostBalance(transactionMeta); -+ } -+ async updatePostBalance(transactionMeta) { -+ try { -+ if (transactionMeta.type !== "swap" /* swap */) { -+ return; -+ } -+ const ethQuery = __privateGet(this, _multichainTrackingHelper).getEthQuery({ -+ networkClientId: transactionMeta.networkClientId, -+ chainId: transactionMeta.chainId -+ }); -+ const { updatedTransactionMeta, approvalTransactionMeta } = await updatePostTransactionBalance(transactionMeta, { -+ ethQuery, -+ getTransaction: this.getTransaction.bind(this), -+ updateTransaction: this.updateTransaction.bind(this) -+ }); -+ this.messagingSystem.publish( -+ `${controllerName}:postTransactionBalanceUpdated`, -+ { -+ transactionMeta: updatedTransactionMeta, -+ approvalTransactionMeta -+ } -+ ); -+ } catch (error) { -+ projectLogger("Error while updating post transaction balance", error); -+ } -+ } -+ async publishTransactionForRetry(ethQuery, rawTx, transactionMeta) { -+ try { -+ const hash = await this.publishTransaction(ethQuery, rawTx); -+ return hash; -+ } catch (error) { -+ if (this.isTransactionAlreadyConfirmedError(error)) { -+ await this.pendingTransactionTracker.forceCheckTransaction( -+ transactionMeta -+ ); -+ throw new Error("Previous transaction is already confirmed"); -+ } -+ throw error; -+ } -+ } -+ /** -+ * Ensures that error is a nonce issue -+ * -+ * @param error - The error to check -+ * @returns Whether or not the error is a nonce issue -+ */ -+ // TODO: Replace `any` with type -+ // Some networks are returning original error in the data field -+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -+ isTransactionAlreadyConfirmedError(error) { -+ return error?.message?.includes("nonce too low") || error?.data?.message?.includes("nonce too low"); -+ } -+}; -+_internalEvents = new WeakMap(); -+_incomingTransactionOptions = new WeakMap(); -+_pendingTransactionOptions = new WeakMap(); -+_transactionHistoryLimit = new WeakMap(); -+_isSimulationEnabled = new WeakMap(); -+_testGasFeeFlows = new WeakMap(); -+_multichainTrackingHelper = new WeakMap(); -+_createNonceTracker = new WeakSet(); -+createNonceTracker_fn = function({ -+ provider, -+ blockTracker, -+ chainId -+}) { -+ return new NonceTracker({ -+ // TODO: Fix types -+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -+ provider, -+ // @ts-expect-error TODO: Fix types -+ blockTracker, -+ getPendingTransactions: __privateMethod(this, _getNonceTrackerPendingTransactions, getNonceTrackerPendingTransactions_fn).bind( -+ this, -+ chainId -+ ), -+ getConfirmedTransactions: this.getNonceTrackerTransactions.bind( -+ this, -+ "confirmed" /* confirmed */ -+ ) -+ }); -+}; -+_createIncomingTransactionHelper = new WeakSet(); -+createIncomingTransactionHelper_fn = function({ -+ blockTracker, -+ etherscanRemoteTransactionSource, -+ chainId -+}) { -+ const incomingTransactionHelper = new IncomingTransactionHelper({ -+ blockTracker, -+ getCurrentAccount: () => __privateMethod(this, _getSelectedAccount, getSelectedAccount_fn).call(this), -+ getLastFetchedBlockNumbers: () => this.state.lastFetchedBlockNumbers, -+ getChainId: chainId ? () => chainId : this.getChainId.bind(this), -+ isEnabled: __privateGet(this, _incomingTransactionOptions).isEnabled, -+ queryEntireHistory: __privateGet(this, _incomingTransactionOptions).queryEntireHistory, -+ remoteTransactionSource: etherscanRemoteTransactionSource, -+ transactionLimit: __privateGet(this, _transactionHistoryLimit), -+ updateTransactions: __privateGet(this, _incomingTransactionOptions).updateTransactions -+ }); -+ __privateMethod(this, _addIncomingTransactionHelperListeners, addIncomingTransactionHelperListeners_fn).call(this, incomingTransactionHelper); -+ return incomingTransactionHelper; -+}; -+_createPendingTransactionTracker = new WeakSet(); -+createPendingTransactionTracker_fn = function({ -+ provider, -+ blockTracker, -+ chainId -+}) { -+ const ethQuery = new EthQuery(provider); -+ const getChainId = chainId ? () => chainId : this.getChainId.bind(this); -+ const pendingTransactionTracker = new PendingTransactionTracker({ -+ blockTracker, -+ getChainId, -+ getEthQuery: () => ethQuery, -+ getTransactions: () => this.state.transactions, -+ isResubmitEnabled: __privateGet(this, _pendingTransactionOptions).isResubmitEnabled, -+ getGlobalLock: () => __privateGet(this, _multichainTrackingHelper).acquireNonceLockForChainIdKey({ -+ chainId: getChainId() -+ }), -+ publishTransaction: this.publishTransaction.bind(this), -+ hooks: { -+ beforeCheckPendingTransaction: this.beforeCheckPendingTransaction.bind(this), -+ beforePublish: this.beforePublish.bind(this) -+ } -+ }); -+ __privateMethod(this, _addPendingTransactionTrackerListeners, addPendingTransactionTrackerListeners_fn).call(this, pendingTransactionTracker); -+ return pendingTransactionTracker; -+}; -+_checkForPendingTransactionAndStartPolling = new WeakMap(); -+_stopAllTracking = new WeakSet(); -+stopAllTracking_fn = function() { -+ this.pendingTransactionTracker.stop(); -+ __privateMethod(this, _removePendingTransactionTrackerListeners, removePendingTransactionTrackerListeners_fn).call(this, this.pendingTransactionTracker); -+ this.incomingTransactionHelper.stop(); -+ __privateMethod(this, _removeIncomingTransactionHelperListeners, removeIncomingTransactionHelperListeners_fn).call(this, this.incomingTransactionHelper); -+ __privateGet(this, _multichainTrackingHelper).stopAllTracking(); -+}; -+_removeIncomingTransactionHelperListeners = new WeakSet(); -+removeIncomingTransactionHelperListeners_fn = function(incomingTransactionHelper) { -+ incomingTransactionHelper.hub.removeAllListeners("transactions"); -+ incomingTransactionHelper.hub.removeAllListeners( -+ "updatedLastFetchedBlockNumbers" -+ ); -+}; -+_addIncomingTransactionHelperListeners = new WeakSet(); -+addIncomingTransactionHelperListeners_fn = function(incomingTransactionHelper) { -+ incomingTransactionHelper.hub.on( -+ "transactions", -+ this.onIncomingTransactions.bind(this) -+ ); -+ incomingTransactionHelper.hub.on( -+ "updatedLastFetchedBlockNumbers", -+ this.onUpdatedLastFetchedBlockNumbers.bind(this) -+ ); -+}; -+_removePendingTransactionTrackerListeners = new WeakSet(); -+removePendingTransactionTrackerListeners_fn = function(pendingTransactionTracker) { -+ pendingTransactionTracker.hub.removeAllListeners("transaction-confirmed"); -+ pendingTransactionTracker.hub.removeAllListeners("transaction-dropped"); -+ pendingTransactionTracker.hub.removeAllListeners("transaction-failed"); -+ pendingTransactionTracker.hub.removeAllListeners("transaction-updated"); -+}; -+_addPendingTransactionTrackerListeners = new WeakSet(); -+addPendingTransactionTrackerListeners_fn = function(pendingTransactionTracker) { -+ pendingTransactionTracker.hub.on( -+ "transaction-confirmed", -+ this.onConfirmedTransaction.bind(this) -+ ); -+ pendingTransactionTracker.hub.on( -+ "transaction-dropped", -+ this.setTransactionStatusDropped.bind(this) -+ ); -+ pendingTransactionTracker.hub.on( -+ "transaction-failed", -+ this.failTransaction.bind(this) -+ ); -+ pendingTransactionTracker.hub.on( -+ "transaction-updated", -+ this.updateTransaction.bind(this) -+ ); -+}; -+_getNonceTrackerPendingTransactions = new WeakSet(); -+getNonceTrackerPendingTransactions_fn = function(chainId, address) { -+ const standardPendingTransactions = this.getNonceTrackerTransactions( -+ "submitted" /* submitted */, -+ address, -+ chainId -+ ); -+ const externalPendingTransactions = this.getExternalPendingTransactions( -+ address, -+ chainId -+ ); -+ return [...standardPendingTransactions, ...externalPendingTransactions]; -+}; -+_getGasFeeFlows = new WeakSet(); -+getGasFeeFlows_fn = function() { -+ if (__privateGet(this, _testGasFeeFlows)) { -+ return [new TestGasFeeFlow()]; -+ } -+ return [new LineaGasFeeFlow(), new DefaultGasFeeFlow()]; -+}; -+_getLayer1GasFeeFlows = new WeakSet(); -+getLayer1GasFeeFlows_fn = function() { -+ return [new OptimismLayer1GasFeeFlow(), new ScrollLayer1GasFeeFlow()]; -+}; -+_updateTransactionInternal = new WeakSet(); -+updateTransactionInternal_fn = function({ -+ transactionId, -+ note, -+ skipHistory, -+ skipValidation -+}, callback) { -+ let updatedTransactionParams = []; -+ this.update((state) => { -+ const index = state.transactions.findIndex( -+ ({ id }) => id === transactionId -+ ); -+ let transactionMeta2 = state.transactions[index]; -+ transactionMeta2 = callback(transactionMeta2) ?? transactionMeta2; -+ if (skipValidation !== true) { -+ transactionMeta2.txParams = normalizeTransactionParams( -+ transactionMeta2.txParams -+ ); -+ validateTxParams(transactionMeta2.txParams); -+ } -+ updatedTransactionParams = __privateMethod(this, _checkIfTransactionParamsUpdated, checkIfTransactionParamsUpdated_fn).call(this, transactionMeta2); -+ const shouldSkipHistory = this.isHistoryDisabled || skipHistory; -+ if (!shouldSkipHistory) { -+ transactionMeta2 = updateTransactionHistory( -+ transactionMeta2, -+ note ?? "Transaction updated" -+ ); -+ } -+ state.transactions[index] = transactionMeta2; -+ }); -+ const transactionMeta = this.getTransaction( -+ transactionId -+ ); -+ if (updatedTransactionParams.length > 0) { -+ __privateMethod(this, _onTransactionParamsUpdated, onTransactionParamsUpdated_fn).call(this, transactionMeta, updatedTransactionParams); -+ } -+ return transactionMeta; -+}; -+_checkIfTransactionParamsUpdated = new WeakSet(); -+checkIfTransactionParamsUpdated_fn = function(newTransactionMeta) { -+ const { id: transactionId, txParams: newParams } = newTransactionMeta; -+ const originalParams = this.getTransaction(transactionId)?.txParams; -+ if (!originalParams || isEqual(originalParams, newParams)) { -+ return []; -+ } -+ const params = Object.keys(newParams); -+ const updatedProperties = params.filter( -+ (param) => newParams[param] !== originalParams[param] -+ ); -+ projectLogger( -+ "Transaction parameters have been updated", -+ transactionId, -+ updatedProperties, -+ originalParams, -+ newParams -+ ); -+ return updatedProperties; -+}; -+_onTransactionParamsUpdated = new WeakSet(); -+onTransactionParamsUpdated_fn = function(transactionMeta, updatedParams) { -+ if (["to", "value", "data"].some( -+ (param) => updatedParams.includes(param) -+ )) { -+ projectLogger("Updating simulation data due to transaction parameter update"); -+ __privateMethod(this, _updateSimulationData, updateSimulationData_fn).call(this, transactionMeta); -+ } -+}; -+_updateSimulationData = new WeakSet(); -+updateSimulationData_fn = async function(transactionMeta) { -+ const { id: transactionId, chainId, txParams } = transactionMeta; -+ const { from, to, value, data } = txParams; -+ let simulationData = { -+ error: { -+ code: "disabled" /* Disabled */, -+ message: "Simulation disabled" -+ }, -+ tokenBalanceChanges: [] -+ }; -+ if (__privateGet(this, _isSimulationEnabled).call(this)) { -+ __privateMethod(this, _updateTransactionInternal, updateTransactionInternal_fn).call(this, { transactionId, skipHistory: true }, (txMeta) => { -+ txMeta.simulationData = void 0; -+ }); -+ simulationData = await getSimulationData({ -+ chainId, -+ from, -+ to, -+ value, -+ data -+ }); -+ } -+ const finalTransactionMeta = this.getTransaction(transactionId); -+ if (!finalTransactionMeta) { -+ projectLogger( -+ "Cannot update simulation data as transaction not found", -+ transactionId, -+ simulationData -+ ); -+ return; -+ } -+ __privateMethod(this, _updateTransactionInternal, updateTransactionInternal_fn).call(this, { -+ transactionId, -+ note: "TransactionController#updateSimulationData - Update simulation data" -+ }, (txMeta) => { -+ txMeta.simulationData = simulationData; -+ }); -+ projectLogger("Updated simulation data", transactionId, simulationData); -+}; -+_onGasFeePollerTransactionUpdate = new WeakSet(); -+onGasFeePollerTransactionUpdate_fn = function({ -+ transactionId, -+ gasFeeEstimates, -+ gasFeeEstimatesLoaded, -+ layer1GasFee -+}) { -+ __privateMethod(this, _updateTransactionInternal, updateTransactionInternal_fn).call(this, { transactionId, skipHistory: true }, (txMeta) => { -+ if (gasFeeEstimates) { -+ txMeta.gasFeeEstimates = gasFeeEstimates; -+ } -+ if (gasFeeEstimatesLoaded !== void 0) { -+ txMeta.gasFeeEstimatesLoaded = gasFeeEstimatesLoaded; -+ } -+ if (layer1GasFee) { -+ txMeta.layer1GasFee = layer1GasFee; -+ } -+ }); -+}; -+_getNetworkClientId = new WeakSet(); -+getNetworkClientId_fn = function({ -+ networkClientId: requestNetworkClientId, -+ chainId -+}) { -+ const globalChainId = __privateMethod(this, _getGlobalChainId, getGlobalChainId_fn).call(this); -+ const globalNetworkClientId = __privateMethod(this, _getGlobalNetworkClientId, getGlobalNetworkClientId_fn).call(this); -+ if (requestNetworkClientId) { -+ return requestNetworkClientId; -+ } -+ if (!chainId || chainId === globalChainId) { -+ return globalNetworkClientId; -+ } -+ return this.messagingSystem.call( -+ `NetworkController:findNetworkClientIdByChainId`, -+ chainId -+ ); -+}; -+_getGlobalNetworkClientId = new WeakSet(); -+getGlobalNetworkClientId_fn = function() { -+ return this.getNetworkState().selectedNetworkClientId; -+}; -+_getGlobalChainId = new WeakSet(); -+getGlobalChainId_fn = function() { -+ return this.messagingSystem.call( -+ `NetworkController:getNetworkClientById`, -+ this.getNetworkState().selectedNetworkClientId -+ ).configuration.chainId; -+}; -+_isCustomNetwork = new WeakSet(); -+isCustomNetwork_fn = function(networkClientId) { -+ const globalNetworkClientId = __privateMethod(this, _getGlobalNetworkClientId, getGlobalNetworkClientId_fn).call(this); -+ if (!networkClientId || networkClientId === globalNetworkClientId) { -+ return !isInfuraNetworkType( -+ this.getNetworkState().selectedNetworkClientId -+ ); -+ } -+ return this.messagingSystem.call( -+ `NetworkController:getNetworkClientById`, -+ networkClientId -+ ).configuration.type === NetworkClientType.Custom; -+}; -+_getSelectedAccount = new WeakSet(); -+getSelectedAccount_fn = function() { -+ return this.messagingSystem.call("AccountsController:getSelectedAccount"); -+}; -+ -+export { -+ HARDFORK, -+ CANCEL_RATE, -+ SPEED_UP_RATE, -+ ApprovalState, -+ TransactionController -+}; -+//# sourceMappingURL=chunk-YQYO6EGF.mjs.map -\ No newline at end of file -diff --git a/dist/chunk-YQYO6EGF.mjs.map b/dist/chunk-YQYO6EGF.mjs.map -new file mode 100644 -index 0000000000000000000000000000000000000000..d0f3b3d31c6e0ad96b3d873dd82bdbdc0cdf7abe ---- /dev/null -+++ b/dist/chunk-YQYO6EGF.mjs.map -@@ -0,0 +1 @@ -+{"version":3,"sources":["../src/TransactionController.ts"],"sourcesContent":["import { Hardfork, Common, type ChainConfig } from '@ethereumjs/common';\nimport type { TypedTransaction } from '@ethereumjs/tx';\nimport { TransactionFactory } from '@ethereumjs/tx';\nimport { bufferToHex } from '@ethereumjs/util';\nimport type { AccountsControllerGetSelectedAccountAction } from '@metamask/accounts-controller';\nimport type {\n AcceptResultCallbacks,\n AddApprovalRequest,\n AddResult,\n} from '@metamask/approval-controller';\nimport type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n RestrictedControllerMessenger,\n} from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport {\n query,\n ApprovalType,\n ORIGIN_METAMASK,\n convertHexToDecimal,\n isInfuraNetworkType,\n} from '@metamask/controller-utils';\nimport EthQuery from '@metamask/eth-query';\nimport type {\n FetchGasFeeEstimateOptions,\n GasFeeState,\n} from '@metamask/gas-fee-controller';\nimport type {\n BlockTracker,\n NetworkClientId,\n NetworkController,\n NetworkControllerStateChangeEvent,\n NetworkState,\n Provider,\n NetworkControllerFindNetworkClientIdByChainIdAction,\n NetworkControllerGetNetworkClientByIdAction,\n} from '@metamask/network-controller';\nimport { NetworkClientType } from '@metamask/network-controller';\nimport type {\n NonceLock,\n Transaction as NonceTrackerTransaction,\n} from '@metamask/nonce-tracker';\nimport { NonceTracker } from '@metamask/nonce-tracker';\nimport { errorCodes, rpcErrors, providerErrors } from '@metamask/rpc-errors';\nimport type { Hex } from '@metamask/utils';\nimport { add0x } from '@metamask/utils';\nimport { Mutex } from 'async-mutex';\nimport { MethodRegistry } from 'eth-method-registry';\nimport { EventEmitter } from 'events';\nimport { cloneDeep, mapValues, merge, pickBy, sortBy, isEqual } from 'lodash';\nimport { v1 as random } from 'uuid';\n\nimport { DefaultGasFeeFlow } from './gas-flows/DefaultGasFeeFlow';\nimport { LineaGasFeeFlow } from './gas-flows/LineaGasFeeFlow';\nimport { OptimismLayer1GasFeeFlow } from './gas-flows/OptimismLayer1GasFeeFlow';\nimport { ScrollLayer1GasFeeFlow } from './gas-flows/ScrollLayer1GasFeeFlow';\nimport { TestGasFeeFlow } from './gas-flows/TestGasFeeFlow';\nimport { EtherscanRemoteTransactionSource } from './helpers/EtherscanRemoteTransactionSource';\nimport { GasFeePoller } from './helpers/GasFeePoller';\nimport type { IncomingTransactionOptions } from './helpers/IncomingTransactionHelper';\nimport { IncomingTransactionHelper } from './helpers/IncomingTransactionHelper';\nimport { MultichainTrackingHelper } from './helpers/MultichainTrackingHelper';\nimport { PendingTransactionTracker } from './helpers/PendingTransactionTracker';\nimport { projectLogger as log } from './logger';\nimport type {\n DappSuggestedGasFees,\n Layer1GasFeeFlow,\n SavedGasFees,\n SecurityProviderRequest,\n SendFlowHistoryEntry,\n TransactionParams,\n TransactionMeta,\n TransactionReceipt,\n WalletDevice,\n SecurityAlertResponse,\n GasFeeFlow,\n SimulationData,\n GasFeeEstimates,\n GasFeeFlowResponse,\n} from './types';\nimport {\n TransactionEnvelopeType,\n TransactionType,\n TransactionStatus,\n SimulationErrorCode,\n} from './types';\nimport { validateConfirmedExternalTransaction } from './utils/external-transactions';\nimport { addGasBuffer, estimateGas, updateGas } from './utils/gas';\nimport { updateGasFees } from './utils/gas-fees';\nimport { getGasFeeFlow } from './utils/gas-flow';\nimport {\n addInitialHistorySnapshot,\n updateTransactionHistory,\n} from './utils/history';\nimport {\n getTransactionLayer1GasFee,\n updateTransactionLayer1GasFee,\n} from './utils/layer1-gas-fee-flow';\nimport {\n getAndFormatTransactionsForNonceTracker,\n getNextNonce,\n} from './utils/nonce';\nimport { getSimulationData } from './utils/simulation';\nimport {\n updatePostTransactionBalance,\n updateSwapsTransaction,\n} from './utils/swaps';\nimport { determineTransactionType } from './utils/transaction-type';\nimport {\n getIncreasedPriceFromExisting,\n normalizeTransactionParams,\n isEIP1559Transaction,\n isFeeMarketEIP1559Values,\n isGasPriceValue,\n validateGasValues,\n validateIfTransactionUnapproved,\n validateMinimumIncrease,\n normalizeTxError,\n normalizeGasFeeValues,\n} from './utils/utils';\nimport {\n validateTransactionOrigin,\n validateTxParams,\n} from './utils/validation';\n\n/**\n * Metadata for the TransactionController state, describing how to \"anonymize\"\n * the state and which parts should be persisted.\n */\nconst metadata = {\n transactions: {\n persist: true,\n anonymous: false,\n },\n methodData: {\n persist: true,\n anonymous: false,\n },\n lastFetchedBlockNumbers: {\n persist: true,\n anonymous: false,\n },\n};\n\nexport const HARDFORK = Hardfork.London;\n\n/**\n * Object with new transaction's meta and a promise resolving to the\n * transaction hash if successful.\n *\n * @property result - Promise resolving to a new transaction hash\n * @property transactionMeta - Meta information about this new transaction\n */\n// This interface was created before this ESLint rule was added.\n// Convert to a `type` in a future major version.\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\nexport interface Result {\n result: Promise;\n transactionMeta: TransactionMeta;\n}\n\n// This interface was created before this ESLint rule was added.\n// Convert to a `type` in a future major version.\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\nexport interface GasPriceValue {\n gasPrice: string;\n}\n\n// This interface was created before this ESLint rule was added.\n// Convert to a `type` in a future major version.\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\nexport interface FeeMarketEIP1559Values {\n maxFeePerGas: string;\n maxPriorityFeePerGas: string;\n}\n\n/**\n * Method data registry object\n *\n * @property registryMethod - Registry method raw string\n * @property parsedRegistryMethod - Registry method object, containing name and method arguments\n */\nexport type MethodData = {\n registryMethod: string;\n parsedRegistryMethod:\n | {\n name: string;\n args: { type: string }[];\n }\n | {\n // We're using `any` instead of `undefined` for compatibility with `Json`\n // TODO: Correct this type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n name?: any;\n // We're using `any` instead of `undefined` for compatibility with `Json`\n // TODO: Correct this type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n args?: any;\n };\n};\n\n/**\n * Transaction controller state\n *\n * @property transactions - A list of TransactionMeta objects\n * @property methodData - Object containing all known method data information\n * @property lastFetchedBlockNumbers - Last fetched block numbers.\n */\nexport type TransactionControllerState = {\n transactions: TransactionMeta[];\n methodData: Record;\n lastFetchedBlockNumbers: { [key: string]: number };\n};\n\n/**\n * Multiplier used to determine a transaction's increased gas fee during cancellation\n */\nexport const CANCEL_RATE = 1.1;\n\n/**\n * Multiplier used to determine a transaction's increased gas fee during speed up\n */\nexport const SPEED_UP_RATE = 1.1;\n\n/**\n * Represents the `TransactionController:getState` action.\n */\nexport type TransactionControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n TransactionControllerState\n>;\n\n/**\n * The internal actions available to the TransactionController.\n */\nexport type TransactionControllerActions = TransactionControllerGetStateAction;\n\n/**\n * Configuration options for the PendingTransactionTracker\n *\n * @property isResubmitEnabled - Whether transaction publishing is automatically retried.\n */\nexport type PendingTransactionOptions = {\n isResubmitEnabled?: () => boolean;\n};\n\n/**\n * TransactionController constructor options.\n *\n * @property blockTracker - The block tracker used to poll for new blocks data.\n * @property disableHistory - Whether to disable storing history in transaction metadata.\n * @property disableSendFlowHistory - Explicitly disable transaction metadata history.\n * @property disableSwaps - Whether to disable additional processing on swaps transactions.\n * @property getCurrentAccountEIP1559Compatibility - Whether or not the account supports EIP-1559.\n * @property getCurrentNetworkEIP1559Compatibility - Whether or not the network supports EIP-1559.\n * @property getExternalPendingTransactions - Callback to retrieve pending transactions from external sources.\n * @property getGasFeeEstimates - Callback to retrieve gas fee estimates.\n * @property getNetworkClientRegistry - Gets the network client registry.\n * @property getNetworkState - Gets the state of the network controller.\n * @property getPermittedAccounts - Get accounts that a given origin has permissions for.\n * @property getSavedGasFees - Gets the saved gas fee config.\n * @property getSelectedAddress - Gets the address of the currently selected account.\n * @property incomingTransactions - Configuration options for incoming transaction support.\n * @property isMultichainEnabled - Enable multichain support.\n * @property isSimulationEnabled - Whether new transactions will be automatically simulated.\n * @property messenger - The controller messenger.\n * @property onNetworkStateChange - Allows subscribing to network controller state changes.\n * @property pendingTransactions - Configuration options for pending transaction support.\n * @property provider - The provider used to create the underlying EthQuery instance.\n * @property securityProviderRequest - A function for verifying a transaction, whether it is malicious or not.\n * @property sign - Function used to sign transactions.\n * @property state - Initial state to set on this controller.\n * @property transactionHistoryLimit - Transaction history limit.\n * @property hooks - The controller hooks.\n * @property hooks.afterSign - Additional logic to execute after signing a transaction. Return false to not change the status to signed.\n * @property hooks.beforeCheckPendingTransaction - Additional logic to execute before checking pending transactions. Return false to prevent the broadcast of the transaction.\n * @property hooks.beforePublish - Additional logic to execute before publishing a transaction. Return false to prevent the broadcast of the transaction.\n * @property hooks.getAdditionalSignArguments - Returns additional arguments required to sign a transaction.\n * @property hooks.publish - Alternate logic to publish a transaction.\n */\nexport type TransactionControllerOptions = {\n blockTracker: BlockTracker;\n disableHistory: boolean;\n disableSendFlowHistory: boolean;\n disableSwaps: boolean;\n getCurrentAccountEIP1559Compatibility?: () => Promise;\n getCurrentNetworkEIP1559Compatibility: () => Promise;\n getExternalPendingTransactions?: (\n address: string,\n chainId?: string,\n ) => NonceTrackerTransaction[];\n getGasFeeEstimates?: (\n options: FetchGasFeeEstimateOptions,\n ) => Promise;\n getNetworkClientRegistry: NetworkController['getNetworkClientRegistry'];\n getNetworkState: () => NetworkState;\n getPermittedAccounts: (origin?: string) => Promise;\n getSavedGasFees?: (chainId: Hex) => SavedGasFees | undefined;\n incomingTransactions?: IncomingTransactionOptions;\n isMultichainEnabled: boolean;\n isSimulationEnabled?: () => boolean;\n messenger: TransactionControllerMessenger;\n onNetworkStateChange: (listener: (state: NetworkState) => void) => void;\n pendingTransactions?: PendingTransactionOptions;\n provider: Provider;\n securityProviderRequest?: SecurityProviderRequest;\n sign?: (\n transaction: TypedTransaction,\n from: string,\n transactionMeta?: TransactionMeta,\n ) => Promise;\n state?: Partial;\n testGasFeeFlows?: boolean;\n transactionHistoryLimit: number;\n hooks: {\n afterSign?: (\n transactionMeta: TransactionMeta,\n signedTx: TypedTransaction,\n ) => boolean;\n beforeCheckPendingTransaction?: (\n transactionMeta: TransactionMeta,\n ) => boolean;\n beforePublish?: (transactionMeta: TransactionMeta) => boolean;\n getAdditionalSignArguments?: (\n transactionMeta: TransactionMeta,\n ) => (TransactionMeta | undefined)[];\n publish?: (\n transactionMeta: TransactionMeta,\n ) => Promise<{ transactionHash: string }>;\n };\n};\n\n/**\n * The name of the {@link TransactionController}.\n */\nconst controllerName = 'TransactionController';\n\n/**\n * The external actions available to the {@link TransactionController}.\n */\nexport type AllowedActions =\n | AddApprovalRequest\n | NetworkControllerFindNetworkClientIdByChainIdAction\n | NetworkControllerGetNetworkClientByIdAction\n | AccountsControllerGetSelectedAccountAction;\n\n/**\n * The external events available to the {@link TransactionController}.\n */\nexport type AllowedEvents = NetworkControllerStateChangeEvent;\n\n/**\n * Represents the `TransactionController:stateChange` event.\n */\nexport type TransactionControllerStateChangeEvent = ControllerStateChangeEvent<\n typeof controllerName,\n TransactionControllerState\n>;\n\n/**\n * Represents the `TransactionController:incomingTransactionBlockReceived` event.\n */\nexport type TransactionControllerIncomingTransactionBlockReceivedEvent = {\n type: `${typeof controllerName}:incomingTransactionBlockReceived`;\n payload: [blockNumber: number];\n};\n\n/**\n * Represents the `TransactionController:postTransactionBalanceUpdated` event.\n */\nexport type TransactionControllerPostTransactionBalanceUpdatedEvent = {\n type: `${typeof controllerName}:postTransactionBalanceUpdated`;\n payload: [\n {\n transactionMeta: TransactionMeta;\n approvalTransactionMeta?: TransactionMeta;\n },\n ];\n};\n\n/**\n * Represents the `TransactionController:speedUpTransactionAdded` event.\n */\nexport type TransactionControllerSpeedupTransactionAddedEvent = {\n type: `${typeof controllerName}:speedupTransactionAdded`;\n payload: [transactionMeta: TransactionMeta];\n};\n\n/**\n * Represents the `TransactionController:transactionApproved` event.\n */\nexport type TransactionControllerTransactionApprovedEvent = {\n type: `${typeof controllerName}:transactionApproved`;\n payload: [\n {\n transactionMeta: TransactionMeta;\n actionId?: string;\n },\n ];\n};\n\n/**\n * Represents the `TransactionController:transactionConfirmed` event.\n */\nexport type TransactionControllerTransactionConfirmedEvent = {\n type: `${typeof controllerName}:transactionConfirmed`;\n payload: [transactionMeta: TransactionMeta];\n};\n\n/**\n * Represents the `TransactionController:transactionDropped` event.\n */\nexport type TransactionControllerTransactionDroppedEvent = {\n type: `${typeof controllerName}:transactionDropped`;\n payload: [{ transactionMeta: TransactionMeta }];\n};\n\n/**\n * Represents the `TransactionController:transactionFailed` event.\n */\nexport type TransactionControllerTransactionFailedEvent = {\n type: `${typeof controllerName}:transactionFailed`;\n payload: [\n {\n actionId?: string;\n error: string;\n transactionMeta: TransactionMeta;\n },\n ];\n};\n\n/**\n * Represents the `TransactionController:transactionFinished` event.\n */\nexport type TransactionControllerTransactionFinishedEvent = {\n type: `${typeof controllerName}:transactionFinished`;\n payload: [transactionMeta: TransactionMeta];\n};\n\n/**\n * Represents the `TransactionController:transactionNewSwapApproval` event.\n */\nexport type TransactionControllerTransactionNewSwapApprovalEvent = {\n type: `${typeof controllerName}:transactionNewSwapApproval`;\n payload: [{ transactionMeta: TransactionMeta }];\n};\n\n/**\n * Represents the `TransactionController:transactionNewSwap` event.\n */\nexport type TransactionControllerTransactionNewSwapEvent = {\n type: `${typeof controllerName}:transactionNewSwap`;\n payload: [{ transactionMeta: TransactionMeta }];\n};\n\n/**\n * Represents the `TransactionController:transactionNewSwapApproval` event.\n */\nexport type TransactionControllerTransactionNewSwapAndSendEvent = {\n type: `${typeof controllerName}:transactionNewSwapAndSend`;\n payload: [{ transactionMeta: TransactionMeta }];\n};\n\n/**\n * Represents the `TransactionController:transactionPublishingSkipped` event.\n */\nexport type TransactionControllerTransactionPublishingSkipped = {\n type: `${typeof controllerName}:transactionPublishingSkipped`;\n payload: [transactionMeta: TransactionMeta];\n};\n\n/**\n * Represents the `TransactionController:transactionRejected` event.\n */\nexport type TransactionControllerTransactionRejectedEvent = {\n type: `${typeof controllerName}:transactionRejected`;\n payload: [\n {\n transactionMeta: TransactionMeta;\n actionId?: string;\n },\n ];\n};\n\n/**\n * Represents the `TransactionController:transactionStatusUpdated` event.\n */\nexport type TransactionControllerTransactionStatusUpdatedEvent = {\n type: `${typeof controllerName}:transactionStatusUpdated`;\n payload: [\n {\n transactionMeta: TransactionMeta;\n },\n ];\n};\n\n/**\n * Represents the `TransactionController:transactionSubmitted` event.\n */\nexport type TransactionControllerTransactionSubmittedEvent = {\n type: `${typeof controllerName}:transactionSubmitted`;\n payload: [\n {\n transactionMeta: TransactionMeta;\n actionId?: string;\n },\n ];\n};\n\n/**\n * Represents the `TransactionController:unapprovedTransactionAdded` event.\n */\nexport type TransactionControllerUnapprovedTransactionAddedEvent = {\n type: `${typeof controllerName}:unapprovedTransactionAdded`;\n payload: [transactionMeta: TransactionMeta];\n};\n\n/**\n * The internal events available to the {@link TransactionController}.\n */\nexport type TransactionControllerEvents =\n | TransactionControllerIncomingTransactionBlockReceivedEvent\n | TransactionControllerPostTransactionBalanceUpdatedEvent\n | TransactionControllerSpeedupTransactionAddedEvent\n | TransactionControllerStateChangeEvent\n | TransactionControllerTransactionApprovedEvent\n | TransactionControllerTransactionConfirmedEvent\n | TransactionControllerTransactionDroppedEvent\n | TransactionControllerTransactionFailedEvent\n | TransactionControllerTransactionFinishedEvent\n | TransactionControllerTransactionNewSwapApprovalEvent\n | TransactionControllerTransactionNewSwapEvent\n | TransactionControllerTransactionNewSwapAndSendEvent\n | TransactionControllerTransactionPublishingSkipped\n | TransactionControllerTransactionRejectedEvent\n | TransactionControllerTransactionStatusUpdatedEvent\n | TransactionControllerTransactionSubmittedEvent\n | TransactionControllerUnapprovedTransactionAddedEvent;\n\n/**\n * The messenger of the {@link TransactionController}.\n */\nexport type TransactionControllerMessenger = RestrictedControllerMessenger<\n typeof controllerName,\n TransactionControllerActions | AllowedActions,\n TransactionControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\n/**\n * Possible states of the approve transaction step.\n */\nexport enum ApprovalState {\n Approved = 'approved',\n NotApproved = 'not-approved',\n SkippedViaBeforePublishHook = 'skipped-via-before-publish-hook',\n}\n\n/**\n * Get the default TransactionsController state.\n *\n * @returns The default TransactionsController state.\n */\nfunction getDefaultTransactionControllerState(): TransactionControllerState {\n return {\n methodData: {},\n transactions: [],\n lastFetchedBlockNumbers: {},\n };\n}\n\n/**\n * Controller responsible for submitting and managing transactions.\n */\nexport class TransactionController extends BaseController<\n typeof controllerName,\n TransactionControllerState,\n TransactionControllerMessenger\n> {\n #internalEvents = new EventEmitter();\n\n private readonly isHistoryDisabled: boolean;\n\n private readonly isSwapsDisabled: boolean;\n\n private readonly isSendFlowHistoryDisabled: boolean;\n\n private readonly approvingTransactionIds: Set = new Set();\n\n private readonly nonceTracker: NonceTracker;\n\n private readonly registry: MethodRegistry;\n\n private readonly mutex = new Mutex();\n\n private readonly gasFeeFlows: GasFeeFlow[];\n\n private readonly getSavedGasFees: (chainId: Hex) => SavedGasFees | undefined;\n\n private readonly getNetworkState: () => NetworkState;\n\n private readonly getCurrentAccountEIP1559Compatibility: () => Promise;\n\n private readonly getCurrentNetworkEIP1559Compatibility: (\n networkClientId?: NetworkClientId,\n ) => Promise;\n\n private readonly getGasFeeEstimates: (\n options: FetchGasFeeEstimateOptions,\n ) => Promise;\n\n private readonly getPermittedAccounts: (origin?: string) => Promise;\n\n private readonly getExternalPendingTransactions: (\n address: string,\n chainId?: string,\n ) => NonceTrackerTransaction[];\n\n private readonly layer1GasFeeFlows: Layer1GasFeeFlow[];\n\n readonly #incomingTransactionOptions: IncomingTransactionOptions;\n\n private readonly incomingTransactionHelper: IncomingTransactionHelper;\n\n private readonly securityProviderRequest?: SecurityProviderRequest;\n\n readonly #pendingTransactionOptions: PendingTransactionOptions;\n\n private readonly pendingTransactionTracker: PendingTransactionTracker;\n\n private readonly signAbortCallbacks: Map void> = new Map();\n\n #transactionHistoryLimit: number;\n\n #isSimulationEnabled: () => boolean;\n\n #testGasFeeFlows: boolean;\n\n private readonly afterSign: (\n transactionMeta: TransactionMeta,\n signedTx: TypedTransaction,\n ) => boolean;\n\n private readonly beforeCheckPendingTransaction: (\n transactionMeta: TransactionMeta,\n ) => boolean;\n\n private readonly beforePublish: (transactionMeta: TransactionMeta) => boolean;\n\n private readonly publish: (\n transactionMeta: TransactionMeta,\n rawTx: string,\n ) => Promise<{ transactionHash?: string }>;\n\n private readonly getAdditionalSignArguments: (\n transactionMeta: TransactionMeta,\n ) => (TransactionMeta | undefined)[];\n\n private failTransaction(\n transactionMeta: TransactionMeta,\n error: Error,\n actionId?: string,\n ) {\n let newTransactionMeta: TransactionMeta;\n\n try {\n newTransactionMeta = this.#updateTransactionInternal(\n {\n transactionId: transactionMeta.id,\n note: 'TransactionController#failTransaction - Add error message and set status to failed',\n skipValidation: true,\n },\n (draftTransactionMeta) => {\n draftTransactionMeta.status = TransactionStatus.failed;\n\n (\n draftTransactionMeta as TransactionMeta & {\n status: TransactionStatus.failed;\n }\n ).error = normalizeTxError(error);\n },\n );\n } catch (err: unknown) {\n log('Failed to mark transaction as failed', err);\n\n newTransactionMeta = {\n ...transactionMeta,\n status: TransactionStatus.failed,\n error: normalizeTxError(error),\n };\n }\n\n this.messagingSystem.publish(`${controllerName}:transactionFailed`, {\n actionId,\n error: error.message,\n transactionMeta: newTransactionMeta,\n });\n\n this.onTransactionStatusChange(newTransactionMeta);\n\n this.messagingSystem.publish(\n `${controllerName}:transactionFinished`,\n newTransactionMeta,\n );\n\n this.#internalEvents.emit(\n `${transactionMeta.id}:finished`,\n newTransactionMeta,\n );\n }\n\n private async registryLookup(fourBytePrefix: string): Promise {\n const registryMethod = await this.registry.lookup(fourBytePrefix);\n if (!registryMethod) {\n return {\n registryMethod: '',\n parsedRegistryMethod: { name: undefined, args: undefined },\n };\n }\n const parsedRegistryMethod = this.registry.parse(registryMethod);\n return { registryMethod, parsedRegistryMethod };\n }\n\n #multichainTrackingHelper: MultichainTrackingHelper;\n\n /**\n * Method used to sign transactions\n */\n sign?: (\n transaction: TypedTransaction,\n from: string,\n transactionMeta?: TransactionMeta,\n ) => Promise;\n\n /**\n * Constructs a TransactionController.\n *\n * @param options - The controller options.\n * @param options.blockTracker - The block tracker used to poll for new blocks data.\n * @param options.disableHistory - Whether to disable storing history in transaction metadata.\n * @param options.disableSendFlowHistory - Explicitly disable transaction metadata history.\n * @param options.disableSwaps - Whether to disable additional processing on swaps transactions.\n * @param options.getCurrentAccountEIP1559Compatibility - Whether or not the account supports EIP-1559.\n * @param options.getCurrentNetworkEIP1559Compatibility - Whether or not the network supports EIP-1559.\n * @param options.getExternalPendingTransactions - Callback to retrieve pending transactions from external sources.\n * @param options.getGasFeeEstimates - Callback to retrieve gas fee estimates.\n * @param options.getNetworkClientRegistry - Gets the network client registry.\n * @param options.getNetworkState - Gets the state of the network controller.\n * @param options.getPermittedAccounts - Get accounts that a given origin has permissions for.\n * @param options.getSavedGasFees - Gets the saved gas fee config.\n * @param options.incomingTransactions - Configuration options for incoming transaction support.\n * @param options.isMultichainEnabled - Enable multichain support.\n * @param options.isSimulationEnabled - Whether new transactions will be automatically simulated.\n * @param options.messenger - The controller messenger.\n * @param options.onNetworkStateChange - Allows subscribing to network controller state changes.\n * @param options.pendingTransactions - Configuration options for pending transaction support.\n * @param options.provider - The provider used to create the underlying EthQuery instance.\n * @param options.securityProviderRequest - A function for verifying a transaction, whether it is malicious or not.\n * @param options.sign - Function used to sign transactions.\n * @param options.state - Initial state to set on this controller.\n * @param options.testGasFeeFlows - Whether to use the test gas fee flow.\n * @param options.transactionHistoryLimit - Transaction history limit.\n * @param options.hooks - The controller hooks.\n */\n constructor({\n blockTracker,\n disableHistory,\n disableSendFlowHistory,\n disableSwaps,\n getCurrentAccountEIP1559Compatibility,\n getCurrentNetworkEIP1559Compatibility,\n getExternalPendingTransactions,\n getGasFeeEstimates,\n getNetworkClientRegistry,\n getNetworkState,\n getPermittedAccounts,\n getSavedGasFees,\n incomingTransactions = {},\n isMultichainEnabled = false,\n isSimulationEnabled,\n messenger,\n onNetworkStateChange,\n pendingTransactions = {},\n provider,\n securityProviderRequest,\n sign,\n state,\n testGasFeeFlows,\n transactionHistoryLimit = 40,\n hooks,\n }: TransactionControllerOptions) {\n super({\n name: controllerName,\n metadata,\n messenger,\n state: {\n ...getDefaultTransactionControllerState(),\n ...state,\n },\n });\n\n this.messagingSystem = messenger;\n this.getNetworkState = getNetworkState;\n this.isSendFlowHistoryDisabled = disableSendFlowHistory ?? false;\n this.isHistoryDisabled = disableHistory ?? false;\n this.isSwapsDisabled = disableSwaps ?? false;\n this.#isSimulationEnabled = isSimulationEnabled ?? (() => true);\n // @ts-expect-error the type in eth-method-registry is inappropriate and should be changed\n this.registry = new MethodRegistry({ provider });\n this.getSavedGasFees = getSavedGasFees ?? ((_chainId) => undefined);\n this.getCurrentAccountEIP1559Compatibility =\n getCurrentAccountEIP1559Compatibility ?? (() => Promise.resolve(true));\n this.getCurrentNetworkEIP1559Compatibility =\n getCurrentNetworkEIP1559Compatibility;\n this.getGasFeeEstimates =\n getGasFeeEstimates || (() => Promise.resolve({} as GasFeeState));\n this.getPermittedAccounts = getPermittedAccounts;\n this.getExternalPendingTransactions =\n getExternalPendingTransactions ?? (() => []);\n this.securityProviderRequest = securityProviderRequest;\n this.#incomingTransactionOptions = incomingTransactions;\n this.#pendingTransactionOptions = pendingTransactions;\n this.#transactionHistoryLimit = transactionHistoryLimit;\n this.sign = sign;\n this.#testGasFeeFlows = testGasFeeFlows === true;\n\n this.afterSign = hooks?.afterSign ?? (() => true);\n this.beforeCheckPendingTransaction =\n hooks?.beforeCheckPendingTransaction ??\n /* istanbul ignore next */\n (() => true);\n this.beforePublish = hooks?.beforePublish ?? (() => true);\n this.getAdditionalSignArguments =\n hooks?.getAdditionalSignArguments ?? (() => []);\n this.publish =\n hooks?.publish ?? (() => Promise.resolve({ transactionHash: undefined }));\n\n this.nonceTracker = this.#createNonceTracker({\n provider,\n blockTracker,\n });\n\n const findNetworkClientIdByChainId = (chainId: Hex) => {\n return this.messagingSystem.call(\n `NetworkController:findNetworkClientIdByChainId`,\n chainId,\n );\n };\n\n this.#multichainTrackingHelper = new MultichainTrackingHelper({\n isMultichainEnabled,\n provider,\n nonceTracker: this.nonceTracker,\n incomingTransactionOptions: incomingTransactions,\n findNetworkClientIdByChainId,\n getNetworkClientById: ((networkClientId: NetworkClientId) => {\n return this.messagingSystem.call(\n `NetworkController:getNetworkClientById`,\n networkClientId,\n );\n }) as NetworkController['getNetworkClientById'],\n getNetworkClientRegistry,\n removeIncomingTransactionHelperListeners:\n this.#removeIncomingTransactionHelperListeners.bind(this),\n removePendingTransactionTrackerListeners:\n this.#removePendingTransactionTrackerListeners.bind(this),\n createNonceTracker: this.#createNonceTracker.bind(this),\n createIncomingTransactionHelper:\n this.#createIncomingTransactionHelper.bind(this),\n createPendingTransactionTracker:\n this.#createPendingTransactionTracker.bind(this),\n onNetworkStateChange: (listener) => {\n this.messagingSystem.subscribe(\n 'NetworkController:stateChange',\n listener,\n );\n },\n });\n this.#multichainTrackingHelper.initialize();\n\n const etherscanRemoteTransactionSource =\n new EtherscanRemoteTransactionSource({\n includeTokenTransfers: incomingTransactions.includeTokenTransfers,\n });\n\n this.incomingTransactionHelper = this.#createIncomingTransactionHelper({\n blockTracker,\n etherscanRemoteTransactionSource,\n });\n\n this.pendingTransactionTracker = this.#createPendingTransactionTracker({\n provider,\n blockTracker,\n });\n\n this.gasFeeFlows = this.#getGasFeeFlows();\n this.layer1GasFeeFlows = this.#getLayer1GasFeeFlows();\n\n const gasFeePoller = new GasFeePoller({\n findNetworkClientIdByChainId,\n gasFeeFlows: this.gasFeeFlows,\n getGasFeeControllerEstimates: this.getGasFeeEstimates,\n getProvider: (chainId, networkClientId) =>\n this.#multichainTrackingHelper.getProvider({\n networkClientId,\n chainId,\n }),\n getTransactions: () => this.state.transactions,\n layer1GasFeeFlows: this.layer1GasFeeFlows,\n onStateChange: (listener) => {\n this.messagingSystem.subscribe(\n 'TransactionController:stateChange',\n listener,\n );\n },\n });\n\n gasFeePoller.hub.on(\n 'transaction-updated',\n this.#onGasFeePollerTransactionUpdate.bind(this),\n );\n\n // when transactionsController state changes\n // check for pending transactions and start polling if there are any\n this.messagingSystem.subscribe(\n 'TransactionController:stateChange',\n this.#checkForPendingTransactionAndStartPolling,\n );\n\n // TODO once v2 is merged make sure this only runs when\n // selectedNetworkClientId changes\n onNetworkStateChange(() => {\n log('Detected network change', this.getChainId());\n this.pendingTransactionTracker.startIfPendingTransactions();\n this.onBootCleanup();\n });\n\n this.onBootCleanup();\n this.#checkForPendingTransactionAndStartPolling();\n }\n\n /**\n * Stops polling and removes listeners to prepare the controller for garbage collection.\n */\n destroy() {\n this.#stopAllTracking();\n }\n\n /**\n * Handle new method data request.\n *\n * @param fourBytePrefix - The method prefix.\n * @returns The method data object corresponding to the given signature prefix.\n */\n async handleMethodData(fourBytePrefix: string): Promise {\n const releaseLock = await this.mutex.acquire();\n try {\n const { methodData } = this.state;\n const knownMethod = Object.keys(methodData).find(\n (knownFourBytePrefix) => fourBytePrefix === knownFourBytePrefix,\n );\n if (knownMethod) {\n return methodData[fourBytePrefix];\n }\n const registry = await this.registryLookup(fourBytePrefix);\n this.update((state) => {\n state.methodData[fourBytePrefix] = registry;\n });\n return registry;\n } finally {\n releaseLock();\n }\n }\n\n /**\n * Add a new unapproved transaction to state. Parameters will be validated, a\n * unique transaction id will be generated, and gas and gasPrice will be calculated\n * if not provided. If A `:unapproved` hub event will be emitted once added.\n *\n * @param txParams - Standard parameters for an Ethereum transaction.\n * @param opts - Additional options to control how the transaction is added.\n * @param opts.actionId - Unique ID to prevent duplicate requests.\n * @param opts.deviceConfirmedOn - An enum to indicate what device confirmed the transaction.\n * @param opts.method - RPC method that requested the transaction.\n * @param opts.origin - The origin of the transaction request, such as a dApp hostname.\n * @param opts.requireApproval - Whether the transaction requires approval by the user, defaults to true unless explicitly disabled.\n * @param opts.securityAlertResponse - Response from security validator.\n * @param opts.sendFlowHistory - The sendFlowHistory entries to add.\n * @param opts.type - Type of transaction to add, such as 'cancel' or 'swap'.\n * @param opts.swaps - Options for swaps transactions.\n * @param opts.swaps.hasApproveTx - Whether the transaction has an approval transaction.\n * @param opts.swaps.meta - Metadata for swap transaction.\n * @param opts.networkClientId - The id of the network client for this transaction.\n * @returns Object containing a promise resolving to the transaction hash if approved.\n */\n async addTransaction(\n txParams: TransactionParams,\n {\n actionId,\n deviceConfirmedOn,\n method,\n origin,\n requireApproval,\n securityAlertResponse,\n sendFlowHistory,\n swaps = {},\n type,\n networkClientId: requestNetworkClientId,\n }: {\n actionId?: string;\n deviceConfirmedOn?: WalletDevice;\n method?: string;\n origin?: string;\n requireApproval?: boolean | undefined;\n securityAlertResponse?: SecurityAlertResponse;\n sendFlowHistory?: SendFlowHistoryEntry[];\n swaps?: {\n hasApproveTx?: boolean;\n meta?: Partial;\n };\n type?: TransactionType;\n networkClientId?: NetworkClientId;\n } = {},\n ): Promise {\n log('Adding transaction', txParams);\n\n txParams = normalizeTransactionParams(txParams);\n if (\n requestNetworkClientId &&\n !this.#multichainTrackingHelper.has(requestNetworkClientId)\n ) {\n throw new Error(\n 'The networkClientId for this transaction could not be found',\n );\n }\n\n const networkClientId =\n requestNetworkClientId ?? this.#getGlobalNetworkClientId();\n\n const isEIP1559Compatible = await this.getEIP1559Compatibility(\n networkClientId,\n );\n\n validateTxParams(txParams, isEIP1559Compatible);\n\n if (origin) {\n await validateTransactionOrigin(\n await this.getPermittedAccounts(origin),\n this.#getSelectedAccount().address,\n txParams.from,\n origin,\n );\n }\n\n const dappSuggestedGasFees = this.generateDappSuggestedGasFees(\n txParams,\n origin,\n );\n\n const chainId = this.getChainId(networkClientId);\n const ethQuery = this.#multichainTrackingHelper.getEthQuery({\n networkClientId,\n chainId,\n });\n\n const transactionType =\n type ?? (await determineTransactionType(txParams, ethQuery)).type;\n\n const existingTransactionMeta = this.getTransactionWithActionId(actionId);\n\n // If a request to add a transaction with the same actionId is submitted again, a new transaction will not be created for it.\n let addedTransactionMeta = existingTransactionMeta\n ? cloneDeep(existingTransactionMeta)\n : {\n // Add actionId to txMeta to check if same actionId is seen again\n actionId,\n chainId,\n dappSuggestedGasFees,\n deviceConfirmedOn,\n id: random(),\n origin,\n securityAlertResponse,\n status: TransactionStatus.unapproved as const,\n time: Date.now(),\n txParams,\n userEditedGasLimit: false,\n verifiedOnBlockchain: false,\n type: transactionType,\n networkClientId,\n };\n\n await this.updateGasProperties(addedTransactionMeta);\n\n // Checks if a transaction already exists with a given actionId\n if (!existingTransactionMeta) {\n // Set security provider response\n if (method && this.securityProviderRequest) {\n const securityProviderResponse = await this.securityProviderRequest(\n addedTransactionMeta,\n method,\n );\n addedTransactionMeta.securityProviderResponse =\n securityProviderResponse;\n }\n\n if (!this.isSendFlowHistoryDisabled) {\n addedTransactionMeta.sendFlowHistory = sendFlowHistory ?? [];\n }\n // Initial history push\n if (!this.isHistoryDisabled) {\n addedTransactionMeta = addInitialHistorySnapshot(addedTransactionMeta);\n }\n\n addedTransactionMeta = updateSwapsTransaction(\n addedTransactionMeta,\n transactionType,\n swaps,\n {\n isSwapsDisabled: this.isSwapsDisabled,\n cancelTransaction: this.cancelTransaction.bind(this),\n messenger: this.messagingSystem,\n },\n );\n\n this.addMetadata(addedTransactionMeta);\n\n if (requireApproval !== false) {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.#updateSimulationData(addedTransactionMeta);\n } else {\n log('Skipping simulation as approval not required');\n }\n\n this.messagingSystem.publish(\n `${controllerName}:unapprovedTransactionAdded`,\n addedTransactionMeta,\n );\n }\n\n return {\n result: this.processApproval(addedTransactionMeta, {\n isExisting: Boolean(existingTransactionMeta),\n requireApproval,\n actionId,\n }),\n transactionMeta: addedTransactionMeta,\n };\n }\n\n startIncomingTransactionPolling(networkClientIds: NetworkClientId[] = []) {\n if (networkClientIds.length === 0) {\n this.incomingTransactionHelper.start();\n return;\n }\n this.#multichainTrackingHelper.startIncomingTransactionPolling(\n networkClientIds,\n );\n }\n\n stopIncomingTransactionPolling(networkClientIds: NetworkClientId[] = []) {\n if (networkClientIds.length === 0) {\n this.incomingTransactionHelper.stop();\n return;\n }\n this.#multichainTrackingHelper.stopIncomingTransactionPolling(\n networkClientIds,\n );\n }\n\n stopAllIncomingTransactionPolling() {\n this.incomingTransactionHelper.stop();\n this.#multichainTrackingHelper.stopAllIncomingTransactionPolling();\n }\n\n async updateIncomingTransactions(networkClientIds: NetworkClientId[] = []) {\n if (networkClientIds.length === 0) {\n await this.incomingTransactionHelper.update();\n return;\n }\n await this.#multichainTrackingHelper.updateIncomingTransactions(\n networkClientIds,\n );\n }\n\n /**\n * Attempts to cancel a transaction based on its ID by setting its status to \"rejected\"\n * and emitting a `:finished` hub event.\n *\n * @param transactionId - The ID of the transaction to cancel.\n * @param gasValues - The gas values to use for the cancellation transaction.\n * @param options - The options for the cancellation transaction.\n * @param options.actionId - Unique ID to prevent duplicate requests.\n * @param options.estimatedBaseFee - The estimated base fee of the transaction.\n */\n async stopTransaction(\n transactionId: string,\n gasValues?: GasPriceValue | FeeMarketEIP1559Values,\n {\n estimatedBaseFee,\n actionId,\n }: { estimatedBaseFee?: string; actionId?: string } = {},\n ) {\n // If transaction is found for same action id, do not create a cancel transaction.\n if (this.getTransactionWithActionId(actionId)) {\n return;\n }\n\n if (gasValues) {\n // Not good practice to reassign a parameter but temporarily avoiding a larger refactor.\n gasValues = normalizeGasFeeValues(gasValues);\n validateGasValues(gasValues);\n }\n\n log('Creating cancel transaction', transactionId, gasValues);\n\n const transactionMeta = this.getTransaction(transactionId);\n if (!transactionMeta) {\n return;\n }\n\n if (!this.sign) {\n throw new Error('No sign method defined.');\n }\n\n // gasPrice (legacy non EIP1559)\n const minGasPrice = getIncreasedPriceFromExisting(\n transactionMeta.txParams.gasPrice,\n CANCEL_RATE,\n );\n\n const gasPriceFromValues = isGasPriceValue(gasValues) && gasValues.gasPrice;\n\n const newGasPrice =\n (gasPriceFromValues &&\n validateMinimumIncrease(gasPriceFromValues, minGasPrice)) ||\n minGasPrice;\n\n // maxFeePerGas (EIP1559)\n const existingMaxFeePerGas = transactionMeta.txParams?.maxFeePerGas;\n const minMaxFeePerGas = getIncreasedPriceFromExisting(\n existingMaxFeePerGas,\n CANCEL_RATE,\n );\n const maxFeePerGasValues =\n isFeeMarketEIP1559Values(gasValues) && gasValues.maxFeePerGas;\n const newMaxFeePerGas =\n (maxFeePerGasValues &&\n validateMinimumIncrease(maxFeePerGasValues, minMaxFeePerGas)) ||\n (existingMaxFeePerGas && minMaxFeePerGas);\n\n // maxPriorityFeePerGas (EIP1559)\n const existingMaxPriorityFeePerGas =\n transactionMeta.txParams?.maxPriorityFeePerGas;\n const minMaxPriorityFeePerGas = getIncreasedPriceFromExisting(\n existingMaxPriorityFeePerGas,\n CANCEL_RATE,\n );\n const maxPriorityFeePerGasValues =\n isFeeMarketEIP1559Values(gasValues) && gasValues.maxPriorityFeePerGas;\n const newMaxPriorityFeePerGas =\n (maxPriorityFeePerGasValues &&\n validateMinimumIncrease(\n maxPriorityFeePerGasValues,\n minMaxPriorityFeePerGas,\n )) ||\n (existingMaxPriorityFeePerGas && minMaxPriorityFeePerGas);\n\n const newTxParams: TransactionParams =\n newMaxFeePerGas && newMaxPriorityFeePerGas\n ? {\n from: transactionMeta.txParams.from,\n gasLimit: transactionMeta.txParams.gas,\n maxFeePerGas: newMaxFeePerGas,\n maxPriorityFeePerGas: newMaxPriorityFeePerGas,\n type: TransactionEnvelopeType.feeMarket,\n nonce: transactionMeta.txParams.nonce,\n to: transactionMeta.txParams.from,\n value: '0x0',\n }\n : {\n from: transactionMeta.txParams.from,\n gasLimit: transactionMeta.txParams.gas,\n gasPrice: newGasPrice,\n nonce: transactionMeta.txParams.nonce,\n to: transactionMeta.txParams.from,\n value: '0x0',\n };\n\n const unsignedEthTx = this.prepareUnsignedEthTx(\n transactionMeta.chainId,\n newTxParams,\n );\n\n const signedTx = await this.sign(\n unsignedEthTx,\n transactionMeta.txParams.from,\n );\n\n const rawTx = bufferToHex(signedTx.serialize());\n\n const newFee = newTxParams.maxFeePerGas ?? newTxParams.gasPrice;\n\n const oldFee = newTxParams.maxFeePerGas\n ? transactionMeta.txParams.maxFeePerGas\n : transactionMeta.txParams.gasPrice;\n\n log('Submitting cancel transaction', {\n oldFee,\n newFee,\n txParams: newTxParams,\n });\n\n const ethQuery = this.#multichainTrackingHelper.getEthQuery({\n networkClientId: transactionMeta.networkClientId,\n chainId: transactionMeta.chainId,\n });\n const hash = await this.publishTransactionForRetry(\n ethQuery,\n rawTx,\n transactionMeta,\n );\n\n const cancelTransactionMeta = {\n actionId,\n chainId: transactionMeta.chainId,\n networkClientId: transactionMeta.networkClientId,\n estimatedBaseFee,\n hash,\n id: random(),\n originalGasEstimate: transactionMeta.txParams.gas,\n rawTx,\n status: TransactionStatus.submitted as const,\n time: Date.now(),\n type: TransactionType.cancel as const,\n txParams: newTxParams,\n };\n\n this.addMetadata(cancelTransactionMeta);\n\n // stopTransaction has no approval request, so we assume the user has already approved the transaction\n this.messagingSystem.publish(`${controllerName}:transactionApproved`, {\n transactionMeta: cancelTransactionMeta,\n actionId,\n });\n this.messagingSystem.publish(`${controllerName}:transactionSubmitted`, {\n transactionMeta: cancelTransactionMeta,\n actionId,\n });\n\n this.messagingSystem.publish(\n `${controllerName}:transactionFinished`,\n cancelTransactionMeta,\n );\n this.#internalEvents.emit(\n `${transactionMeta.id}:finished`,\n cancelTransactionMeta,\n );\n }\n\n /**\n * Attempts to speed up a transaction increasing transaction gasPrice by ten percent.\n *\n * @param transactionId - The ID of the transaction to speed up.\n * @param gasValues - The gas values to use for the speed up transaction.\n * @param options - The options for the speed up transaction.\n * @param options.actionId - Unique ID to prevent duplicate requests\n * @param options.estimatedBaseFee - The estimated base fee of the transaction.\n */\n async speedUpTransaction(\n transactionId: string,\n gasValues?: GasPriceValue | FeeMarketEIP1559Values,\n {\n actionId,\n estimatedBaseFee,\n }: { actionId?: string; estimatedBaseFee?: string } = {},\n ) {\n // If transaction is found for same action id, do not create a new speed up transaction.\n if (this.getTransactionWithActionId(actionId)) {\n return;\n }\n\n if (gasValues) {\n // Not good practice to reassign a parameter but temporarily avoiding a larger refactor.\n gasValues = normalizeGasFeeValues(gasValues);\n validateGasValues(gasValues);\n }\n\n log('Creating speed up transaction', transactionId, gasValues);\n\n const transactionMeta = this.getTransaction(transactionId);\n /* istanbul ignore next */\n if (!transactionMeta) {\n return;\n }\n\n /* istanbul ignore next */\n if (!this.sign) {\n throw new Error('No sign method defined.');\n }\n\n // gasPrice (legacy non EIP1559)\n const minGasPrice = getIncreasedPriceFromExisting(\n transactionMeta.txParams.gasPrice,\n SPEED_UP_RATE,\n );\n\n const gasPriceFromValues = isGasPriceValue(gasValues) && gasValues.gasPrice;\n\n const newGasPrice =\n (gasPriceFromValues &&\n validateMinimumIncrease(gasPriceFromValues, minGasPrice)) ||\n minGasPrice;\n\n // maxFeePerGas (EIP1559)\n const existingMaxFeePerGas = transactionMeta.txParams?.maxFeePerGas;\n const minMaxFeePerGas = getIncreasedPriceFromExisting(\n existingMaxFeePerGas,\n SPEED_UP_RATE,\n );\n const maxFeePerGasValues =\n isFeeMarketEIP1559Values(gasValues) && gasValues.maxFeePerGas;\n const newMaxFeePerGas =\n (maxFeePerGasValues &&\n validateMinimumIncrease(maxFeePerGasValues, minMaxFeePerGas)) ||\n (existingMaxFeePerGas && minMaxFeePerGas);\n\n // maxPriorityFeePerGas (EIP1559)\n const existingMaxPriorityFeePerGas =\n transactionMeta.txParams?.maxPriorityFeePerGas;\n const minMaxPriorityFeePerGas = getIncreasedPriceFromExisting(\n existingMaxPriorityFeePerGas,\n SPEED_UP_RATE,\n );\n const maxPriorityFeePerGasValues =\n isFeeMarketEIP1559Values(gasValues) && gasValues.maxPriorityFeePerGas;\n const newMaxPriorityFeePerGas =\n (maxPriorityFeePerGasValues &&\n validateMinimumIncrease(\n maxPriorityFeePerGasValues,\n minMaxPriorityFeePerGas,\n )) ||\n (existingMaxPriorityFeePerGas && minMaxPriorityFeePerGas);\n\n const txParams: TransactionParams =\n newMaxFeePerGas && newMaxPriorityFeePerGas\n ? {\n ...transactionMeta.txParams,\n gasLimit: transactionMeta.txParams.gas,\n maxFeePerGas: newMaxFeePerGas,\n maxPriorityFeePerGas: newMaxPriorityFeePerGas,\n type: TransactionEnvelopeType.feeMarket,\n }\n : {\n ...transactionMeta.txParams,\n gasLimit: transactionMeta.txParams.gas,\n gasPrice: newGasPrice,\n };\n\n const unsignedEthTx = this.prepareUnsignedEthTx(\n transactionMeta.chainId,\n txParams,\n );\n\n const signedTx = await this.sign(\n unsignedEthTx,\n transactionMeta.txParams.from,\n );\n\n const transactionMetaWithRsv = this.updateTransactionMetaRSV(\n transactionMeta,\n signedTx,\n );\n const rawTx = bufferToHex(signedTx.serialize());\n\n const newFee = txParams.maxFeePerGas ?? txParams.gasPrice;\n\n const oldFee = txParams.maxFeePerGas\n ? transactionMetaWithRsv.txParams.maxFeePerGas\n : transactionMetaWithRsv.txParams.gasPrice;\n\n log('Submitting speed up transaction', { oldFee, newFee, txParams });\n\n const ethQuery = this.#multichainTrackingHelper.getEthQuery({\n networkClientId: transactionMeta.networkClientId,\n chainId: transactionMeta.chainId,\n });\n const hash = await this.publishTransactionForRetry(\n ethQuery,\n rawTx,\n transactionMeta,\n );\n\n const baseTransactionMeta = {\n ...transactionMetaWithRsv,\n estimatedBaseFee,\n id: random(),\n time: Date.now(),\n hash,\n actionId,\n originalGasEstimate: transactionMeta.txParams.gas,\n type: TransactionType.retry as const,\n originalType: transactionMeta.type,\n };\n\n const newTransactionMeta =\n newMaxFeePerGas && newMaxPriorityFeePerGas\n ? {\n ...baseTransactionMeta,\n txParams: {\n ...transactionMeta.txParams,\n maxFeePerGas: newMaxFeePerGas,\n maxPriorityFeePerGas: newMaxPriorityFeePerGas,\n },\n }\n : {\n ...baseTransactionMeta,\n txParams: {\n ...transactionMeta.txParams,\n gasPrice: newGasPrice,\n },\n };\n\n this.addMetadata(newTransactionMeta);\n\n // speedUpTransaction has no approval request, so we assume the user has already approved the transaction\n this.messagingSystem.publish(`${controllerName}:transactionApproved`, {\n transactionMeta: newTransactionMeta,\n actionId,\n });\n\n this.messagingSystem.publish(`${controllerName}:transactionSubmitted`, {\n transactionMeta: newTransactionMeta,\n actionId,\n });\n\n this.messagingSystem.publish(\n `${controllerName}:speedupTransactionAdded`,\n newTransactionMeta,\n );\n }\n\n /**\n * Estimates required gas for a given transaction.\n *\n * @param transaction - The transaction to estimate gas for.\n * @param networkClientId - The network client id to use for the estimate.\n * @returns The gas and gas price.\n */\n async estimateGas(\n transaction: TransactionParams,\n networkClientId?: NetworkClientId,\n ) {\n const ethQuery = this.#multichainTrackingHelper.getEthQuery({\n networkClientId,\n });\n const { estimatedGas, simulationFails } = await estimateGas(\n transaction,\n ethQuery,\n );\n\n return { gas: estimatedGas, simulationFails };\n }\n\n /**\n * Estimates required gas for a given transaction and add additional gas buffer with the given multiplier.\n *\n * @param transaction - The transaction params to estimate gas for.\n * @param multiplier - The multiplier to use for the gas buffer.\n * @param networkClientId - The network client id to use for the estimate.\n */\n async estimateGasBuffered(\n transaction: TransactionParams,\n multiplier: number,\n networkClientId?: NetworkClientId,\n ) {\n const ethQuery = this.#multichainTrackingHelper.getEthQuery({\n networkClientId,\n });\n const { blockGasLimit, estimatedGas, simulationFails } = await estimateGas(\n transaction,\n ethQuery,\n );\n\n const gas = addGasBuffer(estimatedGas, blockGasLimit, multiplier);\n\n return {\n gas,\n simulationFails,\n };\n }\n\n /**\n * Updates an existing transaction in state.\n *\n * @param transactionMeta - The new transaction to store in state.\n * @param note - A note or update reason to include in the transaction history.\n */\n updateTransaction(transactionMeta: TransactionMeta, note: string) {\n const { id: transactionId } = transactionMeta;\n\n this.#updateTransactionInternal({ transactionId, note }, () => ({\n ...transactionMeta,\n }));\n }\n\n /**\n * Update the security alert response for a transaction.\n *\n * @param transactionId - ID of the transaction.\n * @param securityAlertResponse - The new security alert response for the transaction.\n */\n updateSecurityAlertResponse(\n transactionId: string,\n securityAlertResponse: SecurityAlertResponse,\n ) {\n if (!securityAlertResponse) {\n throw new Error(\n 'updateSecurityAlertResponse: securityAlertResponse should not be null',\n );\n }\n const transactionMeta = this.getTransaction(transactionId);\n if (!transactionMeta) {\n throw new Error(\n `Cannot update security alert response as no transaction metadata found`,\n );\n }\n const updatedTransactionMeta = {\n ...transactionMeta,\n securityAlertResponse,\n };\n this.updateTransaction(\n updatedTransactionMeta,\n `${controllerName}:updatesecurityAlertResponse - securityAlertResponse updated`,\n );\n }\n\n /**\n * Removes all transactions from state, optionally based on the current network.\n *\n * @param ignoreNetwork - Determines whether to wipe all transactions, or just those on the\n * current network. If `true`, all transactions are wiped.\n * @param address - If specified, only transactions originating from this address will be\n * wiped on current network.\n */\n wipeTransactions(ignoreNetwork?: boolean, address?: string) {\n /* istanbul ignore next */\n if (ignoreNetwork && !address) {\n this.update((state) => {\n state.transactions = [];\n });\n return;\n }\n const currentChainId = this.getChainId();\n const newTransactions = this.state.transactions.filter(\n ({ chainId, txParams }) => {\n const isMatchingNetwork = ignoreNetwork || chainId === currentChainId;\n\n if (!isMatchingNetwork) {\n return true;\n }\n\n const isMatchingAddress =\n !address || txParams.from?.toLowerCase() === address.toLowerCase();\n\n return !isMatchingAddress;\n },\n );\n\n this.update((state) => {\n state.transactions = this.trimTransactionsForState(newTransactions);\n });\n }\n\n /**\n * Adds external provided transaction to state as confirmed transaction.\n *\n * @param transactionMeta - TransactionMeta to add transactions.\n * @param transactionReceipt - TransactionReceipt of the external transaction.\n * @param baseFeePerGas - Base fee per gas of the external transaction.\n */\n async confirmExternalTransaction(\n transactionMeta: TransactionMeta,\n transactionReceipt: TransactionReceipt,\n baseFeePerGas: Hex,\n ) {\n // Run validation and add external transaction to state.\n const newTransactionMeta = this.addExternalTransaction(transactionMeta);\n\n try {\n const transactionId = newTransactionMeta.id;\n\n // Make sure status is confirmed and define gasUsed as in receipt.\n const updatedTransactionMeta = {\n ...newTransactionMeta,\n status: TransactionStatus.confirmed as const,\n txReceipt: transactionReceipt,\n };\n if (baseFeePerGas) {\n updatedTransactionMeta.baseFeePerGas = baseFeePerGas;\n }\n\n // Update same nonce local transactions as dropped and define replacedBy properties.\n this.markNonceDuplicatesDropped(transactionId);\n\n // Update external provided transaction with updated gas values and confirmed status.\n this.updateTransaction(\n updatedTransactionMeta,\n `${controllerName}:confirmExternalTransaction - Add external transaction`,\n );\n this.onTransactionStatusChange(updatedTransactionMeta);\n\n // Intentional given potential duration of process.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.updatePostBalance(updatedTransactionMeta);\n\n this.messagingSystem.publish(\n `${controllerName}:transactionConfirmed`,\n updatedTransactionMeta,\n );\n } catch (error) {\n console.error('Failed to confirm external transaction', error);\n }\n }\n\n /**\n * Append new send flow history to a transaction.\n *\n * @param transactionID - The ID of the transaction to update.\n * @param currentSendFlowHistoryLength - The length of the current sendFlowHistory array.\n * @param sendFlowHistoryToAdd - The sendFlowHistory entries to add.\n * @returns The updated transactionMeta.\n */\n updateTransactionSendFlowHistory(\n transactionID: string,\n currentSendFlowHistoryLength: number,\n sendFlowHistoryToAdd: SendFlowHistoryEntry[],\n ): TransactionMeta {\n if (this.isSendFlowHistoryDisabled) {\n throw new Error(\n 'Send flow history is disabled for the current transaction controller',\n );\n }\n\n const transactionMeta = this.getTransaction(transactionID);\n\n if (!transactionMeta) {\n throw new Error(\n `Cannot update send flow history as no transaction metadata found`,\n );\n }\n\n validateIfTransactionUnapproved(\n transactionMeta,\n 'updateTransactionSendFlowHistory',\n );\n\n const sendFlowHistory = transactionMeta.sendFlowHistory ?? [];\n if (currentSendFlowHistoryLength === sendFlowHistory.length) {\n const updatedTransactionMeta = {\n ...transactionMeta,\n sendFlowHistory: [...sendFlowHistory, ...sendFlowHistoryToAdd],\n };\n this.updateTransaction(\n updatedTransactionMeta,\n `${controllerName}:updateTransactionSendFlowHistory - sendFlowHistory updated`,\n );\n }\n\n return this.getTransaction(transactionID) as TransactionMeta;\n }\n\n /**\n * Update the gas values of a transaction.\n *\n * @param transactionId - The ID of the transaction to update.\n * @param gasValues - Gas values to update.\n * @param gasValues.gas - Same as transaction.gasLimit.\n * @param gasValues.gasLimit - Maxmimum number of units of gas to use for this transaction.\n * @param gasValues.gasPrice - Price per gas for legacy transactions.\n * @param gasValues.maxPriorityFeePerGas - Maximum amount per gas to give to validator as incentive.\n * @param gasValues.maxFeePerGas - Maximum amount per gas to pay for the transaction, including the priority fee.\n * @param gasValues.estimateUsed - Which estimate level was used.\n * @param gasValues.estimateSuggested - Which estimate level that the API suggested.\n * @param gasValues.defaultGasEstimates - The default estimate for gas.\n * @param gasValues.originalGasEstimate - Original estimate for gas.\n * @param gasValues.userEditedGasLimit - The gas limit supplied by user.\n * @param gasValues.userFeeLevel - Estimate level user selected.\n * @returns The updated transactionMeta.\n */\n updateTransactionGasFees(\n transactionId: string,\n {\n defaultGasEstimates,\n estimateUsed,\n estimateSuggested,\n gas,\n gasLimit,\n gasPrice,\n maxPriorityFeePerGas,\n maxFeePerGas,\n originalGasEstimate,\n userEditedGasLimit,\n userFeeLevel,\n }: {\n defaultGasEstimates?: string;\n estimateUsed?: string;\n estimateSuggested?: string;\n gas?: string;\n gasLimit?: string;\n gasPrice?: string;\n maxPriorityFeePerGas?: string;\n maxFeePerGas?: string;\n originalGasEstimate?: string;\n userEditedGasLimit?: boolean;\n userFeeLevel?: string;\n },\n ): TransactionMeta {\n const transactionMeta = this.getTransaction(transactionId);\n\n if (!transactionMeta) {\n throw new Error(\n `Cannot update transaction as no transaction metadata found`,\n );\n }\n\n validateIfTransactionUnapproved(\n transactionMeta,\n 'updateTransactionGasFees',\n );\n\n let transactionGasFees = {\n txParams: {\n gas,\n gasLimit,\n gasPrice,\n maxPriorityFeePerGas,\n maxFeePerGas,\n },\n defaultGasEstimates,\n estimateUsed,\n estimateSuggested,\n originalGasEstimate,\n userEditedGasLimit,\n userFeeLevel,\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } as any;\n\n // only update what is defined\n transactionGasFees.txParams = pickBy(transactionGasFees.txParams);\n transactionGasFees = pickBy(transactionGasFees);\n\n // merge updated gas values with existing transaction meta\n const updatedMeta = merge({}, transactionMeta, transactionGasFees);\n\n this.updateTransaction(\n updatedMeta,\n `${controllerName}:updateTransactionGasFees - gas values updated`,\n );\n\n return this.getTransaction(transactionId) as TransactionMeta;\n }\n\n /**\n * Update the previous gas values of a transaction.\n *\n * @param transactionId - The ID of the transaction to update.\n * @param previousGas - Previous gas values to update.\n * @param previousGas.gasLimit - Maxmimum number of units of gas to use for this transaction.\n * @param previousGas.maxFeePerGas - Maximum amount per gas to pay for the transaction, including the priority fee.\n * @param previousGas.maxPriorityFeePerGas - Maximum amount per gas to give to validator as incentive.\n * @returns The updated transactionMeta.\n */\n updatePreviousGasParams(\n transactionId: string,\n {\n gasLimit,\n maxFeePerGas,\n maxPriorityFeePerGas,\n }: {\n gasLimit?: string;\n maxFeePerGas?: string;\n maxPriorityFeePerGas?: string;\n },\n ): TransactionMeta {\n const transactionMeta = this.getTransaction(transactionId);\n\n if (!transactionMeta) {\n throw new Error(\n `Cannot update transaction as no transaction metadata found`,\n );\n }\n\n validateIfTransactionUnapproved(transactionMeta, 'updatePreviousGasParams');\n\n const transactionPreviousGas = {\n previousGas: {\n gasLimit,\n maxFeePerGas,\n maxPriorityFeePerGas,\n },\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } as any;\n\n // only update what is defined\n transactionPreviousGas.previousGas = pickBy(\n transactionPreviousGas.previousGas,\n );\n\n // merge updated previous gas values with existing transaction meta\n const updatedMeta = merge({}, transactionMeta, transactionPreviousGas);\n\n this.updateTransaction(\n updatedMeta,\n `${controllerName}:updatePreviousGasParams - Previous gas values updated`,\n );\n\n return this.getTransaction(transactionId) as TransactionMeta;\n }\n\n async getNonceLock(\n address: string,\n networkClientId?: NetworkClientId,\n ): Promise {\n return this.#multichainTrackingHelper.getNonceLock(\n address,\n networkClientId,\n );\n }\n\n /**\n * Updates the editable parameters of a transaction.\n *\n * @param txId - The ID of the transaction to update.\n * @param params - The editable parameters to update.\n * @param params.data - Data to pass with the transaction.\n * @param params.gas - Maximum number of units of gas to use for the transaction.\n * @param params.gasPrice - Price per gas for legacy transactions.\n * @param params.from - Address to send the transaction from.\n * @param params.to - Address to send the transaction to.\n * @param params.value - Value associated with the transaction.\n * @returns The updated transaction metadata.\n */\n async updateEditableParams(\n txId: string,\n {\n data,\n gas,\n gasPrice,\n from,\n to,\n value,\n }: {\n data?: string;\n gas?: string;\n gasPrice?: string;\n from?: string;\n to?: string;\n value?: string;\n },\n ) {\n const transactionMeta = this.getTransaction(txId);\n if (!transactionMeta) {\n throw new Error(\n `Cannot update editable params as no transaction metadata found`,\n );\n }\n\n validateIfTransactionUnapproved(transactionMeta, 'updateEditableParams');\n\n const editableParams = {\n txParams: {\n data,\n from,\n to,\n value,\n gas,\n gasPrice,\n },\n } as Partial;\n\n editableParams.txParams = pickBy(\n editableParams.txParams,\n ) as TransactionParams;\n\n const updatedTransaction = merge({}, transactionMeta, editableParams);\n const provider = this.#multichainTrackingHelper.getProvider({\n chainId: transactionMeta.chainId,\n networkClientId: transactionMeta.networkClientId,\n });\n const ethQuery = new EthQuery(provider);\n const { type } = await determineTransactionType(\n updatedTransaction.txParams,\n ethQuery,\n );\n updatedTransaction.type = type;\n\n await updateTransactionLayer1GasFee({\n layer1GasFeeFlows: this.layer1GasFeeFlows,\n provider,\n transactionMeta: updatedTransaction,\n });\n\n this.updateTransaction(\n updatedTransaction,\n `Update Editable Params for ${txId}`,\n );\n return this.getTransaction(txId);\n }\n\n /**\n * Signs and returns the raw transaction data for provided transaction params list.\n *\n * @param listOfTxParams - The list of transaction params to approve.\n * @param opts - Options bag.\n * @param opts.hasNonce - Whether the transactions already have a nonce.\n * @returns The raw transactions.\n */\n async approveTransactionsWithSameNonce(\n listOfTxParams: (TransactionParams & { chainId: Hex })[] = [],\n { hasNonce }: { hasNonce?: boolean } = {},\n ): Promise {\n log('Approving transactions with same nonce', {\n transactions: listOfTxParams,\n });\n\n if (listOfTxParams.length === 0) {\n return '';\n }\n\n const initialTx = listOfTxParams[0];\n const common = this.getCommonConfiguration(initialTx.chainId);\n\n // We need to ensure we get the nonce using the the NonceTracker on the chain matching\n // the txParams. In this context we only have chainId available to us, but the\n // NonceTrackers are keyed by networkClientId. To workaround this, we attempt to find\n // a networkClientId that matches the chainId. As a fallback, the globally selected\n // network's NonceTracker will be used instead.\n let networkClientId: NetworkClientId | undefined;\n try {\n networkClientId = this.messagingSystem.call(\n `NetworkController:findNetworkClientIdByChainId`,\n initialTx.chainId,\n );\n } catch (err) {\n log('failed to find networkClientId from chainId', err);\n }\n\n const initialTxAsEthTx = TransactionFactory.fromTxData(initialTx, {\n common,\n });\n const initialTxAsSerializedHex = bufferToHex(initialTxAsEthTx.serialize());\n\n if (this.approvingTransactionIds.has(initialTxAsSerializedHex)) {\n return '';\n }\n this.approvingTransactionIds.add(initialTxAsSerializedHex);\n\n let rawTransactions, nonceLock;\n try {\n // TODO: we should add a check to verify that all transactions have the same from address\n const fromAddress = initialTx.from;\n const requiresNonce = hasNonce !== true;\n\n nonceLock = requiresNonce\n ? await this.getNonceLock(fromAddress, networkClientId)\n : undefined;\n\n const nonce = nonceLock\n ? add0x(nonceLock.nextNonce.toString(16))\n : initialTx.nonce;\n\n if (nonceLock) {\n log('Using nonce from nonce tracker', nonce, nonceLock.nonceDetails);\n }\n\n rawTransactions = await Promise.all(\n listOfTxParams.map((txParams) => {\n txParams.nonce = nonce;\n return this.signExternalTransaction(txParams.chainId, txParams);\n }),\n );\n } catch (err) {\n log('Error while signing transactions with same nonce', err);\n // Must set transaction to submitted/failed before releasing lock\n // continue with error chain\n throw err;\n } finally {\n nonceLock?.releaseLock();\n this.approvingTransactionIds.delete(initialTxAsSerializedHex);\n }\n return rawTransactions;\n }\n\n /**\n * Update a custodial transaction.\n *\n * @param transactionId - The ID of the transaction to update.\n * @param options - The custodial transaction options to update.\n * @param options.errorMessage - The error message to be assigned in case transaction status update to failed.\n * @param options.hash - The new hash value to be assigned.\n * @param options.status - The new status value to be assigned.\n */\n updateCustodialTransaction(\n transactionId: string,\n {\n errorMessage,\n hash,\n status,\n }: {\n errorMessage?: string;\n hash?: string;\n status?: TransactionStatus;\n },\n ) {\n const transactionMeta = this.getTransaction(transactionId);\n\n if (!transactionMeta) {\n throw new Error(\n `Cannot update custodial transaction as no transaction metadata found`,\n );\n }\n\n if (!transactionMeta.custodyId) {\n throw new Error('Transaction must be a custodian transaction');\n }\n\n if (\n status &&\n ![\n TransactionStatus.submitted,\n TransactionStatus.signed,\n TransactionStatus.failed,\n ].includes(status)\n ) {\n throw new Error(\n `Cannot update custodial transaction with status: ${status}`,\n );\n }\n\n const updatedTransactionMeta = merge(\n {},\n transactionMeta,\n pickBy({ hash, status }),\n ) as TransactionMeta;\n\n if (updatedTransactionMeta.status === TransactionStatus.submitted) {\n updatedTransactionMeta.submittedTime = new Date().getTime();\n }\n\n if (updatedTransactionMeta.status === TransactionStatus.failed) {\n updatedTransactionMeta.error = normalizeTxError(new Error(errorMessage));\n }\n\n this.updateTransaction(\n updatedTransactionMeta,\n `${controllerName}:updateCustodialTransaction - Custodial transaction updated`,\n );\n\n if (\n [TransactionStatus.submitted, TransactionStatus.failed].includes(\n status as TransactionStatus,\n )\n ) {\n this.messagingSystem.publish(\n `${controllerName}:transactionFinished`,\n updatedTransactionMeta,\n );\n this.#internalEvents.emit(\n `${updatedTransactionMeta.id}:finished`,\n updatedTransactionMeta,\n );\n }\n }\n\n /**\n * Search transaction metadata for matching entries.\n *\n * @param opts - Options bag.\n * @param opts.searchCriteria - An object containing values or functions for transaction properties to filter transactions with.\n * @param opts.initialList - The transactions to search. Defaults to the current state.\n * @param opts.filterToCurrentNetwork - Whether to filter the results to the current network. Defaults to true.\n * @param opts.limit - The maximum number of transactions to return. No limit by default.\n * @returns An array of transactions matching the provided options.\n */\n getTransactions({\n searchCriteria = {},\n initialList,\n filterToCurrentNetwork = true,\n limit,\n }: {\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n searchCriteria?: any;\n initialList?: TransactionMeta[];\n filterToCurrentNetwork?: boolean;\n limit?: number;\n } = {}): TransactionMeta[] {\n const chainId = this.getChainId();\n // searchCriteria is an object that might have values that aren't predicate\n // methods. When providing any other value type (string, number, etc), we\n // consider this shorthand for \"check the value at key for strict equality\n // with the provided value\". To conform this object to be only methods, we\n // mapValues (lodash) such that every value on the object is a method that\n // returns a boolean.\n const predicateMethods = mapValues(searchCriteria, (predicate) => {\n return typeof predicate === 'function'\n ? predicate\n : // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (v: any) => v === predicate;\n });\n\n const transactionsToFilter = initialList ?? this.state.transactions;\n\n // Combine sortBy and pickBy to transform our state object into an array of\n // matching transactions that are sorted by time.\n const filteredTransactions = sortBy(\n pickBy(transactionsToFilter, (transaction) => {\n if (filterToCurrentNetwork && transaction.chainId !== chainId) {\n return false;\n }\n // iterate over the predicateMethods keys to check if the transaction\n // matches the searchCriteria\n for (const [key, predicate] of Object.entries(predicateMethods)) {\n // We return false early as soon as we know that one of the specified\n // search criteria do not match the transaction. This prevents\n // needlessly checking all criteria when we already know the criteria\n // are not fully satisfied. We check both txParams and the base\n // object as predicate keys can be either.\n if (key in transaction.txParams) {\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n if (predicate((transaction.txParams as any)[key]) === false) {\n return false;\n }\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } else if (predicate((transaction as any)[key]) === false) {\n return false;\n }\n }\n\n return true;\n }),\n 'time',\n );\n if (limit !== undefined) {\n // We need to have all transactions of a given nonce in order to display\n // necessary details in the UI. We use the size of this set to determine\n // whether we have reached the limit provided, thus ensuring that all\n // transactions of nonces we include will be sent to the UI.\n const nonces = new Set();\n const txs = [];\n // By default, the transaction list we filter from is sorted by time ASC.\n // To ensure that filtered results prefers the newest transactions we\n // iterate from right to left, inserting transactions into front of a new\n // array. The original order is preserved, but we ensure that newest txs\n // are preferred.\n for (let i = filteredTransactions.length - 1; i > -1; i--) {\n const txMeta = filteredTransactions[i];\n const { nonce } = txMeta.txParams;\n if (!nonces.has(nonce)) {\n if (nonces.size < limit) {\n nonces.add(nonce);\n } else {\n continue;\n }\n }\n // Push transaction into the beginning of our array to ensure the\n // original order is preserved.\n txs.unshift(txMeta);\n }\n return txs;\n }\n return filteredTransactions;\n }\n\n async estimateGasFee({\n transactionParams,\n chainId,\n networkClientId: requestNetworkClientId,\n }: {\n transactionParams: TransactionParams;\n chainId?: Hex;\n networkClientId?: NetworkClientId;\n }): Promise {\n const networkClientId = this.#getNetworkClientId({\n networkClientId: requestNetworkClientId,\n chainId,\n });\n\n const transactionMeta = {\n txParams: transactionParams,\n chainId,\n networkClientId,\n } as TransactionMeta;\n\n // Guaranteed as the default gas fee flow matches all transactions.\n const gasFeeFlow = getGasFeeFlow(\n transactionMeta,\n this.gasFeeFlows,\n ) as GasFeeFlow;\n\n const ethQuery = this.#multichainTrackingHelper.getEthQuery({\n networkClientId,\n chainId,\n });\n\n const gasFeeControllerData = await this.getGasFeeEstimates({\n networkClientId,\n });\n\n return gasFeeFlow.getGasFees({\n ethQuery,\n gasFeeControllerData,\n transactionMeta,\n });\n }\n\n /**\n * Determine the layer 1 gas fee for the given transaction parameters.\n *\n * @param request - The request object.\n * @param request.transactionParams - The transaction parameters to estimate the layer 1 gas fee for.\n * @param request.chainId - The ID of the chain where the transaction will be executed.\n * @param request.networkClientId - The ID of a specific network client to process the transaction.\n */\n async getLayer1GasFee({\n transactionParams,\n chainId,\n networkClientId,\n }: {\n transactionParams: TransactionParams;\n chainId?: Hex;\n networkClientId?: NetworkClientId;\n }): Promise {\n const provider = this.#multichainTrackingHelper.getProvider({\n networkClientId,\n chainId,\n });\n\n return await getTransactionLayer1GasFee({\n layer1GasFeeFlows: this.layer1GasFeeFlows,\n provider,\n transactionMeta: {\n txParams: transactionParams,\n chainId,\n } as TransactionMeta,\n });\n }\n\n private async signExternalTransaction(\n chainId: Hex,\n transactionParams: TransactionParams,\n ): Promise {\n if (!this.sign) {\n throw new Error('No sign method defined.');\n }\n\n const normalizedTransactionParams =\n normalizeTransactionParams(transactionParams);\n const type = isEIP1559Transaction(normalizedTransactionParams)\n ? TransactionEnvelopeType.feeMarket\n : TransactionEnvelopeType.legacy;\n const updatedTransactionParams = {\n ...normalizedTransactionParams,\n type,\n gasLimit: normalizedTransactionParams.gas,\n chainId,\n };\n\n const { from } = updatedTransactionParams;\n const common = this.getCommonConfiguration(chainId);\n const unsignedTransaction = TransactionFactory.fromTxData(\n updatedTransactionParams,\n { common },\n );\n const signedTransaction = await this.sign(unsignedTransaction, from);\n\n const rawTransaction = bufferToHex(signedTransaction.serialize());\n return rawTransaction;\n }\n\n /**\n * Removes unapproved transactions from state.\n */\n clearUnapprovedTransactions() {\n const transactions = this.state.transactions.filter(\n ({ status }) => status !== TransactionStatus.unapproved,\n );\n this.update((state) => {\n state.transactions = this.trimTransactionsForState(transactions);\n });\n }\n\n /**\n * Stop the signing process for a specific transaction.\n * Throws an error causing the transaction status to be set to failed.\n * @param transactionId - The ID of the transaction to stop signing.\n */\n abortTransactionSigning(transactionId: string) {\n const transactionMeta = this.getTransaction(transactionId);\n\n if (!transactionMeta) {\n throw new Error(`Cannot abort signing as no transaction metadata found`);\n }\n\n const abortCallback = this.signAbortCallbacks.get(transactionId);\n\n if (!abortCallback) {\n throw new Error(\n `Cannot abort signing as transaction is not waiting for signing`,\n );\n }\n\n abortCallback();\n\n this.signAbortCallbacks.delete(transactionId);\n }\n\n private addMetadata(transactionMeta: TransactionMeta) {\n this.update((state) => {\n state.transactions = this.trimTransactionsForState([\n ...state.transactions,\n transactionMeta,\n ]);\n });\n }\n\n private async updateGasProperties(transactionMeta: TransactionMeta) {\n const isEIP1559Compatible =\n (await this.getEIP1559Compatibility(transactionMeta.networkClientId)) &&\n transactionMeta.txParams.type !== TransactionEnvelopeType.legacy;\n\n const { networkClientId, chainId } = transactionMeta;\n\n const isCustomNetwork = this.#isCustomNetwork(networkClientId);\n\n const ethQuery = this.#multichainTrackingHelper.getEthQuery({\n networkClientId,\n chainId,\n });\n\n const provider = this.#multichainTrackingHelper.getProvider({\n networkClientId,\n chainId,\n });\n\n await updateGas({\n ethQuery,\n chainId,\n isCustomNetwork,\n txMeta: transactionMeta,\n });\n\n await updateGasFees({\n eip1559: isEIP1559Compatible,\n ethQuery,\n gasFeeFlows: this.gasFeeFlows,\n getGasFeeEstimates: this.getGasFeeEstimates,\n getSavedGasFees: this.getSavedGasFees.bind(this),\n txMeta: transactionMeta,\n });\n\n await updateTransactionLayer1GasFee({\n layer1GasFeeFlows: this.layer1GasFeeFlows,\n provider,\n transactionMeta,\n });\n }\n\n private onBootCleanup() {\n this.clearUnapprovedTransactions();\n this.failIncompleteTransactions();\n }\n\n private failIncompleteTransactions() {\n const incompleteTransactions = this.state.transactions.filter(\n (transaction) =>\n [TransactionStatus.approved, TransactionStatus.signed].includes(\n transaction.status,\n ),\n );\n\n for (const transactionMeta of incompleteTransactions) {\n this.failTransaction(\n transactionMeta,\n new Error('Transaction incomplete at startup'),\n );\n }\n }\n\n private async processApproval(\n transactionMeta: TransactionMeta,\n {\n isExisting = false,\n requireApproval,\n shouldShowRequest = true,\n actionId,\n }: {\n isExisting?: boolean;\n requireApproval?: boolean | undefined;\n shouldShowRequest?: boolean;\n actionId?: string;\n },\n ): Promise {\n const transactionId = transactionMeta.id;\n let resultCallbacks: AcceptResultCallbacks | undefined;\n const { meta, isCompleted } = this.isTransactionCompleted(transactionId);\n const finishedPromise = isCompleted\n ? Promise.resolve(meta)\n : this.waitForTransactionFinished(transactionId);\n\n if (meta && !isExisting && !isCompleted) {\n try {\n if (requireApproval !== false) {\n const acceptResult = await this.requestApproval(transactionMeta, {\n shouldShowRequest,\n });\n resultCallbacks = acceptResult.resultCallbacks;\n\n const approvalValue = acceptResult.value as\n | {\n txMeta?: TransactionMeta;\n }\n | undefined;\n\n const updatedTransaction = approvalValue?.txMeta;\n\n if (updatedTransaction) {\n log('Updating transaction with approval data', {\n customNonce: updatedTransaction.customNonceValue,\n params: updatedTransaction.txParams,\n });\n\n this.updateTransaction(\n updatedTransaction,\n 'TransactionController#processApproval - Updated with approval data',\n );\n }\n }\n\n const { isCompleted: isTxCompleted } =\n this.isTransactionCompleted(transactionId);\n\n if (!isTxCompleted) {\n const approvalResult = await this.approveTransaction(transactionId);\n if (\n approvalResult === ApprovalState.SkippedViaBeforePublishHook &&\n resultCallbacks\n ) {\n resultCallbacks.success();\n }\n const updatedTransactionMeta = this.getTransaction(\n transactionId,\n ) as TransactionMeta;\n this.messagingSystem.publish(\n `${controllerName}:transactionApproved`,\n {\n transactionMeta: updatedTransactionMeta,\n actionId,\n },\n );\n }\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (error: any) {\n const { isCompleted: isTxCompleted } =\n this.isTransactionCompleted(transactionId);\n if (!isTxCompleted) {\n if (error?.code === errorCodes.provider.userRejectedRequest) {\n this.cancelTransaction(transactionId, actionId);\n\n throw providerErrors.userRejectedRequest(\n 'MetaMask Tx Signature: User denied transaction signature.',\n );\n } else {\n this.failTransaction(meta, error, actionId);\n }\n }\n }\n }\n\n const finalMeta = await finishedPromise;\n\n switch (finalMeta?.status) {\n case TransactionStatus.failed:\n resultCallbacks?.error(finalMeta.error);\n throw rpcErrors.internal(finalMeta.error.message);\n\n case TransactionStatus.submitted:\n resultCallbacks?.success();\n return finalMeta.hash as string;\n\n default:\n const internalError = rpcErrors.internal(\n `MetaMask Tx Signature: Unknown problem: ${JSON.stringify(\n finalMeta || transactionId,\n )}`,\n );\n\n resultCallbacks?.error(internalError);\n throw internalError;\n }\n }\n\n /**\n * Approves a transaction and updates it's status in state. If this is not a\n * retry transaction, a nonce will be generated. The transaction is signed\n * using the sign configuration property, then published to the blockchain.\n * A `:finished` hub event is fired after success or failure.\n *\n * @param transactionId - The ID of the transaction to approve.\n */\n private async approveTransaction(transactionId: string) {\n const cleanupTasks = new Array<() => void>();\n cleanupTasks.push(await this.mutex.acquire());\n\n let transactionMeta = this.getTransactionOrThrow(transactionId);\n\n try {\n if (!this.sign) {\n this.failTransaction(\n transactionMeta,\n new Error('No sign method defined.'),\n );\n return ApprovalState.NotApproved;\n } else if (!transactionMeta.chainId) {\n this.failTransaction(transactionMeta, new Error('No chainId defined.'));\n return ApprovalState.NotApproved;\n }\n\n if (this.approvingTransactionIds.has(transactionId)) {\n log('Skipping approval as signing in progress', transactionId);\n return ApprovalState.NotApproved;\n }\n this.approvingTransactionIds.add(transactionId);\n cleanupTasks.push(() =>\n this.approvingTransactionIds.delete(transactionId),\n );\n\n const [nonce, releaseNonce] = await getNextNonce(\n transactionMeta,\n (address: string) =>\n this.#multichainTrackingHelper.getNonceLock(\n address,\n transactionMeta.networkClientId,\n ),\n );\n\n // must set transaction to submitted/failed before releasing lock\n releaseNonce && cleanupTasks.push(releaseNonce);\n\n transactionMeta = this.#updateTransactionInternal(\n {\n transactionId,\n note: 'TransactionController#approveTransaction - Transaction approved',\n },\n (draftTxMeta) => {\n const { txParams, chainId } = draftTxMeta;\n\n draftTxMeta.status = TransactionStatus.approved;\n draftTxMeta.txParams = {\n ...txParams,\n nonce,\n chainId,\n gasLimit: txParams.gas,\n ...(isEIP1559Transaction(txParams) && {\n type: TransactionEnvelopeType.feeMarket,\n }),\n };\n },\n );\n\n this.onTransactionStatusChange(transactionMeta);\n\n const rawTx = await this.signTransaction(\n transactionMeta,\n transactionMeta.txParams,\n );\n\n if (!this.beforePublish(transactionMeta)) {\n log('Skipping publishing transaction based on hook');\n this.messagingSystem.publish(\n `${controllerName}:transactionPublishingSkipped`,\n transactionMeta,\n );\n return ApprovalState.SkippedViaBeforePublishHook;\n }\n\n if (!rawTx) {\n return ApprovalState.NotApproved;\n }\n\n const ethQuery = this.#multichainTrackingHelper.getEthQuery({\n networkClientId: transactionMeta.networkClientId,\n chainId: transactionMeta.chainId,\n });\n\n let preTxBalance: string | undefined;\n const shouldUpdatePreTxBalance =\n transactionMeta.type === TransactionType.swap;\n\n if (shouldUpdatePreTxBalance) {\n log('Determining pre-transaction balance');\n\n preTxBalance = await query(ethQuery, 'getBalance', [\n transactionMeta.txParams.from,\n ]);\n }\n\n log('Publishing transaction', transactionMeta.txParams);\n\n let { transactionHash: hash } = await this.publish(\n transactionMeta,\n rawTx,\n );\n\n if (hash === undefined) {\n hash = await this.publishTransaction(ethQuery, rawTx);\n }\n\n log('Publish successful', hash);\n\n transactionMeta = this.#updateTransactionInternal(\n {\n transactionId,\n note: 'TransactionController#approveTransaction - Transaction submitted',\n },\n (draftTxMeta) => {\n draftTxMeta.hash = hash;\n draftTxMeta.status = TransactionStatus.submitted;\n draftTxMeta.submittedTime = new Date().getTime();\n if (shouldUpdatePreTxBalance) {\n draftTxMeta.preTxBalance = preTxBalance;\n log('Updated pre-transaction balance', preTxBalance);\n }\n },\n );\n\n this.messagingSystem.publish(`${controllerName}:transactionSubmitted`, {\n transactionMeta,\n });\n\n this.messagingSystem.publish(\n `${controllerName}:transactionFinished`,\n transactionMeta,\n );\n this.#internalEvents.emit(`${transactionId}:finished`, transactionMeta);\n\n this.onTransactionStatusChange(transactionMeta);\n return ApprovalState.Approved;\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (error: any) {\n this.failTransaction(transactionMeta, error);\n return ApprovalState.NotApproved;\n } finally {\n cleanupTasks.forEach((task) => task());\n }\n }\n\n private async publishTransaction(\n ethQuery: EthQuery,\n rawTransaction: string,\n ): Promise {\n return await query(ethQuery, 'sendRawTransaction', [rawTransaction]);\n }\n\n /**\n * Cancels a transaction based on its ID by setting its status to \"rejected\"\n * and emitting a `:finished` hub event.\n *\n * @param transactionId - The ID of the transaction to cancel.\n * @param actionId - The actionId passed from UI\n */\n private cancelTransaction(transactionId: string, actionId?: string) {\n const transactionMeta = this.state.transactions.find(\n ({ id }) => id === transactionId,\n );\n if (!transactionMeta) {\n return;\n }\n this.update((state) => {\n const transactions = state.transactions.filter(\n ({ id }) => id !== transactionId,\n );\n state.transactions = this.trimTransactionsForState(transactions);\n });\n const updatedTransactionMeta = {\n ...transactionMeta,\n status: TransactionStatus.rejected as const,\n };\n this.messagingSystem.publish(\n `${controllerName}:transactionFinished`,\n updatedTransactionMeta,\n );\n this.#internalEvents.emit(\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/restrict-template-expressions\n `${transactionMeta.id}:finished`,\n updatedTransactionMeta,\n );\n this.messagingSystem.publish(`${controllerName}:transactionRejected`, {\n transactionMeta: updatedTransactionMeta,\n actionId,\n });\n this.onTransactionStatusChange(updatedTransactionMeta);\n }\n\n /**\n * Trim the amount of transactions that are set on the state. Checks\n * if the length of the tx history is longer then desired persistence\n * limit and then if it is removes the oldest confirmed or rejected tx.\n * Pending or unapproved transactions will not be removed by this\n * operation. For safety of presenting a fully functional transaction UI\n * representation, this function will not break apart transactions with the\n * same nonce, created on the same day, per network. Not accounting for\n * transactions of the same nonce, same day and network combo can result in\n * confusing or broken experiences in the UI.\n *\n * @param transactions - The transactions to be applied to the state.\n * @returns The trimmed list of transactions.\n */\n private trimTransactionsForState(\n transactions: TransactionMeta[],\n ): TransactionMeta[] {\n const nonceNetworkSet = new Set();\n\n const txsToKeep = [...transactions]\n .sort((a, b) => (a.time > b.time ? -1 : 1)) // Descending time order\n .filter((tx) => {\n const { chainId, status, txParams, time } = tx;\n\n if (txParams) {\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/restrict-template-expressions\n const key = `${String(txParams.nonce)}-${convertHexToDecimal(\n chainId,\n )}-${new Date(time).toDateString()}`;\n\n if (nonceNetworkSet.has(key)) {\n return true;\n } else if (\n nonceNetworkSet.size < this.#transactionHistoryLimit ||\n !this.isFinalState(status)\n ) {\n nonceNetworkSet.add(key);\n return true;\n }\n }\n\n return false;\n });\n\n txsToKeep.reverse(); // Ascending time order\n return txsToKeep;\n }\n\n /**\n * Determines if the transaction is in a final state.\n *\n * @param status - The transaction status.\n * @returns Whether the transaction is in a final state.\n */\n private isFinalState(status: TransactionStatus): boolean {\n return (\n status === TransactionStatus.rejected ||\n status === TransactionStatus.confirmed ||\n status === TransactionStatus.failed\n );\n }\n\n /**\n * Whether the transaction has at least completed all local processing.\n *\n * @param status - The transaction status.\n * @returns Whether the transaction is in a final state.\n */\n private isLocalFinalState(status: TransactionStatus): boolean {\n return [\n TransactionStatus.confirmed,\n TransactionStatus.failed,\n TransactionStatus.rejected,\n TransactionStatus.submitted,\n ].includes(status);\n }\n\n private async requestApproval(\n txMeta: TransactionMeta,\n { shouldShowRequest }: { shouldShowRequest: boolean },\n ): Promise {\n const id = this.getApprovalId(txMeta);\n const { origin } = txMeta;\n const type = ApprovalType.Transaction;\n const requestData = { txId: txMeta.id };\n\n return (await this.messagingSystem.call(\n 'ApprovalController:addRequest',\n {\n id,\n origin: origin || ORIGIN_METAMASK,\n type,\n requestData,\n expectsResult: true,\n },\n shouldShowRequest,\n )) as Promise;\n }\n\n private getTransaction(\n transactionId: string,\n ): Readonly | undefined {\n const { transactions } = this.state;\n return transactions.find(({ id }) => id === transactionId);\n }\n\n private getTransactionOrThrow(\n transactionId: string,\n errorMessagePrefix = 'TransactionController',\n ): Readonly {\n const txMeta = this.getTransaction(transactionId);\n if (!txMeta) {\n throw new Error(\n `${errorMessagePrefix}: No transaction found with id ${transactionId}`,\n );\n }\n return txMeta;\n }\n\n private getApprovalId(txMeta: TransactionMeta) {\n return String(txMeta.id);\n }\n\n private isTransactionCompleted(transactionId: string): {\n meta?: TransactionMeta;\n isCompleted: boolean;\n } {\n const transaction = this.getTransaction(transactionId);\n\n if (!transaction) {\n return { meta: undefined, isCompleted: false };\n }\n\n const isCompleted = this.isLocalFinalState(transaction.status);\n\n return { meta: transaction, isCompleted };\n }\n\n private getChainId(networkClientId?: NetworkClientId): Hex {\n const globalChainId = this.#getGlobalChainId();\n const globalNetworkClientId = this.#getGlobalNetworkClientId();\n\n if (!networkClientId || networkClientId === globalNetworkClientId) {\n return globalChainId;\n }\n\n return this.messagingSystem.call(\n `NetworkController:getNetworkClientById`,\n networkClientId,\n ).configuration.chainId;\n }\n\n private prepareUnsignedEthTx(\n chainId: Hex,\n txParams: TransactionParams,\n ): TypedTransaction {\n return TransactionFactory.fromTxData(txParams, {\n freeze: false,\n common: this.getCommonConfiguration(chainId),\n });\n }\n\n /**\n * `@ethereumjs/tx` uses `@ethereumjs/common` as a configuration tool for\n * specifying which chain, network, hardfork and EIPs to support for\n * a transaction. By referencing this configuration, and analyzing the fields\n * specified in txParams, @ethereumjs/tx is able to determine which EIP-2718\n * transaction type to use.\n *\n * @param chainId - The chainId to use for the configuration.\n * @returns common configuration object\n */\n private getCommonConfiguration(chainId: Hex): Common {\n const customChainParams: Partial = {\n chainId: parseInt(chainId, 16),\n defaultHardfork: HARDFORK,\n };\n\n return Common.custom(customChainParams);\n }\n\n private onIncomingTransactions({\n added,\n updated,\n }: {\n added: TransactionMeta[];\n updated: TransactionMeta[];\n }) {\n this.update((state) => {\n const { transactions: currentTransactions } = state;\n const updatedTransactions = [\n ...added,\n ...currentTransactions.map((originalTransaction) => {\n const updatedTransaction = updated.find(\n ({ hash }) => hash === originalTransaction.hash,\n );\n\n return updatedTransaction ?? originalTransaction;\n }),\n ];\n\n state.transactions = this.trimTransactionsForState(updatedTransactions);\n });\n }\n\n private onUpdatedLastFetchedBlockNumbers({\n lastFetchedBlockNumbers,\n blockNumber,\n }: {\n lastFetchedBlockNumbers: {\n [key: string]: number;\n };\n blockNumber: number;\n }) {\n this.update((state) => {\n state.lastFetchedBlockNumbers = lastFetchedBlockNumbers;\n });\n this.messagingSystem.publish(\n `${controllerName}:incomingTransactionBlockReceived`,\n blockNumber,\n );\n }\n\n private generateDappSuggestedGasFees(\n txParams: TransactionParams,\n origin?: string,\n ): DappSuggestedGasFees | undefined {\n if (!origin || origin === ORIGIN_METAMASK) {\n return undefined;\n }\n\n const { gasPrice, maxFeePerGas, maxPriorityFeePerGas, gas } = txParams;\n\n if (\n gasPrice === undefined &&\n maxFeePerGas === undefined &&\n maxPriorityFeePerGas === undefined &&\n gas === undefined\n ) {\n return undefined;\n }\n\n const dappSuggestedGasFees: DappSuggestedGasFees = {};\n\n if (gasPrice !== undefined) {\n dappSuggestedGasFees.gasPrice = gasPrice;\n } else if (\n maxFeePerGas !== undefined ||\n maxPriorityFeePerGas !== undefined\n ) {\n dappSuggestedGasFees.maxFeePerGas = maxFeePerGas;\n dappSuggestedGasFees.maxPriorityFeePerGas = maxPriorityFeePerGas;\n }\n\n if (gas !== undefined) {\n dappSuggestedGasFees.gas = gas;\n }\n\n return dappSuggestedGasFees;\n }\n\n /**\n * Validates and adds external provided transaction to state.\n *\n * @param transactionMeta - Nominated external transaction to be added to state.\n * @returns The new transaction.\n */\n private addExternalTransaction(transactionMeta: TransactionMeta) {\n const { chainId } = transactionMeta;\n const { transactions } = this.state;\n const fromAddress = transactionMeta?.txParams?.from;\n const sameFromAndNetworkTransactions = transactions.filter(\n (transaction) =>\n transaction.txParams.from === fromAddress &&\n transaction.chainId === chainId,\n );\n const confirmedTxs = sameFromAndNetworkTransactions.filter(\n (transaction) => transaction.status === TransactionStatus.confirmed,\n );\n const pendingTxs = sameFromAndNetworkTransactions.filter(\n (transaction) => transaction.status === TransactionStatus.submitted,\n );\n\n validateConfirmedExternalTransaction(\n transactionMeta,\n confirmedTxs,\n pendingTxs,\n );\n\n // Make sure provided external transaction has non empty history array\n const newTransactionMeta =\n (transactionMeta.history ?? []).length === 0 && !this.isHistoryDisabled\n ? addInitialHistorySnapshot(transactionMeta)\n : transactionMeta;\n\n this.update((state) => {\n state.transactions = this.trimTransactionsForState([\n ...state.transactions,\n newTransactionMeta,\n ]);\n });\n\n return newTransactionMeta;\n }\n\n /**\n * Sets other txMeta statuses to dropped if the txMeta that has been confirmed has other transactions\n * in the transactions have the same nonce.\n *\n * @param transactionId - Used to identify original transaction.\n */\n private markNonceDuplicatesDropped(transactionId: string) {\n const transactionMeta = this.getTransaction(transactionId);\n if (!transactionMeta) {\n return;\n }\n const nonce = transactionMeta.txParams?.nonce;\n const from = transactionMeta.txParams?.from;\n const { chainId } = transactionMeta;\n\n const sameNonceTransactions = this.state.transactions.filter(\n (transaction) =>\n transaction.id !== transactionId &&\n transaction.txParams.from === from &&\n transaction.txParams.nonce === nonce &&\n transaction.chainId === chainId &&\n transaction.type !== TransactionType.incoming,\n );\n const sameNonceTransactionIds = sameNonceTransactions.map(\n (transaction) => transaction.id,\n );\n\n if (sameNonceTransactions.length === 0) {\n return;\n }\n\n this.update((state) => {\n for (const transaction of state.transactions) {\n if (sameNonceTransactionIds.includes(transaction.id)) {\n transaction.replacedBy = transactionMeta?.hash;\n transaction.replacedById = transactionMeta?.id;\n }\n }\n });\n\n for (const transaction of this.state.transactions) {\n if (\n sameNonceTransactionIds.includes(transaction.id) &&\n transaction.status !== TransactionStatus.failed\n ) {\n this.setTransactionStatusDropped(transaction);\n }\n }\n }\n\n /**\n * Method to set transaction status to dropped.\n *\n * @param transactionMeta - TransactionMeta of transaction to be marked as dropped.\n */\n private setTransactionStatusDropped(transactionMeta: TransactionMeta) {\n const updatedTransactionMeta = {\n ...transactionMeta,\n status: TransactionStatus.dropped as const,\n };\n this.messagingSystem.publish(`${controllerName}:transactionDropped`, {\n transactionMeta: updatedTransactionMeta,\n });\n this.updateTransaction(\n updatedTransactionMeta,\n 'TransactionController#setTransactionStatusDropped - Transaction dropped',\n );\n this.onTransactionStatusChange(updatedTransactionMeta);\n }\n\n /**\n * Get transaction with provided actionId.\n *\n * @param actionId - Unique ID to prevent duplicate requests\n * @returns the filtered transaction\n */\n private getTransactionWithActionId(actionId?: string) {\n return this.state.transactions.find(\n (transaction) => actionId && transaction.actionId === actionId,\n );\n }\n\n private async waitForTransactionFinished(\n transactionId: string,\n ): Promise {\n return new Promise((resolve) => {\n this.#internalEvents.once(`${transactionId}:finished`, (txMeta) => {\n resolve(txMeta);\n });\n });\n }\n\n /**\n * Updates the r, s, and v properties of a TransactionMeta object\n * with values from a signed transaction.\n *\n * @param transactionMeta - The TransactionMeta object to update.\n * @param signedTx - The encompassing type for all transaction types containing r, s, and v values.\n * @returns The updated TransactionMeta object.\n */\n private updateTransactionMetaRSV(\n transactionMeta: TransactionMeta,\n signedTx: TypedTransaction,\n ): TransactionMeta {\n const transactionMetaWithRsv = cloneDeep(transactionMeta);\n\n for (const key of ['r', 's', 'v'] as const) {\n const value = signedTx[key];\n\n if (value === undefined || value === null) {\n continue;\n }\n\n transactionMetaWithRsv[key] = add0x(value.toString(16));\n }\n\n return transactionMetaWithRsv;\n }\n\n private async getEIP1559Compatibility(networkClientId?: NetworkClientId) {\n const currentNetworkIsEIP1559Compatible =\n await this.getCurrentNetworkEIP1559Compatibility(networkClientId);\n\n const currentAccountIsEIP1559Compatible =\n await this.getCurrentAccountEIP1559Compatibility();\n\n return (\n currentNetworkIsEIP1559Compatible && currentAccountIsEIP1559Compatible\n );\n }\n\n private async signTransaction(\n transactionMeta: TransactionMeta,\n txParams: TransactionParams,\n ): Promise {\n log('Signing transaction', txParams);\n\n const unsignedEthTx = this.prepareUnsignedEthTx(\n transactionMeta.chainId,\n txParams,\n );\n\n this.approvingTransactionIds.add(transactionMeta.id);\n\n const signedTx = await new Promise((resolve, reject) => {\n this.sign?.(\n unsignedEthTx,\n txParams.from,\n ...this.getAdditionalSignArguments(transactionMeta),\n ).then(resolve, reject);\n\n this.signAbortCallbacks.set(transactionMeta.id, () =>\n reject(new Error('Signing aborted by user')),\n );\n });\n\n this.signAbortCallbacks.delete(transactionMeta.id);\n\n if (!signedTx) {\n log('Skipping signed status as no signed transaction');\n return undefined;\n }\n\n const transactionMetaFromHook = cloneDeep(transactionMeta);\n if (!this.afterSign(transactionMetaFromHook, signedTx)) {\n this.updateTransaction(\n transactionMetaFromHook,\n 'TransactionController#signTransaction - Update after sign',\n );\n\n log('Skipping signed status based on hook');\n\n return undefined;\n }\n\n const transactionMetaWithRsv = {\n ...this.updateTransactionMetaRSV(transactionMetaFromHook, signedTx),\n status: TransactionStatus.signed as const,\n };\n\n this.updateTransaction(\n transactionMetaWithRsv,\n 'TransactionController#approveTransaction - Transaction signed',\n );\n\n this.onTransactionStatusChange(transactionMetaWithRsv);\n\n const rawTx = bufferToHex(signedTx.serialize());\n\n const transactionMetaWithRawTx = merge({}, transactionMetaWithRsv, {\n rawTx,\n });\n\n this.updateTransaction(\n transactionMetaWithRawTx,\n 'TransactionController#approveTransaction - RawTransaction added',\n );\n\n return rawTx;\n }\n\n private onTransactionStatusChange(transactionMeta: TransactionMeta) {\n this.messagingSystem.publish(`${controllerName}:transactionStatusUpdated`, {\n transactionMeta,\n });\n }\n\n private getNonceTrackerTransactions(\n status: TransactionStatus,\n address: string,\n chainId: string = this.getChainId(),\n ) {\n return getAndFormatTransactionsForNonceTracker(\n chainId,\n address,\n status,\n this.state.transactions,\n );\n }\n\n private onConfirmedTransaction(transactionMeta: TransactionMeta) {\n log('Processing confirmed transaction', transactionMeta.id);\n\n this.markNonceDuplicatesDropped(transactionMeta.id);\n\n this.messagingSystem.publish(\n `${controllerName}:transactionConfirmed`,\n transactionMeta,\n );\n\n this.onTransactionStatusChange(transactionMeta);\n\n // Intentional given potential duration of process.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.updatePostBalance(transactionMeta);\n }\n\n private async updatePostBalance(transactionMeta: TransactionMeta) {\n try {\n if (transactionMeta.type !== TransactionType.swap) {\n return;\n }\n\n const ethQuery = this.#multichainTrackingHelper.getEthQuery({\n networkClientId: transactionMeta.networkClientId,\n chainId: transactionMeta.chainId,\n });\n const { updatedTransactionMeta, approvalTransactionMeta } =\n await updatePostTransactionBalance(transactionMeta, {\n ethQuery,\n getTransaction: this.getTransaction.bind(this),\n updateTransaction: this.updateTransaction.bind(this),\n });\n\n this.messagingSystem.publish(\n `${controllerName}:postTransactionBalanceUpdated`,\n {\n transactionMeta: updatedTransactionMeta,\n approvalTransactionMeta,\n },\n );\n } catch (error) {\n /* istanbul ignore next */\n log('Error while updating post transaction balance', error);\n }\n }\n\n #createNonceTracker({\n provider,\n blockTracker,\n chainId,\n }: {\n provider: Provider;\n blockTracker: BlockTracker;\n chainId?: Hex;\n }): NonceTracker {\n return new NonceTracker({\n // TODO: Fix types\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n provider: provider as any,\n // @ts-expect-error TODO: Fix types\n blockTracker,\n getPendingTransactions: this.#getNonceTrackerPendingTransactions.bind(\n this,\n chainId,\n ),\n getConfirmedTransactions: this.getNonceTrackerTransactions.bind(\n this,\n TransactionStatus.confirmed,\n ),\n });\n }\n\n #createIncomingTransactionHelper({\n blockTracker,\n etherscanRemoteTransactionSource,\n chainId,\n }: {\n blockTracker: BlockTracker;\n etherscanRemoteTransactionSource: EtherscanRemoteTransactionSource;\n chainId?: Hex;\n }): IncomingTransactionHelper {\n const incomingTransactionHelper = new IncomingTransactionHelper({\n blockTracker,\n getCurrentAccount: () => this.#getSelectedAccount(),\n getLastFetchedBlockNumbers: () => this.state.lastFetchedBlockNumbers,\n getChainId: chainId ? () => chainId : this.getChainId.bind(this),\n isEnabled: this.#incomingTransactionOptions.isEnabled,\n queryEntireHistory: this.#incomingTransactionOptions.queryEntireHistory,\n remoteTransactionSource: etherscanRemoteTransactionSource,\n transactionLimit: this.#transactionHistoryLimit,\n updateTransactions: this.#incomingTransactionOptions.updateTransactions,\n });\n\n this.#addIncomingTransactionHelperListeners(incomingTransactionHelper);\n\n return incomingTransactionHelper;\n }\n\n #createPendingTransactionTracker({\n provider,\n blockTracker,\n chainId,\n }: {\n provider: Provider;\n blockTracker: BlockTracker;\n chainId?: Hex;\n }): PendingTransactionTracker {\n const ethQuery = new EthQuery(provider);\n const getChainId = chainId ? () => chainId : this.getChainId.bind(this);\n\n const pendingTransactionTracker = new PendingTransactionTracker({\n blockTracker,\n getChainId,\n getEthQuery: () => ethQuery,\n getTransactions: () => this.state.transactions,\n isResubmitEnabled: this.#pendingTransactionOptions.isResubmitEnabled,\n getGlobalLock: () =>\n this.#multichainTrackingHelper.acquireNonceLockForChainIdKey({\n chainId: getChainId(),\n }),\n publishTransaction: this.publishTransaction.bind(this),\n hooks: {\n beforeCheckPendingTransaction:\n this.beforeCheckPendingTransaction.bind(this),\n beforePublish: this.beforePublish.bind(this),\n },\n });\n\n this.#addPendingTransactionTrackerListeners(pendingTransactionTracker);\n\n return pendingTransactionTracker;\n }\n\n #checkForPendingTransactionAndStartPolling = () => {\n // PendingTransactionTracker reads state through its getTransactions hook\n this.pendingTransactionTracker.startIfPendingTransactions();\n this.#multichainTrackingHelper.checkForPendingTransactionAndStartPolling();\n };\n\n #stopAllTracking() {\n this.pendingTransactionTracker.stop();\n this.#removePendingTransactionTrackerListeners(\n this.pendingTransactionTracker,\n );\n this.incomingTransactionHelper.stop();\n this.#removeIncomingTransactionHelperListeners(\n this.incomingTransactionHelper,\n );\n\n this.#multichainTrackingHelper.stopAllTracking();\n }\n\n #removeIncomingTransactionHelperListeners(\n incomingTransactionHelper: IncomingTransactionHelper,\n ) {\n incomingTransactionHelper.hub.removeAllListeners('transactions');\n incomingTransactionHelper.hub.removeAllListeners(\n 'updatedLastFetchedBlockNumbers',\n );\n }\n\n #addIncomingTransactionHelperListeners(\n incomingTransactionHelper: IncomingTransactionHelper,\n ) {\n incomingTransactionHelper.hub.on(\n 'transactions',\n this.onIncomingTransactions.bind(this),\n );\n incomingTransactionHelper.hub.on(\n 'updatedLastFetchedBlockNumbers',\n this.onUpdatedLastFetchedBlockNumbers.bind(this),\n );\n }\n\n #removePendingTransactionTrackerListeners(\n pendingTransactionTracker: PendingTransactionTracker,\n ) {\n pendingTransactionTracker.hub.removeAllListeners('transaction-confirmed');\n pendingTransactionTracker.hub.removeAllListeners('transaction-dropped');\n pendingTransactionTracker.hub.removeAllListeners('transaction-failed');\n pendingTransactionTracker.hub.removeAllListeners('transaction-updated');\n }\n\n #addPendingTransactionTrackerListeners(\n pendingTransactionTracker: PendingTransactionTracker,\n ) {\n pendingTransactionTracker.hub.on(\n 'transaction-confirmed',\n this.onConfirmedTransaction.bind(this),\n );\n\n pendingTransactionTracker.hub.on(\n 'transaction-dropped',\n this.setTransactionStatusDropped.bind(this),\n );\n\n pendingTransactionTracker.hub.on(\n 'transaction-failed',\n this.failTransaction.bind(this),\n );\n\n pendingTransactionTracker.hub.on(\n 'transaction-updated',\n this.updateTransaction.bind(this),\n );\n }\n\n #getNonceTrackerPendingTransactions(\n chainId: string | undefined,\n address: string,\n ) {\n const standardPendingTransactions = this.getNonceTrackerTransactions(\n TransactionStatus.submitted,\n address,\n chainId,\n );\n\n const externalPendingTransactions = this.getExternalPendingTransactions(\n address,\n chainId,\n );\n return [...standardPendingTransactions, ...externalPendingTransactions];\n }\n\n private async publishTransactionForRetry(\n ethQuery: EthQuery,\n rawTx: string,\n transactionMeta: TransactionMeta,\n ): Promise {\n try {\n const hash = await this.publishTransaction(ethQuery, rawTx);\n return hash;\n } catch (error: unknown) {\n if (this.isTransactionAlreadyConfirmedError(error as Error)) {\n await this.pendingTransactionTracker.forceCheckTransaction(\n transactionMeta,\n );\n throw new Error('Previous transaction is already confirmed');\n }\n throw error;\n }\n }\n\n /**\n * Ensures that error is a nonce issue\n *\n * @param error - The error to check\n * @returns Whether or not the error is a nonce issue\n */\n // TODO: Replace `any` with type\n // Some networks are returning original error in the data field\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private isTransactionAlreadyConfirmedError(error: any): boolean {\n return (\n error?.message?.includes('nonce too low') ||\n error?.data?.message?.includes('nonce too low')\n );\n }\n\n #getGasFeeFlows(): GasFeeFlow[] {\n if (this.#testGasFeeFlows) {\n return [new TestGasFeeFlow()];\n }\n\n return [new LineaGasFeeFlow(), new DefaultGasFeeFlow()];\n }\n\n #getLayer1GasFeeFlows(): Layer1GasFeeFlow[] {\n return [new OptimismLayer1GasFeeFlow(), new ScrollLayer1GasFeeFlow()];\n }\n\n #updateTransactionInternal(\n {\n transactionId,\n note,\n skipHistory,\n skipValidation,\n }: {\n transactionId: string;\n note?: string;\n skipHistory?: boolean;\n skipValidation?: boolean;\n },\n callback: (transactionMeta: TransactionMeta) => TransactionMeta | void,\n ): Readonly {\n let updatedTransactionParams: (keyof TransactionParams)[] = [];\n\n this.update((state) => {\n const index = state.transactions.findIndex(\n ({ id }) => id === transactionId,\n );\n\n let transactionMeta = state.transactions[index];\n\n // eslint-disable-next-line n/callback-return\n transactionMeta = callback(transactionMeta) ?? transactionMeta;\n\n if (skipValidation !== true) {\n transactionMeta.txParams = normalizeTransactionParams(\n transactionMeta.txParams,\n );\n\n validateTxParams(transactionMeta.txParams);\n }\n\n updatedTransactionParams =\n this.#checkIfTransactionParamsUpdated(transactionMeta);\n\n const shouldSkipHistory = this.isHistoryDisabled || skipHistory;\n\n if (!shouldSkipHistory) {\n transactionMeta = updateTransactionHistory(\n transactionMeta,\n note ?? 'Transaction updated',\n );\n }\n state.transactions[index] = transactionMeta;\n });\n\n const transactionMeta = this.getTransaction(\n transactionId,\n ) as TransactionMeta;\n\n if (updatedTransactionParams.length > 0) {\n this.#onTransactionParamsUpdated(\n transactionMeta,\n updatedTransactionParams,\n );\n }\n\n return transactionMeta;\n }\n\n #checkIfTransactionParamsUpdated(newTransactionMeta: TransactionMeta) {\n const { id: transactionId, txParams: newParams } = newTransactionMeta;\n\n const originalParams = this.getTransaction(transactionId)?.txParams;\n\n if (!originalParams || isEqual(originalParams, newParams)) {\n return [];\n }\n\n const params = Object.keys(newParams) as (keyof TransactionParams)[];\n\n const updatedProperties = params.filter(\n (param) => newParams[param] !== originalParams[param],\n );\n\n log(\n 'Transaction parameters have been updated',\n transactionId,\n updatedProperties,\n originalParams,\n newParams,\n );\n\n return updatedProperties;\n }\n\n #onTransactionParamsUpdated(\n transactionMeta: TransactionMeta,\n updatedParams: (keyof TransactionParams)[],\n ) {\n if (\n (['to', 'value', 'data'] as const).some((param) =>\n updatedParams.includes(param),\n )\n ) {\n log('Updating simulation data due to transaction parameter update');\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.#updateSimulationData(transactionMeta);\n }\n }\n\n async #updateSimulationData(transactionMeta: TransactionMeta) {\n const { id: transactionId, chainId, txParams } = transactionMeta;\n const { from, to, value, data } = txParams;\n\n let simulationData: SimulationData = {\n error: {\n code: SimulationErrorCode.Disabled,\n message: 'Simulation disabled',\n },\n tokenBalanceChanges: [],\n };\n\n if (this.#isSimulationEnabled()) {\n this.#updateTransactionInternal(\n { transactionId, skipHistory: true },\n (txMeta) => {\n txMeta.simulationData = undefined;\n },\n );\n\n simulationData = await getSimulationData({\n chainId,\n from: from as Hex,\n to: to as Hex,\n value: value as Hex,\n data: data as Hex,\n });\n }\n\n const finalTransactionMeta = this.getTransaction(transactionId);\n\n /* istanbul ignore if */\n if (!finalTransactionMeta) {\n log(\n 'Cannot update simulation data as transaction not found',\n transactionId,\n simulationData,\n );\n\n return;\n }\n\n this.#updateTransactionInternal(\n {\n transactionId,\n note: 'TransactionController#updateSimulationData - Update simulation data',\n },\n (txMeta) => {\n txMeta.simulationData = simulationData;\n },\n );\n\n log('Updated simulation data', transactionId, simulationData);\n }\n\n #onGasFeePollerTransactionUpdate({\n transactionId,\n gasFeeEstimates,\n gasFeeEstimatesLoaded,\n layer1GasFee,\n }: {\n transactionId: string;\n gasFeeEstimates?: GasFeeEstimates;\n gasFeeEstimatesLoaded?: boolean;\n layer1GasFee?: Hex;\n }) {\n this.#updateTransactionInternal(\n { transactionId, skipHistory: true },\n (txMeta) => {\n if (gasFeeEstimates) {\n txMeta.gasFeeEstimates = gasFeeEstimates;\n }\n\n if (gasFeeEstimatesLoaded !== undefined) {\n txMeta.gasFeeEstimatesLoaded = gasFeeEstimatesLoaded;\n }\n\n if (layer1GasFee) {\n txMeta.layer1GasFee = layer1GasFee;\n }\n },\n );\n }\n\n #getNetworkClientId({\n networkClientId: requestNetworkClientId,\n chainId,\n }: {\n networkClientId?: NetworkClientId;\n chainId?: Hex;\n }) {\n const globalChainId = this.#getGlobalChainId();\n const globalNetworkClientId = this.#getGlobalNetworkClientId();\n\n if (requestNetworkClientId) {\n return requestNetworkClientId;\n }\n\n if (!chainId || chainId === globalChainId) {\n return globalNetworkClientId;\n }\n\n return this.messagingSystem.call(\n `NetworkController:findNetworkClientIdByChainId`,\n chainId,\n );\n }\n\n #getGlobalNetworkClientId() {\n return this.getNetworkState().selectedNetworkClientId;\n }\n\n #getGlobalChainId() {\n return this.messagingSystem.call(\n `NetworkController:getNetworkClientById`,\n this.getNetworkState().selectedNetworkClientId,\n ).configuration.chainId;\n }\n\n #isCustomNetwork(networkClientId?: NetworkClientId) {\n const globalNetworkClientId = this.#getGlobalNetworkClientId();\n\n if (!networkClientId || networkClientId === globalNetworkClientId) {\n return !isInfuraNetworkType(\n this.getNetworkState().selectedNetworkClientId,\n );\n }\n\n return (\n this.messagingSystem.call(\n `NetworkController:getNetworkClientById`,\n networkClientId,\n ).configuration.type === NetworkClientType.Custom\n );\n }\n\n #getSelectedAccount() {\n return this.messagingSystem.call('AccountsController:getSelectedAccount');\n }\n}\n"],"mappings":"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","names":["ApprovalState","transactionMeta"]} -\ No newline at end of file -diff --git a/dist/helpers/PendingTransactionTracker.js b/dist/helpers/PendingTransactionTracker.js -index 339a7782f047161c1909f8a91bb4299fad55a5f0..fb40ba14596eb229b004073776cfe1ddb171094a 100644 ---- a/dist/helpers/PendingTransactionTracker.js -+++ b/dist/helpers/PendingTransactionTracker.js -@@ -1,10 +1,10 @@ - "use strict";Object.defineProperty(exports, "__esModule", {value: true}); - --var _chunkULD4JC3Qjs = require('../chunk-ULD4JC3Q.js'); -+var _chunk6DODV6OVjs = require('../chunk-6DODV6OV.js'); - require('../chunk-S6VGOPUY.js'); - require('../chunk-AYTU4HU5.js'); - require('../chunk-Z4BLTVTB.js'); - - --exports.PendingTransactionTracker = _chunkULD4JC3Qjs.PendingTransactionTracker; -+exports.PendingTransactionTracker = _chunk6DODV6OVjs.PendingTransactionTracker; - //# sourceMappingURL=PendingTransactionTracker.js.map -\ No newline at end of file -diff --git a/dist/helpers/PendingTransactionTracker.mjs b/dist/helpers/PendingTransactionTracker.mjs -index e942405e82ab16407d8cbf318d84c1991f4e20d4..546ccbc8aede4f95bccc47d7c495000a8128dd82 100644 ---- a/dist/helpers/PendingTransactionTracker.mjs -+++ b/dist/helpers/PendingTransactionTracker.mjs -@@ -1,6 +1,6 @@ - import { - PendingTransactionTracker --} from "../chunk-6B5BEO3R.mjs"; -+} from "../chunk-7M2R5AHC.mjs"; - import "../chunk-UQQWZT6C.mjs"; - import "../chunk-6SJYXSF3.mjs"; - import "../chunk-XUI43LEZ.mjs"; -diff --git a/dist/index.js b/dist/index.js -index 16f7e96ba4efe3f7d7aec6cecbb1fce6f991dd0b..77e7c5cc78efe603bcd6afbe95a5a6dee9cae719 100644 ---- a/dist/index.js -+++ b/dist/index.js -@@ -2,7 +2,7 @@ - - - --var _chunkS7Q622ISjs = require('./chunk-S7Q622IS.js'); -+var _chunkIVR4NMOFjs = require('./chunk-IVR4NMOF.js'); - require('./chunk-PRUNMTRD.js'); - require('./chunk-74W7X6BE.js'); - require('./chunk-KT6UAKBB.js'); -@@ -10,7 +10,7 @@ require('./chunk-KT6UAKBB.js'); - - var _chunkSD6CWFDFjs = require('./chunk-SD6CWFDF.js'); - require('./chunk-RXIUMVA5.js'); --require('./chunk-ULD4JC3Q.js'); -+require('./chunk-6DODV6OV.js'); - require('./chunk-7LXE4KHV.js'); - require('./chunk-V72C4MCR.js'); - require('./chunk-QP75SWIQ.js'); -@@ -67,5 +67,5 @@ require('./chunk-Z4BLTVTB.js'); - - - --exports.CANCEL_RATE = _chunkS7Q622ISjs.CANCEL_RATE; exports.GasFeeEstimateLevel = _chunkAYTU4HU5js.GasFeeEstimateLevel; exports.GasFeeEstimateType = _chunkAYTU4HU5js.GasFeeEstimateType; exports.HARDFORK = _chunkS7Q622ISjs.HARDFORK; exports.SimulationErrorCode = _chunkAYTU4HU5js.SimulationErrorCode; exports.SimulationTokenStandard = _chunkAYTU4HU5js.SimulationTokenStandard; exports.TransactionController = _chunkS7Q622ISjs.TransactionController; exports.TransactionEnvelopeType = _chunkAYTU4HU5js.TransactionEnvelopeType; exports.TransactionStatus = _chunkAYTU4HU5js.TransactionStatus; exports.TransactionType = _chunkAYTU4HU5js.TransactionType; exports.UserFeeLevel = _chunkAYTU4HU5js.UserFeeLevel; exports.WalletDevice = _chunkAYTU4HU5js.WalletDevice; exports.determineTransactionType = _chunkSD6CWFDFjs.determineTransactionType; exports.isEIP1559Transaction = _chunkOZ6UB42Cjs.isEIP1559Transaction; exports.mergeGasFeeEstimates = _chunk76FONEDAjs.mergeGasFeeEstimates; exports.normalizeTransactionParams = _chunkOZ6UB42Cjs.normalizeTransactionParams; -+exports.CANCEL_RATE = _chunkIVR4NMOFjs.CANCEL_RATE; exports.GasFeeEstimateLevel = _chunkAYTU4HU5js.GasFeeEstimateLevel; exports.GasFeeEstimateType = _chunkAYTU4HU5js.GasFeeEstimateType; exports.HARDFORK = _chunkIVR4NMOFjs.HARDFORK; exports.SimulationErrorCode = _chunkAYTU4HU5js.SimulationErrorCode; exports.SimulationTokenStandard = _chunkAYTU4HU5js.SimulationTokenStandard; exports.TransactionController = _chunkIVR4NMOFjs.TransactionController; exports.TransactionEnvelopeType = _chunkAYTU4HU5js.TransactionEnvelopeType; exports.TransactionStatus = _chunkAYTU4HU5js.TransactionStatus; exports.TransactionType = _chunkAYTU4HU5js.TransactionType; exports.UserFeeLevel = _chunkAYTU4HU5js.UserFeeLevel; exports.WalletDevice = _chunkAYTU4HU5js.WalletDevice; exports.determineTransactionType = _chunkSD6CWFDFjs.determineTransactionType; exports.isEIP1559Transaction = _chunkOZ6UB42Cjs.isEIP1559Transaction; exports.mergeGasFeeEstimates = _chunk76FONEDAjs.mergeGasFeeEstimates; exports.normalizeTransactionParams = _chunkOZ6UB42Cjs.normalizeTransactionParams; - //# sourceMappingURL=index.js.map -\ No newline at end of file -diff --git a/dist/index.mjs b/dist/index.mjs -index edca04b36c84ba0245f49301275dbc5d49edeb18..a3506a7fa4f3050ff689540eefcddadd5d934b6b 100644 ---- a/dist/index.mjs -+++ b/dist/index.mjs -@@ -2,7 +2,7 @@ import { - CANCEL_RATE, - HARDFORK, - TransactionController --} from "./chunk-UKV5HIMT.mjs"; -+} from "./chunk-YQYO6EGF.mjs"; - import "./chunk-6DDVVUJC.mjs"; - import "./chunk-EVL6KODQ.mjs"; - import "./chunk-K4KOSAGM.mjs"; -@@ -10,7 +10,7 @@ import { - determineTransactionType - } from "./chunk-KG4UW4K4.mjs"; - import "./chunk-5ZEJT5SN.mjs"; --import "./chunk-6B5BEO3R.mjs"; -+import "./chunk-7M2R5AHC.mjs"; - import "./chunk-FRKQ3Z2L.mjs"; - import "./chunk-5G6OHAXI.mjs"; - import "./chunk-XGRAHX6T.mjs"; -diff --git a/dist/tsconfig.build.tsbuildinfo b/dist/tsconfig.build.tsbuildinfo -index 32138a8b41ae5a233d27a25ee160c68c6a7b5a73..4304f0751c5d4f5457696f8ac9e1b4ab38f843b7 100644 ---- a/dist/tsconfig.build.tsbuildinfo -+++ b/dist/tsconfig.build.tsbuildinfo -@@ -1 +1 @@ --{"program":{"fileNames":["../../../node_modules/typescript/lib/lib.es5.d.ts","../../../node_modules/typescript/lib/lib.es2015.d.ts","../../../node_modules/typescript/lib/lib.es2016.d.ts","../../../node_modules/typescript/lib/lib.es2017.d.ts","../../../node_modules/typescript/lib/lib.es2018.d.ts","../../../node_modules/typescript/lib/lib.es2019.d.ts","../../../node_modules/typescript/lib/lib.es2020.d.ts","../../../node_modules/typescript/lib/lib.dom.d.ts","../../../node_modules/typescript/lib/lib.es2015.core.d.ts","../../../node_modules/typescript/lib/lib.es2015.collection.d.ts","../../../node_modules/typescript/lib/lib.es2015.generator.d.ts","../../../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../../node_modules/typescript/lib/lib.es2015.promise.d.ts","../../../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../../node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../../node_modules/typescript/lib/lib.es2017.object.d.ts","../../../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../../node_modules/typescript/lib/lib.es2017.string.d.ts","../../../node_modules/typescript/lib/lib.es2017.intl.d.ts","../../../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../../node_modules/typescript/lib/lib.es2018.intl.d.ts","../../../node_modules/typescript/lib/lib.es2018.promise.d.ts","../../../node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../../node_modules/typescript/lib/lib.es2019.array.d.ts","../../../node_modules/typescript/lib/lib.es2019.object.d.ts","../../../node_modules/typescript/lib/lib.es2019.string.d.ts","../../../node_modules/typescript/lib/lib.es2019.symbol.d.ts","../../../node_modules/typescript/lib/lib.es2019.intl.d.ts","../../../node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../../node_modules/typescript/lib/lib.es2020.date.d.ts","../../../node_modules/typescript/lib/lib.es2020.promise.d.ts","../../../node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../../../node_modules/typescript/lib/lib.es2020.string.d.ts","../../../node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../../../node_modules/typescript/lib/lib.es2020.intl.d.ts","../../../node_modules/typescript/lib/lib.es2020.number.d.ts","../../../node_modules/typescript/lib/lib.esnext.intl.d.ts","../../../types/eth-ens-namehash.d.ts","../../../types/ethereum-ens-network-map.d.ts","../../../types/global.d.ts","../../../types/single-call-balance-checker-abi.d.ts","../../../types/@metamask/contract-metadata.d.ts","../../../types/@metamask/eth-hd-keyring.d.ts","../../../types/@metamask/eth-simple-keyring.d.ts","../../../types/@metamask/ethjs-provider-http.d.ts","../../../types/@metamask/ethjs-unit.d.ts","../../../types/@metamask/metamask-eth-abis.d.ts","../../../types/eth-json-rpc-infura/src/createProvider.d.ts","../../../types/eth-phishing-detect/src/config.json.d.ts","../../../types/eth-phishing-detect/src/detector.d.ts","../../../node_modules/@types/node/assert.d.ts","../../../node_modules/@types/node/assert/strict.d.ts","../../../node_modules/@types/node/globals.d.ts","../../../node_modules/@types/node/async_hooks.d.ts","../../../node_modules/@types/node/buffer.d.ts","../../../node_modules/@types/node/child_process.d.ts","../../../node_modules/@types/node/cluster.d.ts","../../../node_modules/@types/node/console.d.ts","../../../node_modules/@types/node/constants.d.ts","../../../node_modules/@types/node/crypto.d.ts","../../../node_modules/@types/node/dgram.d.ts","../../../node_modules/@types/node/diagnostics_channel.d.ts","../../../node_modules/@types/node/dns.d.ts","../../../node_modules/@types/node/dns/promises.d.ts","../../../node_modules/@types/node/dom-events.d.ts","../../../node_modules/@types/node/domain.d.ts","../../../node_modules/@types/node/events.d.ts","../../../node_modules/@types/node/fs.d.ts","../../../node_modules/@types/node/fs/promises.d.ts","../../../node_modules/@types/node/http.d.ts","../../../node_modules/@types/node/http2.d.ts","../../../node_modules/@types/node/https.d.ts","../../../node_modules/@types/node/inspector.d.ts","../../../node_modules/@types/node/module.d.ts","../../../node_modules/@types/node/net.d.ts","../../../node_modules/@types/node/os.d.ts","../../../node_modules/@types/node/path.d.ts","../../../node_modules/@types/node/perf_hooks.d.ts","../../../node_modules/@types/node/process.d.ts","../../../node_modules/@types/node/punycode.d.ts","../../../node_modules/@types/node/querystring.d.ts","../../../node_modules/@types/node/readline.d.ts","../../../node_modules/@types/node/repl.d.ts","../../../node_modules/@types/node/stream.d.ts","../../../node_modules/@types/node/stream/promises.d.ts","../../../node_modules/@types/node/stream/consumers.d.ts","../../../node_modules/@types/node/stream/web.d.ts","../../../node_modules/@types/node/string_decoder.d.ts","../../../node_modules/@types/node/test.d.ts","../../../node_modules/@types/node/timers.d.ts","../../../node_modules/@types/node/timers/promises.d.ts","../../../node_modules/@types/node/tls.d.ts","../../../node_modules/@types/node/trace_events.d.ts","../../../node_modules/@types/node/tty.d.ts","../../../node_modules/@types/node/url.d.ts","../../../node_modules/@types/node/util.d.ts","../../../node_modules/@types/node/v8.d.ts","../../../node_modules/@types/node/vm.d.ts","../../../node_modules/@types/node/wasi.d.ts","../../../node_modules/@types/node/worker_threads.d.ts","../../../node_modules/@types/node/zlib.d.ts","../../../node_modules/@types/node/globals.global.d.ts","../../../node_modules/@types/node/index.d.ts","../../../node_modules/@ethereumjs/common/dist/enums.d.ts","../../../node_modules/@ethereumjs/common/dist/types.d.ts","../../../node_modules/buffer/index.d.ts","../../../node_modules/@ethereumjs/util/dist/constants.d.ts","../../../node_modules/@ethereumjs/util/dist/units.d.ts","../../../node_modules/@ethereumjs/util/dist/address.d.ts","../../../node_modules/@ethereumjs/util/dist/bytes.d.ts","../../../node_modules/@ethereumjs/util/dist/types.d.ts","../../../node_modules/@ethereumjs/util/dist/account.d.ts","../../../node_modules/@ethereumjs/util/dist/withdrawal.d.ts","../../../node_modules/@ethereumjs/util/dist/signature.d.ts","../../../node_modules/@ethereumjs/util/dist/encoding.d.ts","../../../node_modules/@ethereumjs/util/dist/asyncEventEmitter.d.ts","../../../node_modules/@ethereumjs/util/dist/internal.d.ts","../../../node_modules/@ethereumjs/util/dist/lock.d.ts","../../../node_modules/@ethereumjs/util/dist/provider.d.ts","../../../node_modules/@ethereumjs/util/dist/index.d.ts","../../../node_modules/@ethereumjs/common/dist/common.d.ts","../../../node_modules/@ethereumjs/common/dist/utils.d.ts","../../../node_modules/@ethereumjs/common/dist/index.d.ts","../../../node_modules/@ethereumjs/tx/dist/eip2930Transaction.d.ts","../../../node_modules/@ethereumjs/tx/dist/legacyTransaction.d.ts","../../../node_modules/@ethereumjs/tx/dist/types.d.ts","../../../node_modules/@ethereumjs/tx/dist/baseTransaction.d.ts","../../../node_modules/@ethereumjs/tx/dist/eip1559Transaction.d.ts","../../../node_modules/@ethereumjs/tx/dist/transactionFactory.d.ts","../../../node_modules/@ethereumjs/tx/dist/index.d.ts","../../base-controller/dist/types/BaseControllerV1.d.ts","../../../node_modules/superstruct/dist/error.d.ts","../../../node_modules/superstruct/dist/utils.d.ts","../../../node_modules/superstruct/dist/struct.d.ts","../../../node_modules/superstruct/dist/structs/coercions.d.ts","../../../node_modules/superstruct/dist/structs/refinements.d.ts","../../../node_modules/superstruct/dist/structs/types.d.ts","../../../node_modules/superstruct/dist/structs/utilities.d.ts","../../../node_modules/superstruct/dist/index.d.ts","../../../node_modules/@metamask/utils/dist/types/assert.d.ts","../../../node_modules/@metamask/utils/dist/types/base64.d.ts","../../../node_modules/@metamask/utils/dist/types/hex.d.ts","../../../node_modules/@metamask/utils/dist/types/bytes.d.ts","../../../node_modules/@metamask/utils/dist/types/caip-types.d.ts","../../../node_modules/@metamask/utils/dist/types/checksum.d.ts","../../../node_modules/@metamask/utils/dist/types/coercers.d.ts","../../../node_modules/@metamask/utils/dist/types/collections.d.ts","../../../node_modules/@metamask/utils/dist/types/encryption-types.d.ts","../../../node_modules/@metamask/utils/dist/types/errors.d.ts","../../../node_modules/@metamask/utils/dist/types/json.d.ts","../../../node_modules/@metamask/utils/dist/types/keyring.d.ts","../../../node_modules/@types/ms/index.d.ts","../../../node_modules/@types/debug/index.d.ts","../../../node_modules/@metamask/utils/dist/types/logging.d.ts","../../../node_modules/@metamask/utils/dist/types/misc.d.ts","../../../node_modules/@metamask/utils/dist/types/number.d.ts","../../../node_modules/@metamask/utils/dist/types/opaque.d.ts","../../../node_modules/@metamask/utils/dist/types/promise.d.ts","../../../node_modules/@metamask/utils/dist/types/time.d.ts","../../../node_modules/@metamask/utils/dist/types/transaction-types.d.ts","../../../node_modules/@metamask/utils/dist/types/versions.d.ts","../../../node_modules/@metamask/utils/dist/types/index.d.ts","../../../node_modules/immer/dist/utils/env.d.ts","../../../node_modules/immer/dist/utils/errors.d.ts","../../../node_modules/immer/dist/types/types-external.d.ts","../../../node_modules/immer/dist/types/types-internal.d.ts","../../../node_modules/immer/dist/utils/common.d.ts","../../../node_modules/immer/dist/utils/plugins.d.ts","../../../node_modules/immer/dist/core/scope.d.ts","../../../node_modules/immer/dist/core/finalize.d.ts","../../../node_modules/immer/dist/core/proxy.d.ts","../../../node_modules/immer/dist/core/immerClass.d.ts","../../../node_modules/immer/dist/core/current.d.ts","../../../node_modules/immer/dist/internal.d.ts","../../../node_modules/immer/dist/plugins/es5.d.ts","../../../node_modules/immer/dist/plugins/patches.d.ts","../../../node_modules/immer/dist/plugins/mapset.d.ts","../../../node_modules/immer/dist/plugins/all.d.ts","../../../node_modules/immer/dist/immer.d.ts","../../base-controller/dist/types/RestrictedControllerMessenger.d.ts","../../base-controller/dist/types/ControllerMessenger.d.ts","../../base-controller/dist/types/BaseControllerV2.d.ts","../../base-controller/dist/types/index.d.ts","../../../node_modules/@metamask/keyring-api/dist/api/account.d.ts","../../../node_modules/@metamask/keyring-api/dist/api/balance.d.ts","../../../node_modules/@metamask/keyring-api/dist/api/caip.d.ts","../../../node_modules/@metamask/keyring-api/dist/api/export.d.ts","../../../node_modules/@metamask/keyring-api/dist/superstruct.d.ts","../../../node_modules/@metamask/keyring-api/dist/api/request.d.ts","../../../node_modules/@metamask/keyring-api/dist/api/response.d.ts","../../../node_modules/@metamask/keyring-api/dist/api/keyring.d.ts","../../../node_modules/@metamask/keyring-api/dist/api/index.d.ts","../../../node_modules/@metamask/keyring-api/dist/btc/types.d.ts","../../../node_modules/@metamask/keyring-api/dist/btc/index.d.ts","../../../node_modules/@metamask/keyring-api/dist/contexts.d.ts","../../../node_modules/@metamask/keyring-api/dist/eth/erc4337/types.d.ts","../../../node_modules/@metamask/keyring-api/dist/eth/erc4337/index.d.ts","../../../node_modules/@metamask/keyring-api/dist/eth/types.d.ts","../../../node_modules/@metamask/keyring-api/dist/eth/utils.d.ts","../../../node_modules/@metamask/keyring-api/dist/eth/index.d.ts","../../../node_modules/@metamask/keyring-api/dist/events.d.ts","../../../node_modules/@metamask/keyring-api/dist/internal/api.d.ts","../../../node_modules/@metamask/keyring-api/dist/internal/eth/EthKeyring.d.ts","../../../node_modules/@metamask/keyring-api/dist/internal/eth/index.d.ts","../../../node_modules/@metamask/keyring-api/dist/internal/events.d.ts","../../../node_modules/@metamask/keyring-api/dist/internal/rpc.d.ts","../../../node_modules/@metamask/keyring-api/dist/internal/types.d.ts","../../../node_modules/@metamask/keyring-api/dist/internal/index.d.ts","../../../node_modules/@metamask/keyring-api/dist/JsonRpcRequest.d.ts","../../../node_modules/@metamask/keyring-api/dist/KeyringClient.d.ts","../../../node_modules/@metamask/safe-event-emitter/dist/cjs/index.d.ts","../../json-rpc-engine/dist/types/JsonRpcEngine.d.ts","../../json-rpc-engine/dist/types/createAsyncMiddleware.d.ts","../../json-rpc-engine/dist/types/createScaffoldMiddleware.d.ts","../../json-rpc-engine/dist/types/getUniqueId.d.ts","../../json-rpc-engine/dist/types/idRemapMiddleware.d.ts","../../json-rpc-engine/dist/types/mergeMiddleware.d.ts","../../json-rpc-engine/dist/types/index.d.ts","../../../node_modules/@metamask/providers/dist/types/utils.d.ts","../../../node_modules/@metamask/providers/dist/types/BaseProvider.d.ts","../../../node_modules/@metamask/providers/dist/types/EIP6963.d.ts","../../../node_modules/@types/readable-stream/node_modules/safe-buffer/index.d.ts","../../../node_modules/@types/readable-stream/index.d.ts","../../../node_modules/@metamask/providers/dist/types/StreamProvider.d.ts","../../../node_modules/@metamask/providers/dist/types/extension-provider/createExternalExtensionProvider.d.ts","../../../node_modules/@metamask/providers/dist/types/MetaMaskInpageProvider.d.ts","../../../node_modules/@metamask/providers/dist/types/initializeInpageProvider.d.ts","../../../node_modules/@metamask/providers/dist/types/shimWeb3.d.ts","../../../node_modules/@metamask/providers/dist/types/index.d.ts","../../../node_modules/@metamask/keyring-api/dist/KeyringSnapRpcClient.d.ts","../../../node_modules/@metamask/keyring-api/dist/rpc-handler.d.ts","../../../node_modules/@metamask/rpc-errors/dist/types/utils.d.ts","../../../node_modules/@metamask/rpc-errors/dist/types/classes.d.ts","../../../node_modules/@metamask/rpc-errors/dist/types/errors.d.ts","../../../node_modules/@metamask/rpc-errors/dist/types/error-constants.d.ts","../../../node_modules/@metamask/rpc-errors/dist/types/index.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/errors.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/internals/error-wrappers.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/internals/errors.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/internals/helpers.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/internals/structs.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/create-interface.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/dialog.d.ts","../../../node_modules/@metamask/key-tree/dist/constants.d.cts","../../../node_modules/@metamask/key-tree/node_modules/@noble/curves/abstract/modular.d.ts","../../../node_modules/@metamask/key-tree/node_modules/@noble/curves/abstract/utils.d.ts","../../../node_modules/@metamask/key-tree/node_modules/@noble/curves/abstract/curve.d.ts","../../../node_modules/@metamask/key-tree/dist/curves/ed25519.d.cts","../../../node_modules/@metamask/key-tree/dist/curves/ed25519Bip32.d.cts","../../../node_modules/@metamask/key-tree/node_modules/@noble/curves/abstract/weierstrass.d.ts","../../../node_modules/@metamask/key-tree/dist/curves/secp256k1.d.cts","../../../node_modules/@metamask/key-tree/dist/curves/curve.d.cts","../../../node_modules/@metamask/key-tree/dist/curves/index.d.cts","../../../node_modules/@metamask/key-tree/dist/utils.d.cts","../../../node_modules/@metamask/key-tree/dist/BIP44CoinTypeNode.d.cts","../../../node_modules/@metamask/key-tree/dist/SLIP10Node.d.cts","../../../node_modules/@metamask/key-tree/dist/BIP44Node.d.cts","../../../node_modules/@metamask/key-tree/dist/derivers/bip32.d.cts","../../../node_modules/@metamask/key-tree/dist/derivers/bip39.d.cts","../../../node_modules/@metamask/key-tree/dist/derivers/cip3.d.cts","../../../node_modules/@metamask/key-tree/dist/derivers/slip10.d.cts","../../../node_modules/@metamask/key-tree/dist/derivers/index.d.cts","../../../node_modules/@metamask/key-tree/dist/index.d.cts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/caip.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/permissions.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/get-bip32-entropy.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/get-bip32-public-key.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/get-bip44-entropy.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/get-client-status.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/get-entropy.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/get-file.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/component.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/Address.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/Box.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/Copyable.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/Divider.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/form/Button.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/form/Option.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/form/Dropdown.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/form/Input.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/form/Field.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/form/Form.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/form/index.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/formatting/Bold.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/formatting/Italic.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/formatting/index.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/Heading.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/Image.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/Link.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/Text.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/Value.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/Row.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/Spinner.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/index.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/jsx-runtime.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/jsx-dev-runtime.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/validation.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/index.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/nodes.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/address.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/copyable.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/divider.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/heading.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/image.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/panel.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/spinner.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/text.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/row.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/button.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/input.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/form.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/index.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/component.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/index.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/interface.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/get-interface-state.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/get-locale.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/snap.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/get-snaps.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/invoke-snap.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/invoke-keyring.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/manage-accounts.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/manage-state.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/notify.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/request-snaps.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/update-interface.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/methods.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/index.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/provider.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/global.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/images.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/handlers/cronjob.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/handlers/home-page.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/handlers/keyring.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/handlers/lifecycle.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/handlers/name-lookup.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/handlers/rpc-request.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/handlers/transaction.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/handlers/signature.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/handlers/user-input.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/handlers/index.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/index.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/internals/jsx.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/internals/svg.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/internals/index.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/error-wrappers.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/images.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/index.d.ts","../../../node_modules/@metamask/keyring-api/dist/snap-utils.d.ts","../../../node_modules/@metamask/keyring-api/dist/index.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/patchCBOR.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/lib/DataItem.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/lib/cbor-sync.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/lib/index.d.ts","../../../node_modules/@ngraveio/bc-ur/dist/ur.d.ts","../../../node_modules/@ngraveio/bc-ur/dist/urEncoder.d.ts","../../../node_modules/@ngraveio/bc-ur/dist/fountainEncoder.d.ts","../../../node_modules/@ngraveio/bc-ur/dist/fountainDecoder.d.ts","../../../node_modules/@ngraveio/bc-ur/dist/urDecoder.d.ts","../../../node_modules/@ngraveio/bc-ur/dist/index.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/RegistryType.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/RegistryItem.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/CryptoCoinInfo.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/PathComponent.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/CryptoKeypath.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/types.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/CryptoHDKey.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/CryptoECKey.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/Bytes.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/MultiKey.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/ScriptExpression.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/CryptoOutput.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/CryptoPSBT.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/CryptoAccount.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/Decoder/index.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/extended/CryptoMultiAccounts.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/errors/index.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/extended/DerivationSchema.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/extended/KeyDerivation.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/extended/QRHardwareCall.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/utils.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/index.d.ts","../../../node_modules/@keystonehq/bc-ur-registry-eth/dist/EthSignRequest.d.ts","../../../node_modules/@keystonehq/bc-ur-registry-eth/dist/EthSignature.d.ts","../../../node_modules/@keystonehq/bc-ur-registry-eth/dist/ETHNFTItem.d.ts","../../../node_modules/@keystonehq/bc-ur-registry-eth/dist/utlis.d.ts","../../../node_modules/@keystonehq/bc-ur-registry-eth/dist/index.d.ts","../../../node_modules/@keystonehq/base-eth-keyring/dist/InteractionProvider.d.ts","../../../node_modules/@keystonehq/base-eth-keyring/dist/BaseKeyring.d.ts","../../../node_modules/@keystonehq/base-eth-keyring/dist/index.d.ts","../../../node_modules/@metamask/obs-store/dist/ObservableStore.d.ts","../../../node_modules/@metamask/obs-store/dist/asStream.d.ts","../../../node_modules/@metamask/obs-store/dist/ComposedStore.d.ts","../../../node_modules/@metamask/obs-store/dist/MergedStore.d.ts","../../../node_modules/@metamask/obs-store/dist/transform.d.ts","../../../node_modules/@metamask/obs-store/dist/index.d.ts","../../../node_modules/@keystonehq/metamask-airgapped-keyring/dist/MetaMaskInteractionProvider.d.ts","../../../node_modules/@keystonehq/metamask-airgapped-keyring/dist/MetaMaskKeyring.d.ts","../../../node_modules/@keystonehq/metamask-airgapped-keyring/dist/index.d.ts","../../../node_modules/@metamask/browser-passworder/dist/index.d.ts","../../message-manager/dist/types/AbstractMessageManager.d.ts","../../controller-utils/dist/types/types.d.ts","../../controller-utils/dist/types/constants.d.ts","../../../node_modules/@metamask/eth-query/index.d.ts","../../../node_modules/@types/bn.js/index.d.ts","../../controller-utils/dist/types/util.d.ts","../../../node_modules/@spruceid/siwe-parser/dist/abnf.d.ts","../../../node_modules/@spruceid/siwe-parser/dist/utils.d.ts","../../../node_modules/@spruceid/siwe-parser/dist/parsers.d.ts","../../controller-utils/dist/types/siwe.d.ts","../../controller-utils/dist/types/index.d.ts","../../message-manager/dist/types/PersonalMessageManager.d.ts","../../message-manager/dist/types/TypedMessageManager.d.ts","../../message-manager/dist/types/EncryptionPublicKeyManager.d.ts","../../message-manager/dist/types/DecryptMessageManager.d.ts","../../message-manager/dist/types/index.d.ts","../../keyring-controller/dist/types/KeyringController.d.ts","../../keyring-controller/dist/types/index.d.ts","../../../node_modules/@metamask/object-multiplex/dist/Substream.d.ts","../../../node_modules/@metamask/object-multiplex/dist/ObjectMultiplex.d.ts","../../../node_modules/@metamask/object-multiplex/dist/index.d.ts","../../../node_modules/@metamask/post-message-stream/dist/utils.d.ts","../../../node_modules/@metamask/post-message-stream/dist/BasePostMessageStream.d.ts","../../../node_modules/@metamask/post-message-stream/dist/window/WindowPostMessageStream.d.ts","../../../node_modules/@metamask/post-message-stream/dist/WebWorker/WebWorkerPostMessageStream.d.ts","../../../node_modules/@metamask/post-message-stream/dist/WebWorker/WebWorkerParentPostMessageStream.d.ts","../../../node_modules/@metamask/post-message-stream/dist/node-process/ProcessParentMessageStream.d.ts","../../../node_modules/@metamask/post-message-stream/dist/node-process/ProcessMessageStream.d.ts","../../../node_modules/@metamask/post-message-stream/dist/node-thread/ThreadParentMessageStream.d.ts","../../../node_modules/@metamask/post-message-stream/dist/node-thread/ThreadMessageStream.d.ts","../../../node_modules/@metamask/post-message-stream/dist/runtime/BrowserRuntimePostMessageStream.d.ts","../../../node_modules/@metamask/post-message-stream/dist/index.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/array.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/auxiliary-files.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/virtual-file/VirtualFile.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/virtual-file/index.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/base64.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/bytes.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/caveats.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/checksum.d.ts","../../../node_modules/cron-parser/types/common.d.ts","../../../node_modules/cron-parser/types/index.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/cronjob.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/deep-clone.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/default-endowments.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/derivation-paths.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/entropy.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/errors.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/handler-types.d.ts","../../../node_modules/@metamask/snaps-sdk/jsx.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/handlers.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/iframe.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/json.d.ts","../../../node_modules/nanoid/index.d.ts","../../approval-controller/dist/types/ApprovalController.d.ts","../../approval-controller/dist/types/errors.d.ts","../../approval-controller/dist/types/index.d.ts","../../../node_modules/@types/deep-freeze-strict/index.d.ts","../../permission-controller/src/permission-middleware.ts","../../permission-controller/src/SubjectMetadataController.ts","../../permission-controller/src/utils.ts","../../permission-controller/src/PermissionController.ts","../../permission-controller/src/Permission.ts","../../permission-controller/src/errors.ts","../../permission-controller/src/Caveat.ts","../../permission-controller/src/rpc-methods/getPermissions.ts","../../permission-controller/src/rpc-methods/requestPermissions.ts","../../permission-controller/src/rpc-methods/revokePermissions.ts","../../permission-controller/src/rpc-methods/index.ts","../../permission-controller/src/index.ts","../../../node_modules/@metamask/snaps-utils/dist/types/json-rpc.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/structs.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/manifest/validation.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/manifest/index.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/localization.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/logging.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/namespace.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/path.d.ts","../../../node_modules/@metamask/snaps-registry/dist/verify.d.ts","../../../node_modules/@metamask/snaps-registry/dist/index.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/types.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/snaps.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/strings.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/ui.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/validation.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/versions.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/index.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/snaps/Timer.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/services/ExecutionService.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/services/AbstractExecutionService.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/services/ProxyPostMessageStream.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/services/iframe/IframeExecutionService.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/services/iframe/index.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/services/proxy/ProxyExecutionService.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/services/offscreen/OffscreenExecutionService.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/services/offscreen/index.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/services/webworker/WebWorkerExecutionService.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/services/webworker/index.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/services/index.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/snaps/location/npm.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/snaps/location/location.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/snaps/location/http.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/snaps/location/local.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/snaps/location/index.d.ts","../../../node_modules/@xstate/fsm/lib/types.d.ts","../../../node_modules/@xstate/fsm/lib/index.d.ts","../../../node_modules/@types/punycode/index.d.ts","../../../node_modules/fastest-levenshtein/mod.d.ts","../../phishing-controller/src/utils.ts","../../phishing-controller/src/PhishingDetector.ts","../../phishing-controller/src/PhishingController.ts","../../phishing-controller/src/index.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/interface/SnapInterfaceController.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/interface/index.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/types/encryptor.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/types/index.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/snaps/registry/registry.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/snaps/registry/json.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/snaps/registry/index.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/snaps/SnapController.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/snaps/selectors.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/snaps/index.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/utils.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/cronjob/CronjobController.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/cronjob/index.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/index.d.ts","../../accounts-controller/dist/types/AccountsController.d.ts","../../../node_modules/@types/uuid/index.d.ts","../../accounts-controller/dist/types/utils.d.ts","../../accounts-controller/dist/types/index.d.ts","../../../node_modules/@metamask/swappable-obj-proxy/dist/types.d.ts","../../../node_modules/@metamask/swappable-obj-proxy/dist/createEventEmitterProxy.d.ts","../../../node_modules/@metamask/swappable-obj-proxy/dist/createSwappableProxy.d.ts","../../../node_modules/@metamask/swappable-obj-proxy/dist/index.d.ts","../../network-controller/dist/types/constants.d.ts","../../eth-json-rpc-provider/dist/types/safe-event-emitter-provider.d.ts","../../eth-json-rpc-provider/dist/types/provider-from-engine.d.ts","../../eth-json-rpc-provider/dist/types/provider-from-middleware.d.ts","../../eth-json-rpc-provider/dist/types/index.d.ts","../../../node_modules/@metamask/eth-block-tracker/dist/BlockTracker.d.ts","../../../node_modules/@metamask/eth-block-tracker/dist/PollingBlockTracker.d.ts","../../../node_modules/@metamask/eth-block-tracker/dist/SubscribeBlockTracker.d.ts","../../../node_modules/@metamask/eth-block-tracker/dist/index.d.ts","../../network-controller/dist/types/types.d.ts","../../network-controller/dist/types/create-auto-managed-network-client.d.ts","../../network-controller/dist/types/NetworkController.d.ts","../../network-controller/dist/types/create-network-client.d.ts","../../network-controller/dist/types/index.d.ts","../../polling-controller/dist/types/types.d.ts","../../polling-controller/dist/types/BlockTrackerPollingController.d.ts","../../polling-controller/dist/types/StaticIntervalPollingController.d.ts","../../polling-controller/dist/types/index.d.ts","../../gas-fee-controller/dist/types/GasFeeController.d.ts","../../gas-fee-controller/dist/types/index.d.ts","../../../node_modules/@metamask/nonce-tracker/node_modules/async-mutex/lib/MutexInterface.d.ts","../../../node_modules/@metamask/nonce-tracker/node_modules/async-mutex/lib/Mutex.d.ts","../../../node_modules/@metamask/nonce-tracker/node_modules/async-mutex/lib/SemaphoreInterface.d.ts","../../../node_modules/@metamask/nonce-tracker/node_modules/async-mutex/lib/Semaphore.d.ts","../../../node_modules/@metamask/nonce-tracker/node_modules/async-mutex/lib/withTimeout.d.ts","../../../node_modules/@metamask/nonce-tracker/node_modules/async-mutex/lib/tryAcquire.d.ts","../../../node_modules/@metamask/nonce-tracker/node_modules/async-mutex/lib/errors.d.ts","../../../node_modules/@metamask/nonce-tracker/node_modules/async-mutex/lib/index.d.ts","../../../node_modules/@metamask/nonce-tracker/dist/NonceTracker.d.ts","../../../node_modules/@metamask/nonce-tracker/dist/index.d.ts","../../../node_modules/async-mutex/lib/MutexInterface.d.ts","../../../node_modules/async-mutex/lib/Mutex.d.ts","../../../node_modules/async-mutex/lib/SemaphoreInterface.d.ts","../../../node_modules/async-mutex/lib/Semaphore.d.ts","../../../node_modules/async-mutex/lib/withTimeout.d.ts","../../../node_modules/async-mutex/lib/tryAcquire.d.ts","../../../node_modules/async-mutex/lib/errors.d.ts","../../../node_modules/async-mutex/lib/index.d.ts","../../../node_modules/eth-method-registry/dist/index.d.ts","../../../node_modules/@types/lodash/common/common.d.ts","../../../node_modules/@types/lodash/common/array.d.ts","../../../node_modules/@types/lodash/common/collection.d.ts","../../../node_modules/@types/lodash/common/date.d.ts","../../../node_modules/@types/lodash/common/function.d.ts","../../../node_modules/@types/lodash/common/lang.d.ts","../../../node_modules/@types/lodash/common/math.d.ts","../../../node_modules/@types/lodash/common/number.d.ts","../../../node_modules/@types/lodash/common/object.d.ts","../../../node_modules/@types/lodash/common/seq.d.ts","../../../node_modules/@types/lodash/common/string.d.ts","../../../node_modules/@types/lodash/common/util.d.ts","../../../node_modules/@types/lodash/index.d.ts","../src/logger.ts","../../../node_modules/fast-json-patch/module/helpers.d.ts","../../../node_modules/fast-json-patch/module/core.d.ts","../../../node_modules/fast-json-patch/module/duplex.d.ts","../../../node_modules/fast-json-patch/index.d.ts","../src/types.ts","../src/utils/gas-flow.ts","../src/constants.ts","../src/utils/utils.ts","../src/utils/swaps.ts","../src/utils/gas-fees.ts","../src/gas-flows/DefaultGasFeeFlow.ts","../src/gas-flows/LineaGasFeeFlow.ts","../../../node_modules/@ethersproject/bytes/lib/index.d.ts","../../../node_modules/@ethersproject/bignumber/lib/bignumber.d.ts","../../../node_modules/@ethersproject/bignumber/lib/fixednumber.d.ts","../../../node_modules/@ethersproject/bignumber/lib/index.d.ts","../../../node_modules/@ethersproject/abi/lib/fragments.d.ts","../../../node_modules/@ethersproject/abi/lib/coders/abstract-coder.d.ts","../../../node_modules/@ethersproject/abi/lib/abi-coder.d.ts","../../../node_modules/@ethersproject/properties/lib/index.d.ts","../../../node_modules/@ethersproject/abi/lib/interface.d.ts","../../../node_modules/@ethersproject/abi/lib/index.d.ts","../../../node_modules/@ethersproject/networks/lib/types.d.ts","../../../node_modules/@ethersproject/networks/lib/index.d.ts","../../../node_modules/@ethersproject/transactions/lib/index.d.ts","../../../node_modules/@ethersproject/web/lib/index.d.ts","../../../node_modules/@ethersproject/abstract-provider/lib/index.d.ts","../../../node_modules/@ethersproject/abstract-signer/lib/index.d.ts","../../../node_modules/@ethersproject/contracts/lib/index.d.ts","../../../node_modules/@ethersproject/providers/lib/formatter.d.ts","../../../node_modules/@ethersproject/providers/lib/base-provider.d.ts","../../../node_modules/@ethersproject/providers/lib/json-rpc-provider.d.ts","../../../node_modules/@ethersproject/providers/lib/websocket-provider.d.ts","../../../node_modules/@ethersproject/providers/lib/url-json-rpc-provider.d.ts","../../../node_modules/@ethersproject/providers/lib/alchemy-provider.d.ts","../../../node_modules/@ethersproject/providers/lib/ankr-provider.d.ts","../../../node_modules/@ethersproject/providers/lib/cloudflare-provider.d.ts","../../../node_modules/@ethersproject/providers/lib/etherscan-provider.d.ts","../../../node_modules/@ethersproject/providers/lib/fallback-provider.d.ts","../../../node_modules/@ethersproject/providers/lib/ipc-provider.d.ts","../../../node_modules/@ethersproject/providers/lib/infura-provider.d.ts","../../../node_modules/@ethersproject/providers/lib/json-rpc-batch-provider.d.ts","../../../node_modules/@ethersproject/providers/lib/nodesmith-provider.d.ts","../../../node_modules/@ethersproject/providers/lib/pocket-provider.d.ts","../../../node_modules/@ethersproject/providers/lib/web3-provider.d.ts","../../../node_modules/@ethersproject/providers/lib/index.d.ts","../src/gas-flows/OracleLayer1GasFeeFlow.ts","../src/gas-flows/OptimismLayer1GasFeeFlow.ts","../src/gas-flows/ScrollLayer1GasFeeFlow.ts","../src/gas-flows/TestGasFeeFlow.ts","../src/utils/etherscan.ts","../src/helpers/EtherscanRemoteTransactionSource.ts","../src/utils/layer1-gas-fee-flow.ts","../src/helpers/GasFeePoller.ts","../src/helpers/IncomingTransactionHelper.ts","../src/helpers/PendingTransactionTracker.ts","../src/helpers/MultichainTrackingHelper.ts","../src/utils/external-transactions.ts","../src/utils/gas.ts","../src/utils/history.ts","../src/utils/nonce.ts","../../../node_modules/@metamask/metamask-eth-abis/dist/abis/abiERC20.d.ts","../../../node_modules/@metamask/metamask-eth-abis/dist/abis/abiERC721.d.ts","../../../node_modules/@metamask/metamask-eth-abis/dist/abis/abiERC1155.d.ts","../../../node_modules/@metamask/metamask-eth-abis/dist/abis/fiatTokenV2.d.ts","../../../node_modules/@metamask/metamask-eth-abis/dist/index.d.ts","../src/errors.ts","../src/utils/simulation-api.ts","../src/utils/simulation.ts","../src/utils/transaction-type.ts","../src/utils/validation.ts","../src/TransactionController.ts","../src/index.ts","../../../node_modules/@babel/types/lib/index.d.ts","../../../node_modules/@types/babel__generator/index.d.ts","../../../node_modules/@babel/parser/typings/babel-parser.d.ts","../../../node_modules/@types/babel__template/index.d.ts","../../../node_modules/@types/babel__traverse/index.d.ts","../../../node_modules/@types/babel__core/index.d.ts","../../../node_modules/@types/eslint/helpers.d.ts","../../../node_modules/@types/estree/index.d.ts","../../../node_modules/@types/json-schema/index.d.ts","../../../node_modules/@types/eslint/index.d.ts","../../../node_modules/@types/graceful-fs/index.d.ts","../../../node_modules/@types/istanbul-lib-coverage/index.d.ts","../../../node_modules/@types/istanbul-lib-report/index.d.ts","../../../node_modules/@types/istanbul-reports/index.d.ts","../../../node_modules/chalk/index.d.ts","../../../node_modules/jest-diff/build/cleanupSemantic.d.ts","../../../node_modules/pretty-format/build/types.d.ts","../../../node_modules/pretty-format/build/index.d.ts","../../../node_modules/jest-diff/build/types.d.ts","../../../node_modules/jest-diff/build/diffLines.d.ts","../../../node_modules/jest-diff/build/printDiffs.d.ts","../../../node_modules/jest-diff/build/index.d.ts","../../../node_modules/jest-matcher-utils/build/index.d.ts","../../../node_modules/@types/jest/index.d.ts","../../../node_modules/@types/jest-when/index.d.ts","../../../node_modules/@types/json5/index.d.ts","../../../node_modules/@types/minimatch/index.d.ts","../../../node_modules/@types/parse-json/index.d.ts","../../../node_modules/@types/pbkdf2/index.d.ts","../../../node_modules/@types/prettier/index.d.ts","../../../node_modules/@types/secp256k1/index.d.ts","../../../node_modules/@types/semver/classes/semver.d.ts","../../../node_modules/@types/semver/functions/parse.d.ts","../../../node_modules/@types/semver/functions/valid.d.ts","../../../node_modules/@types/semver/functions/clean.d.ts","../../../node_modules/@types/semver/functions/inc.d.ts","../../../node_modules/@types/semver/functions/diff.d.ts","../../../node_modules/@types/semver/functions/major.d.ts","../../../node_modules/@types/semver/functions/minor.d.ts","../../../node_modules/@types/semver/functions/patch.d.ts","../../../node_modules/@types/semver/functions/prerelease.d.ts","../../../node_modules/@types/semver/functions/compare.d.ts","../../../node_modules/@types/semver/functions/rcompare.d.ts","../../../node_modules/@types/semver/functions/compare-loose.d.ts","../../../node_modules/@types/semver/functions/compare-build.d.ts","../../../node_modules/@types/semver/functions/sort.d.ts","../../../node_modules/@types/semver/functions/rsort.d.ts","../../../node_modules/@types/semver/functions/gt.d.ts","../../../node_modules/@types/semver/functions/lt.d.ts","../../../node_modules/@types/semver/functions/eq.d.ts","../../../node_modules/@types/semver/functions/neq.d.ts","../../../node_modules/@types/semver/functions/gte.d.ts","../../../node_modules/@types/semver/functions/lte.d.ts","../../../node_modules/@types/semver/functions/cmp.d.ts","../../../node_modules/@types/semver/functions/coerce.d.ts","../../../node_modules/@types/semver/classes/comparator.d.ts","../../../node_modules/@types/semver/classes/range.d.ts","../../../node_modules/@types/semver/functions/satisfies.d.ts","../../../node_modules/@types/semver/ranges/max-satisfying.d.ts","../../../node_modules/@types/semver/ranges/min-satisfying.d.ts","../../../node_modules/@types/semver/ranges/to-comparators.d.ts","../../../node_modules/@types/semver/ranges/min-version.d.ts","../../../node_modules/@types/semver/ranges/valid.d.ts","../../../node_modules/@types/semver/ranges/outside.d.ts","../../../node_modules/@types/semver/ranges/gtr.d.ts","../../../node_modules/@types/semver/ranges/ltr.d.ts","../../../node_modules/@types/semver/ranges/intersects.d.ts","../../../node_modules/@types/semver/ranges/simplify.d.ts","../../../node_modules/@types/semver/ranges/subset.d.ts","../../../node_modules/@types/semver/internals/identifiers.d.ts","../../../node_modules/@types/semver/index.d.ts","../../../node_modules/@types/sinonjs__fake-timers/index.d.ts","../../../node_modules/@types/sinon/index.d.ts","../../../node_modules/@types/stack-utils/index.d.ts","../../../node_modules/@types/yargs-parser/index.d.ts","../../../node_modules/@types/yargs/index.d.ts"],"fileInfos":[{"version":"8730f4bf322026ff5229336391a18bcaa1f94d4f82416c8b2f3954e2ccaae2ba","affectsGlobalScope":true},"dc47c4fa66b9b9890cf076304de2a9c5201e94b740cffdf09f87296d877d71f6","7a387c58583dfca701b6c85e0adaf43fb17d590fb16d5b2dc0a2fbd89f35c467","8a12173c586e95f4433e0c6dc446bc88346be73ffe9ca6eec7aa63c8f3dca7f9","5f4e733ced4e129482ae2186aae29fde948ab7182844c3a5a51dd346182c7b06","4b421cbfb3a38a27c279dec1e9112c3d1da296f77a1a85ddadf7e7a425d45d18","1fc5ab7a764205c68fa10d381b08417795fc73111d6dd16b5b1ed36badb743d9",{"version":"3aafcb693fe5b5c3bd277bd4c3a617b53db474fe498fc5df067c5603b1eebde7","affectsGlobalScope":true},{"version":"adb996790133eb33b33aadb9c09f15c2c575e71fb57a62de8bf74dbf59ec7dfb","affectsGlobalScope":true},{"version":"8cc8c5a3bac513368b0157f3d8b31cfdcfe78b56d3724f30f80ed9715e404af8","affectsGlobalScope":true},{"version":"cdccba9a388c2ee3fd6ad4018c640a471a6c060e96f1232062223063b0a5ac6a","affectsGlobalScope":true},{"version":"c5c05907c02476e4bde6b7e76a79ffcd948aedd14b6a8f56e4674221b0417398","affectsGlobalScope":true},{"version":"5f406584aef28a331c36523df688ca3650288d14f39c5d2e555c95f0d2ff8f6f","affectsGlobalScope":true},{"version":"22f230e544b35349cfb3bd9110b6ef37b41c6d6c43c3314a31bd0d9652fcec72","affectsGlobalScope":true},{"version":"7ea0b55f6b315cf9ac2ad622b0a7813315bb6e97bf4bb3fbf8f8affbca7dc695","affectsGlobalScope":true},{"version":"3013574108c36fd3aaca79764002b3717da09725a36a6fc02eac386593110f93","affectsGlobalScope":true},{"version":"eb26de841c52236d8222f87e9e6a235332e0788af8c87a71e9e210314300410a","affectsGlobalScope":true},{"version":"3be5a1453daa63e031d266bf342f3943603873d890ab8b9ada95e22389389006","affectsGlobalScope":true},{"version":"17bb1fc99591b00515502d264fa55dc8370c45c5298f4a5c2083557dccba5a2a","affectsGlobalScope":true},{"version":"7ce9f0bde3307ca1f944119f6365f2d776d281a393b576a18a2f2893a2d75c98","affectsGlobalScope":true},{"version":"6a6b173e739a6a99629a8594bfb294cc7329bfb7b227f12e1f7c11bc163b8577","affectsGlobalScope":true},{"version":"81cac4cbc92c0c839c70f8ffb94eb61e2d32dc1c3cf6d95844ca099463cf37ea","affectsGlobalScope":true},{"version":"b0124885ef82641903d232172577f2ceb5d3e60aed4da1153bab4221e1f6dd4e","affectsGlobalScope":true},{"version":"0eb85d6c590b0d577919a79e0084fa1744c1beba6fd0d4e951432fa1ede5510a","affectsGlobalScope":true},{"version":"da233fc1c8a377ba9e0bed690a73c290d843c2c3d23a7bd7ec5cd3d7d73ba1e0","affectsGlobalScope":true},{"version":"d154ea5bb7f7f9001ed9153e876b2d5b8f5c2bb9ec02b3ae0d239ec769f1f2ae","affectsGlobalScope":true},{"version":"bb2d3fb05a1d2ffbca947cc7cbc95d23e1d053d6595391bd325deb265a18d36c","affectsGlobalScope":true},{"version":"c80df75850fea5caa2afe43b9949338ce4e2de086f91713e9af1a06f973872b8","affectsGlobalScope":true},{"version":"9d57b2b5d15838ed094aa9ff1299eecef40b190722eb619bac4616657a05f951","affectsGlobalScope":true},{"version":"6c51b5dd26a2c31dbf37f00cfc32b2aa6a92e19c995aefb5b97a3a64f1ac99de","affectsGlobalScope":true},{"version":"6e7997ef61de3132e4d4b2250e75343f487903ddf5370e7ce33cf1b9db9a63ed","affectsGlobalScope":true},{"version":"2ad234885a4240522efccd77de6c7d99eecf9b4de0914adb9a35c0c22433f993","affectsGlobalScope":true},{"version":"5e5e095c4470c8bab227dbbc61374878ecead104c74ab9960d3adcccfee23205","affectsGlobalScope":true},{"version":"09aa50414b80c023553090e2f53827f007a301bc34b0495bfb2c3c08ab9ad1eb","affectsGlobalScope":true},{"version":"d7f680a43f8cd12a6b6122c07c54ba40952b0c8aa140dcfcf32eb9e6cb028596","affectsGlobalScope":true},{"version":"3787b83e297de7c315d55d4a7c546ae28e5f6c0a361b7a1dcec1f1f50a54ef11","affectsGlobalScope":true},{"version":"e7e8e1d368290e9295ef18ca23f405cf40d5456fa9f20db6373a61ca45f75f40","affectsGlobalScope":true},{"version":"faf0221ae0465363c842ce6aa8a0cbda5d9296940a8e26c86e04cc4081eea21e","affectsGlobalScope":true},{"version":"06393d13ea207a1bfe08ec8d7be562549c5e2da8983f2ee074e00002629d1871","affectsGlobalScope":true},{"version":"2768ef564cfc0689a1b76106c421a2909bdff0acbe87da010785adab80efdd5c","affectsGlobalScope":true},{"version":"b248e32ca52e8f5571390a4142558ae4f203ae2f94d5bac38a3084d529ef4e58","affectsGlobalScope":true},{"version":"52d1bb7ab7a3306fd0375c8bff560feed26ed676a5b0457fa8027b563aecb9a4","affectsGlobalScope":true},"70bbfaec021ac4a0c805374225b55d70887f987df8b8dd7711d79464bb7b4385","869089d60b67219f63e6aca810284c89bae1b384b5cbc7ce64e53d82ad223ed5",{"version":"f31113ac9492fdd6e78bf6151b338c92e5b1837be426ef4aa0648ce82d13b518","affectsGlobalScope":true},"62a0875a0397b35a2364f1d401c0ce17975dfa4d47bf6844de858ae04da349f9","ee7491d0318d1fafcba97d5b72b450eb52671570f7a4ecd9e8898d40eaae9472","e3e7d217d89b380c1f34395eadc9289542851b0f0a64007dfe1fb7cf7423d24e","fd79909e93b4d50fd0ed9f3d39ddf8ba0653290bac25c295aac49f6befbd081b","345a9cc2945406f53051cd0e9b51f82e1e53929848eab046fdda91ee8aa7da31","9debe2de883da37a914e5e784a7be54c201b8f1d783822ad6f443ff409a5ea21","dee5d5c5440cda1f3668f11809a5503c30db0476ad117dd450f7ba5a45300e8f","f5e396c1424c391078c866d6f84afe0b4d2f7f85a160b9c756cd63b5b1775d93","5caa6f4fff16066d377d4e254f6c34c16540da3809cd66cd626a303bc33c419f","730d055528bdf12c8524870bb33d237991be9084c57634e56e5d8075f6605e02","5b3cd03ae354ea96eff1f74d7c410fe4852e6382227e8b0ecf87ab5e3a5bbcd4","7394959e5a741b185456e1ef5d64599c36c60a323207450991e7a42e08911419",{"version":"056097110efd16869ec118cedb44ecbac9a019576eee808d61304ca6d5cb2cbe","affectsGlobalScope":true},"f51b4042a3ac86f1f707500a9768f88d0b0c1fc3f3e45a73333283dea720cdc6",{"version":"6fb8358e10ed92a7f515b7d79da3904c955a3ffd4e14aa9df6f0ea113041f1cf","affectsGlobalScope":true},"45c831238c6dac21c72da5f335747736a56a3847192bf03c84b958a7e9ec93e2","661a11d16ad2e3543a77c53bcd4017ee9a450f47ab7def3ab493a86eae4d550c",{"version":"8cdc646cec7819581ef343b83855b1bfe4fe674f2c84f4fb8dc90d82fb56bd3a","affectsGlobalScope":true},"a40826e8476694e90da94aa008283a7de50d1dafd37beada623863f1901cb7fb","9dd56225cc2d8cb8fe5ceb0043ff386987637e12fecc6078896058a99deae284","2375ed4b439215aa3b6d0c6fd175c78a4384b30cb43cbadaecbf0a18954c98cb","7693b90b3075deaccafd5efb467bf9f2b747a3075be888652ef73e64396d8628","41231da15bb5e3e806a8395bd15c7befd2ec90f9f4e3c9d0ae1356bccb76dbb0","fccfef201d057cb407fa515311bd608549bab6c7b8adcf8f2df31f5d3b796478",{"version":"ee1ee365d88c4c6c0c0a5a5701d66ebc27ccd0bcfcfaa482c6e2e7fe7b98edf7","affectsGlobalScope":true},"5f20d20b7607174caf1a6da9141aeb9f2142159ae2410ca30c7a0fccd1d19c99",{"version":"464762c6213566d072f1ced5e8e9a954785ec5e53883b7397198abb5ef5b8f71","affectsGlobalScope":true},"6387920dc3e18927335b086deec75bf8e50f879a5e273d32ee7bb7a55ba50572","9bba37424094688c4663c177a1379b229f919b8912889a472f32fdc5f08ddb4d","29a4be13b3a30d3e66667b75c58ec61fb2df8fa0422534fdee3cfb30c5dbf450","83366d901beda79d6eb37aaaf6ca248dcd88946302b2a7d975590783be51e88e","bf268a0aea37ad4ae3b7a9b58559190b6fc01ea16a31e35cd05817a0a60f895a","43ec77c369473e92e2ecebf0554a0fdaa9c256644a6070f28228dfcceec77351",{"version":"d7dad6db394a3d9f7b49755e4b610fbf8ed6eb0c9810ae5f1a119f6b5d76de45","affectsGlobalScope":true},"95ed02bacb4502c985b69742ec82a4576d4ff4a6620ecc91593f611d502ae546","bf755525c4e6f85a970b98c4755d98e8aa1b6dbd83a5d8fcc57d3d497351b936","dd67d2b5e4e8a182a38de8e69fb736945eaa4588e0909c14e01a14bd3cc1fd1e",{"version":"28084e15b63e6211769db2fe646d8bc5c4c6776321e0deffe2d12eefd52cb6b9","affectsGlobalScope":true},{"version":"aed37dabf86c99d6c8508700576ecede86688397bc12523541858705a0c737c2","affectsGlobalScope":true},"cc6ef5733d4ea6d2e06310a32dffd2c16418b467c5033d49cecc4f3a25de7497","94768454c3348b6ebe48e45fbad8c92e2bb7af4a35243edbe2b90823d0bd7f9a","0be79b3ff0f16b6c2f9bc8c4cc7097ea417d8d67f8267f7e1eec8e32b548c2ff","1c61ffa3a71b77363b30d19832c269ef62fba787f5610cac7254728d3b69ab2e","84da3c28344e621fd1d591f2c09e9595292d2b70018da28a553268ac122597d4","269929a24b2816343a178008ac9ae9248304d92a8ba8e233055e0ed6dbe6ef71","6e191fea1db6e9e4fa828259cf489e820ec9170effff57fb081a2f3295db4722","aed943465fbce1efe49ee16b5ea409050f15cd8eaf116f6fadb64ef0772e7d95","70d08483a67bf7050dbedace398ef3fee9f436fcd60517c97c4c1e22e3c6f3e8","c40fdf7b2e18df49ce0568e37f0292c12807a0748be79e272745e7216bed2606",{"version":"e933de8143e1d12dd51d89b398760fd5a9081896be366dad88a922d0b29f3c69","affectsGlobalScope":true},"4e228e78c1e9b0a75c70588d59288f63a6258e8b1fe4a67b0c53fe03461421d9","b38d55d08708c2410a3039687db70b4a5bfa69fc4845617c313b5a10d9c5c637","205d50c24359ead003dc537b9b65d2a64208dfdffe368f403cf9e0357831db9e","1265fddcd0c68be9d2a3b29805d0280484c961264dd95e0b675f7bd91f777e78",{"version":"a05e2d784c9be7051c4ac87a407c66d2106e23490c18c038bbd0712bde7602fd","affectsGlobalScope":true},{"version":"df90b9d0e9980762da8daf8adf6ffa0c853e76bfd269c377be0d07a9ad87acd2","affectsGlobalScope":true},"cf434b5c04792f62d6f4bdd5e2c8673f36e638e910333c172614d5def9b17f98","1d65d4798df9c2df008884035c41d3e67731f29db5ecb64cd7378797c7c53a2f","0faee6b555890a1cb106e2adc5d3ffd89545b1da894d474e9d436596d654998f","c6c01ea1c42508edf11a36d13b70f6e35774f74355ba5d358354d4a77cc67ea1","867f95abf1df444aab146b19847391fc2f922a55f6a970a27ed8226766cee29f",{"version":"ab9b9a36e5284fd8d3bf2f7d5fcbc60052f25f27e4d20954782099282c60d23e","affectsGlobalScope":true},"b0297b09e607bec9698cac7cf55463d6731406efb1161ee4d448293b47397c84","175323e2a79a6076e0bada8a390d535a3ea817158bf1b1f46e31efca9028a0a2","7a10053aadc19335532a4d02756db4865974fd69bea5439ddcc5bfdf062d9476","4967529644e391115ca5592184d4b63980569adf60ee685f968fd59ab1557188","aed9e712a9b168345362e8f3a949f16c99ca1e05d21328f05735dfdbb24414ef","b04fe6922ed3db93afdbd49cdda8576aa75f744592fceea96fb0d5f32158c4f5","ed8d6c8de90fc2a4faaebc28e91f2469928738efd5208fb75ade0fa607e892b7","d7c52b198d680fe65b1a8d1b001f0173ffa2536ca2e7082431d726ce1f6714cd","c07f251e1c4e415a838e5498380b55cfea94f3513229de292d2aa85ae52fc3e9","0ed401424892d6bf294a5374efe512d6951b54a71e5dd0290c55b6d0d915f6f7","b945be6da6a3616ef3a250bfe223362b1c7c6872e775b0c4d82a1bf7a28ff902","beea49237dd7c7110fabf3c7509919c9cb9da841d847c53cac162dc3479e2f87","0f45f8a529c450d8f394106cc622bff79e44a1716e1ac9c3cc68b43f7ecf65ee","c624ce90b04c27ce4f318ba6330d39bde3d4e306f0f497ce78d4bda5ab8e22ca","9b8253aa5cb2c82d505f72afdbf96e83b15cc6b9a6f4fadbbbab46210d5f1977","86a8f52e4b1ac49155e889376bcfa8528a634c90c27fec65aa0e949f77b740c5","aab5dd41c1e2316cc0b42a7dd15684f8582d5a1d16c0516276a2a8a7d0fecd9c","59948226626ee210045296ba1fc6cb0fe748d1ff613204e08e7157ab6862dee7","ec3e54d8b713c170fdc8110a7e4a6a97513a7ab6b05ac9e1100cb064d2bb7349","43beb30ecb39a603fde4376554887310b0699f25f7f39c5c91e3147b51bb3a26","666b77d7f06f49da114b090a399abbfa66d5b6c01a3fd9dc4f063a52ace28507","31997714a93fbc570f52d47d6a8ebfb021a34a68ea9ba58bbb69cdec9565657e","6032e4262822160128e644de3fc4410bcd7517c2f137525fd2623d2bb23cb0d3","8bd5c9b1016629c144fd228983395b9dbf0676a576716bc3d316cab612c33cd5","2ed90bd3925b23aed8f859ffd0e885250be0424ca2b57e9866dabef152e1d6b7","93f6bd17d92dab9db7897e1430a5aeaa03bcf51623156213d8397710367a76ce","3f62b770a42e8c47c7008726f95aa383e69d97e85e680d237b99fcb0ee601dd8","5b84cfe78028c35c3bb89c042f18bf08d09da11e82d275c378ae4d07d8477e6c","75b22c74010ba649de1a1676a4c4b8b5bb4294fecd05089e2094429b16d7840c","5615ccf831db2ffc82145243081ebdb60ea8e1005ee8f975d1c0c1401a9c894e","38682ed3630bb6ecdace80d5a9adc811fc20a419f1940446e306c3a020d083b9","cc182e6e4f691cd6f7bf7cb491247a4c7818f9f1cb2db1d45c65ff906e3f741b","a50599c08934a62f11657bdbe0dc929ab66da1b1f09974408fd9a33ec1bb8060","5a20e7d6c630b91be15e9b837853173829d00273197481dc8d3e94df61105a71","8d478048d71cc16f806d4b71b252ecb67c7444ccf4f4b09b29a312712184f859","e0eda929c6b9b628cdeb0e54cd3582cb97e64f28aab34612fc1431c545899584","9df4662ca3dbc2522bc115833ee04faa1afbb4e249a85ef4a0a09c621346bd08","b25d9065cf1c1f537a140bbc508e953ed2262f77134574c432d206ff36f4bdbf","1b103313097041aa9cd705a682c652f08613cb5cf8663321061c0902f845e81c","68ccec8662818911d8a12b8ed028bc5729fb4f1d34793c4701265ba60bc73cf4","5f85b8b79dc4d36af672c035b2beb71545de63a5d60bccbeee64c260941672ab","b3d48529ae61dc27d0bfbfa2cb3e0dff8189644bd155bdf5df1e8e14669f7043","40fe4b689225816b31fe5794c0fbf3534568819709e40295ead998a2bc1ab237","f65b5e33b9ad545a1eebbd6afe857314725ad42aaf069913e33f928ab3e4990a","fb6f2a87beb7fb1f4c2b762d0c76a9459fc91f557231569b0ee21399e22aa13d","31c858dc85996fac4b7fa944e1016d5c72f514930a72357ab5001097bf6511c7","3de30a871b3340be8b679c52aa12f90dd1c8c60874517be58968fdbcc4d79445","6fd985bd31eaf77542625306fb0404d32bff978990f0a06428e5f0b9a3b58109","980d21b0081cbf81774083b1e3a46f4bbdcd2b68858df0f66d7fad9c82bc34bc","68cc8d6fcc2f270d7108f02f3ebc59480a54615be3e09a47e14527f349e9d53e","3eb11dbf3489064a47a2e1cf9d261b1f100ef0b3b50ffca6c44dd99d6dd81ac1","b17f3bb7d8333479c7e45e5f3d876761b9bca58f97594eca3f6a944fd825e632","3c1f1236cce6d6e0c4e2c1b4371e6f72d7c14842ecd76a98ed0748ee5730c8f3","6d7f58d5ea72d7834946fd7104a734dc7d40661be8b2e1eaced1ddce3268ebaf","4c26222991e6c97d5a8f541d4f2c67585eda9e8b33cf9f52931b098045236e88","277983d414aa99d78655186c3ee1e1c38c302e336aff1d77b47fcdc39d8273fe","47383b45796d525a4039cd22d2840ac55a1ff03a43d027f7f867ba7314a9cf53","6548773b3abbc18de29176c2141f766d4e437e40596ee480447abf83575445ad","6ddd27af0436ce59dd4c1896e2bfdb2bdb2529847d078b83ce67a144dff05491","816264799aef3fd5a09a3b6c25217d5ec26a9dfc7465eac7d6073bcdc7d88f3f","4df0891b133884cd9ed752d31c7d0ec0a09234e9ed5394abffd3c660761598db","b603b62d3dcd31ef757dc7339b4fa8acdbca318b0fb9ac485f9a1351955615f9","e642bd47b75ad6b53cbf0dfd7ddfa0f120bd10193f0c58ec37d87b59bf604aca","be90b24d2ee6f875ce3aaa482e7c41a54278856b03d04212681c4032df62baf9","78f5ff400b3cb37e7b90eef1ff311253ed31c8cb66505e9828fad099bffde021","372c47090e1131305d163469a895ff2938f33fa73aad988df31cd31743f9efb6","71c67dc6987bdbd5599353f90009ff825dd7db0450ef9a0aee5bb0c574d18512","6f12403b5eca6ae7ca8e3efe3eeb9c683b06ce3e3844ccfd04098d83cd7e4957","282c535df88175d64d9df4550d2fd1176fd940c1c6822f1e7584003237f179d3","c3a4752cf103e4c6034d5bd449c8f9d5e7b352d22a5f8f9a41a8efb11646f9c2","11a9e38611ac3c77c74240c58b6bd64a0032128b29354e999650f1de1e034b1c","4ed103ca6fff9cb244f7c4b86d1eb28ce8069c32db720784329946731badb5bb","d738f282842970e058672663311c6875482ee36607c88b98ffb6604fba99cb2a","ec859cd8226aa623e41bbb47c249a55ee16dc1b8647359585244d57d3a5ed0c7","8891c6e959d253a66434ff5dc9ae46058fb3493e84b4ca39f710ef2d350656b1","c4463cf02535444dcbc3e67ecd29f1972490f74e49957d6fd4282a1013796ba6","0cb0a957ff02de0b25fd0f3f37130ca7f22d1e0dea256569c714c1f73c6791f8","2f5075dc512d51786b1ba3b1696565641dfaae3ac854f5f13d61fa12ef81a47e","ca3353cc82b1981f0d25d71d7432d583a6ef882ccdea82d65fbe49af37be51cb","50679a8e27aacf72f8c40bcab15d7ef5e83494089b4726b83eec4554344d5cdc","45351e0d51780b6f4088277a4457b9879506ee2720a887de232df0f1efcb33d8","e2d6963e7bf7186e30b7a4c9859aba4e96eda6d1be537e5b1a43bdddc7e9dc8f","10afdd7bba6ec9b7f95a4b419b2dbb64245fea4a61bbe7d68e2f841b414f7312","413121b26b3bd9f7fea237f15f564ee2b95bcd0cceec1b1621075375ccc0c0e0","d2af215963d01cef397ce8fa2f7ad08ee8beffdd39fe14b96021ddf26554b59f","2fc9848431d0f5e2b49bb312aaf07dd2d5a34300a2ced60200a2da49e6a82b43","c5ee2b685431ea2b9aacd9bb9e15cac1ecfa5c448972b188432313354d47c848","3e69be1137d88eb0730332aed359caedea4a27903da15dbe6a1615fa11206807","2283d079c3945b6e5ca8b9355311a213e03b74bffc65a3234c3c141a0a795700","f47272f05bd57f4356abc81232bded724d13e54f0fd801e0fb93a58237db1829","07ae8e9890f49ef6ebe629e339ac590025606a1e96754965bbb2bf889199ced2","d5c19655468e29f60c871b21e73af8ebc653f736e7123ade916f22c4a5f80ce5","6a8649609161e2794b383ba275b0a6cb4a072dde7b954648f83dc6cdf1bfe4a8","601d4a40a69c782addaf84185d4547568ec072095ab9976610d89922d1291f8b","d5c19655468e29f60c871b21e73af8ebc653f736e7123ade916f22c4a5f80ce5","b5c9c8c4a2cd1cb9f76d849fb472d475c3cebdd48306414a4a19bd11d82c4055","b61e6a808f5f50873ac03f35d5a38fa8d4dd23a24f80ab69df1a032e8c71562d","c8be9283a381044a392a0687af5d98d3f51cbada2320b1801a82c948b6e39499","85052c71d72b9b017c88179f57a464d66e22619c7acd7d83b117a79cf1608979","502cd7c30fe21e2c36b62913d0cb5d20affc8779b3ad40881b26d90a22de3aaa","6d3101b183ea67ef606b93fe42127f30b2db5ac3b72c34ca9d6d8b00eb85d0f6","f5d7a36ff056cc314b0f61c89a03c4c36a24183b246e61d958e75e86521304cd","ff30e8237e23dde68041b5f09526ee86835b12c5d8b6421c1153093fdbeb9438","f516fc1e77e5ffd71fbe705b679797c3c5eb76bf76a88549e6316a29f3e197f7","b5b1110565ac688b660a893654a6c1bce6747f3aa6f847001a8a5ff4412394ba","3a971ea3e36685b96f24fbd53a94ad8dc061711b84e51fde4cf201f7041e618d","9b6c162d20e2ad4abdcff61a24082564ac59e63092220618162aef6e440c9228","7804ff981554ba1a0592481072806fc39dc1484791beda43eb7a60e16e70a360","fcc8beef29f39f09b1d9c9f99c42f9fed605ab1c28d2a630185f732b9ba53763","d6e6620a30d582182acc3f0a992a0c311adc589f111096aea11ab83fc09a5ccc","6213b8f686f56beab22b59a0f468590fd3a4c5fa931236a017efeca91d7c9584","c451cec9a588b1f105a5ea2c6063d4fca112b9d70105cacdadda0e1ef67e9379","cb047832dc68f5a2c41c62c5e95ddcacbae3a8b034d40cd15319a8cb7f25104a","980336ccdfc3c08f3c3b201aa6662e6016e20f15847f8465b68f3e8e67b4665c","5a3493939995f46ff3d9073cd534fb8961c3bf4e08c71db27066ff03d906dea8","8f333214062532989f190aed5f99c62eb820722e41956e8229e17cd246fbdd10","d1f010c19eb9c8190bd0859fa3b6f4975543b912b8b85e20bbb0b5bfbdf4d2b3","de4ccc96cef3f97fab148640799abb32a24b567a902a8233913f98481e3131bf",{"version":"801934aa449fe6df584bccdcc5d5b9280295cb7ac84918b6014fc5086e6f9ff6","affectsGlobalScope":true},"5e379df3d61561c2ed7789b5995b9ba2143bbba21a905e2381e16efe7d1fa424","f07a137bbe2de7a122c37bfea00e761975fb264c49f18003d398d71b3fb35a5f","6af760fb9ea02dc807c5053d8aee86389c4fce72fbb26af7b9568cac6c4710d5","c62c4ba5e910b4523f7e7adf4a55ec45c2bac99d9d8e9b0fe0c2a800a6f641b9","92131434f876fdd6fcbc40bd54a9d7500c66974362b16bd42641f990468587f4","8cf023c0bd57992fdd2ce6a7030a1874f49c8edc62eaffa9bfffcf18d2a2a1a2","8ea8f3040e38fb50d7dc3653f3b8a0dbb5244e82111576f99ce096bdc0fbf94c","48ed788ad126545a6156fcc37cd3bcf17de18a3e3fe6b6ef62cfb8140d1a45a2","63c271a745f628ffd4bd7ad0a63b021c362c9bd6bf8b18441a7162892395a214","8d3457e6c7c5cb890729fb60cb8db18f261226a3ea3ff6a4db4b84ea78313ace","9f9e5bae412fa5909fae636d6733aee27a108cc2ed5b13980611016336774d3c","662fe197bba64bd3f17ee118058cd2d0d2dbe33d7c0c865fd6365d90bfc44e1e","030519c351f800551cac2658038804969ca4584d2c0175a710602ac234ca1340","0278a6939ca83cd040b08ff8c5fc7838b6693ddc52f22526bf158e6b10e0246c","c2d6206e5ba4fd3063b01218c2b3b997afc1cfbeb49fcee991fa8595842ce53d","6a8096993458a3d71229031aa7415974eb5b47b320213e29660adfb519d6a3f4","cb7996a1af5b1d276483cd0c9b9de6540eff021abc90a720511ff4464519a2ff","9df6ec68878d65bc690ea3a33ce3ef5aa8254c36bc5f8346c0c2fd1f3b88a35c","a4fad04c4acc8a4b195cbbccef4c55019104753d547d5c94441643ccc89108a0","0244c23ea642361f7c192c1f0cfff9c12cfa5f51f9b155edd5c0a89fef308d34","c7298e68632ab03155f6de963d3d09cc4a5874c28a81524f56c667d8a052e538","3c69a83bde847af6fc3a53e1bb6b13cd06d38a27a142814b8dacc374f3b93284","5b46f7113f54565e7ffc83f2b474f557a1f54c7e5946769d5be220454656be73","fb58035d39c5759283cb73cfb3548aefe370aa3ad4e81fdb4e46f0979eb7669f","1311c325948b2d5576cebc70b1bf968d3446b4630802bef54120daf04ce1f625","d0b3609e8e7afed0fd0570152255458407e67249b94f6603afdfd68599423f21","17f4c5a1d6eaa87ea27eadcdff9085af3190533d98f799dda79a3af6f9a630ea","3e6f734ddf40e2e99ff7fff9568b7d9720663af9a0632c26a352c8d3270a3f0e","ec13f78303abcf550c5569dfae1446b8ceb89050f68ce04491481e72e8122ae2","a3fc57dbaa7f1efb010399ad4ef4fd9b462aa4e93bf74a9a34b099b97ffcc9cb","ffddd7ec6a450b0cb6f2f73f80de1df963ead312d7c81a8440268f34146ecb87","5d6a36ca0087fd6876df654d1b4192f0e402adfde994ad47e5c065da33692f9c","eb157a09c5f543d98644e2a99a785f9e0e91f054f9fecbf1c3e15831ff5d63a7","edd5530e2b1ccdf65093296e40a8634fcb11ecda3c164c31383a8c34cb04bc9d","9dfaf96d090fe8d96143465d85b4837661ae535143eea9ef99cd20df2e66338e","209d45c27e03c1417c42985252de6c25a2ec23abdc199d88e6139c88b93abd11","0ee5cdba58cfde3012bb9ff2e9edcc4e35a651373a2aa2c83ff9eb7df635419a","540f4dca27ea5a232828b6d91e1b2fce2720bdabaa4c1f3fbf59b672cc58bd8a","ba086b99d545ec6c9ff356989f076b5652ea1b09bcc65b87dfc43a5195a2efcc","c85d9776b36166b928ab1488d9224ebf970d41b0a35f09a3ee0b9bee3e698061","683196f606c5dab1c8c4a24a66d26e00f16f2d4b2a5abe25ebedd37d2954f930","9c3a1b01cba1238fb723ce06b6c163ef6c53be755394406782564d5c42c636b2","6e795e6270d39e918c7a0e62ac73793cda06fcf4b3692ee46583e15f5bf57ab8","0e821ef1eb67fa6144ea4de4277d913f5b1982d7407afd5f93754a8239d41554","5c09195ef359ffa9c6bbdb4fefb101d87ede4b9e9c28213faf5b45d102e4c609","80b4d93a4dcc90a12f6f4bb7c6851a8182ae29e556716d0d80b5c012a5ef554a","2556ef9d1820e0b6bbca6dd65a50ea64f525c4d8247ab50dff44c3f0d14a5643","cbd1c836db190d6e3add07165afc228f04e1f6170e1fe3aa5e6fc24a7e9573a3","9b13881feb958237232586d888a10a39d47cdffe3ee34688ed41888fa7baad94","122fe82cf5af80f0b26832b258b537b7dfe3ec28449c301b259ab10204b50d45","c467dada8fea6d60dff8a8be2675f737cacc76e14e50b72daa0f0710376df84b","9cb80bba611c2dd155a446ce424fe4bb1df2129751bc9416b7e42c055d1ddbff","6ee568039016b81ed70292a595ab781ab978cba4243a5fe49507040ee4f7ac8a","043783bebe87efb440183c9ebc8c4fdc1bb92060a5a0f7ce847e30dee7013ac3","e3dc0a97a59dea936b4fb7b1f6f4117b4aac9c86d0cd08b69bab2d0532a8a5e3","5d897601f8a4fe913057019d8211b99b06e3138f625a0cfb601d074f4278271d","a68bb369c4ba8ab43a78f3fad2d3ec130e1418bc946521b9c84e9b336d6e88f1","65f219e6e1f9d27c677a49d41ae7989b83bf6baa56debbeb50d95c3ab21632e2","cfde5d194dd858ad68f910defaed5b0d28730f8bf38359a9265a93ab29bc7bef","c89354ae268153d965011e484150f0c92faa87f3f66507c25b496973178e0400","f20aae41b169cddcbf3fde8ac380443182c8d7225194e788c404d9e11e6dc75d","a6f4816a634bb1ceb513634c1ef7c0535f461ed2565336eed69f6ac2babbe15b","c48566cb13403fca44192b4528e3f2ac993869d39526bd42cd2f2167c0285add","efae20e0c581240c7522e04829da4f0453ca263068596554d4b0e27878c7dfac","3af68ef927788cda7daab34be513fa4508229fdc6e5130d564a0a1ccb3fefafe","bbbd2cbb15a37d5f4dd54ad8c7c537d3df8352117523030fcec7dcbe62a05a58","b50d24ebc117f8805332e7e260e9587f572bb7b2ff0ca1ff6cfafb38015781f3","5cc8b8e18fe7fefab4b3c53a39467b5a0deb4200abae7f063ff0624b9e856c51","8e990781eb0107c25429b1274a31a4f3866a9a46290cce40f354b2a6e71c6c21","608c45069e89c4c8f0ab29f896cc93c6553808072d6304b23611b6c6de3c24bb","22cbabe752781b5f35482af9d1fcf1455cb1ece74e8b84700d4abcb44abe3776","b9ce4613536386a98897f1e3d8f61a851ce6cb34dc3c9db4f2ef5f55f007e9e1","a5d1209c7bf277af86281392d46e12ce3dd6052586053f757fb2e606cc75c0f3","31b5f53e3d57470830e87f9e03c02d4569ac81d4a758fdda75092f9a3f58beba","d765fbab22fd7003a65ed670100362ec1c90d55a772e6773a774135594e7ea41","c1f11d9b42bfb0823d34d93c58df91ffb6690b5a717b7d310d83f258f1784e58","775b207f00d4df5b3b0b536aa696d572cdd2cabe8ea18dd28e8b52f691fa2a55","f75cd30f162c2af5e5aca39c01c1a521bfa034fae523793de872815a3468bc08","0cf1123db73dabd86466a462375a6addae52f58d23030c6033f8aadc23539a36","e29cef4158591ed213b1c2cba8988237b1ff369f7a6ecd8cb8ac0302bad1fba8","5307876e4d0021ea01235eb2f7c24671f3d8b37590f4b446cd132a4e1dc9a335","92550acd737790dc60c4c130e6aac78656dd48a8334a4882f40e7f86bdf7a590","3df821880914f8bb3c8107b1107be75c8ddbe2120a2cefabbaf9b65936b5f4dd","f46ba7c6fa7fcc8b3d57c4618c18db3f4d8bfe1fcab5551d7f6d9a82cf4d6078","078b7043bea0968860374bf4671ed74dd9f6be4e28ab659517d81f74be463c51","68b139ebb9a7f3ee4ded6286d74f978a47968727665120f3bfc560476ce33c4d","56d02c29b2fd39b1b1a1265df291f3f98e6ec3e6119aff9f4cfa44fe888efaa7","2d01884891da6495cb4a2f060e4898209a507e711464c4c1480df85264e863ed","c485c6497f7587314c4c4a59b74850cbca4c0c4bc08146a918cfd237ef821dbb","e9eec004735b1bf7015edf5400aeb914a53132134d230e93786590d904d094cc","080b1aa93227952b4dd74b9d2c6e4f6002eb8403533749116a1c53bb9961c02d","874087eec1d457f6e3baf5ac46c42ea200e55040b394fac667aa3a64c49f5f6c","6e8a5b04a18abb192abc89d7219b9c6f633cb3136777ec808673a65f111ca749","4e7ac7e5dd58a6c29c724728b031669e3068b194b62c2b83f92e76a36cb34dbb","d74d2a92b54f95e47d2b76bd5ee516aab7ae93afb79cd34c6681dd29eb09e72a","747e6326a724bc54f799a466a5b5c4978a601a04a063a5bdabe150af2f25b9e2","b57e22e53b56cca7a57bfcfb234aa6a66f9b9e4c07159d7388f94f17a3eaee2c","e47709ec4d1618ef429648cd8ef967aef2005526b34fcbfac33037add347dc71","b81abb3e47fbbb3af41fa75bada89bbcfa4b0feed9a0d6d4b19ed1ce1033b53c","15b330546e9784461058e5fd6e2346bf272140fa6f0cda34e193ae501d8b17b1","4d8ce72fd080bf9a46bdcc274bcbacccedd66d84e203966b197ac25a96932183","73327e6ae34e3f6591877fb75b451cf620cbbd76ee2b678213a9f793633cd0d3","3f1ba2f69944fa346789db7f60d53c9bec00032de0d797967978dea42e77b941","3f5df31539fee4816b97d4e45b4344fbdaf3ca59f6df941f8d780ee441e92cc1","50aaf44eb4d0e086af13729b3471a0a7dce95ea35ebd21c762ba26e203134b2e","3857c1773b8503c3ca45b7bc09ac89c3930c85ce93021054503f73d5d9101b5c","72702bd07fd6fb3ef64aadbcb909103aadfe71ee76e9fdeb11e0c92693cff6cb","f0dd6f7c9783637655478db7d7caf6becd41a79d54482aa59578ce88ab38e9bf",{"version":"cd756ccdabf433dd02b84d755383e489f14b3c1aede0477783aa04830fd5d695","affectsGlobalScope":true},"a4c88dbecdf8ee0c79f5b7c2bf31cd77e593f5d78384e2b674f67d754a549a9e","9cbdff04326da794ba008c0fc977ab062d1fe3fa2e9759654c72ffbe54b64a7c","aa60f8d20d36116fe05edaab24adee3c275209f71b65e272692cf99daf9489e1","150855f967a6490161d5aeed4cc4adf31fcb8f5dbe54b75799c12b8687fc9cc2","79576487ac18e047e8192fc582ff488ce375fe4df0cb028a17f831cf42b976f2","47ddb601df40bfa01cebdd06ee8b87d0b72aa1259a4ceba3ad3b5cf68130112a","6b6392704ddb3f50e647dbbb716782bdd0cf8ea9cc134aae256a26223e632b47","afc3ad2a50f7f4de908e26fcf467e09ab8528c0e90f91e602b4865d953839228","df90b0c6b1d81851364c4d97fa23b91a993482bcf4a7bed7c7a24aa41632d494","db34610570eed46b8b72bc662a91261200b8578af0ac02781ce7d9aca99bc683","11ee9ab699b4619d217c640d917ca198f58066a86bd58c2917197d62aa6601e0","cf9d589d9e73bf32c8e7a6cae6b4a1cf9bef39e5594072533fdce985581a6ddc","959544feb1ca2df29eec6c500f27ea10f4885df245ebd8418fb4b87914614383","6548ab4b57eb9d092471a04513091673345f2fd95d5b876f600402ea8d603ee0","2793e8c6a023d26f78d6777a6d7f20fae3a9a8169863d46d8d54c73071851232","d0f11e830aa1350a31d9c00a0197243e9711e4882947aef53a96c629f405cb10","6610b9f45f1f71d2b1fb67df49cbcabe3f9e668a1ccb7d8328a51407b259ffb3","abbcc437e0792ab2fe08797ceca1ec85a95ec413c51612313b18ab8e75f690f6","e29d76ef1183ac0edf94b4712b6e51730c447c7e773e75ceb44a720b0c9a9fd9","4ee6dc3424998eede9a2a9b114acaaf7969cdda67baf82ba2c9cf88a8eec0ab1","8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","25139d6a726e0e19d9fc4fa3197367b4a82ec34a08a5ecf23963e142c202c0f3","e3328bffc8eab74665a4fe9c59d6f12f4c8570c3d858497e241eb37efe17dfcf","29389551e426a46421134b55182d6fcf5b143670998bf81db2619c1228235392","c18f7e16982695bdd04e3e183a327d116185f77f1a37b9b2e849d7d93269cd74","2cfb37011989c21dc70b91d521a2d5a4e0f18507f5f536b5dfe504edb15916e8","bb5e02df7aaec7a4ea642639a9963b24b8d9fd6798351f07d8c58616942fbcbf","299a899cb4d061f5d83843ec453e936e9659b2c435746823f90c40eddaef4745","d5610c0fd12870f644b0f42c1bcc4fa2295ac3e3ca01916bdb42c3bdc4c80c36","2c56a8e249b1f45dbdf973100cd37fe2ea68709573cf1fdf2e3052c593be68d8","3553da417ee7b07e388b13bd12a70a1c03e65a6132ba5427fe68f5b362373e6f","612358502042d351c227ba779fdcf6d875d827e424930e60297c533524e50668","d2b5be376ef162aa0c24a826e7dd2d77671a045c085e16d1c1276db4bdccbac7","c4138d8dcccedaff6621e009cf0a54a7bed2a5ad4c509a3513bccc4f417ef939","ad8747fe978dff3e80f4b12b48d37cc8dff11b61d04c035aefbc982ce21201ce","b154f789fd65298e1ba6cbba6944ea892d564c95f3d3700ed85baf8f80748473","c660265aedd7c5b236e2017e53095cb98da66200eb0e8d023b5bf713c36494e8","0efc36bf5c0daca6217fec7063359ccdab8c3a23bb405d25340fae22cf72d74f","5abff0c87d4f9c89715107042d4c73b68ef7a128759f451c8a0fc450cbaaf660","5a03308fbd1af441065149a84c692931bebc7e7735afc23be8684f4e10d3aa06","c787bf4f8f0abbf815cfbd348be41046f2b8f270be24fe7aa8a8fcdd2b7df8c2","e7a5191c663a3228f30104961d548b372e51c5936c01ffc8eddd262bb98d7d7c","43fdc9abe6f8640fda4cdc55a1ee5f666d3fce554277043df925c383137ddf69","f0b09665c9d52de465687fbd3cfb65111d3ffc59ae00c6f42654150f3db05518","72f8c078d06cff690e24ff2b0e118a9de2833dcebf7c53e762dcb505ddf36a68","9705efb0fd901180de84ca4dd11d86f87fd73f99d6a5660a664c048a7487e385","f9b9d0950fdfb90f57e3f045fe73dce7fa6e7921b37622fc12e64fcd90afbd0f","e61b36e7fde608f8bb4b9c973d81556553a715eaef42a181a16ddd7a28da4ac7","03b8389b222af729eae0fb3c33366dcbb1f5a0102ce319bf1d7d5ee987e59fd0","2bf6be7c04db280fdd9b786764f8650c23f9f4d533791cb25a11b25314b76a55","dbb5fc7edd36bfba95cc4dd564e4458276ced30eed18bc05fbda948b3fda8686","c2b556c7cff0dabce2e31cb373ac61c14d8ebc35f1086dff30b39e9ec5357d0d","f958af01131076e8af55d28c4835a51063226ab488ca8738fdee38aeef7d0d33","9f3797b01e3d83d4e4b875699ae984f380ca86aa0a0c9df43ac5bba1cb1f8b7b","752b15ad1b34887adeaa838fc55f5d4ca399026afd266d4ed4db0e3db02eae4e","778331eaea1093451e50be9844bd2b6937c3bb81b0b1ee700624c9774ecfcf2b","0ca0dfc9f657d0822eca9530c7870b22a1d2a5fc48182bdd4d0e6e88e4ad9c35","5c746f034288e6842dd1589b169dcfcc16c5ce5abbd928889ab67aea4fe0b501","92ce6dbbcc135cffd09a58e19fef34bf351391bec92c40d849e4e9997d475769","99e77d092fed72b6b8578d00c7af004f76e98b30ba99f1947535eb4c04a51676","b5ef52a9f9083724decc5d060f0b34e3a480deed71c32d55ca16c214eb4cc928","5d3d7938b2d7d0a9f851276741327c2ae4c013e7eb420fc3f7caed3b79c8f37f","14df6b81e50a035e9e391558cf30a0420d03e2eb42c7db9c57f44b818e5d5179","f100912a3785eed4a3d29c12f5910b101af9353454de5ddba9b4d43456c56dd1","446439eacf81a163fd7dfc53b28f80deca3d13b250d67639739aa25aa4491090","98034cd285344125f7165a3bb68246d38ab35fabe7f6d6a7c8f80407d31f548d","06b4a23064991251512df4edc12341d5bc69a17b942da18372312d383c90eee7","0f898802705f9a534b537f1be6c57265080e0abd6993d935554c255e6d56cc1a","745efa7b6e27b7216cccede166a822b56acc41b10a8090966c8cf2c96239cb83","6ab2a6257ae7bb05559841100c786c845fe465a90be7b904db9096c2fb14696b","26958d6f77e6db2425ca65df0fbfaba439396ef7f4457f5643fc32e4b62568a6","5d697a4b315cc5bb3042ae869abffd10c3b0d7b182cda0e4c45d8819937e5796","89b040dec8fcfc1de98827e1f4d4977e6ff5d3302c6790e9f10b54b916e1c742","6ee58aa536dabb19b09bc036f1abe83feb51e13d63b23d30b2d0631a2de99b8f","8aceb205dcc6f814ad99635baf1e40b6e01d06d3fe27b72fd766c6d0b8c0c600","299567f84bfedd1468dca2755a829cb19e607a6811673788807dc8921e211bc9","795d9fb85aad92221504db74dd179b506bd189bba0c104426f7e7bb8a66ffee5","1311bc194e0a69fe61031e852c1c0b439e2a2a3d1d5e2d8ff795499b9f283459","4b7ce19369d7e7fae76720c2c6c7f671bf3fa0f7093edb864f1ac358ca7c456c","c972ef44deca1fa8fab465915ffa00f82e126aacf3dfc8979c03b1b066ce5bb6","30285a1011c6d6b52f3ba3abb0a984be8148c05cdefb8eb6eb562335a3991f35","e0de9f50e80fed1cc161b50e8e68dc056e38df75a4ef667a06b1922e372de169","6a8b31be08b212d1fc96de0ddd1ea49f32382ba712fea24c70bb56447f643f82","19ac6d624e4c18de4584db4bbdbc55387dbe3d19b3c134e50346bdf165658a17","54e3798c2801e8f3bc7a825d3d26c6a80ce763e19e6cb0b714594c430ef72332","70b8333214aadaccda8d38435911d3e3a686e503837dfda6b8c3f8c83e05729b","fe849e916564b8172f31a547395516668f3c122bfe017f82e7140d8dac402208","d42c6e985bdb10a2aaa3dae14d9b0d8589e74a7c2f9475bf543b855bb3c010ba","56c48fb5bb6316dfc27fbad065966b4ddbc38e9a0a1a5060d19b5da405ae7d6e","7091568b9a1b74b699ad09df6c130db712ed089d173a235e301a7a7ee0a4ca44","de33aa2a38affd9e71297ef7ec001a4525502878b09744308fb6518159f77d2d","57476e482c9b4e152bd23d0dc3c29375e48afee0de775674a3c1ea63cb4cf843","3ec4ecf6502ebdb1f3e24c712eb70160c606214ba2e71b4304b5a50fc2e4f293","83f7b6c1dc91deece32c3bee746a43f3616b7cc9f6510764bd53451f6712ff25","c23f2e8772304163fa7e4335be11f3dbdfd720d2209057566b7dfef746ef1862","2a26cb78d3de9708cd656787a663902270c9421ef89188286c3b7ec89b63bb15","e61fda2800677c210116c397dd85079a0956c87fd714826c08b25b10fdd56546","ef7bdfb4f157f9c9b9bd7f5766f0f8e07fac8e7482eec071673f3c9d08082982","d2f2ac1436cbb7c8d122cc7de96521345254e5b36591d9d064d9763de2a7b254","3cd2ba07285d01224f9595924dc7f760c7babb386a6eb825cb551f8d829fe6fa","3ae9770861c2ece5849778e9f15567d95b87df0165c0a5b1312181df19458a56","37b51656ff8302a4556e29c775f311eb9ad813948d2c527405cea041dba3baf3","00abf32ca3af92f8be9ecbc9b63090b4909a756317d791927a83cffd24e9c8ac","cd28efe88fac7a92f3f5cfc7dd3c764f0b31bdaaa061ff044de1639810d6a7da","8b2100d3ba68063b7baf5038f26eefe46543dcebf1e7dbaf46277f24307cefcb","131b7f32092aa78e12fcb2a6db7c71e17f85e076c0635ad8d991b80d10130c19","d1c84af1e6d5fa4a5f4badd45b03b67c9255a675df235a3ec25307a0f5524278","aa4d6dc9282133162a76109d99c5795583276c4fd27284f128d484acf12b0841","3355c4c572f076ad963d95f0e28075b8558e2ab492c84eb94f9e2c48f1c2368b","5638cfd48b0c56bc9ed0c779d53a40b92c9cd9c9d6312e3a21c52542d38094f3","827eb54656695635a6e25543f711f0fe86d1083e5e1c0e84f394ffc122bd3ad7","2309cee540edc190aa607149b673b437cb8807f4e8d921bf7f5a50e6aa8d609c","703509e96cc30dce834ef8188c958c69306473b8a7e5cb3a6f324cee05a1f7bb","900daf04dc607dc3858c0f976d6f9e17b829a07de58d62dc6f730eaf06986075","08e0ac95e650bd4c87145b6ab2257b70c06254bf76a0b9f8a7d60c51fb8ed6b8","4b57ec505a035491c692b89af2c6902c312ec22f8fa9b6dae3e93686659fb7e0","7d796672940d3b2d37f2edea4d7bcf4c7993966286006228cbc8fa35ac92871d","132fd53917ed7f55275faa52c35e4d4d41e9576fea231d12740b723df2bade93","de2ecf9b1d6f60338f7b59b6f593ef77af9abd0e70ba8f2942953d0c6e1850af","cf18e9d003f1d3d1d61a04eb2d1cff3e8a8cf9cd306d0532ea82700069f2fc42","393192a39f26f9247a74ecbaea6668972af8e9125c955d1798234dceca6010f7","27ca878cf70b3030e8403f51ce65949d364fa776d6dae3527f91635a40836672","178e2de7a8702742957ad24deaeddec84a48cd913b5d932b16afd2a707b3e416","a45ee7555d019a67fbe092898d1aef0b1d02a9f6679ab84461ff515b4460d706","29c188a2c660f99f1b4835022e011c4268d7af989d4b7dda33c0a69ca1a777f8","1ed0bf138e87912d741e28333b58cbf814ae863783b3b404d2454cbabb9c5fc0","3452ee7d8ef0b1bbd47b2a56924a1dc3c79dc84a19d212e9dc496f92e4943aa0","8c95f96ccd4be0674944077aec1e4f2cccd515ca06d4327562dd017250e7d3fc","6fe7571c8a80808224648046008d1366ba4e29206ac79ce4c56d6fab3350492b","a98be76d8c257aa9e316bdb305b8c4228f0cf904d4b70547fc2999f3f99b5a01","7419d99dfe020d543c8ee736ab7ec17127d6a2c61c40e5f245c6dbd3fa6eaea4","2495815b16258136f98d91e441f4462f9b694520af86bb8c8373724cdc410096","a64568c16a5821575de4f6280ba1ea4686a1ceecd649fa90ba957c8b1b007013","ac46f284c80582f7c1284eef93f2d1c80add2d3b0e8a4076d6ca3db58d3af747","dee4dbaef83bb1061a44f39a91a59300d3dc02528eb57f748222235dd8e02159","a39c32b055d2e6103e5c49b9aed2d7bb5b06571c98fc31105264d280431bdbd7","618ebb93311695a13844118cdc9a7314dd3a2c6f35092d87f76828cac555ddc9","d36c3d116ce59a3f072c0014f0c020c76e916ba906066ddc4f193f546a43bceb","9bed8447acaa89be63540ec500b165442fcb0de020015175b5a5c66d42a61c4a","f128a2d1209d243ba2f7755c2fc313be2c7569fa0d9b4dc5cc60714fb0cc6634","a17e6861b709149f29a2bd896cee94526df2f06b24a2b60614b56649b5e9aabe","9c79ace686f720f4dd833740f7190e12cdce363362c982c164745527a412ef40","439850ca5075c6db55487b2c7fb27a6051fecbf180eee0809b67bb2783a89813","75d48857bc4216880443a24d985071262bb8b89a9952c77fd430cb0caa21f9bf","33e40cf77499b3d9712db82e15683373925e85817dbe82a24ee0ee6e44bffb70","d5bbd453310990e851908183fbbef9e6e2db8e0c86d97b42b723fd5238f71c81","95e76bed30f6e993e1fcc1b90a4675682e4800ae43403547a775d6e3c7ab2b0f","8b206b995edc6dd849b85c1c56531b9780e3ba75302fd02a2d173f008028707e","97040b190f0daa10cf9a15e51a2fac66b26ddefd7b65998bd6027d1dd67647b7","877c25dfae100e555014e45d1d80364496a0c876201e5dea91a0fd0a6a4ff765","d53f9f96afd41359edeb2d5ad98559f3bfad261391d5aef95320fefb0c6a8742","23d98226adf3be74e1f0470f85e7fd154cd7aa979d60b43190a7437f0d0426eb","639f9321a98b734242a3573764d7f1de5369b0b0b10c768ae37639e8bda5dd03","a42c39d8b7d1b1eccb69c7919ea60dcc2670ea672a0af90b70a730974ec0e9fb","dc5fe5f6b39c3fdfaeba333bcd5f0cc98bb3068797a4d7010f585366f549ddf7","4a3ab8cb278bfd1f18f24cc45a02188b63afa6aef50035df6d79c4638f24059a","e724c9ce92f2a8a31ed260764c5455852a13d292e2a31d26acc6840ec0e83208","40220ba1b091aff0cb20df5467202b62af561b09fcf3b24c22a60066d46f9e62","30abf588759f9e828a94f0c7f031eae094bb668c6dd4d902fa296779267c05c5","bd875d031474860131eadb42300aa57a71527bbb2b239d5b31ab6a9e352c84f0","773bf9af93b5027de9b5b4c779d5cda35f0eb92c7f43a97f2ef3ca081495a191","617f2b4f5115969c7b0f225d4962e6bec1cec7e5c687d84370eba4931b7dd047","59625b1fcc91f2686751fd5b623126f434d7b405bd8d866a555963ce2ac69846","5e0dc1bd24b45c46f2188d2f7f4b67f311610c72b706f963c5bf04c2e1fcc35d","fc69ffd599d3e525aba38f80c7cc2ecd187dbf148287364c75f199c8294a00e6","2ad138be6972de52ed966e6402aa6403af79e9f183486e0a725ffa075a0555fb","480274a4f75a7b3bd5c697a55e1905887b62a928592c0db3c282525fb235ba70","967fb6e86b55db228ab50c81f85f39d6a23a0c15bcfa6e19d255e0952d33a65a","c39e7d32dddfcdaa93b18b99fa430ebb1d6ba366459563d400add22f92e3644b","e3932de252bbe43132ad3226865b2a376ad945dbc1d767540c01b7bddc6477c2","b2f52f3cbd863dc4e690614b5cddbf412dea435d0de099db6d8adfd3cbefcd65","557c93b35f3b58e6844a9b8817559da1e0641f7f08f918e3cd1a8efee126746f","80ad2ae93d57dadac5e377ec6743df5e0211ea30bafd4b648c52366af057bb2b","07f90213b5800a0b43a6d6f309517dcca5afc6ffeb4bed396878a29fc5d6ceb0","bb0e637020f81cb40d16f202c3a783f0e269e29547fb84ca9f187a5ea8556965","462da802b50ac0d94a3c8f7f58a6a0aa08108bfc1394449ea56f1e0f63f5132e","2ccea88888048bbfcacbc9531a5596ea48a3e7dcd0a25f531a81bb717903ba4f","b7eece07762a9a944f097ee05d6f6b642b1e2dd87e6fc1b09600d881e5475377","1916218868966583a3c9f18501ee78929cab8450ebb8076ebd609873c258154d","98ca5ae10ab02fe747a7a53138f43525e0129aa1107892ea4e1fe9c99575809c","9760678d20c9faa0d0e1269806bce578bb76598a4a188a4d3987171263be20c5","21f706150e32f03ecd1714d7a7ac55ce3caadc7c7a2a960ba57cc5d39ad84c9d","6954ec87361b77bb8895426909fecfd154e3fd72a2b82f681c8bb15bc46f2389","da1963f37d566ff9f71bf8ca5c628656bae02fc9509050041547e9c7063cc58f","57e4bed825036f7f1328505bc512af492f28b1b57a48f1ff9b6d90b930041a52","3ef0957915b7719ac58153eaea6ce810ff8688276e570f8938455f3ec7930df7","05e0ad043fdd4e2d4874a97bd716174af64d63e43851c09830c00e819a80d395","2dff0ed1eb2046fbdbc2c13914117e1ff1112e217f90542ea5e7f41e39f0393e","a0ba1e2711c2520189ed980225e7a429b0706a1eabf9113e53f0e72550a1b23d","169b66aee819a4b165c397b832b31691f0be8d35cf8f2ec6364c23ee727b20b8","badb4cfbfc6eca3a038be22c76297bec0b5c1478d8b73d60e8b50725b7dcc15c","21e7e0eddddc112f2b891d1066eac74680291db768d3ef9b908965935380ab98","489e195150979dc291520e6f3289f055516cf342f314931c6b4553aebf2859bb","516efe800aaa0b7504b71f2d7e7e9bed5f28eb6c9c739bfdf237f09c7addea46","10ae729013e6620dc937df5dd7077c34e29ad313a28aa75cec39957640cdc8b0","ac5f95dee5e4787fa7c68a81a052cdfa0e03adec8331d3276389384df36cb849","0aaa321f1f662ec931e55c85867d316d8af16b59337111e22901516a0e1caacb","fab58e600970e66547644a44bc9918e3223aa2cbd9e8763cec004b2cfb48827e","a120dfb4736e6ec4c78f1bff5ff7f977d346152e6b7020659ee1ce4717f6f66f","9eda7b58498bed72dd98ebf1d6f8dd3bf5df5004b2f91c610093bf48f970c615","8e7adb22c0adecf7464861fc58ae3fc617b41ffbd70c97aa8493dc0966a82273","755f3cd1d9c1b564cff090e3b0e29200ae55690a91b87cb9e7a64c2dbeb314d3","d6bb7e0a6877b7856c183bff13d09dd9ae599ea43c6f6b33d3d5f72a830ed460","f1b51ae93c762d7c43f559933cd4842dd870367e8d92e90704ffa685dd5b29a3","3f450762fd7c34ed545e738abccb0af6a703572a10521643cf8fc88e3724c99c","bf5d041f2440b4a9391e2b5eb3b8d94cbf1e3b8ff4703b6539d4e65e758c8f37","8516469eb90e723b0eb03df1be098f7e6a4709f6f48fd4532868d20a0a934f6e","d60e9ab369a72d234aac49adbe2900d8ef1408a6ea4db552cf2a48c9d8d6a1bc","0ebb4698803f01e2e7df6acce572fff068f4a20c47221721dafd70a27e372831","03460a54d0e0481d1e11097f66ad43f054bc95efdafe5f81bbc7a82be181af75","ded24ddc7157689a5aa14bd651272ab8cd6e7812da2196a65d8c5e63131cfb23","2cea9689efa8591732096235abe7f084fc29c92badd5b0897a5e876b77e71887","4ed4e504126014fee13aaef5e3fc140f2ff7031ff3a8b5386717905820ea2d09","00e77e0bf106bc1e4f303ab02724df5173506d44acb6c22b4f582a88f22c3250","30d2170e1a718b5035611af55e3618b4ba8f42f0749bb52ee593da6082c4e2ce","568c86c9edf716ae08cd3c8ca61647a3eb43ff52a9aeb7c972f7be62cd35b797","a3b8b6be7620897d1e481e8650c980a210a138fceb6e710eaf95fd9dd0dfe94a","12c89d0e32758c120a569045f21cf5b77244f86792611ced8de7f86b37e77781","14bd47270e654c8eb3b1489fa8c095912ee62a0a29bb92743393203722347c53","cc8b411aec64e03abfc3936a397025c781adb89942ec2fcc66e2a353f93ce677","db5624ecf400ed47648e72353a0ebefd3293d2e6295834a3f013c548ecbd0edf","92cb686a9ca5eb5dd7d5d8d43a3707194c1e91ea07a027b3bcb60b6011b24632","da3ab7396ab4fe390f01091bd0d4c4a4e1e2a15a46d47446d6fb7194897d0f6c","66bbae6120d307ec2021ebd2241b8ad23f832b663e13363ca8b4c8dbc131a4e6","fb14266ae4070bd16db6b071e98887452bc359695c40742e38515a89dbc80a63","4a24d83c0d8f489465c4d38ed9fd87121c8a2cf50c47efe09c2eca93d39fa908","c052e32b9de53cd2596f196a0901801961bd7a31be9fac4ac2f117e4103e3a07","b15cdbb45919bc3b8e6b6f962d65382e85061d70bc26a968604f3dce4ad3a891","d6b58d955981bc1742501b792f1ab9f4cba0c4611f28dcf1c99376c1c33c9f9c","f0b9f6d5db82c3d1679f71b187c4451dbc2875ba734ce416a4804ad47390970a","a5c38939c3e22954a7166d80ab931ac6757283737b000f1e6dc924c6f4402b88","31a863da9da2a3edec16665695bdbc3134e853195f82dafec58e98c8e1bb3119","a00417f73bbba413d1345dd77252ede0bd0c957e37a9cadc9abb4c34cbd0eac1","90d1ad8d2983cb003d6f237b41c56a8f252f72071bbc53576e02b3c96d7ea47a","f3815045e126ec1b9d224782805a915ae01876a1c7d1eb9b3e320ffadbd63535","d07557f21b2ad690bfe37864aa28090bd7d01c7152b77938d92d97c8419c7144","b843ea5227a9873512aa1226b546a7e52ea5e922b89461f8b202a2f2a3f0b013","64b4d440f905da272e0568224ef8d62c5cd730755c6d453043f2e606e060ec5a","d6b58d955981bc1742501b792f1ab9f4cba0c4611f28dcf1c99376c1c33c9f9c","f0b9f6d5db82c3d1679f71b187c4451dbc2875ba734ce416a4804ad47390970a","a5c38939c3e22954a7166d80ab931ac6757283737b000f1e6dc924c6f4402b88","31a863da9da2a3edec16665695bdbc3134e853195f82dafec58e98c8e1bb3119","c0e03327bc548757709a7e2ca3063ca8b46227b5e13cd981ca3483035ef5ac44","b8442e9db28157344d1bc5d8a5a256f1692de213f0c0ddeb84359834015a008c","458111fc89d11d2151277c822dfdc1a28fa5b6b2493cf942e37d4cd0a6ee5f22","da2b6356b84a40111aaecb18304ea4e4fcb43d70efb1c13ca7d7a906445ee0d3","187119ff4f9553676a884e296089e131e8cc01691c546273b1d0089c3533ce42","febf0b2de54781102b00f61653b21377390a048fbf5262718c91860d11ff34a6","6f294731b495c65ecf46a5694f0082954b961cf05463bea823f8014098eaffa0","0aaef8cded245bf5036a7a40b65622dd6c4da71f7a35343112edbe112b348a1e","00baffbe8a2f2e4875367479489b5d43b5fc1429ecb4a4cc98cfc3009095f52a","68a0d0c508e1b6d8d23a519a8a0a3303dc5baa4849ca049f21e5bad41945e3fc","3c92b6dfd43cc1c2485d9eba5ff0b74a19bb8725b692773ef1d66dac48cda4bd","b03afe4bec768ae333582915146f48b161e567a81b5ebc31c4d78af089770ac9","df996e25faa505f85aeb294d15ebe61b399cf1d1e49959cdfaf2cc0815c203f9","4f6a12044ee6f458db11964153830abbc499e73d065c51c329ec97407f4b13dd",{"version":"99e8bb8d262bece129ac203f0c7436a07771e9cf5ba06a308d1b16993606eaf2","signature":"8705a9680ed4afb15edbd7bb9ee24af33060d1165117f293559f3073bf8d0101"},"ebf6e19cb84d78da20d022a95f05e8aef12e56f816a1ee12835a4da40d7b14cf","589357c2f88f1188a0dfc48c4c4cf4d22fac9f654805df5f2789a01b5616b74f","6abe62ec5b9b6a747c1a7687d58ff179cdfb61adee717b6e4882120f7da4399f","5c1301f550be26133f4fd34eadf38815db096ecaf9b75948b444a063097f496d",{"version":"26e64fa5fc9c7fce9daf4131f396fb5012dbdd92fb2e2bcda5aa083a76d18888","signature":"cdca22d444beb7cbe168d11a666b994be4b19c5ed7df1856612ac4dd7c2242fe"},{"version":"34ef3dd636b7074beec81346987a81ac245e1cfd75adf0babc68e6cb6c572ca3","signature":"82065c62b6a8089452cb40191a55299b2d0718ddce833446faa6c01f48f05b29"},{"version":"c1eb1aa5e32fd31d4564bffb458942d8caf500d86388c811cbb853c274e4773a","signature":"a7fe41f597b2631d3fb439d9b3ee32d1606c651a45ce2fa0d170a2614e68d280"},{"version":"327fd9ca522780f73a64e32e400a6c5bcdd89a5e706314d57ff1611bf1a99a0d","signature":"70b3082385b926b4bb0dbcef0b2f444c4f807d312546f27ee248d50b0dfa5877"},{"version":"3b1ea19c2b95501c5d8e87fe4c8044d204c4402a8b48f282bd348f973355f3c5","signature":"01b86f9481ddaf74b65f12e90ae2d5bedbc0e67e64e8cb273c7a1907cc66dbec"},{"version":"897a42f20db3ee955b1cc64506c040b0b1dcebe45d9ba3147e133d110f487f6e","signature":"3089238aed154b07430dd80de65df3115d268f21f1afcd8568a58d65c7827c5f"},{"version":"2d41bac312ef892971b2344a102feb99985e87f79edc18ed2c43ece97703fb91","signature":"2642375958909546f682d51f9c3682f553ae5f919f7b4a77d49262c200bca248"},{"version":"db3db9885deb334e6606785a0bfe7aecdcae172d36a6b4bd55958c756b92ac6c","signature":"79cdba32abf1fd279e588363d3048cb4b3d537a81530d32079cea1df22d66f93"},"1fcb8b15db812281d69a3090d488903f9e93033004aef9d8889ca3ad0753a96f","bdf5a95eb0a2dd1d39805bdf51b46ba012bb9b92b2ddaae16219595bba7678a5","9f794a0e8550a03baff865a3961cc22afbd85bc4ba9672bdda036971928f85f4","66a697d1e4cdbf25cdce4644a8085a8563041fa8c7731d4d9f5e8f22e66ba72c","a0c8e17f4d1ea2704c62d7349bc3b8d9a12e3761b5960cb44144d3f0333b3fcb","3471c0df3d0391e1dffe6e8bf150294531b2b71a2afa5f2b86e52bf84a5db60a","5d4df4de055eddf3187094f938a640f8d96e4c551a47d6686596fdb6ba4c3014","8bc2cad630da1033c1fd8d7df2bffb18af0da6113bd086a8bbec04a2471a1e00","a1059d1bbc8ad46bfe668b8450e7e8002887c4ab987bdb96d6108d8023f8bb8f","5134885e9648e2c6745f8aa1c3e7f5ab3b3617258b3d81ca02de6655ede3d74e","4f1ae3f24125216cf07c5211a3f00d2bb4782d7cc76c0681603f8249f9232ff0","d3fb92a5640f83f7844d60b35317a0f95c27e3658a749d76d218c461ad091668","d1f8bfcd91b284657ef8187c55ace7db91a3c43e642c3f14e54364154932f7e4","f54c92bfcae54f360fe79514746efce4870e4ddabc064e95d406bba291e9f672","175fd7186fa6a70f9db9b270a04a503cae23cf01cb77e3905bac115c38424cf7","c993f7ed1b8e1023c1f2ee5b262dbc3b70b27475674e40a53a58591f9972dacc","c914014ab7c7001178663f29d31a495398234a41219af61f26d7e8e91b46af96","277afd6ab6ec72889e2988e0ddd7d138c1f512e68a1fa4e90eedfd71e2097a51","c0908f85f2b645d375127a3b53a17a65f782e17962d5c1eb68f08b1188acbf15","3fadac5d409cc2f27b1d2f4e7568600f02840205f301c9ae7a3068b46476438b","da6aae64ad559286830fd44c81e3d33303348f184af7db4fde8dd99ae9749407","3633f87c97d359cb55fa7bf0668fb2be8a23342951af6ec2d06e6d0cf7409371","cc3a5427d44fc77ff25e80b3edee4650a51f83de761faf5e633994ecf1ab1b44","b350eda75c6e47299b36002b31d5b220c405c21c365e708989829db013fadbb4","f421882756b6714834ae4687ab1aeadf344a1cc45437d2edffbac020ff3801c1","1d61d6ad832dabafbf63b86c5a79d704f2c8763ada9318e135b17a3cb2d09b32","e5cef5de3e5ad3436d414d20743231e284733b9cf4375dc79eff4fcca4282f99","e624419ba84e33e661e89a28083119ca41f6953dba09a4f82b660684087afe6d","942be430bd0feaced2e3e598273b17e50ea565ec9dac840b580b0b99e1a3cd5c","73350006cec5a0c6b71d53b0b0ddbfb82be96752a9c4e3c904c59e633bc9485e","a7df5c2e9594966c7e0d4a763b13ed5727506d892669df5f7bc9826f539c1d35","258cc5cd6891f6bcbaccefd953997038844e7f65d582cac987ffabf7181bcd4c","00a6db28fc4df6ddf10adbe630d9df620ec13af19039c1869653e60dafa739d2","649324d5abb5464aabe35d86cd0eef16562df811f0971481cee664afa5acbc88",{"version":"628749b6edfc907c32583a77f7dde111642dbfc13265fa566e9a8fa47f224b51","signature":"495944b274165419ec08446dbd612d6276e2c12b92caa1f1e6c645cbc044ef25"},{"version":"e2f7d4348da1a42007547574ec71504de5e9df04d270bcc4c672bec1068257e4","signature":"0d7e153773886e59a74ffe1fac08bef805541411de160b9f3af36f8a6a3c6022"},{"version":"70fa251413c8e1926804d27e8aa01f96bf56141270e8adaeedfeaf0cc7147cef","signature":"2e85c128d27849ff4bd436f75d32d8a64d9013d420f09c82c6eae63cb7131020"},{"version":"334a6eff67fdb6feabbe5a612552a0714c424ccd07abbb096672085e7d43fb4a","signature":"19756a360a54eda2a10138b94b37a87519fd1a27c678a1b82187295e40bbfacd"},{"version":"722e48bdd1c494feabfb081d7d582d4554276abacce92f69128511918c125273","signature":"b195f1ad5886c1600c53bc7296210f9ded9a9a673e01988eecf9f20f48a4d9d5"},{"version":"1b5f109f8e1b74f648bf19b878188928678f443c2b2a21db0861f57d0715ef69","signature":"55310e6719d6bd9462e76cbba6a582712b30a85ee4949b8d98e14e0f46738e78"},{"version":"d184310a8c121c1ed754995dc55f8ca212bb1ef94979f99423dcdc48569b3c51","signature":"99ec28bacd04a3185d90660fe18bae48f33cbb1d50c73c64cae98e67f7c0ca01"},{"version":"48d475a0c6f91f62a89b128923cdec08f1f30a12df0068493f0d9b2774125b01","signature":"6a90b1b75bb0eb776ae223adc1f3f1cc343abf3e68df619933a3248910061290"},{"version":"e581d928f182594fe6aa7c0dd2e0ce02fe65fb53b7d40a59af9c2f171eee6428","signature":"1ac721bca31657133deb33e2ae005d557e8e6e0aa9a466142a2b0388e2e2638c"},{"version":"f18e14371215da28d2375c023017adcbd420314020f2a4ca4e9d9369ca80c1f4","signature":"0aeb9a7ce850134709dee9ecb63c1883e387eb70f960e0510100f4b2bb70caa4"},{"version":"bcc7e1fd0b70240f11846f0c5a284be69834446899b64477371cee7aaca38965","signature":"bab97a4f0736f1c1cd0546f79f993ecf30b34404cf4479a4f39068915880cb1c"},{"version":"6720778d4192df7ececcfd9dfebed8a006c9c44f88fe8b74880ab3ba7e14cce4","signature":"121c82998e23aa414d41a2f08e108074760318a1c11a2a5183b88b0d9be4ef60"},{"version":"e3fe28954899e21bf8a7db496cb4b90313e826bb5ae938d84bc73c3bdaa31cc9","signature":"4e1f22dbfc0754b698f1e291c7c92bf1220834bd5620207084236399cfd02e2d"},{"version":"7f2a2cdf8eecbe353d449055d91c6ee619f90ca3b3a49ba5a44563c44aab5d1a","signature":"8e6165fa13e0d2f40e2403ab20b72804d02c663709a3f7383a320050e893fe8b"},{"version":"a02cb9d0a7363cbbce45fa86bbd7d64615811e30f2a7c47a2718fcf53f20eae7","signature":"9af2721670eb3402e476cd827256d4af7ab1d6db57f1615cbef18c75164df9e5"},"14c94f7888c75007a94132f03caef0f6b58bcb136c2994213fd2d3b99f3d7f85","4695042a55a75a6c62dc57f2efe60ef3c7bbe19adedb5081f6e51dd664bbc3f7","b006ad8d854471e7a270bd8918508090961bdc1cfe77ed51f13f471fe682acac","310901df1081433ff7c3b7918496cabb92ded208b04294d3d2bd35acae2de836","c8646410cd2a6bf05eb7e7a51c881776410d07fd5d8f75092a2c815c9c6fda52",{"version":"127604bb56d364ecc35cbb4927ba7c53f552353fc7913b07a4f5cc957210aabd","signature":"973a1e0a155ab26d66226ff9d64a36cf61227e9240b21cabdc67df29847a6599"},{"version":"dcea5769c8b69d7b7a5ee6ffd4d22260e47d53d22990e91d504cbdc0c0120c14","signature":"16c51743932253da5b661b0a5068eb1423a6f020f62e6783ce8ac5259cff10f2"},{"version":"67be5e00299e02d108b294758dcc0218da9f2a2823dea61d708ddbe705771ae5","signature":"05fe3dec4dc02961a8959758da54c6ff9d32a232183041163d4d52cc6bf39015"},{"version":"e2e5ebf01c7004f157b8c750fdddb9f227fbf3119a87297e3a014db04c3f0887","signature":"fc6fe9c667e291d0bbdc904c921d2c1d385175f8c135d9e549298c96265acaec"},{"version":"6b0da45d7a1027dd4a9b14ac009b018761e8851c84a9e54ecc1be9086f0516c6","signature":"38fd30580198d072da98f6dbcb7535f47359ca91ffe57e3b9bfd1961a3b209ab"},{"version":"b75a82fbc994a947805538c6652361c50c70f121e173c9ae62b190b8936790e0","signature":"0e92b9a9c01325fa2e1bc7574a0ff68328f69f8db609be2156a4c3da5ce493df"},{"version":"4c408d170f00539f8957a9cfce1d7f3e4a2d36651dbf4b16337a7af5c568d7e5","signature":"a0c04e7f7ac63b60e113ff0a7ad4fbf9214babb1db0572641709a5859710e27c"},"4489c6a9fde8934733aa7df6f7911461ee6e9e4ad092736bd416f6b2cc20b2c6","2c8e55457aaf4902941dfdba4061935922e8ee6e120539c9801cd7b400fae050","8041cfce439ff29d339742389de04c136e3029d6b1817f07b2d7fcbfb7534990","670a76db379b27c8ff42f1ba927828a22862e2ab0b0908e38b671f0e912cc5ed","9d38964b57191567a14b396422c87488cecd48f405c642daa734159875ee81d9","069bebfee29864e3955378107e243508b163e77ab10de6a5ee03ae06939f0bb9",{"version":"64d4b35c5456adf258d2cf56c341e203a073253f229ef3208fc0d5020253b241","affectsGlobalScope":true},"ee7d8894904b465b072be0d2e4b45cf6b887cdba16a467645c4e200982ece7ea","f3d8c757e148ad968f0d98697987db363070abada5f503da3c06aefd9d4248c1","bc3cba7b0af2d52e7425299aee518db479d44004eff6fbbd206d1ee7e5ec3fb5","afe73051ff6a03a9565cbd8ebb0e956ee3df5e913ad5c1ded64218aabfa3dcb5","035a5df183489c2e22f3cf59fc1ed2b043d27f357eecc0eb8d8e840059d44245","a4809f4d92317535e6b22b01019437030077a76fec1d93b9881c9ed4738fcc54","5f53fa0bd22096d2a78533f94e02c899143b8f0f9891a46965294ee8b91a9434","0d14fa22c41fdc7277e6f71473b20ebc07f40f00e38875142335d5b63cdfc9d2","d8aab31ba8e618cc3eea10b0945de81cb93b7e8150a013a482332263b9305322","462bccdf75fcafc1ae8c30400c9425e1a4681db5d605d1a0edb4f990a54d8094","5923d8facbac6ecf7c84739a5c701a57af94a6f6648d6229a6c768cf28f0f8cb","7adecb2c3238794c378d336a8182d4c3dd2c4fa6fa1785e2797a3db550edea62","dc12dc0e5aa06f4e1a7692149b78f89116af823b9e1f1e4eae140cd3e0e674e6","1bfc6565b90c8771615cd8cfcf9b36efc0275e5e83ac7d9181307e96eb495161","8a8a96898906f065f296665e411f51010b51372fa260d5373bf9f64356703190","7f82ef88bdb67d9a850dd1c7cd2d690f33e0f0acd208e3c9eba086f3670d4f73",{"version":"ccfd8774cd9b929f63ff7dcf657977eb0652e3547f1fcac1b3a1dc5db22d4d58","affectsGlobalScope":true},"d92dc90fecd2552db74d8dc3c6fb4db9145b2aa0efe2c127236ba035969068d4","96d14f21b7652903852eef49379d04dbda28c16ed36468f8c9fa08f7c14c9538","8841e2aa774b89bd23302dede20663306dc1b9902431ac64b24be8b8d0e3f649","916be7d770b0ae0406be9486ac12eb9825f21514961dd050594c4b250617d5a8","254d9fb8c872d73d34594be8a200fd7311dbfa10a4116bfc465fba408052f2b3","d88a5e779faf033be3d52142a04fbe1cb96009868e3bbdd296b2bc6c59e06c0e","d8f7109e14f20eb735225a62fd3f8366da1a8349e90331cdad57f4b04caf6c5a","cf3d384d082b933d987c4e2fe7bfb8710adfd9dc8155190056ed6695a25a559e","9871b7ee672bc16c78833bdab3052615834b08375cb144e4d2cba74473f4a589","c863198dae89420f3c552b5a03da6ed6d0acfa3807a64772b895db624b0de707","8b03a5e327d7db67112ebbc93b4f744133eda2c1743dbb0a990c61a8007823ef","86c73f2ee1752bac8eeeece234fd05dfcf0637a4fbd8032e4f5f43102faa8eec","42fad1f540271e35ca37cecda12c4ce2eef27f0f5cf0f8dd761d723c744d3159","ff3743a5de32bee10906aff63d1de726f6a7fd6ee2da4b8229054dfa69de2c34","83acd370f7f84f203e71ebba33ba61b7f1291ca027d7f9a662c6307d74e4ac22","1445cec898f90bdd18b2949b9590b3c012f5b7e1804e6e329fb0fe053946d5ec","0e5318ec2275d8da858b541920d9306650ae6ac8012f0e872fe66eb50321a669","cf530297c3fb3a92ec9591dd4fa229d58b5981e45fe6702a0bd2bea53a5e59be","c1f6f7d08d42148ddfe164d36d7aba91f467dbcb3caa715966ff95f55048b3a4","f4e9bf9103191ef3b3612d3ec0044ca4044ca5be27711fe648ada06fad4bcc85","0c1ee27b8f6a00097c2d6d91a21ee4d096ab52c1e28350f6362542b55380059a","7677d5b0db9e020d3017720f853ba18f415219fb3a9597343b1b1012cfd699f7","bc1c6bc119c1784b1a2be6d9c47addec0d83ef0d52c8fbe1f14a51b4dfffc675","52cf2ce99c2a23de70225e252e9822a22b4e0adb82643ab0b710858810e00bf1","770625067bb27a20b9826255a8d47b6b5b0a2d3dfcbd21f89904c731f671ba77","d1ed6765f4d7906a05968fb5cd6d1db8afa14dbe512a4884e8ea5c0f5e142c80","799c0f1b07c092626cf1efd71d459997635911bb5f7fc1196efe449bba87e965","2a184e4462b9914a30b1b5c41cf80c6d3428f17b20d3afb711fff3f0644001fd","9eabde32a3aa5d80de34af2c2206cdc3ee094c6504a8d0c2d6d20c7c179503cc","397c8051b6cfcb48aa22656f0faca2553c5f56187262135162ee79d2b2f6c966","a8ead142e0c87dcd5dc130eba1f8eeed506b08952d905c47621dc2f583b1bff9","a02f10ea5f73130efca046429254a4e3c06b5475baecc8f7b99a0014731be8b3","c2576a4083232b0e2d9bd06875dd43d371dee2e090325a9eac0133fd5650c1cb","4c9a0564bb317349de6a24eb4efea8bb79898fa72ad63a1809165f5bd42970dd","f40ac11d8859092d20f953aae14ba967282c3bb056431a37fced1866ec7a2681","cc11e9e79d4746cc59e0e17473a59d6f104692fd0eeea1bdb2e206eabed83b03","b444a410d34fb5e98aa5ee2b381362044f4884652e8bc8a11c8fe14bbd85518e","c35808c1f5e16d2c571aa65067e3cb95afeff843b259ecfa2fc107a9519b5392","14d5dc055143e941c8743c6a21fa459f961cbc3deedf1bfe47b11587ca4b3ef5","a3ad4e1fc542751005267d50a6298e6765928c0c3a8dce1572f2ba6ca518661c","f237e7c97a3a89f4591afd49ecb3bd8d14f51a1c4adc8fcae3430febedff5eb6","3ffdfbec93b7aed71082af62b8c3e0cc71261cc68d796665faa1e91604fbae8f","662201f943ed45b1ad600d03a90dffe20841e725203ced8b708c91fcd7f9379a","c9ef74c64ed051ea5b958621e7fb853fe3b56e8787c1587aefc6ea988b3c7e79","2462ccfac5f3375794b861abaa81da380f1bbd9401de59ffa43119a0b644253d","34baf65cfee92f110d6653322e2120c2d368ee64b3c7981dff08ed105c4f19b0","7d8ddf0f021c53099e34ee831a06c394d50371816caa98684812f089b4c6b3d4","7d2a0ba1297be385a89b5515b88cd31b4a1eeef5236f710166dc1b36b1741e1b","9d92b037978bb9525bc4b673ebddd443277542e010c0aef019c03a170ccdaa73","ab82804a14454734010dcdcd43f564ff7b0389bee4c5692eec76ff5b30d4cf66","bae8d023ef6b23df7da26f51cea44321f95817c190342a36882e93b80d07a960","ae271d475b632ce7b03fea6d9cf6da72439e57a109672671cbc79f54e1386938"],"options":{"composite":true,"declaration":true,"declarationMap":true,"emitDeclarationOnly":true,"esModuleInterop":true,"inlineSources":true,"module":1,"outDir":"./types","rootDir":"../src","sourceMap":true,"strict":true,"target":7},"fileIdsList":[[666],[72,108,109,110,125],[109,110,126,127],[108,109],[108,125,128,131],[108,128,131,132],[129,130,131,133,134],[108,131],[108,125,128,129,130,133],[108,116],[108],[72,108],[60,108],[112,113,114,115,116,117,118,119,120,121,122,123,124],[108,114,115],[108,114,116],[605,609,610],[605,608],[608],[609,611,613],[605,608,609,610,611,612],[605,608,612,616,617,618],[605,608,612,619],[605],[605,606],[606,607],[605,608,614,617,619,620],[615],[616,618,622,625,626],[616,618,626],[108,608,612,616,617,619,622],[616,626],[616,619,623],[608,617,619],[616,619,622,623,624,625,626,627,628,629,630,631,632,633,634,635,636,637],[616,624],[108,624],[605,612,616,617,618,619,620,623],[616,618,622,624],[616,623,624],[108,135,392,393],[392],[393,394],[108,387],[387,388,389,390,391],[108,359,366,367],[108,359,366,367,387],[108,359,366,367,371],[108,359,366,367,368,370,371],[108,359,366,367,369],[108,359,366,367,372,373,375,376],[365,387],[357,366,367,372,373],[359,365,366],[108,359,366,367,372],[108,359,366,367,370],[108,359,366,367,383],[108,359,366,367,384],[111,356,359,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386],[357],[357,358],[72,108,392,395,401],[395,402],[402,403],[216],[216,544,545],[545,546,547],[249,258,259,262],[249,258,261],[249,258,260,262],[250,253,254,256],[250,251,252],[253,254,256,257],[250,251,255],[261,267],[249,258,261,267],[258,261,267],[258,261,263,264,265,266],[249,258,259,260,261,262,267],[249,258],[250],[144,167,193],[167,197,214],[167,214,215,234],[144,167],[144],[189,190,191,192,194,195,196],[167,189,190,191,192,194,195],[198],[201],[144,193],[202,203,204],[197],[193,197,199,200,205,206,213,215,235,236,354],[167,200,205],[208],[207,209,210,211,212],[144,167,193,197],[167,206,353],[138,144],[654,655,656,657],[548,567],[568],[560],[562],[560,561,562,563,564,565,566],[560,562],[108,228,424],[108,228,425],[425],[396],[108,228,396],[396,397,398,399,400],[108,228],[228,427],[228,428],[228,427,428],[427,428,429,430,431,432,433,434,435,436],[61,108,228,427,428],[105,108,228,427,428],[167,216,223,224],[225],[108,167,225,228,229],[108,167,216,223,225,228],[229],[224,225,226,229,230,231,232,233],[108,226,228,231],[224,231],[167,223],[167,237],[237,238],[237,238,239,240],[167],[188,353,475,492,493,506,531],[529],[167,504,519,527,528,530],[188,353,455,517],[518],[108,223,228,426,437,492,493,494,506],[167,188,492,506],[167,437],[437,495],[497],[494,495,496,498,501,503],[495,499,504],[500],[437,495,496],[495,496],[502],[167,184,188,353,462,475,485,492,493,504,506,509,511,519,521,524],[509,524,525,526],[492,506],[505,506,507,508],[492,506,507],[167,492,505,506],[167,492,506],[522,523],[167,188,485,522],[167,353,485],[520],[167,353,475,492,493,506,527],[144,167,484],[108,167,242],[167,350],[319],[167,242,319,347,350,351,352],[108,167,241,242],[243,244,245,246,348,349],[138,144,347],[138,144,245],[277],[277,291],[277,278,293,295,296],[277,291,294],[277,283],[277,282,284,285],[277,282,286],[282,283,284,285,286,287],[289,290],[278,279,280,281,288,291,292,293,294,295,296,297,298],[277,299,300,301,302],[144,277,299,350],[334],[347],[337,338,339,340,341,342,343,344,345],[167,269],[319,343,350],[269,347,350],[144,320],[269,270,320,323,333,334,335,346],[144,167,303,319],[347,350],[268,270],[270],[350],[320],[167,323],[247,248,271,272,273,274,275,276,321,322,324,325,326,327,328,329,330,331,332],[167,325],[247,248,271,272,273,274,275,276,321,322,324,325,326,327,328,329,330,331,350],[167,268,269],[234,333],[167,270],[317],[144,304],[305,306,307,308,309,310,311,312,313,314,315,316],[304,317,318],[303],[353],[441],[440],[144,167,447],[268],[167,241,353],[144,353,454,455],[438,439,441,442,443,444,445,448,449,450,451,452,453,454,456,457,458,476,477,479,480,481,482,483,486,487,488,489,490,491],[144,167,475],[144,167,353,441,479],[158],[478],[144,167,268,353,477],[144,353,477],[144,353,475,478,480,485,486],[144,167,441,444,456,479,480],[353,455],[486],[536],[536,537,538],[147],[144,147],[145,146,147,148,149,150,151,152,153,154,155,156,159,160,161,162,163,164,165,166],[138,144,145],[135,147,153,155],[147,148],[144,162],[108,362],[360,361,364],[360,363],[108,360],[412,413],[666,667,668,669,670],[666,668],[157],[672,673,674],[73,108],[677],[678],[689],[683,688],[579,581,582,583,584,585,586,587,588,589,590,591],[579,580,582,583,584,585,586,587,588,589,590,591],[580,581,582,583,584,585,586,587,588,589,590,591],[579,580,581,583,584,585,586,587,588,589,590,591],[579,580,581,582,584,585,586,587,588,589,590,591],[579,580,581,582,583,585,586,587,588,589,590,591],[579,580,581,582,583,584,586,587,588,589,590,591],[579,580,581,582,583,584,585,587,588,589,590,591],[579,580,581,582,583,584,585,586,588,589,590,591],[579,580,581,582,583,584,585,586,587,589,590,591],[579,580,581,582,583,584,585,586,587,588,590,591],[579,580,581,582,583,584,585,586,587,588,589,591],[579,580,581,582,583,584,585,586,587,588,589,590],[56],[59],[60,65,92],[61,72,73,80,89,100],[61,62,72,80],[63,101],[64,65,73,81],[65,89,97],[66,68,72,80],[67],[68,69],[72],[71,72],[59,72],[72,73,74,89,100],[72,73,74,89],[72,75,80,89,100],[72,73,75,76,80,89,97,100],[75,77,89,97,100],[56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107],[72,78],[79,100,105],[68,72,80,89],[81],[82],[59,83],[84,99,105],[85],[86],[72,87],[87,88,101,103],[60,72,89,90,91],[60,89,91],[89,90],[92],[93],[72,95,96],[95,96],[65,80,89,97],[98],[80,99],[60,75,86,100],[65,101],[89,102],[103],[104],[60,65,72,74,83,89,100,103,105],[89,106],[108,227],[697,736],[697,721,736],[736],[697],[697,722,736],[697,698,699,700,701,702,703,704,705,706,707,708,709,710,711,712,713,714,715,716,717,718,719,720,721,722,723,724,725,726,727,728,729,730,731,732,733,734,735],[722,736],[737],[740],[510],[570],[572],[570,571,572,573,574,575,576],[570,572],[446],[593,594,595],[593],[594],[179],[179,180,181,182,183],[168,169,170,171,172,173,174,175,176,177,178],[681,684],[681,684,685,686],[683],[680,687],[682],[137,139,140,141,142,143],[137,138],[139],[138,139],[137,139],[167,188,355,423,531],[532,534],[423,533],[167,188,241],[460,461],[167,184,185,186],[185],[186],[136,185,186,187],[407],[407,408,411,415],[414],[167,409,410],[541,542,543],[223,541],[167,223,541],[167,216,223],[167,188,553,557],[558],[167,216],[167,217],[217,218,219,220,221,222],[135,167,184,188,355,404,405,421],[422],[72,108,167,188],[406],[406,416],[406,417,418,419,420],[167,188,409,416,539,540,549,550],[549],[540,549,551,552],[167,416,544,548],[167,467,468,469],[167,188,416,459,465,467,470],[167,184,188,241,416,459,462,463,464,465,466,468,469,470],[167,188,467],[241,467,468,470],[465,466,467,468,469,470,474],[167,223,467,469,475],[167,223,466,467,468],[471,472,473],[167,223,416,466,468,469],[167,223,466,468,469],[167,223,468,470],[188,416,512,514,515],[513,514],[516],[515,516],[167,188,553,554],[108,167,188,553,554],[554,555,556],[167,553],[72,125,128,135,167,188,241,409,416,462,533,535,553,559,569,577,578,591,592,597,598,600,601,602,603,604,640,641,642,644,645,646,647,648,649,650,651,652,653,661,662,663],[167,597],[167,559,592,597,602],[167,409,410,416,592,597,603],[167,597,599,639],[128,135,167,591,592,597,621,638],[167,416,597],[167,410,416,533,577,592,597,599,643],[72,167,409,553,559,592,597,598,645],[72,167,535,553,577,592,597],[167,409,553,569,577,592,644,647,648],[72,409,416,553,591,592,597],[597,598,600,643,662,664],[135,167,409,553,559,596],[167,416,592,599],[241,597],[167,409,416,559,592,597,598,601],[416,559,597],[167,409,416,592,597,599],[591,596,597],[167,553,592,597],[416,569,592,597],[167,416,592,659],[52,167,416,592,597,599,614,659,660],[409,416,591,592,597,599,600,664],[52,409,416,597,614],[167,416,597,664],[52,241,416,597,600,614],[128,135,167,188,462,535,553,559,569,597,647],[597],[597,639],[72,167,553,559,597],[72,167,535,553,597],[167,409,553,569,644,647,648],[72,409,553,597],[158,167],[167,409,559,597],[559,597],[158,167,409,597],[167,553,597],[569,597],[167,597,614,660],[409,597,664],[409,597],[597,664]],"referencedMap":[[668,1],[126,2],[128,3],[110,4],[132,5],[133,6],[129,6],[135,7],[130,6],[134,8],[131,9],[117,10],[114,11],[121,12],[115,10],[112,13],[125,14],[119,11],[116,15],[118,16],[611,17],[610,18],[609,19],[614,20],[613,21],[619,22],[620,23],[606,24],[607,25],[608,26],[621,27],[616,28],[627,29],[628,30],[623,31],[629,32],[630,33],[631,33],[622,34],[638,35],[633,29],[632,36],[634,37],[624,38],[635,32],[636,30],[626,39],[637,36],[625,40],[617,18],[394,41],[393,42],[395,43],[390,44],[388,44],[389,44],[392,45],[374,46],[379,47],[368,46],[373,48],[372,49],[370,50],[377,51],[378,46],[380,52],[375,53],[367,54],[381,55],[383,56],[384,57],[385,58],[387,59],[358,60],[359,61],[402,62],[403,63],[404,64],[545,65],[546,66],[547,66],[548,67],[260,68],[262,69],[261,70],[257,71],[253,72],[254,72],[258,73],[256,74],[263,75],[264,76],[265,77],[267,78],[266,75],[268,79],[259,80],[252,81],[255,72],[214,82],[215,83],[235,84],[189,85],[190,86],[191,86],[192,85],[197,87],[196,88],[194,82],[195,85],[199,89],[198,85],[202,90],[201,91],[205,92],[203,85],[204,93],[355,94],[207,82],[208,95],[209,96],[210,82],[213,97],[212,98],[236,83],[354,99],[193,100],[658,101],[568,102],[569,103],[561,104],[563,105],[567,106],[565,107],[564,107],[425,108],[424,109],[426,110],[398,111],[399,111],[396,65],[397,112],[401,113],[400,114],[428,115],[431,116],[430,117],[437,118],[433,117],[432,119],[435,117],[434,120],[436,116],[429,116],[225,121],[226,122],[231,123],[229,124],[230,125],[234,126],[232,127],[233,128],[224,129],[238,130],[239,131],[241,132],[237,133],[216,12],[529,134],[530,135],[531,136],[518,137],[519,138],[495,139],[494,140],[496,141],[497,142],[498,143],[504,144],[500,145],[501,146],[499,147],[502,148],[503,149],[525,150],[527,151],[507,152],[509,153],[508,154],[506,155],[505,156],[524,157],[523,158],[522,159],[526,152],[520,133],[521,160],[528,161],[485,162],[484,85],[351,163],[242,164],[352,165],[353,166],[243,167],[244,133],[245,133],[350,168],[348,169],[246,170],[277,133],[278,171],[279,171],[280,171],[281,171],[292,171],[293,171],[294,172],[297,173],[298,171],[295,174],[296,171],[282,171],[284,175],[286,176],[287,177],[285,171],[283,171],[288,178],[289,171],[290,171],[291,179],[299,180],[303,181],[301,171],[300,171],[302,182],[335,183],[337,133],[338,184],[346,185],[339,133],[341,186],[342,133],[344,187],[343,188],[345,189],[347,190],[320,191],[247,184],[248,192],[271,193],[272,194],[273,193],[275,133],[276,195],[321,196],[324,197],[333,198],[326,199],[325,133],[327,133],[328,164],[332,200],[329,195],[330,197],[331,184],[270,201],[334,202],[323,203],[318,204],[305,205],[314,205],[306,205],[307,205],[316,205],[308,205],[309,205],[317,206],[315,205],[310,205],[313,205],[311,205],[312,205],[319,207],[304,86],[455,208],[439,209],[442,210],[443,211],[445,210],[448,212],[451,213],[453,214],[456,215],[492,216],[476,217],[458,133],[480,218],[481,219],[479,220],[478,221],[482,222],[487,223],[477,100],[486,224],[489,225],[490,226],[491,133],[441,211],[537,227],[538,227],[539,228],[145,86],[146,86],[148,229],[149,86],[150,86],[151,230],[147,86],[167,231],[155,232],[156,233],[159,219],[165,234],[166,235],[363,236],[362,11],[365,237],[360,11],[364,238],[361,239],[414,240],[671,241],[667,1],[669,242],[670,1],[410,11],[158,243],[675,244],[676,245],[678,246],[679,247],[690,248],[689,249],[580,250],[581,251],[579,252],[582,253],[583,254],[584,255],[585,256],[586,257],[587,258],[588,259],[589,260],[590,261],[591,262],[56,263],[57,263],[59,264],[60,265],[61,266],[62,267],[63,268],[64,269],[65,270],[66,271],[67,272],[68,273],[69,273],[70,274],[71,275],[72,276],[73,277],[74,278],[75,279],[76,280],[77,281],[108,282],[78,283],[79,284],[80,285],[81,286],[82,287],[83,288],[84,289],[85,290],[86,291],[87,292],[88,293],[89,294],[91,295],[90,296],[92,297],[93,298],[95,299],[96,300],[97,301],[98,302],[99,303],[100,304],[101,305],[102,306],[103,307],[104,308],[105,309],[106,310],[694,11],[228,311],[696,11],[721,312],[722,313],[697,314],[700,314],[719,312],[720,312],[710,312],[709,315],[707,312],[702,312],[715,312],[713,312],[717,312],[701,312],[714,312],[718,312],[703,312],[704,312],[716,312],[698,312],[705,312],[706,312],[708,312],[712,312],[723,316],[711,312],[699,312],[736,317],[730,316],[732,318],[731,316],[724,316],[725,316],[727,316],[729,316],[733,318],[734,318],[726,318],[728,318],[738,319],[741,320],[511,321],[571,322],[573,323],[577,324],[575,325],[574,325],[447,326],[596,327],[594,328],[595,329],[175,330],[177,330],[176,330],[174,330],[184,331],[179,332],[170,330],[171,330],[172,330],[173,330],[685,333],[687,334],[686,333],[684,335],[688,336],[683,337],[144,338],[139,339],[140,340],[141,340],[142,341],[143,341],[138,342],[532,343],[535,344],[534,345],[460,346],[462,347],[187,348],[186,349],[185,350],[188,351],[408,352],[416,353],[415,354],[411,355],[544,356],[542,357],[543,358],[541,359],[558,360],[559,361],[217,362],[218,363],[219,363],[221,363],[223,364],[222,363],[422,365],[423,366],[406,367],[420,368],[419,368],[417,369],[418,368],[421,370],[551,371],[550,372],[552,372],[553,373],[549,374],[470,375],[468,376],[467,377],[465,378],[469,379],[475,380],[464,381],[471,382],[474,383],[472,384],[473,385],[466,386],[516,387],[515,388],[517,389],[514,390],[555,391],[556,392],[557,393],[554,394],[664,395],[659,396],[603,397],[604,398],[640,399],[639,400],[641,399],[642,401],[644,402],[646,403],[647,404],[649,405],[648,406],[665,407],[592,133],[597,408],[643,409],[650,410],[602,411],[598,412],[651,413],[652,414],[645,415],[653,416],[660,417],[661,418],[601,419],[662,420],[600,421],[663,422]],"exportedModulesMap":[[668,1],[126,2],[128,3],[110,4],[132,5],[133,6],[129,6],[135,7],[130,6],[134,8],[131,9],[117,10],[114,11],[121,12],[115,10],[112,13],[125,14],[119,11],[116,15],[118,16],[611,17],[610,18],[609,19],[614,20],[613,21],[619,22],[620,23],[606,24],[607,25],[608,26],[621,27],[616,28],[627,29],[628,30],[623,31],[629,32],[630,33],[631,33],[622,34],[638,35],[633,29],[632,36],[634,37],[624,38],[635,32],[636,30],[626,39],[637,36],[625,40],[617,18],[394,41],[393,42],[395,43],[390,44],[388,44],[389,44],[392,45],[374,46],[379,47],[368,46],[373,48],[372,49],[370,50],[377,51],[378,46],[380,52],[375,53],[367,54],[381,55],[383,56],[384,57],[385,58],[387,59],[358,60],[359,61],[402,62],[403,63],[404,64],[545,65],[546,66],[547,66],[548,67],[260,68],[262,69],[261,70],[257,71],[253,72],[254,72],[258,73],[256,74],[263,75],[264,76],[265,77],[267,78],[266,75],[268,79],[259,80],[252,81],[255,72],[214,82],[215,83],[235,84],[189,85],[190,86],[191,86],[192,85],[197,87],[196,88],[194,82],[195,85],[199,89],[198,85],[202,90],[201,91],[205,92],[203,85],[204,93],[355,94],[207,82],[208,95],[209,96],[210,82],[213,97],[212,98],[236,83],[354,99],[193,100],[658,101],[568,102],[569,103],[561,104],[563,105],[567,106],[565,107],[564,107],[425,108],[424,109],[426,110],[398,111],[399,111],[396,65],[397,112],[401,113],[400,114],[428,115],[431,116],[430,117],[437,118],[433,117],[432,119],[435,117],[434,120],[436,116],[429,116],[225,121],[226,122],[231,123],[229,124],[230,125],[234,126],[232,127],[233,128],[224,129],[238,130],[239,131],[241,132],[237,133],[216,12],[529,134],[530,135],[531,136],[518,137],[519,138],[495,139],[494,140],[496,141],[497,142],[498,143],[504,144],[500,145],[501,146],[499,147],[502,148],[503,149],[525,150],[527,151],[507,152],[509,153],[508,154],[506,155],[505,156],[524,157],[523,158],[522,159],[526,152],[520,133],[521,160],[528,161],[485,162],[484,85],[351,163],[242,164],[352,165],[353,166],[243,167],[244,133],[245,133],[350,168],[348,169],[246,170],[277,133],[278,171],[279,171],[280,171],[281,171],[292,171],[293,171],[294,172],[297,173],[298,171],[295,174],[296,171],[282,171],[284,175],[286,176],[287,177],[285,171],[283,171],[288,178],[289,171],[290,171],[291,179],[299,180],[303,181],[301,171],[300,171],[302,182],[335,183],[337,133],[338,184],[346,185],[339,133],[341,186],[342,133],[344,187],[343,188],[345,189],[347,190],[320,191],[247,184],[248,192],[271,193],[272,194],[273,193],[275,133],[276,195],[321,196],[324,197],[333,198],[326,199],[325,133],[327,133],[328,164],[332,200],[329,195],[330,197],[331,184],[270,201],[334,202],[323,203],[318,204],[305,205],[314,205],[306,205],[307,205],[316,205],[308,205],[309,205],[317,206],[315,205],[310,205],[313,205],[311,205],[312,205],[319,207],[304,86],[455,208],[439,209],[442,210],[443,211],[445,210],[448,212],[451,213],[453,214],[456,215],[492,216],[476,217],[458,133],[480,218],[481,219],[479,220],[478,221],[482,222],[487,223],[477,100],[486,224],[489,225],[490,226],[491,133],[441,211],[537,227],[538,227],[539,228],[145,86],[146,86],[148,229],[149,86],[150,86],[151,230],[147,86],[167,231],[155,232],[156,233],[159,219],[165,234],[166,235],[363,236],[362,11],[365,237],[360,11],[364,238],[361,239],[414,240],[671,241],[667,1],[669,242],[670,1],[410,11],[158,243],[675,244],[676,245],[678,246],[679,247],[690,248],[689,249],[580,250],[581,251],[579,252],[582,253],[583,254],[584,255],[585,256],[586,257],[587,258],[588,259],[589,260],[590,261],[591,262],[56,263],[57,263],[59,264],[60,265],[61,266],[62,267],[63,268],[64,269],[65,270],[66,271],[67,272],[68,273],[69,273],[70,274],[71,275],[72,276],[73,277],[74,278],[75,279],[76,280],[77,281],[108,282],[78,283],[79,284],[80,285],[81,286],[82,287],[83,288],[84,289],[85,290],[86,291],[87,292],[88,293],[89,294],[91,295],[90,296],[92,297],[93,298],[95,299],[96,300],[97,301],[98,302],[99,303],[100,304],[101,305],[102,306],[103,307],[104,308],[105,309],[106,310],[694,11],[228,311],[696,11],[721,312],[722,313],[697,314],[700,314],[719,312],[720,312],[710,312],[709,315],[707,312],[702,312],[715,312],[713,312],[717,312],[701,312],[714,312],[718,312],[703,312],[704,312],[716,312],[698,312],[705,312],[706,312],[708,312],[712,312],[723,316],[711,312],[699,312],[736,317],[730,316],[732,318],[731,316],[724,316],[725,316],[727,316],[729,316],[733,318],[734,318],[726,318],[728,318],[738,319],[741,320],[511,321],[571,322],[573,323],[577,324],[575,325],[574,325],[447,326],[596,327],[594,328],[595,329],[175,330],[177,330],[176,330],[174,330],[184,331],[179,332],[170,330],[171,330],[172,330],[173,330],[685,333],[687,334],[686,333],[684,335],[688,336],[683,337],[144,338],[139,339],[140,340],[141,340],[142,341],[143,341],[138,342],[532,343],[535,344],[534,345],[460,346],[462,347],[187,348],[186,349],[185,350],[188,351],[408,352],[416,353],[415,354],[411,355],[544,356],[542,357],[543,358],[541,359],[558,360],[559,361],[217,362],[218,363],[219,363],[221,363],[223,364],[222,363],[422,365],[423,366],[406,367],[420,368],[419,368],[417,369],[418,368],[421,370],[551,371],[550,372],[552,372],[553,373],[549,374],[470,375],[468,376],[467,377],[465,378],[469,379],[475,380],[464,381],[471,382],[474,383],[472,384],[473,385],[466,386],[516,387],[515,388],[517,389],[514,390],[555,391],[556,392],[557,393],[554,394],[664,423],[659,133],[603,424],[604,424],[640,425],[639,396],[641,425],[642,424],[644,396],[646,426],[647,427],[649,428],[648,429],[665,407],[592,430],[597,408],[643,133],[650,424],[602,431],[598,432],[651,433],[652,424],[645,434],[653,435],[660,133],[661,436],[601,437],[662,438],[600,439],[663,424]],"semanticDiagnosticsPerFile":[668,666,126,109,128,110,127,132,133,129,135,130,134,131,117,114,121,115,112,120,125,122,123,124,119,116,113,118,611,610,609,614,613,619,620,606,607,608,605,621,616,615,612,627,628,623,629,630,631,622,638,633,632,634,624,635,636,626,637,625,617,618,394,393,395,390,388,389,392,391,374,379,368,373,372,370,377,378,380,375,369,367,366,376,382,381,383,384,385,387,357,358,359,356,371,386,402,403,404,405,545,546,547,548,409,260,262,261,249,257,253,254,258,256,263,264,265,267,266,268,259,252,250,251,255,214,215,235,189,190,191,192,197,196,194,195,199,198,200,202,201,205,203,204,206,355,207,208,209,210,213,211,212,236,354,193,656,654,655,657,658,568,569,561,560,563,562,566,567,565,564,425,424,426,398,399,396,397,401,400,428,431,430,437,433,432,435,434,436,427,429,225,226,231,229,230,234,232,233,224,238,240,239,241,237,216,529,530,531,518,519,495,494,496,497,498,504,500,501,499,502,503,525,493,527,507,509,508,506,505,524,523,522,526,520,521,528,485,484,351,242,352,353,243,244,245,350,348,246,349,277,278,279,280,281,292,293,294,297,298,295,296,282,284,286,287,285,283,288,289,290,291,299,303,301,300,302,269,335,337,338,346,339,340,341,342,344,343,345,336,347,320,247,248,271,272,273,274,275,276,321,322,324,333,326,325,327,328,332,329,330,331,270,334,323,318,305,314,306,307,316,308,309,317,315,310,313,311,312,319,304,455,438,439,442,443,444,445,448,449,450,451,452,453,454,456,457,492,476,458,480,481,479,478,482,483,487,488,477,486,489,490,491,440,441,537,538,539,536,145,146,148,149,150,151,152,153,154,147,167,155,156,159,160,161,162,163,164,165,166,363,362,365,360,364,361,412,414,413,671,667,669,670,410,158,463,672,675,673,676,677,678,679,690,689,674,691,580,581,579,582,583,584,585,586,587,588,589,590,591,692,157,56,57,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,58,107,75,76,77,108,78,79,80,81,82,83,84,85,86,87,88,89,91,90,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,693,694,695,512,228,227,696,721,722,697,700,719,720,710,709,707,702,715,713,717,701,714,718,703,704,716,698,705,706,708,712,723,711,699,736,735,730,732,731,724,725,727,729,733,734,726,728,738,737,739,533,740,741,511,510,571,570,573,572,576,577,575,574,111,680,446,447,578,596,594,595,593,513,178,175,177,176,174,184,179,183,180,182,181,170,171,172,168,169,173,681,685,687,686,684,688,459,683,682,137,144,139,140,141,142,143,138,8,10,9,2,11,12,13,14,15,16,17,18,3,4,22,19,20,21,23,24,25,5,26,27,28,29,6,33,30,31,32,34,7,35,40,41,36,37,38,39,1,42,532,535,534,460,461,462,136,187,186,185,188,408,416,415,407,411,544,542,543,541,558,559,217,218,219,220,221,223,222,422,423,406,420,419,417,418,421,551,540,550,552,553,549,470,468,467,465,469,475,464,471,474,472,473,466,516,515,517,514,555,556,557,554,664,599,659,603,604,640,639,641,642,644,646,647,649,648,665,592,597,643,650,602,598,651,652,645,653,660,661,601,662,600,663,47,48,49,50,51,52,43,53,54,55,44,45,46],"latestChangedDtsFile":"./types/index.d.ts"},"version":"4.9.5"} -\ No newline at end of file -+{"program":{"fileNames":["../../../node_modules/typescript/lib/lib.es5.d.ts","../../../node_modules/typescript/lib/lib.es2015.d.ts","../../../node_modules/typescript/lib/lib.es2016.d.ts","../../../node_modules/typescript/lib/lib.es2017.d.ts","../../../node_modules/typescript/lib/lib.es2018.d.ts","../../../node_modules/typescript/lib/lib.es2019.d.ts","../../../node_modules/typescript/lib/lib.es2020.d.ts","../../../node_modules/typescript/lib/lib.dom.d.ts","../../../node_modules/typescript/lib/lib.es2015.core.d.ts","../../../node_modules/typescript/lib/lib.es2015.collection.d.ts","../../../node_modules/typescript/lib/lib.es2015.generator.d.ts","../../../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../../node_modules/typescript/lib/lib.es2015.promise.d.ts","../../../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../../node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../../node_modules/typescript/lib/lib.es2017.object.d.ts","../../../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../../node_modules/typescript/lib/lib.es2017.string.d.ts","../../../node_modules/typescript/lib/lib.es2017.intl.d.ts","../../../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../../node_modules/typescript/lib/lib.es2018.intl.d.ts","../../../node_modules/typescript/lib/lib.es2018.promise.d.ts","../../../node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../../node_modules/typescript/lib/lib.es2019.array.d.ts","../../../node_modules/typescript/lib/lib.es2019.object.d.ts","../../../node_modules/typescript/lib/lib.es2019.string.d.ts","../../../node_modules/typescript/lib/lib.es2019.symbol.d.ts","../../../node_modules/typescript/lib/lib.es2019.intl.d.ts","../../../node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../../node_modules/typescript/lib/lib.es2020.date.d.ts","../../../node_modules/typescript/lib/lib.es2020.promise.d.ts","../../../node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../../../node_modules/typescript/lib/lib.es2020.string.d.ts","../../../node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../../../node_modules/typescript/lib/lib.es2020.intl.d.ts","../../../node_modules/typescript/lib/lib.es2020.number.d.ts","../../../node_modules/typescript/lib/lib.esnext.intl.d.ts","../../../types/eth-ens-namehash.d.ts","../../../types/ethereum-ens-network-map.d.ts","../../../types/global.d.ts","../../../types/single-call-balance-checker-abi.d.ts","../../../types/@metamask/contract-metadata.d.ts","../../../types/@metamask/eth-hd-keyring.d.ts","../../../types/@metamask/eth-simple-keyring.d.ts","../../../types/@metamask/ethjs-provider-http.d.ts","../../../types/@metamask/ethjs-unit.d.ts","../../../types/@metamask/metamask-eth-abis.d.ts","../../../types/eth-json-rpc-infura/src/createprovider.d.ts","../../../types/eth-phishing-detect/src/config.json.d.ts","../../../types/eth-phishing-detect/src/detector.d.ts","../../../node_modules/@types/node/assert.d.ts","../../../node_modules/@types/node/assert/strict.d.ts","../../../node_modules/@types/node/globals.d.ts","../../../node_modules/@types/node/async_hooks.d.ts","../../../node_modules/@types/node/buffer.d.ts","../../../node_modules/@types/node/child_process.d.ts","../../../node_modules/@types/node/cluster.d.ts","../../../node_modules/@types/node/console.d.ts","../../../node_modules/@types/node/constants.d.ts","../../../node_modules/@types/node/crypto.d.ts","../../../node_modules/@types/node/dgram.d.ts","../../../node_modules/@types/node/diagnostics_channel.d.ts","../../../node_modules/@types/node/dns.d.ts","../../../node_modules/@types/node/dns/promises.d.ts","../../../node_modules/@types/node/dom-events.d.ts","../../../node_modules/@types/node/domain.d.ts","../../../node_modules/@types/node/events.d.ts","../../../node_modules/@types/node/fs.d.ts","../../../node_modules/@types/node/fs/promises.d.ts","../../../node_modules/@types/node/http.d.ts","../../../node_modules/@types/node/http2.d.ts","../../../node_modules/@types/node/https.d.ts","../../../node_modules/@types/node/inspector.d.ts","../../../node_modules/@types/node/module.d.ts","../../../node_modules/@types/node/net.d.ts","../../../node_modules/@types/node/os.d.ts","../../../node_modules/@types/node/path.d.ts","../../../node_modules/@types/node/perf_hooks.d.ts","../../../node_modules/@types/node/process.d.ts","../../../node_modules/@types/node/punycode.d.ts","../../../node_modules/@types/node/querystring.d.ts","../../../node_modules/@types/node/readline.d.ts","../../../node_modules/@types/node/repl.d.ts","../../../node_modules/@types/node/stream.d.ts","../../../node_modules/@types/node/stream/promises.d.ts","../../../node_modules/@types/node/stream/consumers.d.ts","../../../node_modules/@types/node/stream/web.d.ts","../../../node_modules/@types/node/string_decoder.d.ts","../../../node_modules/@types/node/test.d.ts","../../../node_modules/@types/node/timers.d.ts","../../../node_modules/@types/node/timers/promises.d.ts","../../../node_modules/@types/node/tls.d.ts","../../../node_modules/@types/node/trace_events.d.ts","../../../node_modules/@types/node/tty.d.ts","../../../node_modules/@types/node/url.d.ts","../../../node_modules/@types/node/util.d.ts","../../../node_modules/@types/node/v8.d.ts","../../../node_modules/@types/node/vm.d.ts","../../../node_modules/@types/node/wasi.d.ts","../../../node_modules/@types/node/worker_threads.d.ts","../../../node_modules/@types/node/zlib.d.ts","../../../node_modules/@types/node/globals.global.d.ts","../../../node_modules/@types/node/index.d.ts","../../../node_modules/@ethereumjs/common/dist/enums.d.ts","../../../node_modules/@ethereumjs/common/dist/types.d.ts","../../../node_modules/buffer/index.d.ts","../../../node_modules/@ethereumjs/util/dist/constants.d.ts","../../../node_modules/@ethereumjs/util/dist/units.d.ts","../../../node_modules/@ethereumjs/util/dist/address.d.ts","../../../node_modules/@ethereumjs/util/dist/bytes.d.ts","../../../node_modules/@ethereumjs/util/dist/types.d.ts","../../../node_modules/@ethereumjs/util/dist/account.d.ts","../../../node_modules/@ethereumjs/util/dist/withdrawal.d.ts","../../../node_modules/@ethereumjs/util/dist/signature.d.ts","../../../node_modules/@ethereumjs/util/dist/encoding.d.ts","../../../node_modules/@ethereumjs/util/dist/asynceventemitter.d.ts","../../../node_modules/@ethereumjs/util/dist/internal.d.ts","../../../node_modules/@ethereumjs/util/dist/lock.d.ts","../../../node_modules/@ethereumjs/util/dist/provider.d.ts","../../../node_modules/@ethereumjs/util/dist/index.d.ts","../../../node_modules/@ethereumjs/common/dist/common.d.ts","../../../node_modules/@ethereumjs/common/dist/utils.d.ts","../../../node_modules/@ethereumjs/common/dist/index.d.ts","../../../node_modules/@ethereumjs/tx/dist/eip2930transaction.d.ts","../../../node_modules/@ethereumjs/tx/dist/legacytransaction.d.ts","../../../node_modules/@ethereumjs/tx/dist/types.d.ts","../../../node_modules/@ethereumjs/tx/dist/basetransaction.d.ts","../../../node_modules/@ethereumjs/tx/dist/eip1559transaction.d.ts","../../../node_modules/@ethereumjs/tx/dist/transactionfactory.d.ts","../../../node_modules/@ethereumjs/tx/dist/index.d.ts","../../base-controller/dist/types/basecontrollerv1.d.ts","../../../node_modules/superstruct/dist/error.d.ts","../../../node_modules/superstruct/dist/utils.d.ts","../../../node_modules/superstruct/dist/struct.d.ts","../../../node_modules/superstruct/dist/structs/coercions.d.ts","../../../node_modules/superstruct/dist/structs/refinements.d.ts","../../../node_modules/superstruct/dist/structs/types.d.ts","../../../node_modules/superstruct/dist/structs/utilities.d.ts","../../../node_modules/superstruct/dist/index.d.ts","../../../node_modules/@metamask/utils/dist/types/assert.d.ts","../../../node_modules/@metamask/utils/dist/types/base64.d.ts","../../../node_modules/@metamask/utils/dist/types/hex.d.ts","../../../node_modules/@metamask/utils/dist/types/bytes.d.ts","../../../node_modules/@metamask/utils/dist/types/caip-types.d.ts","../../../node_modules/@metamask/utils/dist/types/checksum.d.ts","../../../node_modules/@metamask/utils/dist/types/coercers.d.ts","../../../node_modules/@metamask/utils/dist/types/collections.d.ts","../../../node_modules/@metamask/utils/dist/types/encryption-types.d.ts","../../../node_modules/@metamask/utils/dist/types/errors.d.ts","../../../node_modules/@metamask/utils/dist/types/json.d.ts","../../../node_modules/@metamask/utils/dist/types/keyring.d.ts","../../../node_modules/@types/ms/index.d.ts","../../../node_modules/@types/debug/index.d.ts","../../../node_modules/@metamask/utils/dist/types/logging.d.ts","../../../node_modules/@metamask/utils/dist/types/misc.d.ts","../../../node_modules/@metamask/utils/dist/types/number.d.ts","../../../node_modules/@metamask/utils/dist/types/opaque.d.ts","../../../node_modules/@metamask/utils/dist/types/promise.d.ts","../../../node_modules/@metamask/utils/dist/types/time.d.ts","../../../node_modules/@metamask/utils/dist/types/transaction-types.d.ts","../../../node_modules/@metamask/utils/dist/types/versions.d.ts","../../../node_modules/@metamask/utils/dist/types/index.d.ts","../../../node_modules/immer/dist/utils/env.d.ts","../../../node_modules/immer/dist/utils/errors.d.ts","../../../node_modules/immer/dist/types/types-external.d.ts","../../../node_modules/immer/dist/types/types-internal.d.ts","../../../node_modules/immer/dist/utils/common.d.ts","../../../node_modules/immer/dist/utils/plugins.d.ts","../../../node_modules/immer/dist/core/scope.d.ts","../../../node_modules/immer/dist/core/finalize.d.ts","../../../node_modules/immer/dist/core/proxy.d.ts","../../../node_modules/immer/dist/core/immerclass.d.ts","../../../node_modules/immer/dist/core/current.d.ts","../../../node_modules/immer/dist/internal.d.ts","../../../node_modules/immer/dist/plugins/es5.d.ts","../../../node_modules/immer/dist/plugins/patches.d.ts","../../../node_modules/immer/dist/plugins/mapset.d.ts","../../../node_modules/immer/dist/plugins/all.d.ts","../../../node_modules/immer/dist/immer.d.ts","../../base-controller/dist/types/restrictedcontrollermessenger.d.ts","../../base-controller/dist/types/controllermessenger.d.ts","../../base-controller/dist/types/basecontrollerv2.d.ts","../../base-controller/dist/types/index.d.ts","../../../node_modules/@metamask/keyring-api/dist/api/account.d.ts","../../../node_modules/@metamask/keyring-api/dist/api/balance.d.ts","../../../node_modules/@metamask/keyring-api/dist/api/caip.d.ts","../../../node_modules/@metamask/keyring-api/dist/api/export.d.ts","../../../node_modules/@metamask/keyring-api/dist/superstruct.d.ts","../../../node_modules/@metamask/keyring-api/dist/api/request.d.ts","../../../node_modules/@metamask/keyring-api/dist/api/response.d.ts","../../../node_modules/@metamask/keyring-api/dist/api/keyring.d.ts","../../../node_modules/@metamask/keyring-api/dist/api/index.d.ts","../../../node_modules/@metamask/keyring-api/dist/btc/types.d.ts","../../../node_modules/@metamask/keyring-api/dist/btc/index.d.ts","../../../node_modules/@metamask/keyring-api/dist/contexts.d.ts","../../../node_modules/@metamask/keyring-api/dist/eth/erc4337/types.d.ts","../../../node_modules/@metamask/keyring-api/dist/eth/erc4337/index.d.ts","../../../node_modules/@metamask/keyring-api/dist/eth/types.d.ts","../../../node_modules/@metamask/keyring-api/dist/eth/utils.d.ts","../../../node_modules/@metamask/keyring-api/dist/eth/index.d.ts","../../../node_modules/@metamask/keyring-api/dist/events.d.ts","../../../node_modules/@metamask/keyring-api/dist/internal/api.d.ts","../../../node_modules/@metamask/keyring-api/dist/internal/eth/ethkeyring.d.ts","../../../node_modules/@metamask/keyring-api/dist/internal/eth/index.d.ts","../../../node_modules/@metamask/keyring-api/dist/internal/events.d.ts","../../../node_modules/@metamask/keyring-api/dist/internal/rpc.d.ts","../../../node_modules/@metamask/keyring-api/dist/internal/types.d.ts","../../../node_modules/@metamask/keyring-api/dist/internal/index.d.ts","../../../node_modules/@metamask/keyring-api/dist/jsonrpcrequest.d.ts","../../../node_modules/@metamask/keyring-api/dist/keyringclient.d.ts","../../../node_modules/@metamask/safe-event-emitter/dist/cjs/index.d.ts","../../json-rpc-engine/dist/types/jsonrpcengine.d.ts","../../json-rpc-engine/dist/types/createasyncmiddleware.d.ts","../../json-rpc-engine/dist/types/createscaffoldmiddleware.d.ts","../../json-rpc-engine/dist/types/getuniqueid.d.ts","../../json-rpc-engine/dist/types/idremapmiddleware.d.ts","../../json-rpc-engine/dist/types/mergemiddleware.d.ts","../../json-rpc-engine/dist/types/index.d.ts","../../../node_modules/@metamask/providers/dist/types/utils.d.ts","../../../node_modules/@metamask/providers/dist/types/baseprovider.d.ts","../../../node_modules/@metamask/providers/dist/types/eip6963.d.ts","../../../node_modules/@types/readable-stream/node_modules/safe-buffer/index.d.ts","../../../node_modules/@types/readable-stream/index.d.ts","../../../node_modules/@metamask/providers/dist/types/streamprovider.d.ts","../../../node_modules/@metamask/providers/dist/types/extension-provider/createexternalextensionprovider.d.ts","../../../node_modules/@metamask/providers/dist/types/metamaskinpageprovider.d.ts","../../../node_modules/@metamask/providers/dist/types/initializeinpageprovider.d.ts","../../../node_modules/@metamask/providers/dist/types/shimweb3.d.ts","../../../node_modules/@metamask/providers/dist/types/index.d.ts","../../../node_modules/@metamask/keyring-api/dist/keyringsnaprpcclient.d.ts","../../../node_modules/@metamask/keyring-api/dist/rpc-handler.d.ts","../../../node_modules/@metamask/rpc-errors/dist/types/utils.d.ts","../../../node_modules/@metamask/rpc-errors/dist/types/classes.d.ts","../../../node_modules/@metamask/rpc-errors/dist/types/errors.d.ts","../../../node_modules/@metamask/rpc-errors/dist/types/error-constants.d.ts","../../../node_modules/@metamask/rpc-errors/dist/types/index.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/errors.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/internals/error-wrappers.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/internals/errors.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/internals/helpers.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/internals/structs.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/create-interface.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/dialog.d.ts","../../../node_modules/@metamask/key-tree/dist/constants.d.cts","../../../node_modules/@metamask/key-tree/node_modules/@noble/curves/abstract/modular.d.ts","../../../node_modules/@metamask/key-tree/node_modules/@noble/curves/abstract/utils.d.ts","../../../node_modules/@metamask/key-tree/node_modules/@noble/curves/abstract/curve.d.ts","../../../node_modules/@metamask/key-tree/dist/curves/ed25519.d.cts","../../../node_modules/@metamask/key-tree/dist/curves/ed25519bip32.d.cts","../../../node_modules/@metamask/key-tree/node_modules/@noble/curves/abstract/weierstrass.d.ts","../../../node_modules/@metamask/key-tree/dist/curves/secp256k1.d.cts","../../../node_modules/@metamask/key-tree/dist/curves/curve.d.cts","../../../node_modules/@metamask/key-tree/dist/curves/index.d.cts","../../../node_modules/@metamask/key-tree/dist/utils.d.cts","../../../node_modules/@metamask/key-tree/dist/bip44cointypenode.d.cts","../../../node_modules/@metamask/key-tree/dist/slip10node.d.cts","../../../node_modules/@metamask/key-tree/dist/bip44node.d.cts","../../../node_modules/@metamask/key-tree/dist/derivers/bip32.d.cts","../../../node_modules/@metamask/key-tree/dist/derivers/bip39.d.cts","../../../node_modules/@metamask/key-tree/dist/derivers/cip3.d.cts","../../../node_modules/@metamask/key-tree/dist/derivers/slip10.d.cts","../../../node_modules/@metamask/key-tree/dist/derivers/index.d.cts","../../../node_modules/@metamask/key-tree/dist/index.d.cts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/caip.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/permissions.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/get-bip32-entropy.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/get-bip32-public-key.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/get-bip44-entropy.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/get-client-status.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/get-entropy.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/get-file.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/component.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/address.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/box.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/copyable.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/divider.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/form/button.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/form/option.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/form/dropdown.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/form/input.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/form/field.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/form/form.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/form/index.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/formatting/bold.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/formatting/italic.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/formatting/index.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/heading.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/image.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/link.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/text.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/value.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/row.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/spinner.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/components/index.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/jsx-runtime.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/jsx-dev-runtime.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/validation.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/jsx/index.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/nodes.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/address.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/copyable.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/divider.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/heading.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/image.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/panel.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/spinner.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/text.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/row.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/button.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/input.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/form.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/components/index.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/component.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/ui/index.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/interface.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/get-interface-state.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/get-locale.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/snap.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/get-snaps.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/invoke-snap.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/invoke-keyring.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/manage-accounts.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/manage-state.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/notify.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/request-snaps.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/update-interface.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/methods.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/methods/index.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/provider.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/global.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/images.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/handlers/cronjob.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/handlers/home-page.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/handlers/keyring.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/handlers/lifecycle.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/handlers/name-lookup.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/handlers/rpc-request.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/handlers/transaction.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/handlers/signature.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/handlers/user-input.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/handlers/index.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/types/index.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/internals/jsx.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/internals/svg.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/internals/index.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/error-wrappers.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/images.d.ts","../../../node_modules/@metamask/snaps-sdk/dist/types/index.d.ts","../../../node_modules/@metamask/keyring-api/dist/snap-utils.d.ts","../../../node_modules/@metamask/keyring-api/dist/index.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/patchcbor.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/lib/dataitem.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/lib/cbor-sync.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/lib/index.d.ts","../../../node_modules/@ngraveio/bc-ur/dist/ur.d.ts","../../../node_modules/@ngraveio/bc-ur/dist/urencoder.d.ts","../../../node_modules/@ngraveio/bc-ur/dist/fountainencoder.d.ts","../../../node_modules/@ngraveio/bc-ur/dist/fountaindecoder.d.ts","../../../node_modules/@ngraveio/bc-ur/dist/urdecoder.d.ts","../../../node_modules/@ngraveio/bc-ur/dist/index.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/registrytype.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/registryitem.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/cryptocoininfo.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/pathcomponent.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/cryptokeypath.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/types.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/cryptohdkey.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/cryptoeckey.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/bytes.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/multikey.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/scriptexpression.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/cryptooutput.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/cryptopsbt.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/cryptoaccount.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/decoder/index.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/extended/cryptomultiaccounts.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/errors/index.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/extended/derivationschema.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/extended/keyderivation.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/extended/qrhardwarecall.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/utils.d.ts","../../../node_modules/@keystonehq/bc-ur-registry/dist/index.d.ts","../../../node_modules/@keystonehq/bc-ur-registry-eth/dist/ethsignrequest.d.ts","../../../node_modules/@keystonehq/bc-ur-registry-eth/dist/ethsignature.d.ts","../../../node_modules/@keystonehq/bc-ur-registry-eth/dist/ethnftitem.d.ts","../../../node_modules/@keystonehq/bc-ur-registry-eth/dist/utlis.d.ts","../../../node_modules/@keystonehq/bc-ur-registry-eth/dist/index.d.ts","../../../node_modules/@keystonehq/base-eth-keyring/dist/interactionprovider.d.ts","../../../node_modules/@keystonehq/base-eth-keyring/dist/basekeyring.d.ts","../../../node_modules/@keystonehq/base-eth-keyring/dist/index.d.ts","../../../node_modules/@metamask/obs-store/dist/observablestore.d.ts","../../../node_modules/@metamask/obs-store/dist/asstream.d.ts","../../../node_modules/@metamask/obs-store/dist/composedstore.d.ts","../../../node_modules/@metamask/obs-store/dist/mergedstore.d.ts","../../../node_modules/@metamask/obs-store/dist/transform.d.ts","../../../node_modules/@metamask/obs-store/dist/index.d.ts","../../../node_modules/@keystonehq/metamask-airgapped-keyring/dist/metamaskinteractionprovider.d.ts","../../../node_modules/@keystonehq/metamask-airgapped-keyring/dist/metamaskkeyring.d.ts","../../../node_modules/@keystonehq/metamask-airgapped-keyring/dist/index.d.ts","../../../node_modules/@metamask/browser-passworder/dist/index.d.ts","../../message-manager/dist/types/abstractmessagemanager.d.ts","../../controller-utils/dist/types/types.d.ts","../../controller-utils/dist/types/constants.d.ts","../../../node_modules/@metamask/eth-query/index.d.ts","../../../node_modules/@types/bn.js/index.d.ts","../../controller-utils/dist/types/util.d.ts","../../../node_modules/@spruceid/siwe-parser/dist/abnf.d.ts","../../../node_modules/@spruceid/siwe-parser/dist/utils.d.ts","../../../node_modules/@spruceid/siwe-parser/dist/parsers.d.ts","../../controller-utils/dist/types/siwe.d.ts","../../controller-utils/dist/types/index.d.ts","../../message-manager/dist/types/personalmessagemanager.d.ts","../../message-manager/dist/types/typedmessagemanager.d.ts","../../message-manager/dist/types/encryptionpublickeymanager.d.ts","../../message-manager/dist/types/decryptmessagemanager.d.ts","../../message-manager/dist/types/index.d.ts","../../keyring-controller/dist/types/keyringcontroller.d.ts","../../keyring-controller/dist/types/index.d.ts","../../../node_modules/@metamask/object-multiplex/dist/substream.d.ts","../../../node_modules/@metamask/object-multiplex/dist/objectmultiplex.d.ts","../../../node_modules/@metamask/object-multiplex/dist/index.d.ts","../../../node_modules/@metamask/post-message-stream/dist/utils.d.ts","../../../node_modules/@metamask/post-message-stream/dist/basepostmessagestream.d.ts","../../../node_modules/@metamask/post-message-stream/dist/window/windowpostmessagestream.d.ts","../../../node_modules/@metamask/post-message-stream/dist/webworker/webworkerpostmessagestream.d.ts","../../../node_modules/@metamask/post-message-stream/dist/webworker/webworkerparentpostmessagestream.d.ts","../../../node_modules/@metamask/post-message-stream/dist/node-process/processparentmessagestream.d.ts","../../../node_modules/@metamask/post-message-stream/dist/node-process/processmessagestream.d.ts","../../../node_modules/@metamask/post-message-stream/dist/node-thread/threadparentmessagestream.d.ts","../../../node_modules/@metamask/post-message-stream/dist/node-thread/threadmessagestream.d.ts","../../../node_modules/@metamask/post-message-stream/dist/runtime/browserruntimepostmessagestream.d.ts","../../../node_modules/@metamask/post-message-stream/dist/index.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/array.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/auxiliary-files.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/virtual-file/virtualfile.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/virtual-file/index.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/base64.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/bytes.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/caveats.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/checksum.d.ts","../../../node_modules/cron-parser/types/common.d.ts","../../../node_modules/cron-parser/types/index.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/cronjob.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/deep-clone.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/default-endowments.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/derivation-paths.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/entropy.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/errors.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/handler-types.d.ts","../../../node_modules/@metamask/snaps-sdk/jsx.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/handlers.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/iframe.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/json.d.ts","../../../node_modules/nanoid/index.d.ts","../../approval-controller/dist/types/approvalcontroller.d.ts","../../approval-controller/dist/types/errors.d.ts","../../approval-controller/dist/types/index.d.ts","../../../node_modules/@types/deep-freeze-strict/index.d.ts","../../permission-controller/src/permission-middleware.ts","../../permission-controller/src/subjectmetadatacontroller.ts","../../permission-controller/src/utils.ts","../../permission-controller/src/permissioncontroller.ts","../../permission-controller/src/permission.ts","../../permission-controller/src/errors.ts","../../permission-controller/src/caveat.ts","../../permission-controller/src/rpc-methods/getpermissions.ts","../../permission-controller/src/rpc-methods/requestpermissions.ts","../../permission-controller/src/rpc-methods/revokepermissions.ts","../../permission-controller/src/rpc-methods/index.ts","../../permission-controller/src/index.ts","../../../node_modules/@metamask/snaps-utils/dist/types/json-rpc.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/structs.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/manifest/validation.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/manifest/index.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/localization.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/logging.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/namespace.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/path.d.ts","../../../node_modules/@metamask/snaps-registry/dist/verify.d.ts","../../../node_modules/@metamask/snaps-registry/dist/index.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/types.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/snaps.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/strings.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/ui.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/validation.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/versions.d.ts","../../../node_modules/@metamask/snaps-utils/dist/types/index.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/snaps/timer.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/services/executionservice.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/services/abstractexecutionservice.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/services/proxypostmessagestream.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/services/iframe/iframeexecutionservice.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/services/iframe/index.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/services/proxy/proxyexecutionservice.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/services/offscreen/offscreenexecutionservice.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/services/offscreen/index.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/services/webworker/webworkerexecutionservice.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/services/webworker/index.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/services/index.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/snaps/location/npm.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/snaps/location/location.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/snaps/location/http.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/snaps/location/local.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/snaps/location/index.d.ts","../../../node_modules/@xstate/fsm/lib/types.d.ts","../../../node_modules/@xstate/fsm/lib/index.d.ts","../../../node_modules/@types/punycode/index.d.ts","../../../node_modules/fastest-levenshtein/mod.d.ts","../../phishing-controller/src/utils.ts","../../phishing-controller/src/phishingdetector.ts","../../phishing-controller/src/phishingcontroller.ts","../../phishing-controller/src/index.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/interface/snapinterfacecontroller.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/interface/index.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/types/encryptor.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/types/index.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/snaps/registry/registry.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/snaps/registry/json.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/snaps/registry/index.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/snaps/snapcontroller.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/snaps/selectors.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/snaps/index.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/utils.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/cronjob/cronjobcontroller.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/cronjob/index.d.ts","../../../node_modules/@metamask/snaps-controllers/dist/types/index.d.ts","../../accounts-controller/dist/types/accountscontroller.d.ts","../../../node_modules/@types/uuid/index.d.ts","../../accounts-controller/dist/types/utils.d.ts","../../accounts-controller/dist/types/index.d.ts","../../../node_modules/@metamask/swappable-obj-proxy/dist/types.d.ts","../../../node_modules/@metamask/swappable-obj-proxy/dist/createeventemitterproxy.d.ts","../../../node_modules/@metamask/swappable-obj-proxy/dist/createswappableproxy.d.ts","../../../node_modules/@metamask/swappable-obj-proxy/dist/index.d.ts","../../network-controller/dist/types/constants.d.ts","../../eth-json-rpc-provider/dist/types/safe-event-emitter-provider.d.ts","../../eth-json-rpc-provider/dist/types/provider-from-engine.d.ts","../../eth-json-rpc-provider/dist/types/provider-from-middleware.d.ts","../../eth-json-rpc-provider/dist/types/index.d.ts","../../../node_modules/@metamask/eth-block-tracker/dist/blocktracker.d.ts","../../../node_modules/@metamask/eth-block-tracker/dist/pollingblocktracker.d.ts","../../../node_modules/@metamask/eth-block-tracker/dist/subscribeblocktracker.d.ts","../../../node_modules/@metamask/eth-block-tracker/dist/index.d.ts","../../network-controller/dist/types/types.d.ts","../../network-controller/dist/types/create-auto-managed-network-client.d.ts","../../network-controller/dist/types/networkcontroller.d.ts","../../network-controller/dist/types/create-network-client.d.ts","../../network-controller/dist/types/index.d.ts","../../polling-controller/dist/types/types.d.ts","../../polling-controller/dist/types/blocktrackerpollingcontroller.d.ts","../../polling-controller/dist/types/staticintervalpollingcontroller.d.ts","../../polling-controller/dist/types/index.d.ts","../../gas-fee-controller/dist/types/gasfeecontroller.d.ts","../../gas-fee-controller/dist/types/index.d.ts","../../../node_modules/@metamask/nonce-tracker/node_modules/async-mutex/lib/mutexinterface.d.ts","../../../node_modules/@metamask/nonce-tracker/node_modules/async-mutex/lib/mutex.d.ts","../../../node_modules/@metamask/nonce-tracker/node_modules/async-mutex/lib/semaphoreinterface.d.ts","../../../node_modules/@metamask/nonce-tracker/node_modules/async-mutex/lib/semaphore.d.ts","../../../node_modules/@metamask/nonce-tracker/node_modules/async-mutex/lib/withtimeout.d.ts","../../../node_modules/@metamask/nonce-tracker/node_modules/async-mutex/lib/tryacquire.d.ts","../../../node_modules/@metamask/nonce-tracker/node_modules/async-mutex/lib/errors.d.ts","../../../node_modules/@metamask/nonce-tracker/node_modules/async-mutex/lib/index.d.ts","../../../node_modules/@metamask/nonce-tracker/dist/noncetracker.d.ts","../../../node_modules/@metamask/nonce-tracker/dist/index.d.ts","../../../node_modules/async-mutex/lib/mutexinterface.d.ts","../../../node_modules/async-mutex/lib/mutex.d.ts","../../../node_modules/async-mutex/lib/semaphoreinterface.d.ts","../../../node_modules/async-mutex/lib/semaphore.d.ts","../../../node_modules/async-mutex/lib/withtimeout.d.ts","../../../node_modules/async-mutex/lib/tryacquire.d.ts","../../../node_modules/async-mutex/lib/errors.d.ts","../../../node_modules/async-mutex/lib/index.d.ts","../../../node_modules/eth-method-registry/dist/index.d.ts","../../../node_modules/@types/lodash/common/common.d.ts","../../../node_modules/@types/lodash/common/array.d.ts","../../../node_modules/@types/lodash/common/collection.d.ts","../../../node_modules/@types/lodash/common/date.d.ts","../../../node_modules/@types/lodash/common/function.d.ts","../../../node_modules/@types/lodash/common/lang.d.ts","../../../node_modules/@types/lodash/common/math.d.ts","../../../node_modules/@types/lodash/common/number.d.ts","../../../node_modules/@types/lodash/common/object.d.ts","../../../node_modules/@types/lodash/common/seq.d.ts","../../../node_modules/@types/lodash/common/string.d.ts","../../../node_modules/@types/lodash/common/util.d.ts","../../../node_modules/@types/lodash/index.d.ts","../src/logger.ts","../../../node_modules/fast-json-patch/module/helpers.d.ts","../../../node_modules/fast-json-patch/module/core.d.ts","../../../node_modules/fast-json-patch/module/duplex.d.ts","../../../node_modules/fast-json-patch/index.d.ts","../src/types.ts","../src/utils/gas-flow.ts","../src/constants.ts","../src/utils/utils.ts","../src/utils/swaps.ts","../src/utils/gas-fees.ts","../src/gas-flows/defaultgasfeeflow.ts","../src/gas-flows/lineagasfeeflow.ts","../../../node_modules/@ethersproject/bytes/lib/index.d.ts","../../../node_modules/@ethersproject/bignumber/lib/bignumber.d.ts","../../../node_modules/@ethersproject/bignumber/lib/fixednumber.d.ts","../../../node_modules/@ethersproject/bignumber/lib/index.d.ts","../../../node_modules/@ethersproject/abi/lib/fragments.d.ts","../../../node_modules/@ethersproject/abi/lib/coders/abstract-coder.d.ts","../../../node_modules/@ethersproject/abi/lib/abi-coder.d.ts","../../../node_modules/@ethersproject/properties/lib/index.d.ts","../../../node_modules/@ethersproject/abi/lib/interface.d.ts","../../../node_modules/@ethersproject/abi/lib/index.d.ts","../../../node_modules/@ethersproject/networks/lib/types.d.ts","../../../node_modules/@ethersproject/networks/lib/index.d.ts","../../../node_modules/@ethersproject/transactions/lib/index.d.ts","../../../node_modules/@ethersproject/web/lib/index.d.ts","../../../node_modules/@ethersproject/abstract-provider/lib/index.d.ts","../../../node_modules/@ethersproject/abstract-signer/lib/index.d.ts","../../../node_modules/@ethersproject/contracts/lib/index.d.ts","../../../node_modules/@ethersproject/providers/lib/formatter.d.ts","../../../node_modules/@ethersproject/providers/lib/base-provider.d.ts","../../../node_modules/@ethersproject/providers/lib/json-rpc-provider.d.ts","../../../node_modules/@ethersproject/providers/lib/websocket-provider.d.ts","../../../node_modules/@ethersproject/providers/lib/url-json-rpc-provider.d.ts","../../../node_modules/@ethersproject/providers/lib/alchemy-provider.d.ts","../../../node_modules/@ethersproject/providers/lib/ankr-provider.d.ts","../../../node_modules/@ethersproject/providers/lib/cloudflare-provider.d.ts","../../../node_modules/@ethersproject/providers/lib/etherscan-provider.d.ts","../../../node_modules/@ethersproject/providers/lib/fallback-provider.d.ts","../../../node_modules/@ethersproject/providers/lib/ipc-provider.d.ts","../../../node_modules/@ethersproject/providers/lib/infura-provider.d.ts","../../../node_modules/@ethersproject/providers/lib/json-rpc-batch-provider.d.ts","../../../node_modules/@ethersproject/providers/lib/nodesmith-provider.d.ts","../../../node_modules/@ethersproject/providers/lib/pocket-provider.d.ts","../../../node_modules/@ethersproject/providers/lib/web3-provider.d.ts","../../../node_modules/@ethersproject/providers/lib/index.d.ts","../src/gas-flows/oraclelayer1gasfeeflow.ts","../src/gas-flows/optimismlayer1gasfeeflow.ts","../src/gas-flows/scrolllayer1gasfeeflow.ts","../src/gas-flows/testgasfeeflow.ts","../src/utils/etherscan.ts","../src/helpers/etherscanremotetransactionsource.ts","../src/utils/layer1-gas-fee-flow.ts","../src/helpers/gasfeepoller.ts","../src/helpers/incomingtransactionhelper.ts","../src/helpers/pendingtransactiontracker.ts","../src/helpers/multichaintrackinghelper.ts","../src/utils/external-transactions.ts","../src/utils/gas.ts","../src/utils/history.ts","../src/utils/nonce.ts","../../../node_modules/@metamask/metamask-eth-abis/dist/abis/abierc20.d.ts","../../../node_modules/@metamask/metamask-eth-abis/dist/abis/abierc721.d.ts","../../../node_modules/@metamask/metamask-eth-abis/dist/abis/abierc1155.d.ts","../../../node_modules/@metamask/metamask-eth-abis/dist/abis/fiattokenv2.d.ts","../../../node_modules/@metamask/metamask-eth-abis/dist/index.d.ts","../src/errors.ts","../src/utils/simulation-api.ts","../src/utils/simulation.ts","../src/utils/transaction-type.ts","../src/utils/validation.ts","../src/transactioncontroller.ts","../src/index.ts","../../../node_modules/@babel/types/lib/index.d.ts","../../../node_modules/@types/babel__generator/index.d.ts","../../../node_modules/@babel/parser/typings/babel-parser.d.ts","../../../node_modules/@types/babel__template/index.d.ts","../../../node_modules/@types/babel__traverse/index.d.ts","../../../node_modules/@types/babel__core/index.d.ts","../../../node_modules/@types/eslint/helpers.d.ts","../../../node_modules/@types/estree/index.d.ts","../../../node_modules/@types/json-schema/index.d.ts","../../../node_modules/@types/eslint/index.d.ts","../../../node_modules/@types/graceful-fs/index.d.ts","../../../node_modules/@types/istanbul-lib-coverage/index.d.ts","../../../node_modules/@types/istanbul-lib-report/index.d.ts","../../../node_modules/@types/istanbul-reports/index.d.ts","../../../node_modules/chalk/index.d.ts","../../../node_modules/jest-diff/build/cleanupsemantic.d.ts","../../../node_modules/pretty-format/build/types.d.ts","../../../node_modules/pretty-format/build/index.d.ts","../../../node_modules/jest-diff/build/types.d.ts","../../../node_modules/jest-diff/build/difflines.d.ts","../../../node_modules/jest-diff/build/printdiffs.d.ts","../../../node_modules/jest-diff/build/index.d.ts","../../../node_modules/jest-matcher-utils/build/index.d.ts","../../../node_modules/@types/jest/index.d.ts","../../../node_modules/@types/jest-when/index.d.ts","../../../node_modules/@types/json5/index.d.ts","../../../node_modules/@types/minimatch/index.d.ts","../../../node_modules/@types/parse-json/index.d.ts","../../../node_modules/@types/pbkdf2/index.d.ts","../../../node_modules/@types/prettier/index.d.ts","../../../node_modules/@types/secp256k1/index.d.ts","../../../node_modules/@types/semver/classes/semver.d.ts","../../../node_modules/@types/semver/functions/parse.d.ts","../../../node_modules/@types/semver/functions/valid.d.ts","../../../node_modules/@types/semver/functions/clean.d.ts","../../../node_modules/@types/semver/functions/inc.d.ts","../../../node_modules/@types/semver/functions/diff.d.ts","../../../node_modules/@types/semver/functions/major.d.ts","../../../node_modules/@types/semver/functions/minor.d.ts","../../../node_modules/@types/semver/functions/patch.d.ts","../../../node_modules/@types/semver/functions/prerelease.d.ts","../../../node_modules/@types/semver/functions/compare.d.ts","../../../node_modules/@types/semver/functions/rcompare.d.ts","../../../node_modules/@types/semver/functions/compare-loose.d.ts","../../../node_modules/@types/semver/functions/compare-build.d.ts","../../../node_modules/@types/semver/functions/sort.d.ts","../../../node_modules/@types/semver/functions/rsort.d.ts","../../../node_modules/@types/semver/functions/gt.d.ts","../../../node_modules/@types/semver/functions/lt.d.ts","../../../node_modules/@types/semver/functions/eq.d.ts","../../../node_modules/@types/semver/functions/neq.d.ts","../../../node_modules/@types/semver/functions/gte.d.ts","../../../node_modules/@types/semver/functions/lte.d.ts","../../../node_modules/@types/semver/functions/cmp.d.ts","../../../node_modules/@types/semver/functions/coerce.d.ts","../../../node_modules/@types/semver/classes/comparator.d.ts","../../../node_modules/@types/semver/classes/range.d.ts","../../../node_modules/@types/semver/functions/satisfies.d.ts","../../../node_modules/@types/semver/ranges/max-satisfying.d.ts","../../../node_modules/@types/semver/ranges/min-satisfying.d.ts","../../../node_modules/@types/semver/ranges/to-comparators.d.ts","../../../node_modules/@types/semver/ranges/min-version.d.ts","../../../node_modules/@types/semver/ranges/valid.d.ts","../../../node_modules/@types/semver/ranges/outside.d.ts","../../../node_modules/@types/semver/ranges/gtr.d.ts","../../../node_modules/@types/semver/ranges/ltr.d.ts","../../../node_modules/@types/semver/ranges/intersects.d.ts","../../../node_modules/@types/semver/ranges/simplify.d.ts","../../../node_modules/@types/semver/ranges/subset.d.ts","../../../node_modules/@types/semver/internals/identifiers.d.ts","../../../node_modules/@types/semver/index.d.ts","../../../node_modules/@types/sinonjs__fake-timers/index.d.ts","../../../node_modules/@types/sinon/index.d.ts","../../../node_modules/@types/stack-utils/index.d.ts","../../../node_modules/@types/yargs-parser/index.d.ts","../../../node_modules/@types/yargs/index.d.ts"],"fileInfos":[{"version":"8730f4bf322026ff5229336391a18bcaa1f94d4f82416c8b2f3954e2ccaae2ba","affectsGlobalScope":true},"dc47c4fa66b9b9890cf076304de2a9c5201e94b740cffdf09f87296d877d71f6","7a387c58583dfca701b6c85e0adaf43fb17d590fb16d5b2dc0a2fbd89f35c467","8a12173c586e95f4433e0c6dc446bc88346be73ffe9ca6eec7aa63c8f3dca7f9","5f4e733ced4e129482ae2186aae29fde948ab7182844c3a5a51dd346182c7b06","4b421cbfb3a38a27c279dec1e9112c3d1da296f77a1a85ddadf7e7a425d45d18","1fc5ab7a764205c68fa10d381b08417795fc73111d6dd16b5b1ed36badb743d9",{"version":"3aafcb693fe5b5c3bd277bd4c3a617b53db474fe498fc5df067c5603b1eebde7","affectsGlobalScope":true},{"version":"adb996790133eb33b33aadb9c09f15c2c575e71fb57a62de8bf74dbf59ec7dfb","affectsGlobalScope":true},{"version":"8cc8c5a3bac513368b0157f3d8b31cfdcfe78b56d3724f30f80ed9715e404af8","affectsGlobalScope":true},{"version":"cdccba9a388c2ee3fd6ad4018c640a471a6c060e96f1232062223063b0a5ac6a","affectsGlobalScope":true},{"version":"c5c05907c02476e4bde6b7e76a79ffcd948aedd14b6a8f56e4674221b0417398","affectsGlobalScope":true},{"version":"5f406584aef28a331c36523df688ca3650288d14f39c5d2e555c95f0d2ff8f6f","affectsGlobalScope":true},{"version":"22f230e544b35349cfb3bd9110b6ef37b41c6d6c43c3314a31bd0d9652fcec72","affectsGlobalScope":true},{"version":"7ea0b55f6b315cf9ac2ad622b0a7813315bb6e97bf4bb3fbf8f8affbca7dc695","affectsGlobalScope":true},{"version":"3013574108c36fd3aaca79764002b3717da09725a36a6fc02eac386593110f93","affectsGlobalScope":true},{"version":"eb26de841c52236d8222f87e9e6a235332e0788af8c87a71e9e210314300410a","affectsGlobalScope":true},{"version":"3be5a1453daa63e031d266bf342f3943603873d890ab8b9ada95e22389389006","affectsGlobalScope":true},{"version":"17bb1fc99591b00515502d264fa55dc8370c45c5298f4a5c2083557dccba5a2a","affectsGlobalScope":true},{"version":"7ce9f0bde3307ca1f944119f6365f2d776d281a393b576a18a2f2893a2d75c98","affectsGlobalScope":true},{"version":"6a6b173e739a6a99629a8594bfb294cc7329bfb7b227f12e1f7c11bc163b8577","affectsGlobalScope":true},{"version":"81cac4cbc92c0c839c70f8ffb94eb61e2d32dc1c3cf6d95844ca099463cf37ea","affectsGlobalScope":true},{"version":"b0124885ef82641903d232172577f2ceb5d3e60aed4da1153bab4221e1f6dd4e","affectsGlobalScope":true},{"version":"0eb85d6c590b0d577919a79e0084fa1744c1beba6fd0d4e951432fa1ede5510a","affectsGlobalScope":true},{"version":"da233fc1c8a377ba9e0bed690a73c290d843c2c3d23a7bd7ec5cd3d7d73ba1e0","affectsGlobalScope":true},{"version":"d154ea5bb7f7f9001ed9153e876b2d5b8f5c2bb9ec02b3ae0d239ec769f1f2ae","affectsGlobalScope":true},{"version":"bb2d3fb05a1d2ffbca947cc7cbc95d23e1d053d6595391bd325deb265a18d36c","affectsGlobalScope":true},{"version":"c80df75850fea5caa2afe43b9949338ce4e2de086f91713e9af1a06f973872b8","affectsGlobalScope":true},{"version":"9d57b2b5d15838ed094aa9ff1299eecef40b190722eb619bac4616657a05f951","affectsGlobalScope":true},{"version":"6c51b5dd26a2c31dbf37f00cfc32b2aa6a92e19c995aefb5b97a3a64f1ac99de","affectsGlobalScope":true},{"version":"6e7997ef61de3132e4d4b2250e75343f487903ddf5370e7ce33cf1b9db9a63ed","affectsGlobalScope":true},{"version":"2ad234885a4240522efccd77de6c7d99eecf9b4de0914adb9a35c0c22433f993","affectsGlobalScope":true},{"version":"5e5e095c4470c8bab227dbbc61374878ecead104c74ab9960d3adcccfee23205","affectsGlobalScope":true},{"version":"09aa50414b80c023553090e2f53827f007a301bc34b0495bfb2c3c08ab9ad1eb","affectsGlobalScope":true},{"version":"d7f680a43f8cd12a6b6122c07c54ba40952b0c8aa140dcfcf32eb9e6cb028596","affectsGlobalScope":true},{"version":"3787b83e297de7c315d55d4a7c546ae28e5f6c0a361b7a1dcec1f1f50a54ef11","affectsGlobalScope":true},{"version":"e7e8e1d368290e9295ef18ca23f405cf40d5456fa9f20db6373a61ca45f75f40","affectsGlobalScope":true},{"version":"faf0221ae0465363c842ce6aa8a0cbda5d9296940a8e26c86e04cc4081eea21e","affectsGlobalScope":true},{"version":"06393d13ea207a1bfe08ec8d7be562549c5e2da8983f2ee074e00002629d1871","affectsGlobalScope":true},{"version":"2768ef564cfc0689a1b76106c421a2909bdff0acbe87da010785adab80efdd5c","affectsGlobalScope":true},{"version":"b248e32ca52e8f5571390a4142558ae4f203ae2f94d5bac38a3084d529ef4e58","affectsGlobalScope":true},{"version":"52d1bb7ab7a3306fd0375c8bff560feed26ed676a5b0457fa8027b563aecb9a4","affectsGlobalScope":true},"70bbfaec021ac4a0c805374225b55d70887f987df8b8dd7711d79464bb7b4385","869089d60b67219f63e6aca810284c89bae1b384b5cbc7ce64e53d82ad223ed5",{"version":"f31113ac9492fdd6e78bf6151b338c92e5b1837be426ef4aa0648ce82d13b518","affectsGlobalScope":true},"62a0875a0397b35a2364f1d401c0ce17975dfa4d47bf6844de858ae04da349f9","ee7491d0318d1fafcba97d5b72b450eb52671570f7a4ecd9e8898d40eaae9472","e3e7d217d89b380c1f34395eadc9289542851b0f0a64007dfe1fb7cf7423d24e","fd79909e93b4d50fd0ed9f3d39ddf8ba0653290bac25c295aac49f6befbd081b","345a9cc2945406f53051cd0e9b51f82e1e53929848eab046fdda91ee8aa7da31","9debe2de883da37a914e5e784a7be54c201b8f1d783822ad6f443ff409a5ea21","dee5d5c5440cda1f3668f11809a5503c30db0476ad117dd450f7ba5a45300e8f","f5e396c1424c391078c866d6f84afe0b4d2f7f85a160b9c756cd63b5b1775d93","5caa6f4fff16066d377d4e254f6c34c16540da3809cd66cd626a303bc33c419f","730d055528bdf12c8524870bb33d237991be9084c57634e56e5d8075f6605e02","5b3cd03ae354ea96eff1f74d7c410fe4852e6382227e8b0ecf87ab5e3a5bbcd4","7394959e5a741b185456e1ef5d64599c36c60a323207450991e7a42e08911419",{"version":"056097110efd16869ec118cedb44ecbac9a019576eee808d61304ca6d5cb2cbe","affectsGlobalScope":true},"f51b4042a3ac86f1f707500a9768f88d0b0c1fc3f3e45a73333283dea720cdc6",{"version":"6fb8358e10ed92a7f515b7d79da3904c955a3ffd4e14aa9df6f0ea113041f1cf","affectsGlobalScope":true},"45c831238c6dac21c72da5f335747736a56a3847192bf03c84b958a7e9ec93e2","661a11d16ad2e3543a77c53bcd4017ee9a450f47ab7def3ab493a86eae4d550c",{"version":"8cdc646cec7819581ef343b83855b1bfe4fe674f2c84f4fb8dc90d82fb56bd3a","affectsGlobalScope":true},"a40826e8476694e90da94aa008283a7de50d1dafd37beada623863f1901cb7fb","9dd56225cc2d8cb8fe5ceb0043ff386987637e12fecc6078896058a99deae284","2375ed4b439215aa3b6d0c6fd175c78a4384b30cb43cbadaecbf0a18954c98cb","7693b90b3075deaccafd5efb467bf9f2b747a3075be888652ef73e64396d8628","41231da15bb5e3e806a8395bd15c7befd2ec90f9f4e3c9d0ae1356bccb76dbb0","fccfef201d057cb407fa515311bd608549bab6c7b8adcf8f2df31f5d3b796478",{"version":"ee1ee365d88c4c6c0c0a5a5701d66ebc27ccd0bcfcfaa482c6e2e7fe7b98edf7","affectsGlobalScope":true},"5f20d20b7607174caf1a6da9141aeb9f2142159ae2410ca30c7a0fccd1d19c99",{"version":"464762c6213566d072f1ced5e8e9a954785ec5e53883b7397198abb5ef5b8f71","affectsGlobalScope":true},"6387920dc3e18927335b086deec75bf8e50f879a5e273d32ee7bb7a55ba50572","9bba37424094688c4663c177a1379b229f919b8912889a472f32fdc5f08ddb4d","29a4be13b3a30d3e66667b75c58ec61fb2df8fa0422534fdee3cfb30c5dbf450","83366d901beda79d6eb37aaaf6ca248dcd88946302b2a7d975590783be51e88e","bf268a0aea37ad4ae3b7a9b58559190b6fc01ea16a31e35cd05817a0a60f895a","43ec77c369473e92e2ecebf0554a0fdaa9c256644a6070f28228dfcceec77351",{"version":"d7dad6db394a3d9f7b49755e4b610fbf8ed6eb0c9810ae5f1a119f6b5d76de45","affectsGlobalScope":true},"95ed02bacb4502c985b69742ec82a4576d4ff4a6620ecc91593f611d502ae546","bf755525c4e6f85a970b98c4755d98e8aa1b6dbd83a5d8fcc57d3d497351b936","dd67d2b5e4e8a182a38de8e69fb736945eaa4588e0909c14e01a14bd3cc1fd1e",{"version":"28084e15b63e6211769db2fe646d8bc5c4c6776321e0deffe2d12eefd52cb6b9","affectsGlobalScope":true},{"version":"aed37dabf86c99d6c8508700576ecede86688397bc12523541858705a0c737c2","affectsGlobalScope":true},"cc6ef5733d4ea6d2e06310a32dffd2c16418b467c5033d49cecc4f3a25de7497","94768454c3348b6ebe48e45fbad8c92e2bb7af4a35243edbe2b90823d0bd7f9a","0be79b3ff0f16b6c2f9bc8c4cc7097ea417d8d67f8267f7e1eec8e32b548c2ff","1c61ffa3a71b77363b30d19832c269ef62fba787f5610cac7254728d3b69ab2e","84da3c28344e621fd1d591f2c09e9595292d2b70018da28a553268ac122597d4","269929a24b2816343a178008ac9ae9248304d92a8ba8e233055e0ed6dbe6ef71","6e191fea1db6e9e4fa828259cf489e820ec9170effff57fb081a2f3295db4722","aed943465fbce1efe49ee16b5ea409050f15cd8eaf116f6fadb64ef0772e7d95","70d08483a67bf7050dbedace398ef3fee9f436fcd60517c97c4c1e22e3c6f3e8","c40fdf7b2e18df49ce0568e37f0292c12807a0748be79e272745e7216bed2606",{"version":"e933de8143e1d12dd51d89b398760fd5a9081896be366dad88a922d0b29f3c69","affectsGlobalScope":true},"4e228e78c1e9b0a75c70588d59288f63a6258e8b1fe4a67b0c53fe03461421d9","b38d55d08708c2410a3039687db70b4a5bfa69fc4845617c313b5a10d9c5c637","205d50c24359ead003dc537b9b65d2a64208dfdffe368f403cf9e0357831db9e","1265fddcd0c68be9d2a3b29805d0280484c961264dd95e0b675f7bd91f777e78",{"version":"a05e2d784c9be7051c4ac87a407c66d2106e23490c18c038bbd0712bde7602fd","affectsGlobalScope":true},{"version":"df90b9d0e9980762da8daf8adf6ffa0c853e76bfd269c377be0d07a9ad87acd2","affectsGlobalScope":true},"cf434b5c04792f62d6f4bdd5e2c8673f36e638e910333c172614d5def9b17f98","1d65d4798df9c2df008884035c41d3e67731f29db5ecb64cd7378797c7c53a2f","0faee6b555890a1cb106e2adc5d3ffd89545b1da894d474e9d436596d654998f","c6c01ea1c42508edf11a36d13b70f6e35774f74355ba5d358354d4a77cc67ea1","867f95abf1df444aab146b19847391fc2f922a55f6a970a27ed8226766cee29f",{"version":"ab9b9a36e5284fd8d3bf2f7d5fcbc60052f25f27e4d20954782099282c60d23e","affectsGlobalScope":true},"b0297b09e607bec9698cac7cf55463d6731406efb1161ee4d448293b47397c84","175323e2a79a6076e0bada8a390d535a3ea817158bf1b1f46e31efca9028a0a2","7a10053aadc19335532a4d02756db4865974fd69bea5439ddcc5bfdf062d9476","4967529644e391115ca5592184d4b63980569adf60ee685f968fd59ab1557188","aed9e712a9b168345362e8f3a949f16c99ca1e05d21328f05735dfdbb24414ef","b04fe6922ed3db93afdbd49cdda8576aa75f744592fceea96fb0d5f32158c4f5","ed8d6c8de90fc2a4faaebc28e91f2469928738efd5208fb75ade0fa607e892b7","d7c52b198d680fe65b1a8d1b001f0173ffa2536ca2e7082431d726ce1f6714cd","c07f251e1c4e415a838e5498380b55cfea94f3513229de292d2aa85ae52fc3e9","0ed401424892d6bf294a5374efe512d6951b54a71e5dd0290c55b6d0d915f6f7","b945be6da6a3616ef3a250bfe223362b1c7c6872e775b0c4d82a1bf7a28ff902","beea49237dd7c7110fabf3c7509919c9cb9da841d847c53cac162dc3479e2f87","0f45f8a529c450d8f394106cc622bff79e44a1716e1ac9c3cc68b43f7ecf65ee","c624ce90b04c27ce4f318ba6330d39bde3d4e306f0f497ce78d4bda5ab8e22ca","9b8253aa5cb2c82d505f72afdbf96e83b15cc6b9a6f4fadbbbab46210d5f1977","86a8f52e4b1ac49155e889376bcfa8528a634c90c27fec65aa0e949f77b740c5","aab5dd41c1e2316cc0b42a7dd15684f8582d5a1d16c0516276a2a8a7d0fecd9c","59948226626ee210045296ba1fc6cb0fe748d1ff613204e08e7157ab6862dee7","ec3e54d8b713c170fdc8110a7e4a6a97513a7ab6b05ac9e1100cb064d2bb7349","43beb30ecb39a603fde4376554887310b0699f25f7f39c5c91e3147b51bb3a26","666b77d7f06f49da114b090a399abbfa66d5b6c01a3fd9dc4f063a52ace28507","31997714a93fbc570f52d47d6a8ebfb021a34a68ea9ba58bbb69cdec9565657e","6032e4262822160128e644de3fc4410bcd7517c2f137525fd2623d2bb23cb0d3","8bd5c9b1016629c144fd228983395b9dbf0676a576716bc3d316cab612c33cd5","2ed90bd3925b23aed8f859ffd0e885250be0424ca2b57e9866dabef152e1d6b7","93f6bd17d92dab9db7897e1430a5aeaa03bcf51623156213d8397710367a76ce","3f62b770a42e8c47c7008726f95aa383e69d97e85e680d237b99fcb0ee601dd8","5b84cfe78028c35c3bb89c042f18bf08d09da11e82d275c378ae4d07d8477e6c","75b22c74010ba649de1a1676a4c4b8b5bb4294fecd05089e2094429b16d7840c","5615ccf831db2ffc82145243081ebdb60ea8e1005ee8f975d1c0c1401a9c894e","38682ed3630bb6ecdace80d5a9adc811fc20a419f1940446e306c3a020d083b9","cc182e6e4f691cd6f7bf7cb491247a4c7818f9f1cb2db1d45c65ff906e3f741b","a50599c08934a62f11657bdbe0dc929ab66da1b1f09974408fd9a33ec1bb8060","5a20e7d6c630b91be15e9b837853173829d00273197481dc8d3e94df61105a71","8d478048d71cc16f806d4b71b252ecb67c7444ccf4f4b09b29a312712184f859","e0eda929c6b9b628cdeb0e54cd3582cb97e64f28aab34612fc1431c545899584","9df4662ca3dbc2522bc115833ee04faa1afbb4e249a85ef4a0a09c621346bd08","b25d9065cf1c1f537a140bbc508e953ed2262f77134574c432d206ff36f4bdbf","1b103313097041aa9cd705a682c652f08613cb5cf8663321061c0902f845e81c","68ccec8662818911d8a12b8ed028bc5729fb4f1d34793c4701265ba60bc73cf4","5f85b8b79dc4d36af672c035b2beb71545de63a5d60bccbeee64c260941672ab","b3d48529ae61dc27d0bfbfa2cb3e0dff8189644bd155bdf5df1e8e14669f7043","40fe4b689225816b31fe5794c0fbf3534568819709e40295ead998a2bc1ab237","f65b5e33b9ad545a1eebbd6afe857314725ad42aaf069913e33f928ab3e4990a","fb6f2a87beb7fb1f4c2b762d0c76a9459fc91f557231569b0ee21399e22aa13d","31c858dc85996fac4b7fa944e1016d5c72f514930a72357ab5001097bf6511c7","3de30a871b3340be8b679c52aa12f90dd1c8c60874517be58968fdbcc4d79445","6fd985bd31eaf77542625306fb0404d32bff978990f0a06428e5f0b9a3b58109","980d21b0081cbf81774083b1e3a46f4bbdcd2b68858df0f66d7fad9c82bc34bc","68cc8d6fcc2f270d7108f02f3ebc59480a54615be3e09a47e14527f349e9d53e","3eb11dbf3489064a47a2e1cf9d261b1f100ef0b3b50ffca6c44dd99d6dd81ac1","b17f3bb7d8333479c7e45e5f3d876761b9bca58f97594eca3f6a944fd825e632","3c1f1236cce6d6e0c4e2c1b4371e6f72d7c14842ecd76a98ed0748ee5730c8f3","6d7f58d5ea72d7834946fd7104a734dc7d40661be8b2e1eaced1ddce3268ebaf","4c26222991e6c97d5a8f541d4f2c67585eda9e8b33cf9f52931b098045236e88","277983d414aa99d78655186c3ee1e1c38c302e336aff1d77b47fcdc39d8273fe","47383b45796d525a4039cd22d2840ac55a1ff03a43d027f7f867ba7314a9cf53","6548773b3abbc18de29176c2141f766d4e437e40596ee480447abf83575445ad","6ddd27af0436ce59dd4c1896e2bfdb2bdb2529847d078b83ce67a144dff05491","816264799aef3fd5a09a3b6c25217d5ec26a9dfc7465eac7d6073bcdc7d88f3f","4df0891b133884cd9ed752d31c7d0ec0a09234e9ed5394abffd3c660761598db","b603b62d3dcd31ef757dc7339b4fa8acdbca318b0fb9ac485f9a1351955615f9","e642bd47b75ad6b53cbf0dfd7ddfa0f120bd10193f0c58ec37d87b59bf604aca","be90b24d2ee6f875ce3aaa482e7c41a54278856b03d04212681c4032df62baf9","78f5ff400b3cb37e7b90eef1ff311253ed31c8cb66505e9828fad099bffde021","372c47090e1131305d163469a895ff2938f33fa73aad988df31cd31743f9efb6","71c67dc6987bdbd5599353f90009ff825dd7db0450ef9a0aee5bb0c574d18512","6f12403b5eca6ae7ca8e3efe3eeb9c683b06ce3e3844ccfd04098d83cd7e4957","282c535df88175d64d9df4550d2fd1176fd940c1c6822f1e7584003237f179d3","c3a4752cf103e4c6034d5bd449c8f9d5e7b352d22a5f8f9a41a8efb11646f9c2","11a9e38611ac3c77c74240c58b6bd64a0032128b29354e999650f1de1e034b1c","4ed103ca6fff9cb244f7c4b86d1eb28ce8069c32db720784329946731badb5bb","d738f282842970e058672663311c6875482ee36607c88b98ffb6604fba99cb2a","ec859cd8226aa623e41bbb47c249a55ee16dc1b8647359585244d57d3a5ed0c7","8891c6e959d253a66434ff5dc9ae46058fb3493e84b4ca39f710ef2d350656b1","c4463cf02535444dcbc3e67ecd29f1972490f74e49957d6fd4282a1013796ba6","0cb0a957ff02de0b25fd0f3f37130ca7f22d1e0dea256569c714c1f73c6791f8","2f5075dc512d51786b1ba3b1696565641dfaae3ac854f5f13d61fa12ef81a47e","ca3353cc82b1981f0d25d71d7432d583a6ef882ccdea82d65fbe49af37be51cb","50679a8e27aacf72f8c40bcab15d7ef5e83494089b4726b83eec4554344d5cdc","45351e0d51780b6f4088277a4457b9879506ee2720a887de232df0f1efcb33d8","e2d6963e7bf7186e30b7a4c9859aba4e96eda6d1be537e5b1a43bdddc7e9dc8f","10afdd7bba6ec9b7f95a4b419b2dbb64245fea4a61bbe7d68e2f841b414f7312","413121b26b3bd9f7fea237f15f564ee2b95bcd0cceec1b1621075375ccc0c0e0","d2af215963d01cef397ce8fa2f7ad08ee8beffdd39fe14b96021ddf26554b59f","2fc9848431d0f5e2b49bb312aaf07dd2d5a34300a2ced60200a2da49e6a82b43","c5ee2b685431ea2b9aacd9bb9e15cac1ecfa5c448972b188432313354d47c848","3e69be1137d88eb0730332aed359caedea4a27903da15dbe6a1615fa11206807","2283d079c3945b6e5ca8b9355311a213e03b74bffc65a3234c3c141a0a795700","f47272f05bd57f4356abc81232bded724d13e54f0fd801e0fb93a58237db1829","07ae8e9890f49ef6ebe629e339ac590025606a1e96754965bbb2bf889199ced2","d5c19655468e29f60c871b21e73af8ebc653f736e7123ade916f22c4a5f80ce5","6a8649609161e2794b383ba275b0a6cb4a072dde7b954648f83dc6cdf1bfe4a8","601d4a40a69c782addaf84185d4547568ec072095ab9976610d89922d1291f8b","d5c19655468e29f60c871b21e73af8ebc653f736e7123ade916f22c4a5f80ce5","b5c9c8c4a2cd1cb9f76d849fb472d475c3cebdd48306414a4a19bd11d82c4055","b61e6a808f5f50873ac03f35d5a38fa8d4dd23a24f80ab69df1a032e8c71562d","c8be9283a381044a392a0687af5d98d3f51cbada2320b1801a82c948b6e39499","85052c71d72b9b017c88179f57a464d66e22619c7acd7d83b117a79cf1608979","502cd7c30fe21e2c36b62913d0cb5d20affc8779b3ad40881b26d90a22de3aaa","6d3101b183ea67ef606b93fe42127f30b2db5ac3b72c34ca9d6d8b00eb85d0f6","f5d7a36ff056cc314b0f61c89a03c4c36a24183b246e61d958e75e86521304cd","ff30e8237e23dde68041b5f09526ee86835b12c5d8b6421c1153093fdbeb9438","f516fc1e77e5ffd71fbe705b679797c3c5eb76bf76a88549e6316a29f3e197f7","b5b1110565ac688b660a893654a6c1bce6747f3aa6f847001a8a5ff4412394ba","3a971ea3e36685b96f24fbd53a94ad8dc061711b84e51fde4cf201f7041e618d","9b6c162d20e2ad4abdcff61a24082564ac59e63092220618162aef6e440c9228","7804ff981554ba1a0592481072806fc39dc1484791beda43eb7a60e16e70a360","fcc8beef29f39f09b1d9c9f99c42f9fed605ab1c28d2a630185f732b9ba53763","d6e6620a30d582182acc3f0a992a0c311adc589f111096aea11ab83fc09a5ccc","6213b8f686f56beab22b59a0f468590fd3a4c5fa931236a017efeca91d7c9584","c451cec9a588b1f105a5ea2c6063d4fca112b9d70105cacdadda0e1ef67e9379","cb047832dc68f5a2c41c62c5e95ddcacbae3a8b034d40cd15319a8cb7f25104a","980336ccdfc3c08f3c3b201aa6662e6016e20f15847f8465b68f3e8e67b4665c","5a3493939995f46ff3d9073cd534fb8961c3bf4e08c71db27066ff03d906dea8","8f333214062532989f190aed5f99c62eb820722e41956e8229e17cd246fbdd10","d1f010c19eb9c8190bd0859fa3b6f4975543b912b8b85e20bbb0b5bfbdf4d2b3","de4ccc96cef3f97fab148640799abb32a24b567a902a8233913f98481e3131bf",{"version":"801934aa449fe6df584bccdcc5d5b9280295cb7ac84918b6014fc5086e6f9ff6","affectsGlobalScope":true},"5e379df3d61561c2ed7789b5995b9ba2143bbba21a905e2381e16efe7d1fa424","f07a137bbe2de7a122c37bfea00e761975fb264c49f18003d398d71b3fb35a5f","6af760fb9ea02dc807c5053d8aee86389c4fce72fbb26af7b9568cac6c4710d5","c62c4ba5e910b4523f7e7adf4a55ec45c2bac99d9d8e9b0fe0c2a800a6f641b9","92131434f876fdd6fcbc40bd54a9d7500c66974362b16bd42641f990468587f4","8cf023c0bd57992fdd2ce6a7030a1874f49c8edc62eaffa9bfffcf18d2a2a1a2","8ea8f3040e38fb50d7dc3653f3b8a0dbb5244e82111576f99ce096bdc0fbf94c","48ed788ad126545a6156fcc37cd3bcf17de18a3e3fe6b6ef62cfb8140d1a45a2","63c271a745f628ffd4bd7ad0a63b021c362c9bd6bf8b18441a7162892395a214","8d3457e6c7c5cb890729fb60cb8db18f261226a3ea3ff6a4db4b84ea78313ace","9f9e5bae412fa5909fae636d6733aee27a108cc2ed5b13980611016336774d3c","662fe197bba64bd3f17ee118058cd2d0d2dbe33d7c0c865fd6365d90bfc44e1e","030519c351f800551cac2658038804969ca4584d2c0175a710602ac234ca1340","0278a6939ca83cd040b08ff8c5fc7838b6693ddc52f22526bf158e6b10e0246c","c2d6206e5ba4fd3063b01218c2b3b997afc1cfbeb49fcee991fa8595842ce53d","6a8096993458a3d71229031aa7415974eb5b47b320213e29660adfb519d6a3f4","cb7996a1af5b1d276483cd0c9b9de6540eff021abc90a720511ff4464519a2ff","9df6ec68878d65bc690ea3a33ce3ef5aa8254c36bc5f8346c0c2fd1f3b88a35c","a4fad04c4acc8a4b195cbbccef4c55019104753d547d5c94441643ccc89108a0","0244c23ea642361f7c192c1f0cfff9c12cfa5f51f9b155edd5c0a89fef308d34","c7298e68632ab03155f6de963d3d09cc4a5874c28a81524f56c667d8a052e538","3c69a83bde847af6fc3a53e1bb6b13cd06d38a27a142814b8dacc374f3b93284","5b46f7113f54565e7ffc83f2b474f557a1f54c7e5946769d5be220454656be73","fb58035d39c5759283cb73cfb3548aefe370aa3ad4e81fdb4e46f0979eb7669f","1311c325948b2d5576cebc70b1bf968d3446b4630802bef54120daf04ce1f625","d0b3609e8e7afed0fd0570152255458407e67249b94f6603afdfd68599423f21","17f4c5a1d6eaa87ea27eadcdff9085af3190533d98f799dda79a3af6f9a630ea","3e6f734ddf40e2e99ff7fff9568b7d9720663af9a0632c26a352c8d3270a3f0e","ec13f78303abcf550c5569dfae1446b8ceb89050f68ce04491481e72e8122ae2","a3fc57dbaa7f1efb010399ad4ef4fd9b462aa4e93bf74a9a34b099b97ffcc9cb","ffddd7ec6a450b0cb6f2f73f80de1df963ead312d7c81a8440268f34146ecb87","5d6a36ca0087fd6876df654d1b4192f0e402adfde994ad47e5c065da33692f9c","eb157a09c5f543d98644e2a99a785f9e0e91f054f9fecbf1c3e15831ff5d63a7","edd5530e2b1ccdf65093296e40a8634fcb11ecda3c164c31383a8c34cb04bc9d","9dfaf96d090fe8d96143465d85b4837661ae535143eea9ef99cd20df2e66338e","209d45c27e03c1417c42985252de6c25a2ec23abdc199d88e6139c88b93abd11","0ee5cdba58cfde3012bb9ff2e9edcc4e35a651373a2aa2c83ff9eb7df635419a","540f4dca27ea5a232828b6d91e1b2fce2720bdabaa4c1f3fbf59b672cc58bd8a","ba086b99d545ec6c9ff356989f076b5652ea1b09bcc65b87dfc43a5195a2efcc","c85d9776b36166b928ab1488d9224ebf970d41b0a35f09a3ee0b9bee3e698061","683196f606c5dab1c8c4a24a66d26e00f16f2d4b2a5abe25ebedd37d2954f930","9c3a1b01cba1238fb723ce06b6c163ef6c53be755394406782564d5c42c636b2","6e795e6270d39e918c7a0e62ac73793cda06fcf4b3692ee46583e15f5bf57ab8","0e821ef1eb67fa6144ea4de4277d913f5b1982d7407afd5f93754a8239d41554","5c09195ef359ffa9c6bbdb4fefb101d87ede4b9e9c28213faf5b45d102e4c609","80b4d93a4dcc90a12f6f4bb7c6851a8182ae29e556716d0d80b5c012a5ef554a","2556ef9d1820e0b6bbca6dd65a50ea64f525c4d8247ab50dff44c3f0d14a5643","cbd1c836db190d6e3add07165afc228f04e1f6170e1fe3aa5e6fc24a7e9573a3","9b13881feb958237232586d888a10a39d47cdffe3ee34688ed41888fa7baad94","122fe82cf5af80f0b26832b258b537b7dfe3ec28449c301b259ab10204b50d45","c467dada8fea6d60dff8a8be2675f737cacc76e14e50b72daa0f0710376df84b","9cb80bba611c2dd155a446ce424fe4bb1df2129751bc9416b7e42c055d1ddbff","6ee568039016b81ed70292a595ab781ab978cba4243a5fe49507040ee4f7ac8a","043783bebe87efb440183c9ebc8c4fdc1bb92060a5a0f7ce847e30dee7013ac3","e3dc0a97a59dea936b4fb7b1f6f4117b4aac9c86d0cd08b69bab2d0532a8a5e3","5d897601f8a4fe913057019d8211b99b06e3138f625a0cfb601d074f4278271d","a68bb369c4ba8ab43a78f3fad2d3ec130e1418bc946521b9c84e9b336d6e88f1","65f219e6e1f9d27c677a49d41ae7989b83bf6baa56debbeb50d95c3ab21632e2","cfde5d194dd858ad68f910defaed5b0d28730f8bf38359a9265a93ab29bc7bef","c89354ae268153d965011e484150f0c92faa87f3f66507c25b496973178e0400","f20aae41b169cddcbf3fde8ac380443182c8d7225194e788c404d9e11e6dc75d","a6f4816a634bb1ceb513634c1ef7c0535f461ed2565336eed69f6ac2babbe15b","c48566cb13403fca44192b4528e3f2ac993869d39526bd42cd2f2167c0285add","efae20e0c581240c7522e04829da4f0453ca263068596554d4b0e27878c7dfac","3af68ef927788cda7daab34be513fa4508229fdc6e5130d564a0a1ccb3fefafe","bbbd2cbb15a37d5f4dd54ad8c7c537d3df8352117523030fcec7dcbe62a05a58","b50d24ebc117f8805332e7e260e9587f572bb7b2ff0ca1ff6cfafb38015781f3","5cc8b8e18fe7fefab4b3c53a39467b5a0deb4200abae7f063ff0624b9e856c51","8e990781eb0107c25429b1274a31a4f3866a9a46290cce40f354b2a6e71c6c21","608c45069e89c4c8f0ab29f896cc93c6553808072d6304b23611b6c6de3c24bb","22cbabe752781b5f35482af9d1fcf1455cb1ece74e8b84700d4abcb44abe3776","b9ce4613536386a98897f1e3d8f61a851ce6cb34dc3c9db4f2ef5f55f007e9e1","a5d1209c7bf277af86281392d46e12ce3dd6052586053f757fb2e606cc75c0f3","31b5f53e3d57470830e87f9e03c02d4569ac81d4a758fdda75092f9a3f58beba","d765fbab22fd7003a65ed670100362ec1c90d55a772e6773a774135594e7ea41","c1f11d9b42bfb0823d34d93c58df91ffb6690b5a717b7d310d83f258f1784e58","775b207f00d4df5b3b0b536aa696d572cdd2cabe8ea18dd28e8b52f691fa2a55","f75cd30f162c2af5e5aca39c01c1a521bfa034fae523793de872815a3468bc08","0cf1123db73dabd86466a462375a6addae52f58d23030c6033f8aadc23539a36","e29cef4158591ed213b1c2cba8988237b1ff369f7a6ecd8cb8ac0302bad1fba8","5307876e4d0021ea01235eb2f7c24671f3d8b37590f4b446cd132a4e1dc9a335","92550acd737790dc60c4c130e6aac78656dd48a8334a4882f40e7f86bdf7a590","3df821880914f8bb3c8107b1107be75c8ddbe2120a2cefabbaf9b65936b5f4dd","f46ba7c6fa7fcc8b3d57c4618c18db3f4d8bfe1fcab5551d7f6d9a82cf4d6078","078b7043bea0968860374bf4671ed74dd9f6be4e28ab659517d81f74be463c51","68b139ebb9a7f3ee4ded6286d74f978a47968727665120f3bfc560476ce33c4d","56d02c29b2fd39b1b1a1265df291f3f98e6ec3e6119aff9f4cfa44fe888efaa7","2d01884891da6495cb4a2f060e4898209a507e711464c4c1480df85264e863ed","c485c6497f7587314c4c4a59b74850cbca4c0c4bc08146a918cfd237ef821dbb","e9eec004735b1bf7015edf5400aeb914a53132134d230e93786590d904d094cc","080b1aa93227952b4dd74b9d2c6e4f6002eb8403533749116a1c53bb9961c02d","874087eec1d457f6e3baf5ac46c42ea200e55040b394fac667aa3a64c49f5f6c","6e8a5b04a18abb192abc89d7219b9c6f633cb3136777ec808673a65f111ca749","4e7ac7e5dd58a6c29c724728b031669e3068b194b62c2b83f92e76a36cb34dbb","d74d2a92b54f95e47d2b76bd5ee516aab7ae93afb79cd34c6681dd29eb09e72a","747e6326a724bc54f799a466a5b5c4978a601a04a063a5bdabe150af2f25b9e2","b57e22e53b56cca7a57bfcfb234aa6a66f9b9e4c07159d7388f94f17a3eaee2c","e47709ec4d1618ef429648cd8ef967aef2005526b34fcbfac33037add347dc71","b81abb3e47fbbb3af41fa75bada89bbcfa4b0feed9a0d6d4b19ed1ce1033b53c","15b330546e9784461058e5fd6e2346bf272140fa6f0cda34e193ae501d8b17b1","4d8ce72fd080bf9a46bdcc274bcbacccedd66d84e203966b197ac25a96932183","73327e6ae34e3f6591877fb75b451cf620cbbd76ee2b678213a9f793633cd0d3","3f1ba2f69944fa346789db7f60d53c9bec00032de0d797967978dea42e77b941","3f5df31539fee4816b97d4e45b4344fbdaf3ca59f6df941f8d780ee441e92cc1","50aaf44eb4d0e086af13729b3471a0a7dce95ea35ebd21c762ba26e203134b2e","3857c1773b8503c3ca45b7bc09ac89c3930c85ce93021054503f73d5d9101b5c","72702bd07fd6fb3ef64aadbcb909103aadfe71ee76e9fdeb11e0c92693cff6cb","f0dd6f7c9783637655478db7d7caf6becd41a79d54482aa59578ce88ab38e9bf",{"version":"cd756ccdabf433dd02b84d755383e489f14b3c1aede0477783aa04830fd5d695","affectsGlobalScope":true},"a4c88dbecdf8ee0c79f5b7c2bf31cd77e593f5d78384e2b674f67d754a549a9e","9cbdff04326da794ba008c0fc977ab062d1fe3fa2e9759654c72ffbe54b64a7c","aa60f8d20d36116fe05edaab24adee3c275209f71b65e272692cf99daf9489e1","150855f967a6490161d5aeed4cc4adf31fcb8f5dbe54b75799c12b8687fc9cc2","79576487ac18e047e8192fc582ff488ce375fe4df0cb028a17f831cf42b976f2","47ddb601df40bfa01cebdd06ee8b87d0b72aa1259a4ceba3ad3b5cf68130112a","6b6392704ddb3f50e647dbbb716782bdd0cf8ea9cc134aae256a26223e632b47","afc3ad2a50f7f4de908e26fcf467e09ab8528c0e90f91e602b4865d953839228","df90b0c6b1d81851364c4d97fa23b91a993482bcf4a7bed7c7a24aa41632d494","db34610570eed46b8b72bc662a91261200b8578af0ac02781ce7d9aca99bc683","11ee9ab699b4619d217c640d917ca198f58066a86bd58c2917197d62aa6601e0","cf9d589d9e73bf32c8e7a6cae6b4a1cf9bef39e5594072533fdce985581a6ddc","959544feb1ca2df29eec6c500f27ea10f4885df245ebd8418fb4b87914614383","6548ab4b57eb9d092471a04513091673345f2fd95d5b876f600402ea8d603ee0","2793e8c6a023d26f78d6777a6d7f20fae3a9a8169863d46d8d54c73071851232","d0f11e830aa1350a31d9c00a0197243e9711e4882947aef53a96c629f405cb10","6610b9f45f1f71d2b1fb67df49cbcabe3f9e668a1ccb7d8328a51407b259ffb3","abbcc437e0792ab2fe08797ceca1ec85a95ec413c51612313b18ab8e75f690f6","e29d76ef1183ac0edf94b4712b6e51730c447c7e773e75ceb44a720b0c9a9fd9","4ee6dc3424998eede9a2a9b114acaaf7969cdda67baf82ba2c9cf88a8eec0ab1","8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","25139d6a726e0e19d9fc4fa3197367b4a82ec34a08a5ecf23963e142c202c0f3","e3328bffc8eab74665a4fe9c59d6f12f4c8570c3d858497e241eb37efe17dfcf","29389551e426a46421134b55182d6fcf5b143670998bf81db2619c1228235392","c18f7e16982695bdd04e3e183a327d116185f77f1a37b9b2e849d7d93269cd74","2cfb37011989c21dc70b91d521a2d5a4e0f18507f5f536b5dfe504edb15916e8","bb5e02df7aaec7a4ea642639a9963b24b8d9fd6798351f07d8c58616942fbcbf","299a899cb4d061f5d83843ec453e936e9659b2c435746823f90c40eddaef4745","d5610c0fd12870f644b0f42c1bcc4fa2295ac3e3ca01916bdb42c3bdc4c80c36","2c56a8e249b1f45dbdf973100cd37fe2ea68709573cf1fdf2e3052c593be68d8","3553da417ee7b07e388b13bd12a70a1c03e65a6132ba5427fe68f5b362373e6f","612358502042d351c227ba779fdcf6d875d827e424930e60297c533524e50668","d2b5be376ef162aa0c24a826e7dd2d77671a045c085e16d1c1276db4bdccbac7","c4138d8dcccedaff6621e009cf0a54a7bed2a5ad4c509a3513bccc4f417ef939","ad8747fe978dff3e80f4b12b48d37cc8dff11b61d04c035aefbc982ce21201ce","b154f789fd65298e1ba6cbba6944ea892d564c95f3d3700ed85baf8f80748473","c660265aedd7c5b236e2017e53095cb98da66200eb0e8d023b5bf713c36494e8","0efc36bf5c0daca6217fec7063359ccdab8c3a23bb405d25340fae22cf72d74f","5abff0c87d4f9c89715107042d4c73b68ef7a128759f451c8a0fc450cbaaf660","5a03308fbd1af441065149a84c692931bebc7e7735afc23be8684f4e10d3aa06","c787bf4f8f0abbf815cfbd348be41046f2b8f270be24fe7aa8a8fcdd2b7df8c2","e7a5191c663a3228f30104961d548b372e51c5936c01ffc8eddd262bb98d7d7c","43fdc9abe6f8640fda4cdc55a1ee5f666d3fce554277043df925c383137ddf69","f0b09665c9d52de465687fbd3cfb65111d3ffc59ae00c6f42654150f3db05518","72f8c078d06cff690e24ff2b0e118a9de2833dcebf7c53e762dcb505ddf36a68","9705efb0fd901180de84ca4dd11d86f87fd73f99d6a5660a664c048a7487e385","f9b9d0950fdfb90f57e3f045fe73dce7fa6e7921b37622fc12e64fcd90afbd0f","e61b36e7fde608f8bb4b9c973d81556553a715eaef42a181a16ddd7a28da4ac7","03b8389b222af729eae0fb3c33366dcbb1f5a0102ce319bf1d7d5ee987e59fd0","2bf6be7c04db280fdd9b786764f8650c23f9f4d533791cb25a11b25314b76a55","dbb5fc7edd36bfba95cc4dd564e4458276ced30eed18bc05fbda948b3fda8686","c2b556c7cff0dabce2e31cb373ac61c14d8ebc35f1086dff30b39e9ec5357d0d","f958af01131076e8af55d28c4835a51063226ab488ca8738fdee38aeef7d0d33","9f3797b01e3d83d4e4b875699ae984f380ca86aa0a0c9df43ac5bba1cb1f8b7b","752b15ad1b34887adeaa838fc55f5d4ca399026afd266d4ed4db0e3db02eae4e","778331eaea1093451e50be9844bd2b6937c3bb81b0b1ee700624c9774ecfcf2b","0ca0dfc9f657d0822eca9530c7870b22a1d2a5fc48182bdd4d0e6e88e4ad9c35","5c746f034288e6842dd1589b169dcfcc16c5ce5abbd928889ab67aea4fe0b501","92ce6dbbcc135cffd09a58e19fef34bf351391bec92c40d849e4e9997d475769","99e77d092fed72b6b8578d00c7af004f76e98b30ba99f1947535eb4c04a51676","b5ef52a9f9083724decc5d060f0b34e3a480deed71c32d55ca16c214eb4cc928","5d3d7938b2d7d0a9f851276741327c2ae4c013e7eb420fc3f7caed3b79c8f37f","14df6b81e50a035e9e391558cf30a0420d03e2eb42c7db9c57f44b818e5d5179","f100912a3785eed4a3d29c12f5910b101af9353454de5ddba9b4d43456c56dd1","446439eacf81a163fd7dfc53b28f80deca3d13b250d67639739aa25aa4491090","98034cd285344125f7165a3bb68246d38ab35fabe7f6d6a7c8f80407d31f548d","06b4a23064991251512df4edc12341d5bc69a17b942da18372312d383c90eee7","0f898802705f9a534b537f1be6c57265080e0abd6993d935554c255e6d56cc1a","745efa7b6e27b7216cccede166a822b56acc41b10a8090966c8cf2c96239cb83","6ab2a6257ae7bb05559841100c786c845fe465a90be7b904db9096c2fb14696b","26958d6f77e6db2425ca65df0fbfaba439396ef7f4457f5643fc32e4b62568a6","5d697a4b315cc5bb3042ae869abffd10c3b0d7b182cda0e4c45d8819937e5796","89b040dec8fcfc1de98827e1f4d4977e6ff5d3302c6790e9f10b54b916e1c742","6ee58aa536dabb19b09bc036f1abe83feb51e13d63b23d30b2d0631a2de99b8f","8aceb205dcc6f814ad99635baf1e40b6e01d06d3fe27b72fd766c6d0b8c0c600","299567f84bfedd1468dca2755a829cb19e607a6811673788807dc8921e211bc9","795d9fb85aad92221504db74dd179b506bd189bba0c104426f7e7bb8a66ffee5","1311bc194e0a69fe61031e852c1c0b439e2a2a3d1d5e2d8ff795499b9f283459","4b7ce19369d7e7fae76720c2c6c7f671bf3fa0f7093edb864f1ac358ca7c456c","c972ef44deca1fa8fab465915ffa00f82e126aacf3dfc8979c03b1b066ce5bb6","30285a1011c6d6b52f3ba3abb0a984be8148c05cdefb8eb6eb562335a3991f35","e0de9f50e80fed1cc161b50e8e68dc056e38df75a4ef667a06b1922e372de169","6a8b31be08b212d1fc96de0ddd1ea49f32382ba712fea24c70bb56447f643f82","19ac6d624e4c18de4584db4bbdbc55387dbe3d19b3c134e50346bdf165658a17","54e3798c2801e8f3bc7a825d3d26c6a80ce763e19e6cb0b714594c430ef72332","70b8333214aadaccda8d38435911d3e3a686e503837dfda6b8c3f8c83e05729b","fe849e916564b8172f31a547395516668f3c122bfe017f82e7140d8dac402208","d42c6e985bdb10a2aaa3dae14d9b0d8589e74a7c2f9475bf543b855bb3c010ba","56c48fb5bb6316dfc27fbad065966b4ddbc38e9a0a1a5060d19b5da405ae7d6e","7091568b9a1b74b699ad09df6c130db712ed089d173a235e301a7a7ee0a4ca44","de33aa2a38affd9e71297ef7ec001a4525502878b09744308fb6518159f77d2d","57476e482c9b4e152bd23d0dc3c29375e48afee0de775674a3c1ea63cb4cf843","3ec4ecf6502ebdb1f3e24c712eb70160c606214ba2e71b4304b5a50fc2e4f293","83f7b6c1dc91deece32c3bee746a43f3616b7cc9f6510764bd53451f6712ff25","c23f2e8772304163fa7e4335be11f3dbdfd720d2209057566b7dfef746ef1862","2a26cb78d3de9708cd656787a663902270c9421ef89188286c3b7ec89b63bb15","e61fda2800677c210116c397dd85079a0956c87fd714826c08b25b10fdd56546","ef7bdfb4f157f9c9b9bd7f5766f0f8e07fac8e7482eec071673f3c9d08082982","d2f2ac1436cbb7c8d122cc7de96521345254e5b36591d9d064d9763de2a7b254","3cd2ba07285d01224f9595924dc7f760c7babb386a6eb825cb551f8d829fe6fa","3ae9770861c2ece5849778e9f15567d95b87df0165c0a5b1312181df19458a56","37b51656ff8302a4556e29c775f311eb9ad813948d2c527405cea041dba3baf3","00abf32ca3af92f8be9ecbc9b63090b4909a756317d791927a83cffd24e9c8ac","cd28efe88fac7a92f3f5cfc7dd3c764f0b31bdaaa061ff044de1639810d6a7da","8b2100d3ba68063b7baf5038f26eefe46543dcebf1e7dbaf46277f24307cefcb","131b7f32092aa78e12fcb2a6db7c71e17f85e076c0635ad8d991b80d10130c19","d1c84af1e6d5fa4a5f4badd45b03b67c9255a675df235a3ec25307a0f5524278","aa4d6dc9282133162a76109d99c5795583276c4fd27284f128d484acf12b0841","3355c4c572f076ad963d95f0e28075b8558e2ab492c84eb94f9e2c48f1c2368b","5638cfd48b0c56bc9ed0c779d53a40b92c9cd9c9d6312e3a21c52542d38094f3","827eb54656695635a6e25543f711f0fe86d1083e5e1c0e84f394ffc122bd3ad7","2309cee540edc190aa607149b673b437cb8807f4e8d921bf7f5a50e6aa8d609c","703509e96cc30dce834ef8188c958c69306473b8a7e5cb3a6f324cee05a1f7bb","900daf04dc607dc3858c0f976d6f9e17b829a07de58d62dc6f730eaf06986075","08e0ac95e650bd4c87145b6ab2257b70c06254bf76a0b9f8a7d60c51fb8ed6b8","4b57ec505a035491c692b89af2c6902c312ec22f8fa9b6dae3e93686659fb7e0","7d796672940d3b2d37f2edea4d7bcf4c7993966286006228cbc8fa35ac92871d","132fd53917ed7f55275faa52c35e4d4d41e9576fea231d12740b723df2bade93","de2ecf9b1d6f60338f7b59b6f593ef77af9abd0e70ba8f2942953d0c6e1850af","cf18e9d003f1d3d1d61a04eb2d1cff3e8a8cf9cd306d0532ea82700069f2fc42","393192a39f26f9247a74ecbaea6668972af8e9125c955d1798234dceca6010f7","27ca878cf70b3030e8403f51ce65949d364fa776d6dae3527f91635a40836672","178e2de7a8702742957ad24deaeddec84a48cd913b5d932b16afd2a707b3e416","a45ee7555d019a67fbe092898d1aef0b1d02a9f6679ab84461ff515b4460d706","29c188a2c660f99f1b4835022e011c4268d7af989d4b7dda33c0a69ca1a777f8","1ed0bf138e87912d741e28333b58cbf814ae863783b3b404d2454cbabb9c5fc0","3452ee7d8ef0b1bbd47b2a56924a1dc3c79dc84a19d212e9dc496f92e4943aa0","8c95f96ccd4be0674944077aec1e4f2cccd515ca06d4327562dd017250e7d3fc","6fe7571c8a80808224648046008d1366ba4e29206ac79ce4c56d6fab3350492b","a98be76d8c257aa9e316bdb305b8c4228f0cf904d4b70547fc2999f3f99b5a01","7419d99dfe020d543c8ee736ab7ec17127d6a2c61c40e5f245c6dbd3fa6eaea4","2495815b16258136f98d91e441f4462f9b694520af86bb8c8373724cdc410096","a64568c16a5821575de4f6280ba1ea4686a1ceecd649fa90ba957c8b1b007013","ac46f284c80582f7c1284eef93f2d1c80add2d3b0e8a4076d6ca3db58d3af747","dee4dbaef83bb1061a44f39a91a59300d3dc02528eb57f748222235dd8e02159","a39c32b055d2e6103e5c49b9aed2d7bb5b06571c98fc31105264d280431bdbd7","618ebb93311695a13844118cdc9a7314dd3a2c6f35092d87f76828cac555ddc9","d36c3d116ce59a3f072c0014f0c020c76e916ba906066ddc4f193f546a43bceb","9bed8447acaa89be63540ec500b165442fcb0de020015175b5a5c66d42a61c4a","f128a2d1209d243ba2f7755c2fc313be2c7569fa0d9b4dc5cc60714fb0cc6634","a17e6861b709149f29a2bd896cee94526df2f06b24a2b60614b56649b5e9aabe","9c79ace686f720f4dd833740f7190e12cdce363362c982c164745527a412ef40","439850ca5075c6db55487b2c7fb27a6051fecbf180eee0809b67bb2783a89813","75d48857bc4216880443a24d985071262bb8b89a9952c77fd430cb0caa21f9bf","33e40cf77499b3d9712db82e15683373925e85817dbe82a24ee0ee6e44bffb70","d5bbd453310990e851908183fbbef9e6e2db8e0c86d97b42b723fd5238f71c81","95e76bed30f6e993e1fcc1b90a4675682e4800ae43403547a775d6e3c7ab2b0f","8b206b995edc6dd849b85c1c56531b9780e3ba75302fd02a2d173f008028707e","97040b190f0daa10cf9a15e51a2fac66b26ddefd7b65998bd6027d1dd67647b7","877c25dfae100e555014e45d1d80364496a0c876201e5dea91a0fd0a6a4ff765","d53f9f96afd41359edeb2d5ad98559f3bfad261391d5aef95320fefb0c6a8742","23d98226adf3be74e1f0470f85e7fd154cd7aa979d60b43190a7437f0d0426eb","639f9321a98b734242a3573764d7f1de5369b0b0b10c768ae37639e8bda5dd03","a42c39d8b7d1b1eccb69c7919ea60dcc2670ea672a0af90b70a730974ec0e9fb","dc5fe5f6b39c3fdfaeba333bcd5f0cc98bb3068797a4d7010f585366f549ddf7","4a3ab8cb278bfd1f18f24cc45a02188b63afa6aef50035df6d79c4638f24059a","e724c9ce92f2a8a31ed260764c5455852a13d292e2a31d26acc6840ec0e83208","40220ba1b091aff0cb20df5467202b62af561b09fcf3b24c22a60066d46f9e62","30abf588759f9e828a94f0c7f031eae094bb668c6dd4d902fa296779267c05c5","bd875d031474860131eadb42300aa57a71527bbb2b239d5b31ab6a9e352c84f0","773bf9af93b5027de9b5b4c779d5cda35f0eb92c7f43a97f2ef3ca081495a191","617f2b4f5115969c7b0f225d4962e6bec1cec7e5c687d84370eba4931b7dd047","59625b1fcc91f2686751fd5b623126f434d7b405bd8d866a555963ce2ac69846","5e0dc1bd24b45c46f2188d2f7f4b67f311610c72b706f963c5bf04c2e1fcc35d","fc69ffd599d3e525aba38f80c7cc2ecd187dbf148287364c75f199c8294a00e6","2ad138be6972de52ed966e6402aa6403af79e9f183486e0a725ffa075a0555fb","480274a4f75a7b3bd5c697a55e1905887b62a928592c0db3c282525fb235ba70","967fb6e86b55db228ab50c81f85f39d6a23a0c15bcfa6e19d255e0952d33a65a","c39e7d32dddfcdaa93b18b99fa430ebb1d6ba366459563d400add22f92e3644b","e3932de252bbe43132ad3226865b2a376ad945dbc1d767540c01b7bddc6477c2","b2f52f3cbd863dc4e690614b5cddbf412dea435d0de099db6d8adfd3cbefcd65","557c93b35f3b58e6844a9b8817559da1e0641f7f08f918e3cd1a8efee126746f","80ad2ae93d57dadac5e377ec6743df5e0211ea30bafd4b648c52366af057bb2b","07f90213b5800a0b43a6d6f309517dcca5afc6ffeb4bed396878a29fc5d6ceb0","bb0e637020f81cb40d16f202c3a783f0e269e29547fb84ca9f187a5ea8556965","462da802b50ac0d94a3c8f7f58a6a0aa08108bfc1394449ea56f1e0f63f5132e","2ccea88888048bbfcacbc9531a5596ea48a3e7dcd0a25f531a81bb717903ba4f","b7eece07762a9a944f097ee05d6f6b642b1e2dd87e6fc1b09600d881e5475377","1916218868966583a3c9f18501ee78929cab8450ebb8076ebd609873c258154d","98ca5ae10ab02fe747a7a53138f43525e0129aa1107892ea4e1fe9c99575809c","9760678d20c9faa0d0e1269806bce578bb76598a4a188a4d3987171263be20c5","21f706150e32f03ecd1714d7a7ac55ce3caadc7c7a2a960ba57cc5d39ad84c9d","6954ec87361b77bb8895426909fecfd154e3fd72a2b82f681c8bb15bc46f2389","da1963f37d566ff9f71bf8ca5c628656bae02fc9509050041547e9c7063cc58f","57e4bed825036f7f1328505bc512af492f28b1b57a48f1ff9b6d90b930041a52","3ef0957915b7719ac58153eaea6ce810ff8688276e570f8938455f3ec7930df7","05e0ad043fdd4e2d4874a97bd716174af64d63e43851c09830c00e819a80d395","2dff0ed1eb2046fbdbc2c13914117e1ff1112e217f90542ea5e7f41e39f0393e","a0ba1e2711c2520189ed980225e7a429b0706a1eabf9113e53f0e72550a1b23d","169b66aee819a4b165c397b832b31691f0be8d35cf8f2ec6364c23ee727b20b8","badb4cfbfc6eca3a038be22c76297bec0b5c1478d8b73d60e8b50725b7dcc15c","21e7e0eddddc112f2b891d1066eac74680291db768d3ef9b908965935380ab98","489e195150979dc291520e6f3289f055516cf342f314931c6b4553aebf2859bb","516efe800aaa0b7504b71f2d7e7e9bed5f28eb6c9c739bfdf237f09c7addea46","10ae729013e6620dc937df5dd7077c34e29ad313a28aa75cec39957640cdc8b0","ac5f95dee5e4787fa7c68a81a052cdfa0e03adec8331d3276389384df36cb849","0aaa321f1f662ec931e55c85867d316d8af16b59337111e22901516a0e1caacb","fab58e600970e66547644a44bc9918e3223aa2cbd9e8763cec004b2cfb48827e","a120dfb4736e6ec4c78f1bff5ff7f977d346152e6b7020659ee1ce4717f6f66f","9eda7b58498bed72dd98ebf1d6f8dd3bf5df5004b2f91c610093bf48f970c615","8e7adb22c0adecf7464861fc58ae3fc617b41ffbd70c97aa8493dc0966a82273","755f3cd1d9c1b564cff090e3b0e29200ae55690a91b87cb9e7a64c2dbeb314d3","d6bb7e0a6877b7856c183bff13d09dd9ae599ea43c6f6b33d3d5f72a830ed460","f1b51ae93c762d7c43f559933cd4842dd870367e8d92e90704ffa685dd5b29a3","3f450762fd7c34ed545e738abccb0af6a703572a10521643cf8fc88e3724c99c","bf5d041f2440b4a9391e2b5eb3b8d94cbf1e3b8ff4703b6539d4e65e758c8f37","8516469eb90e723b0eb03df1be098f7e6a4709f6f48fd4532868d20a0a934f6e","d60e9ab369a72d234aac49adbe2900d8ef1408a6ea4db552cf2a48c9d8d6a1bc","0ebb4698803f01e2e7df6acce572fff068f4a20c47221721dafd70a27e372831","03460a54d0e0481d1e11097f66ad43f054bc95efdafe5f81bbc7a82be181af75","ded24ddc7157689a5aa14bd651272ab8cd6e7812da2196a65d8c5e63131cfb23","2cea9689efa8591732096235abe7f084fc29c92badd5b0897a5e876b77e71887","4ed4e504126014fee13aaef5e3fc140f2ff7031ff3a8b5386717905820ea2d09","00e77e0bf106bc1e4f303ab02724df5173506d44acb6c22b4f582a88f22c3250","30d2170e1a718b5035611af55e3618b4ba8f42f0749bb52ee593da6082c4e2ce","568c86c9edf716ae08cd3c8ca61647a3eb43ff52a9aeb7c972f7be62cd35b797","a3b8b6be7620897d1e481e8650c980a210a138fceb6e710eaf95fd9dd0dfe94a","12c89d0e32758c120a569045f21cf5b77244f86792611ced8de7f86b37e77781","14bd47270e654c8eb3b1489fa8c095912ee62a0a29bb92743393203722347c53","cc8b411aec64e03abfc3936a397025c781adb89942ec2fcc66e2a353f93ce677","db5624ecf400ed47648e72353a0ebefd3293d2e6295834a3f013c548ecbd0edf","92cb686a9ca5eb5dd7d5d8d43a3707194c1e91ea07a027b3bcb60b6011b24632","da3ab7396ab4fe390f01091bd0d4c4a4e1e2a15a46d47446d6fb7194897d0f6c","66bbae6120d307ec2021ebd2241b8ad23f832b663e13363ca8b4c8dbc131a4e6","fb14266ae4070bd16db6b071e98887452bc359695c40742e38515a89dbc80a63","4a24d83c0d8f489465c4d38ed9fd87121c8a2cf50c47efe09c2eca93d39fa908","c052e32b9de53cd2596f196a0901801961bd7a31be9fac4ac2f117e4103e3a07","b15cdbb45919bc3b8e6b6f962d65382e85061d70bc26a968604f3dce4ad3a891","d6b58d955981bc1742501b792f1ab9f4cba0c4611f28dcf1c99376c1c33c9f9c","f0b9f6d5db82c3d1679f71b187c4451dbc2875ba734ce416a4804ad47390970a","a5c38939c3e22954a7166d80ab931ac6757283737b000f1e6dc924c6f4402b88","31a863da9da2a3edec16665695bdbc3134e853195f82dafec58e98c8e1bb3119","a00417f73bbba413d1345dd77252ede0bd0c957e37a9cadc9abb4c34cbd0eac1","90d1ad8d2983cb003d6f237b41c56a8f252f72071bbc53576e02b3c96d7ea47a","f3815045e126ec1b9d224782805a915ae01876a1c7d1eb9b3e320ffadbd63535","d07557f21b2ad690bfe37864aa28090bd7d01c7152b77938d92d97c8419c7144","b843ea5227a9873512aa1226b546a7e52ea5e922b89461f8b202a2f2a3f0b013","64b4d440f905da272e0568224ef8d62c5cd730755c6d453043f2e606e060ec5a","d6b58d955981bc1742501b792f1ab9f4cba0c4611f28dcf1c99376c1c33c9f9c","f0b9f6d5db82c3d1679f71b187c4451dbc2875ba734ce416a4804ad47390970a","a5c38939c3e22954a7166d80ab931ac6757283737b000f1e6dc924c6f4402b88","31a863da9da2a3edec16665695bdbc3134e853195f82dafec58e98c8e1bb3119","c0e03327bc548757709a7e2ca3063ca8b46227b5e13cd981ca3483035ef5ac44","b8442e9db28157344d1bc5d8a5a256f1692de213f0c0ddeb84359834015a008c","458111fc89d11d2151277c822dfdc1a28fa5b6b2493cf942e37d4cd0a6ee5f22","da2b6356b84a40111aaecb18304ea4e4fcb43d70efb1c13ca7d7a906445ee0d3","187119ff4f9553676a884e296089e131e8cc01691c546273b1d0089c3533ce42","febf0b2de54781102b00f61653b21377390a048fbf5262718c91860d11ff34a6","6f294731b495c65ecf46a5694f0082954b961cf05463bea823f8014098eaffa0","0aaef8cded245bf5036a7a40b65622dd6c4da71f7a35343112edbe112b348a1e","00baffbe8a2f2e4875367479489b5d43b5fc1429ecb4a4cc98cfc3009095f52a","68a0d0c508e1b6d8d23a519a8a0a3303dc5baa4849ca049f21e5bad41945e3fc","3c92b6dfd43cc1c2485d9eba5ff0b74a19bb8725b692773ef1d66dac48cda4bd","b03afe4bec768ae333582915146f48b161e567a81b5ebc31c4d78af089770ac9","df996e25faa505f85aeb294d15ebe61b399cf1d1e49959cdfaf2cc0815c203f9","4f6a12044ee6f458db11964153830abbc499e73d065c51c329ec97407f4b13dd",{"version":"99e8bb8d262bece129ac203f0c7436a07771e9cf5ba06a308d1b16993606eaf2","signature":"8705a9680ed4afb15edbd7bb9ee24af33060d1165117f293559f3073bf8d0101"},"ebf6e19cb84d78da20d022a95f05e8aef12e56f816a1ee12835a4da40d7b14cf","589357c2f88f1188a0dfc48c4c4cf4d22fac9f654805df5f2789a01b5616b74f","6abe62ec5b9b6a747c1a7687d58ff179cdfb61adee717b6e4882120f7da4399f","5c1301f550be26133f4fd34eadf38815db096ecaf9b75948b444a063097f496d",{"version":"26e64fa5fc9c7fce9daf4131f396fb5012dbdd92fb2e2bcda5aa083a76d18888","signature":"cdca22d444beb7cbe168d11a666b994be4b19c5ed7df1856612ac4dd7c2242fe"},{"version":"34ef3dd636b7074beec81346987a81ac245e1cfd75adf0babc68e6cb6c572ca3","signature":"82065c62b6a8089452cb40191a55299b2d0718ddce833446faa6c01f48f05b29"},{"version":"c1eb1aa5e32fd31d4564bffb458942d8caf500d86388c811cbb853c274e4773a","signature":"a7fe41f597b2631d3fb439d9b3ee32d1606c651a45ce2fa0d170a2614e68d280"},{"version":"327fd9ca522780f73a64e32e400a6c5bcdd89a5e706314d57ff1611bf1a99a0d","signature":"70b3082385b926b4bb0dbcef0b2f444c4f807d312546f27ee248d50b0dfa5877"},{"version":"3b1ea19c2b95501c5d8e87fe4c8044d204c4402a8b48f282bd348f973355f3c5","signature":"01b86f9481ddaf74b65f12e90ae2d5bedbc0e67e64e8cb273c7a1907cc66dbec"},{"version":"897a42f20db3ee955b1cc64506c040b0b1dcebe45d9ba3147e133d110f487f6e","signature":"3089238aed154b07430dd80de65df3115d268f21f1afcd8568a58d65c7827c5f"},{"version":"2d41bac312ef892971b2344a102feb99985e87f79edc18ed2c43ece97703fb91","signature":"2642375958909546f682d51f9c3682f553ae5f919f7b4a77d49262c200bca248"},{"version":"db3db9885deb334e6606785a0bfe7aecdcae172d36a6b4bd55958c756b92ac6c","signature":"79cdba32abf1fd279e588363d3048cb4b3d537a81530d32079cea1df22d66f93"},"1fcb8b15db812281d69a3090d488903f9e93033004aef9d8889ca3ad0753a96f","bdf5a95eb0a2dd1d39805bdf51b46ba012bb9b92b2ddaae16219595bba7678a5","9f794a0e8550a03baff865a3961cc22afbd85bc4ba9672bdda036971928f85f4","66a697d1e4cdbf25cdce4644a8085a8563041fa8c7731d4d9f5e8f22e66ba72c","a0c8e17f4d1ea2704c62d7349bc3b8d9a12e3761b5960cb44144d3f0333b3fcb","3471c0df3d0391e1dffe6e8bf150294531b2b71a2afa5f2b86e52bf84a5db60a","5d4df4de055eddf3187094f938a640f8d96e4c551a47d6686596fdb6ba4c3014","8bc2cad630da1033c1fd8d7df2bffb18af0da6113bd086a8bbec04a2471a1e00","a1059d1bbc8ad46bfe668b8450e7e8002887c4ab987bdb96d6108d8023f8bb8f","5134885e9648e2c6745f8aa1c3e7f5ab3b3617258b3d81ca02de6655ede3d74e","4f1ae3f24125216cf07c5211a3f00d2bb4782d7cc76c0681603f8249f9232ff0","d3fb92a5640f83f7844d60b35317a0f95c27e3658a749d76d218c461ad091668","d1f8bfcd91b284657ef8187c55ace7db91a3c43e642c3f14e54364154932f7e4","f54c92bfcae54f360fe79514746efce4870e4ddabc064e95d406bba291e9f672","175fd7186fa6a70f9db9b270a04a503cae23cf01cb77e3905bac115c38424cf7","c993f7ed1b8e1023c1f2ee5b262dbc3b70b27475674e40a53a58591f9972dacc","c914014ab7c7001178663f29d31a495398234a41219af61f26d7e8e91b46af96","277afd6ab6ec72889e2988e0ddd7d138c1f512e68a1fa4e90eedfd71e2097a51","c0908f85f2b645d375127a3b53a17a65f782e17962d5c1eb68f08b1188acbf15","3fadac5d409cc2f27b1d2f4e7568600f02840205f301c9ae7a3068b46476438b","da6aae64ad559286830fd44c81e3d33303348f184af7db4fde8dd99ae9749407","3633f87c97d359cb55fa7bf0668fb2be8a23342951af6ec2d06e6d0cf7409371","cc3a5427d44fc77ff25e80b3edee4650a51f83de761faf5e633994ecf1ab1b44","b350eda75c6e47299b36002b31d5b220c405c21c365e708989829db013fadbb4","f421882756b6714834ae4687ab1aeadf344a1cc45437d2edffbac020ff3801c1","1d61d6ad832dabafbf63b86c5a79d704f2c8763ada9318e135b17a3cb2d09b32","e5cef5de3e5ad3436d414d20743231e284733b9cf4375dc79eff4fcca4282f99","e624419ba84e33e661e89a28083119ca41f6953dba09a4f82b660684087afe6d","942be430bd0feaced2e3e598273b17e50ea565ec9dac840b580b0b99e1a3cd5c","73350006cec5a0c6b71d53b0b0ddbfb82be96752a9c4e3c904c59e633bc9485e","a7df5c2e9594966c7e0d4a763b13ed5727506d892669df5f7bc9826f539c1d35","258cc5cd6891f6bcbaccefd953997038844e7f65d582cac987ffabf7181bcd4c","00a6db28fc4df6ddf10adbe630d9df620ec13af19039c1869653e60dafa739d2","649324d5abb5464aabe35d86cd0eef16562df811f0971481cee664afa5acbc88",{"version":"628749b6edfc907c32583a77f7dde111642dbfc13265fa566e9a8fa47f224b51","signature":"495944b274165419ec08446dbd612d6276e2c12b92caa1f1e6c645cbc044ef25"},{"version":"e2f7d4348da1a42007547574ec71504de5e9df04d270bcc4c672bec1068257e4","signature":"0d7e153773886e59a74ffe1fac08bef805541411de160b9f3af36f8a6a3c6022"},{"version":"70fa251413c8e1926804d27e8aa01f96bf56141270e8adaeedfeaf0cc7147cef","signature":"2e85c128d27849ff4bd436f75d32d8a64d9013d420f09c82c6eae63cb7131020"},{"version":"334a6eff67fdb6feabbe5a612552a0714c424ccd07abbb096672085e7d43fb4a","signature":"19756a360a54eda2a10138b94b37a87519fd1a27c678a1b82187295e40bbfacd"},{"version":"722e48bdd1c494feabfb081d7d582d4554276abacce92f69128511918c125273","signature":"b195f1ad5886c1600c53bc7296210f9ded9a9a673e01988eecf9f20f48a4d9d5"},{"version":"1b5f109f8e1b74f648bf19b878188928678f443c2b2a21db0861f57d0715ef69","signature":"55310e6719d6bd9462e76cbba6a582712b30a85ee4949b8d98e14e0f46738e78"},{"version":"d184310a8c121c1ed754995dc55f8ca212bb1ef94979f99423dcdc48569b3c51","signature":"99ec28bacd04a3185d90660fe18bae48f33cbb1d50c73c64cae98e67f7c0ca01"},{"version":"48d475a0c6f91f62a89b128923cdec08f1f30a12df0068493f0d9b2774125b01","signature":"6a90b1b75bb0eb776ae223adc1f3f1cc343abf3e68df619933a3248910061290"},{"version":"e581d928f182594fe6aa7c0dd2e0ce02fe65fb53b7d40a59af9c2f171eee6428","signature":"1ac721bca31657133deb33e2ae005d557e8e6e0aa9a466142a2b0388e2e2638c"},{"version":"f2cf0410b95d14c070af93ed700294f19680b00a823d2eb5c8dd6ce433381e4b","signature":"85744b65d5c0962e4e7df8e5b1d1646a759e21fffd160b91d207b78d28da7724"},{"version":"bcc7e1fd0b70240f11846f0c5a284be69834446899b64477371cee7aaca38965","signature":"bab97a4f0736f1c1cd0546f79f993ecf30b34404cf4479a4f39068915880cb1c"},{"version":"6720778d4192df7ececcfd9dfebed8a006c9c44f88fe8b74880ab3ba7e14cce4","signature":"121c82998e23aa414d41a2f08e108074760318a1c11a2a5183b88b0d9be4ef60"},{"version":"e3fe28954899e21bf8a7db496cb4b90313e826bb5ae938d84bc73c3bdaa31cc9","signature":"4e1f22dbfc0754b698f1e291c7c92bf1220834bd5620207084236399cfd02e2d"},{"version":"7f2a2cdf8eecbe353d449055d91c6ee619f90ca3b3a49ba5a44563c44aab5d1a","signature":"8e6165fa13e0d2f40e2403ab20b72804d02c663709a3f7383a320050e893fe8b"},{"version":"a02cb9d0a7363cbbce45fa86bbd7d64615811e30f2a7c47a2718fcf53f20eae7","signature":"9af2721670eb3402e476cd827256d4af7ab1d6db57f1615cbef18c75164df9e5"},"14c94f7888c75007a94132f03caef0f6b58bcb136c2994213fd2d3b99f3d7f85","4695042a55a75a6c62dc57f2efe60ef3c7bbe19adedb5081f6e51dd664bbc3f7","b006ad8d854471e7a270bd8918508090961bdc1cfe77ed51f13f471fe682acac","310901df1081433ff7c3b7918496cabb92ded208b04294d3d2bd35acae2de836","c8646410cd2a6bf05eb7e7a51c881776410d07fd5d8f75092a2c815c9c6fda52",{"version":"127604bb56d364ecc35cbb4927ba7c53f552353fc7913b07a4f5cc957210aabd","signature":"973a1e0a155ab26d66226ff9d64a36cf61227e9240b21cabdc67df29847a6599"},{"version":"dcea5769c8b69d7b7a5ee6ffd4d22260e47d53d22990e91d504cbdc0c0120c14","signature":"16c51743932253da5b661b0a5068eb1423a6f020f62e6783ce8ac5259cff10f2"},{"version":"67be5e00299e02d108b294758dcc0218da9f2a2823dea61d708ddbe705771ae5","signature":"05fe3dec4dc02961a8959758da54c6ff9d32a232183041163d4d52cc6bf39015"},{"version":"e2e5ebf01c7004f157b8c750fdddb9f227fbf3119a87297e3a014db04c3f0887","signature":"fc6fe9c667e291d0bbdc904c921d2c1d385175f8c135d9e549298c96265acaec"},{"version":"6b0da45d7a1027dd4a9b14ac009b018761e8851c84a9e54ecc1be9086f0516c6","signature":"38fd30580198d072da98f6dbcb7535f47359ca91ffe57e3b9bfd1961a3b209ab"},{"version":"407e1457c37a63fc162e15d0684d0f196550e0db0c84903c6057a37359305d20","signature":"4d0ada1fd2655eab6195efa682371a7a4322868bc75c466832a2776c4a7ea4c1"},{"version":"4c408d170f00539f8957a9cfce1d7f3e4a2d36651dbf4b16337a7af5c568d7e5","signature":"a0c04e7f7ac63b60e113ff0a7ad4fbf9214babb1db0572641709a5859710e27c"},"4489c6a9fde8934733aa7df6f7911461ee6e9e4ad092736bd416f6b2cc20b2c6","2c8e55457aaf4902941dfdba4061935922e8ee6e120539c9801cd7b400fae050","8041cfce439ff29d339742389de04c136e3029d6b1817f07b2d7fcbfb7534990","670a76db379b27c8ff42f1ba927828a22862e2ab0b0908e38b671f0e912cc5ed","9d38964b57191567a14b396422c87488cecd48f405c642daa734159875ee81d9","069bebfee29864e3955378107e243508b163e77ab10de6a5ee03ae06939f0bb9",{"version":"64d4b35c5456adf258d2cf56c341e203a073253f229ef3208fc0d5020253b241","affectsGlobalScope":true},"ee7d8894904b465b072be0d2e4b45cf6b887cdba16a467645c4e200982ece7ea","f3d8c757e148ad968f0d98697987db363070abada5f503da3c06aefd9d4248c1","bc3cba7b0af2d52e7425299aee518db479d44004eff6fbbd206d1ee7e5ec3fb5","afe73051ff6a03a9565cbd8ebb0e956ee3df5e913ad5c1ded64218aabfa3dcb5","035a5df183489c2e22f3cf59fc1ed2b043d27f357eecc0eb8d8e840059d44245","a4809f4d92317535e6b22b01019437030077a76fec1d93b9881c9ed4738fcc54","5f53fa0bd22096d2a78533f94e02c899143b8f0f9891a46965294ee8b91a9434","0d14fa22c41fdc7277e6f71473b20ebc07f40f00e38875142335d5b63cdfc9d2","d8aab31ba8e618cc3eea10b0945de81cb93b7e8150a013a482332263b9305322","462bccdf75fcafc1ae8c30400c9425e1a4681db5d605d1a0edb4f990a54d8094","5923d8facbac6ecf7c84739a5c701a57af94a6f6648d6229a6c768cf28f0f8cb","7adecb2c3238794c378d336a8182d4c3dd2c4fa6fa1785e2797a3db550edea62","dc12dc0e5aa06f4e1a7692149b78f89116af823b9e1f1e4eae140cd3e0e674e6","1bfc6565b90c8771615cd8cfcf9b36efc0275e5e83ac7d9181307e96eb495161","8a8a96898906f065f296665e411f51010b51372fa260d5373bf9f64356703190","7f82ef88bdb67d9a850dd1c7cd2d690f33e0f0acd208e3c9eba086f3670d4f73",{"version":"ccfd8774cd9b929f63ff7dcf657977eb0652e3547f1fcac1b3a1dc5db22d4d58","affectsGlobalScope":true},"d92dc90fecd2552db74d8dc3c6fb4db9145b2aa0efe2c127236ba035969068d4","96d14f21b7652903852eef49379d04dbda28c16ed36468f8c9fa08f7c14c9538","8841e2aa774b89bd23302dede20663306dc1b9902431ac64b24be8b8d0e3f649","916be7d770b0ae0406be9486ac12eb9825f21514961dd050594c4b250617d5a8","254d9fb8c872d73d34594be8a200fd7311dbfa10a4116bfc465fba408052f2b3","d88a5e779faf033be3d52142a04fbe1cb96009868e3bbdd296b2bc6c59e06c0e","d8f7109e14f20eb735225a62fd3f8366da1a8349e90331cdad57f4b04caf6c5a","cf3d384d082b933d987c4e2fe7bfb8710adfd9dc8155190056ed6695a25a559e","9871b7ee672bc16c78833bdab3052615834b08375cb144e4d2cba74473f4a589","c863198dae89420f3c552b5a03da6ed6d0acfa3807a64772b895db624b0de707","8b03a5e327d7db67112ebbc93b4f744133eda2c1743dbb0a990c61a8007823ef","86c73f2ee1752bac8eeeece234fd05dfcf0637a4fbd8032e4f5f43102faa8eec","42fad1f540271e35ca37cecda12c4ce2eef27f0f5cf0f8dd761d723c744d3159","ff3743a5de32bee10906aff63d1de726f6a7fd6ee2da4b8229054dfa69de2c34","83acd370f7f84f203e71ebba33ba61b7f1291ca027d7f9a662c6307d74e4ac22","1445cec898f90bdd18b2949b9590b3c012f5b7e1804e6e329fb0fe053946d5ec","0e5318ec2275d8da858b541920d9306650ae6ac8012f0e872fe66eb50321a669","cf530297c3fb3a92ec9591dd4fa229d58b5981e45fe6702a0bd2bea53a5e59be","c1f6f7d08d42148ddfe164d36d7aba91f467dbcb3caa715966ff95f55048b3a4","f4e9bf9103191ef3b3612d3ec0044ca4044ca5be27711fe648ada06fad4bcc85","0c1ee27b8f6a00097c2d6d91a21ee4d096ab52c1e28350f6362542b55380059a","7677d5b0db9e020d3017720f853ba18f415219fb3a9597343b1b1012cfd699f7","bc1c6bc119c1784b1a2be6d9c47addec0d83ef0d52c8fbe1f14a51b4dfffc675","52cf2ce99c2a23de70225e252e9822a22b4e0adb82643ab0b710858810e00bf1","770625067bb27a20b9826255a8d47b6b5b0a2d3dfcbd21f89904c731f671ba77","d1ed6765f4d7906a05968fb5cd6d1db8afa14dbe512a4884e8ea5c0f5e142c80","799c0f1b07c092626cf1efd71d459997635911bb5f7fc1196efe449bba87e965","2a184e4462b9914a30b1b5c41cf80c6d3428f17b20d3afb711fff3f0644001fd","9eabde32a3aa5d80de34af2c2206cdc3ee094c6504a8d0c2d6d20c7c179503cc","397c8051b6cfcb48aa22656f0faca2553c5f56187262135162ee79d2b2f6c966","a8ead142e0c87dcd5dc130eba1f8eeed506b08952d905c47621dc2f583b1bff9","a02f10ea5f73130efca046429254a4e3c06b5475baecc8f7b99a0014731be8b3","c2576a4083232b0e2d9bd06875dd43d371dee2e090325a9eac0133fd5650c1cb","4c9a0564bb317349de6a24eb4efea8bb79898fa72ad63a1809165f5bd42970dd","f40ac11d8859092d20f953aae14ba967282c3bb056431a37fced1866ec7a2681","cc11e9e79d4746cc59e0e17473a59d6f104692fd0eeea1bdb2e206eabed83b03","b444a410d34fb5e98aa5ee2b381362044f4884652e8bc8a11c8fe14bbd85518e","c35808c1f5e16d2c571aa65067e3cb95afeff843b259ecfa2fc107a9519b5392","14d5dc055143e941c8743c6a21fa459f961cbc3deedf1bfe47b11587ca4b3ef5","a3ad4e1fc542751005267d50a6298e6765928c0c3a8dce1572f2ba6ca518661c","f237e7c97a3a89f4591afd49ecb3bd8d14f51a1c4adc8fcae3430febedff5eb6","3ffdfbec93b7aed71082af62b8c3e0cc71261cc68d796665faa1e91604fbae8f","662201f943ed45b1ad600d03a90dffe20841e725203ced8b708c91fcd7f9379a","c9ef74c64ed051ea5b958621e7fb853fe3b56e8787c1587aefc6ea988b3c7e79","2462ccfac5f3375794b861abaa81da380f1bbd9401de59ffa43119a0b644253d","34baf65cfee92f110d6653322e2120c2d368ee64b3c7981dff08ed105c4f19b0","7d8ddf0f021c53099e34ee831a06c394d50371816caa98684812f089b4c6b3d4","7d2a0ba1297be385a89b5515b88cd31b4a1eeef5236f710166dc1b36b1741e1b","9d92b037978bb9525bc4b673ebddd443277542e010c0aef019c03a170ccdaa73","ab82804a14454734010dcdcd43f564ff7b0389bee4c5692eec76ff5b30d4cf66","bae8d023ef6b23df7da26f51cea44321f95817c190342a36882e93b80d07a960","ae271d475b632ce7b03fea6d9cf6da72439e57a109672671cbc79f54e1386938"],"options":{"composite":true,"declaration":true,"declarationMap":true,"emitDeclarationOnly":true,"esModuleInterop":true,"inlineSources":true,"module":1,"outDir":"./types","rootDir":"../src","sourceMap":true,"strict":true,"target":7},"fileIdsList":[[666],[72,108,109,110,125],[109,110,126,127],[108,109],[108,125,128,131],[108,128,131,132],[129,130,131,133,134],[108,131],[108,125,128,129,130,133],[108,116],[108],[72,108],[60,108],[112,113,114,115,116,117,118,119,120,121,122,123,124],[108,114,115],[108,114,116],[605,609,610],[605,608],[608],[609,611,613],[605,608,609,610,611,612],[605,608,612,616,617,618],[605,608,612,619],[605],[605,606],[606,607],[605,608,614,617,619,620],[615],[616,618,622,625,626],[616,618,626],[108,608,612,616,617,619,622],[616,626],[616,619,623],[608,617,619],[616,619,622,623,624,625,626,627,628,629,630,631,632,633,634,635,636,637],[616,624],[108,624],[605,612,616,617,618,619,620,623],[616,618,622,624],[616,623,624],[108,135,392,393],[393,394],[392],[108,387],[387,388,389,390,391],[108,359,366,367],[108,359,366,367,387],[108,359,366,367,371],[108,359,366,367,368,370,371],[108,359,366,367,369],[108,359,366,367,372,373,375,376],[365,387],[108,359,366,367,372],[108,359,366,367,370],[108,359,366,367,383],[108,359,366,367,384],[111,356,359,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386],[357],[357,358],[357,366,367,372,373],[359,365,366],[402,403],[72,108,392,395,401],[395,402],[216],[545,546,547],[216,544,545],[249,258,259,262],[249,258,261],[250,253,254,256],[250,251,252],[253,254,256,257],[250,251,255],[261,267],[249,258,261,267],[258,261,267],[258,261,263,264,265,266],[249,258,259,260,261,262,267],[249,258,260,262],[249,258],[250],[144,167],[144],[189,190,191,192,194,195,196],[167,189,190,191,192,194,195],[144,167,193],[198],[201],[144,193],[202,203,204],[197],[193,197,199,200,205,206,213,215,235,236,354],[167,200,205],[208],[207,209,210,211,212],[144,167,193,197],[167,197,214],[167,214,215,234],[167,206,353],[138,144],[654,655,656,657],[568],[548,567],[560,561,562,563,564,565,566],[560],[562],[560,562],[425],[108,228,424],[108,228,425],[108,228,396],[396],[396,397,398,399,400],[108,228],[228,427],[427,428,429,430,431,432,433,434,435,436],[228,427,428],[61,108,228,427,428],[105,108,228,427,428],[228,428],[167,216,223,224],[225],[229],[224,225,226,229,230,231,232,233],[108,226,228,231],[108,167,225,228,229],[224,231],[108,167,216,223,225,228],[167,223],[167,237],[237,238],[237,238,239,240],[167],[188,353,475,492,493,506,531],[529],[167,504,519,527,528,530],[518],[188,353,455,517],[108,223,228,426,437,492,493,494,506],[167,188,492,506],[437,495],[497],[494,495,496,498,501,503],[500],[495,499,504],[437,495,496],[167,437],[502],[495,496],[509,524,525,526],[492,506],[505,506,507,508],[492,506,507],[167,492,505,506],[167,492,506],[522,523],[167,188,485,522],[167,353,485],[167,184,188,353,462,475,485,492,493,504,506,509,511,519,521,524],[520],[167,353,475,492,493,506,527],[144,167,484],[108,167,242],[167,350],[319],[167,242,319,347,350,351,352],[108,167,241,242],[243,244,245,246,348,349],[138,144,347],[138,144,245],[277],[277,283],[277,282,284,285],[277,282,286],[282,283,284,285,286,287],[289,290],[278,279,280,281,288,291,292,293,294,295,296,297,298],[277,291],[277,278,293,295,296],[277,291,294],[277,299,300,301,302],[144,277,299,350],[334],[347],[337,338,339,340,341,342,343,344,345],[167,269],[319,343,350],[269,347,350],[144,320],[269,270,320,323,333,334,335,346],[144,167,303,319],[347,350],[268,270],[270],[350],[320],[167,323],[247,248,271,272,273,274,275,276,321,322,324,325,326,327,328,329,330,331,332],[167,325],[247,248,271,272,273,274,275,276,321,322,324,325,326,327,328,329,330,331,350],[167,268,269],[234,333],[167,270],[317],[144,304],[305,306,307,308,309,310,311,312,313,314,315,316],[304,317,318],[303],[353],[441],[440],[144,167,447],[268],[167,241,353],[144,353,454,455],[438,439,441,442,443,444,445,448,449,450,451,452,453,454,456,457,458,476,477,479,480,481,482,483,486,487,488,489,490,491],[144,167,475],[144,167,353,441,479],[158],[478],[144,167,268,353,477],[144,353,477],[144,353,475,478,480,485,486],[144,167,441,444,456,479,480],[353,455],[486],[536],[536,537,538],[147],[144,147],[145,146,147,148,149,150,151,152,153,154,155,156,159,160,161,162,163,164,165,166],[138,144,145],[135,147,153,155],[147,148],[144,162],[108,362],[360,361,364],[360,363],[108,360],[412,413],[666,667,668,669,670],[666,668],[157],[672,673,674],[73,108],[677],[678],[689],[683,688],[579,581,582,583,584,585,586,587,588,589,590,591],[579,580,582,583,584,585,586,587,588,589,590,591],[580,581,582,583,584,585,586,587,588,589,590,591],[579,580,581,583,584,585,586,587,588,589,590,591],[579,580,581,582,584,585,586,587,588,589,590,591],[579,580,581,582,583,585,586,587,588,589,590,591],[579,580,581,582,583,584,586,587,588,589,590,591],[579,580,581,582,583,584,585,587,588,589,590,591],[579,580,581,582,583,584,585,586,588,589,590,591],[579,580,581,582,583,584,585,586,587,589,590,591],[579,580,581,582,583,584,585,586,587,588,590,591],[579,580,581,582,583,584,585,586,587,588,589,591],[579,580,581,582,583,584,585,586,587,588,589,590],[56],[59],[60,65,92],[61,72,73,80,89,100],[61,62,72,80],[63,101],[64,65,73,81],[65,89,97],[66,68,72,80],[67],[68,69],[72],[71,72],[59,72],[72,73,74,89,100],[72,73,74,89],[72,75,80,89,100],[72,73,75,76,80,89,97,100],[75,77,89,97,100],[56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107],[72,78],[79,100,105],[68,72,80,89],[81],[82],[59,83],[84,99,105],[85],[86],[72,87],[87,88,101,103],[60,72,89,90,91],[60,89,91],[89,90],[92],[93],[72,95,96],[95,96],[65,80,89,97],[98],[80,99],[60,75,86,100],[65,101],[89,102],[103],[104],[60,65,72,74,83,89,100,103,105],[89,106],[108,227],[697,736],[697,721,736],[736],[697],[697,722,736],[697,698,699,700,701,702,703,704,705,706,707,708,709,710,711,712,713,714,715,716,717,718,719,720,721,722,723,724,725,726,727,728,729,730,731,732,733,734,735],[722,736],[737],[740],[510],[570,571,572,573,574,575,576],[570],[572],[570,572],[446],[593,594,595],[593],[594],[179],[179,180,181,182,183],[168,169,170,171,172,173,174,175,176,177,178],[681,684],[681,684,685,686],[683],[680,687],[682],[137,139,140,141,142,143],[137,138],[139],[138,139],[137,139],[167,188,355,423,531],[532,534],[423,533],[167,188,241],[460,461],[167,184,185,186],[185],[136,185,186,187],[186],[407],[407,408,411,415],[414],[167,409,410],[541,542,543],[223,541],[167,223,541],[167,216,223],[167,188,553,557],[558],[167,217],[217,218,219,220,221,222],[167,216],[422],[135,167,184,188,355,404,405,421],[72,108,167,188],[406],[406,417,418,419,420],[406,416],[549],[540,549,551,552],[167,188,409,416,539,540,549,550],[167,416,544,548],[167,467,468,469],[241,467,468,470],[465,466,467,468,469,470,474],[167,223,467,469,475],[167,188,416,459,465,467,470],[167,184,188,241,416,459,462,463,464,465,466,468,469,470],[167,223,466,467,468],[471,472,473],[167,223,416,466,468,469],[167,223,466,468,469],[167,188,467],[167,223,468,470],[516],[188,416,512,514,515],[513,514],[515,516],[167,188,553,554],[554,555,556],[108,167,188,553,554],[167,553],[167,597],[167,559,592,597,602],[167,409,410,416,592,597,603],[167,597,599,639],[128,135,167,591,592,597,621,638],[167,416,597],[167,410,416,533,577,592,597,599,643],[72,167,409,553,559,592,597,598,645],[72,167,535,553,577,592,597],[167,409,553,569,577,592,644,647,648],[72,409,416,553,591,592,597],[597,598,600,643,662,664],[72,125,128,135,167,188,241,409,416,462,533,535,553,559,569,577,578,591,592,597,598,600,601,602,603,604,640,641,642,644,645,646,647,648,649,650,651,652,653,661,662,663],[135,167,409,553,559,596],[167,416,592,599],[241,597],[167,409,416,559,592,597,598,601],[416,559,597],[167,409,416,592,597,599],[591,596,597],[167,553,592,597],[416,569,592,597],[167,416,592,659],[52,167,416,592,597,599,614,659,660],[409,416,591,592,597,599,600,664],[52,409,416,597,614],[167,416,597,664],[52,241,416,597,600,614],[597],[597,639],[72,167,553,559,597],[72,167,535,553,597],[167,409,553,569,644,647,648],[72,409,553,597],[158,167],[128,135,167,188,462,535,553,559,569,597,647],[167,409,559,597],[559,597],[158,167,409,597],[167,553,597],[569,597],[167,597,614,660],[409,597,664],[409,597],[597,664]],"referencedMap":[[668,1],[126,2],[128,3],[110,4],[132,5],[133,6],[129,6],[135,7],[130,6],[134,8],[131,9],[117,10],[114,11],[121,12],[115,10],[112,13],[125,14],[119,11],[116,15],[118,16],[611,17],[610,18],[609,19],[614,20],[613,21],[619,22],[620,23],[606,24],[607,25],[608,26],[621,27],[616,28],[627,29],[628,30],[623,31],[629,32],[630,33],[631,33],[622,34],[638,35],[633,29],[632,36],[634,37],[624,38],[635,32],[636,30],[626,39],[637,36],[625,40],[617,18],[394,41],[395,42],[393,43],[390,44],[389,44],[388,44],[392,45],[374,46],[379,47],[368,46],[373,48],[372,49],[370,50],[377,51],[378,46],[380,52],[381,53],[383,54],[384,55],[385,56],[387,57],[358,58],[359,59],[375,60],[367,61],[404,62],[402,63],[403,64],[545,65],[548,66],[546,67],[547,67],[260,68],[262,69],[257,70],[253,71],[254,71],[258,72],[256,73],[263,74],[264,75],[265,76],[267,77],[266,74],[268,78],[261,79],[259,80],[252,81],[255,71],[189,82],[190,83],[191,83],[192,82],[197,84],[196,85],[194,86],[195,82],[199,87],[198,82],[202,88],[201,89],[205,90],[203,82],[204,91],[355,92],[207,86],[208,93],[209,94],[210,86],[213,95],[212,96],[214,86],[215,97],[235,98],[236,97],[354,99],[193,100],[658,101],[569,102],[568,103],[567,104],[561,105],[563,106],[565,107],[564,107],[426,108],[425,109],[424,110],[397,111],[398,112],[401,113],[399,112],[396,65],[400,114],[428,115],[437,116],[433,117],[432,118],[435,117],[434,119],[436,120],[431,120],[430,117],[429,120],[225,121],[226,122],[230,123],[234,124],[232,125],[231,126],[233,127],[229,128],[224,129],[238,130],[239,131],[241,132],[237,133],[216,12],[529,134],[530,135],[531,136],[519,137],[518,138],[495,139],[494,140],[497,141],[498,142],[504,143],[501,144],[500,145],[499,146],[496,147],[503,148],[502,149],[527,150],[507,151],[509,152],[508,153],[506,154],[505,155],[524,156],[523,157],[522,158],[526,151],[525,159],[520,133],[521,160],[528,161],[485,162],[484,82],[351,163],[242,164],[352,165],[353,166],[243,167],[244,133],[245,133],[350,168],[348,169],[246,170],[277,133],[278,171],[279,171],[280,171],[281,171],[282,171],[284,172],[286,173],[287,174],[288,175],[285,171],[283,171],[289,171],[291,176],[290,171],[292,171],[293,171],[299,177],[294,178],[297,179],[298,171],[295,180],[296,171],[303,181],[301,171],[300,171],[302,182],[335,183],[337,133],[338,184],[346,185],[339,133],[341,186],[342,133],[344,187],[343,188],[345,189],[347,190],[320,191],[247,184],[248,192],[271,193],[272,194],[273,193],[275,133],[276,195],[321,196],[324,197],[333,198],[326,199],[325,133],[327,133],[328,164],[332,200],[329,195],[330,197],[331,184],[270,201],[334,202],[323,203],[318,204],[305,205],[314,205],[306,205],[307,205],[316,205],[308,205],[309,205],[317,206],[315,205],[310,205],[313,205],[311,205],[312,205],[319,207],[304,83],[455,208],[439,209],[442,210],[443,211],[445,210],[448,212],[451,213],[453,214],[456,215],[492,216],[476,217],[458,133],[480,218],[481,219],[479,220],[478,221],[482,222],[487,223],[477,100],[486,224],[489,225],[490,226],[491,133],[441,211],[537,227],[538,227],[539,228],[145,83],[146,83],[148,229],[149,83],[150,83],[151,230],[147,83],[167,231],[155,232],[156,233],[159,219],[165,234],[166,235],[363,236],[362,11],[365,237],[360,11],[364,238],[361,239],[414,240],[671,241],[667,1],[669,242],[670,1],[410,11],[158,243],[675,244],[676,245],[678,246],[679,247],[690,248],[689,249],[580,250],[581,251],[579,252],[582,253],[583,254],[584,255],[585,256],[586,257],[587,258],[588,259],[589,260],[590,261],[591,262],[56,263],[57,263],[59,264],[60,265],[61,266],[62,267],[63,268],[64,269],[65,270],[66,271],[67,272],[68,273],[69,273],[70,274],[71,275],[72,276],[73,277],[74,278],[75,279],[76,280],[77,281],[108,282],[78,283],[79,284],[80,285],[81,286],[82,287],[83,288],[84,289],[85,290],[86,291],[87,292],[88,293],[89,294],[91,295],[90,296],[92,297],[93,298],[95,299],[96,300],[97,301],[98,302],[99,303],[100,304],[101,305],[102,306],[103,307],[104,308],[105,309],[106,310],[694,11],[228,311],[696,11],[721,312],[722,313],[697,314],[700,314],[719,312],[720,312],[710,312],[709,315],[707,312],[702,312],[715,312],[713,312],[717,312],[701,312],[714,312],[718,312],[703,312],[704,312],[716,312],[698,312],[705,312],[706,312],[708,312],[712,312],[723,316],[711,312],[699,312],[736,317],[730,316],[732,318],[731,316],[724,316],[725,316],[727,316],[729,316],[733,318],[734,318],[726,318],[728,318],[738,319],[741,320],[511,321],[577,322],[571,323],[573,324],[575,325],[574,325],[447,326],[596,327],[594,328],[595,329],[175,330],[177,330],[176,330],[174,330],[184,331],[179,332],[170,330],[171,330],[172,330],[173,330],[685,333],[687,334],[686,333],[684,335],[688,336],[683,337],[144,338],[139,339],[140,340],[141,340],[142,341],[143,341],[138,342],[532,343],[535,344],[534,345],[460,346],[462,347],[187,348],[186,349],[188,350],[185,351],[408,352],[416,353],[415,354],[411,355],[544,356],[542,357],[543,358],[541,359],[558,360],[559,361],[218,362],[219,362],[221,362],[223,363],[217,364],[222,362],[423,365],[422,366],[406,367],[420,368],[419,368],[421,369],[417,370],[418,368],[550,371],[552,371],[553,372],[551,373],[549,374],[470,375],[469,376],[475,377],[464,378],[468,379],[467,380],[471,381],[474,382],[472,383],[473,384],[465,385],[466,386],[517,387],[516,388],[515,389],[514,390],[555,391],[557,392],[556,393],[554,394],[659,395],[603,396],[604,397],[640,398],[639,399],[641,398],[642,400],[644,401],[646,402],[647,403],[649,404],[648,405],[665,406],[592,133],[664,407],[597,408],[643,409],[650,410],[602,411],[598,412],[651,413],[652,414],[645,415],[653,416],[660,417],[661,418],[601,419],[662,420],[600,421],[663,422]],"exportedModulesMap":[[668,1],[126,2],[128,3],[110,4],[132,5],[133,6],[129,6],[135,7],[130,6],[134,8],[131,9],[117,10],[114,11],[121,12],[115,10],[112,13],[125,14],[119,11],[116,15],[118,16],[611,17],[610,18],[609,19],[614,20],[613,21],[619,22],[620,23],[606,24],[607,25],[608,26],[621,27],[616,28],[627,29],[628,30],[623,31],[629,32],[630,33],[631,33],[622,34],[638,35],[633,29],[632,36],[634,37],[624,38],[635,32],[636,30],[626,39],[637,36],[625,40],[617,18],[394,41],[395,42],[393,43],[390,44],[389,44],[388,44],[392,45],[374,46],[379,47],[368,46],[373,48],[372,49],[370,50],[377,51],[378,46],[380,52],[381,53],[383,54],[384,55],[385,56],[387,57],[358,58],[359,59],[375,60],[367,61],[404,62],[402,63],[403,64],[545,65],[548,66],[546,67],[547,67],[260,68],[262,69],[257,70],[253,71],[254,71],[258,72],[256,73],[263,74],[264,75],[265,76],[267,77],[266,74],[268,78],[261,79],[259,80],[252,81],[255,71],[189,82],[190,83],[191,83],[192,82],[197,84],[196,85],[194,86],[195,82],[199,87],[198,82],[202,88],[201,89],[205,90],[203,82],[204,91],[355,92],[207,86],[208,93],[209,94],[210,86],[213,95],[212,96],[214,86],[215,97],[235,98],[236,97],[354,99],[193,100],[658,101],[569,102],[568,103],[567,104],[561,105],[563,106],[565,107],[564,107],[426,108],[425,109],[424,110],[397,111],[398,112],[401,113],[399,112],[396,65],[400,114],[428,115],[437,116],[433,117],[432,118],[435,117],[434,119],[436,120],[431,120],[430,117],[429,120],[225,121],[226,122],[230,123],[234,124],[232,125],[231,126],[233,127],[229,128],[224,129],[238,130],[239,131],[241,132],[237,133],[216,12],[529,134],[530,135],[531,136],[519,137],[518,138],[495,139],[494,140],[497,141],[498,142],[504,143],[501,144],[500,145],[499,146],[496,147],[503,148],[502,149],[527,150],[507,151],[509,152],[508,153],[506,154],[505,155],[524,156],[523,157],[522,158],[526,151],[525,159],[520,133],[521,160],[528,161],[485,162],[484,82],[351,163],[242,164],[352,165],[353,166],[243,167],[244,133],[245,133],[350,168],[348,169],[246,170],[277,133],[278,171],[279,171],[280,171],[281,171],[282,171],[284,172],[286,173],[287,174],[288,175],[285,171],[283,171],[289,171],[291,176],[290,171],[292,171],[293,171],[299,177],[294,178],[297,179],[298,171],[295,180],[296,171],[303,181],[301,171],[300,171],[302,182],[335,183],[337,133],[338,184],[346,185],[339,133],[341,186],[342,133],[344,187],[343,188],[345,189],[347,190],[320,191],[247,184],[248,192],[271,193],[272,194],[273,193],[275,133],[276,195],[321,196],[324,197],[333,198],[326,199],[325,133],[327,133],[328,164],[332,200],[329,195],[330,197],[331,184],[270,201],[334,202],[323,203],[318,204],[305,205],[314,205],[306,205],[307,205],[316,205],[308,205],[309,205],[317,206],[315,205],[310,205],[313,205],[311,205],[312,205],[319,207],[304,83],[455,208],[439,209],[442,210],[443,211],[445,210],[448,212],[451,213],[453,214],[456,215],[492,216],[476,217],[458,133],[480,218],[481,219],[479,220],[478,221],[482,222],[487,223],[477,100],[486,224],[489,225],[490,226],[491,133],[441,211],[537,227],[538,227],[539,228],[145,83],[146,83],[148,229],[149,83],[150,83],[151,230],[147,83],[167,231],[155,232],[156,233],[159,219],[165,234],[166,235],[363,236],[362,11],[365,237],[360,11],[364,238],[361,239],[414,240],[671,241],[667,1],[669,242],[670,1],[410,11],[158,243],[675,244],[676,245],[678,246],[679,247],[690,248],[689,249],[580,250],[581,251],[579,252],[582,253],[583,254],[584,255],[585,256],[586,257],[587,258],[588,259],[589,260],[590,261],[591,262],[56,263],[57,263],[59,264],[60,265],[61,266],[62,267],[63,268],[64,269],[65,270],[66,271],[67,272],[68,273],[69,273],[70,274],[71,275],[72,276],[73,277],[74,278],[75,279],[76,280],[77,281],[108,282],[78,283],[79,284],[80,285],[81,286],[82,287],[83,288],[84,289],[85,290],[86,291],[87,292],[88,293],[89,294],[91,295],[90,296],[92,297],[93,298],[95,299],[96,300],[97,301],[98,302],[99,303],[100,304],[101,305],[102,306],[103,307],[104,308],[105,309],[106,310],[694,11],[228,311],[696,11],[721,312],[722,313],[697,314],[700,314],[719,312],[720,312],[710,312],[709,315],[707,312],[702,312],[715,312],[713,312],[717,312],[701,312],[714,312],[718,312],[703,312],[704,312],[716,312],[698,312],[705,312],[706,312],[708,312],[712,312],[723,316],[711,312],[699,312],[736,317],[730,316],[732,318],[731,316],[724,316],[725,316],[727,316],[729,316],[733,318],[734,318],[726,318],[728,318],[738,319],[741,320],[511,321],[577,322],[571,323],[573,324],[575,325],[574,325],[447,326],[596,327],[594,328],[595,329],[175,330],[177,330],[176,330],[174,330],[184,331],[179,332],[170,330],[171,330],[172,330],[173,330],[685,333],[687,334],[686,333],[684,335],[688,336],[683,337],[144,338],[139,339],[140,340],[141,340],[142,341],[143,341],[138,342],[532,343],[535,344],[534,345],[460,346],[462,347],[187,348],[186,349],[188,350],[185,351],[408,352],[416,353],[415,354],[411,355],[544,356],[542,357],[543,358],[541,359],[558,360],[559,361],[218,362],[219,362],[221,362],[223,363],[217,364],[222,362],[423,365],[422,366],[406,367],[420,368],[419,368],[421,369],[417,370],[418,368],[550,371],[552,371],[553,372],[551,373],[549,374],[470,375],[469,376],[475,377],[464,378],[468,379],[467,380],[471,381],[474,382],[472,383],[473,384],[465,385],[466,386],[517,387],[516,388],[515,389],[514,390],[555,391],[557,392],[556,393],[554,394],[659,133],[603,423],[604,423],[640,424],[639,395],[641,424],[642,423],[644,395],[646,425],[647,426],[649,427],[648,428],[665,406],[592,429],[664,430],[597,408],[643,133],[650,423],[602,431],[598,432],[651,433],[652,423],[645,434],[653,435],[660,133],[661,436],[601,437],[662,438],[600,439],[663,423]],"semanticDiagnosticsPerFile":[668,666,126,109,128,110,127,132,133,129,135,130,134,131,117,114,121,115,112,120,125,122,123,124,119,116,113,118,611,610,609,614,613,619,620,606,607,608,605,621,616,615,612,627,628,623,629,630,631,622,638,633,632,634,624,635,636,626,637,625,617,618,394,395,393,390,389,388,392,391,374,379,368,373,372,370,377,378,380,382,381,383,384,385,387,358,357,359,375,356,369,367,366,376,371,386,404,402,403,405,545,548,546,547,409,260,262,249,257,253,254,258,256,263,264,265,267,266,268,261,259,252,250,251,255,189,190,191,192,197,196,194,195,199,198,200,202,201,205,203,204,206,355,207,208,209,210,213,211,212,214,215,235,236,354,193,656,654,655,657,658,569,568,566,567,561,560,563,562,565,564,426,425,424,397,398,401,399,396,400,428,437,433,432,435,434,436,427,431,430,429,225,226,230,234,232,231,233,229,224,238,240,239,241,237,216,529,530,531,519,518,495,494,497,498,504,501,500,499,496,503,502,527,507,509,508,506,505,524,523,522,526,525,493,520,521,528,485,484,351,242,352,353,243,244,245,350,348,246,349,277,278,279,280,281,282,284,286,287,288,285,283,289,291,290,292,293,299,294,297,298,295,296,303,301,300,302,269,335,337,338,346,339,340,341,342,344,343,345,336,347,320,247,248,271,272,273,274,275,276,321,322,324,333,326,325,327,328,332,329,330,331,270,334,323,318,305,314,306,307,316,308,309,317,315,310,313,311,312,319,304,455,438,439,442,443,444,445,448,449,450,451,452,453,454,456,457,492,476,458,480,481,479,478,482,483,487,488,477,486,489,490,491,441,440,537,538,539,536,145,146,148,149,150,151,152,153,154,147,167,155,156,159,160,161,162,163,164,165,166,363,362,365,360,364,361,412,414,413,671,667,669,670,410,158,463,672,675,673,676,677,678,679,690,689,674,691,580,581,579,582,583,584,585,586,587,588,589,590,591,692,157,56,57,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,58,107,75,76,77,108,78,79,80,81,82,83,84,85,86,87,88,89,91,90,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,693,694,695,512,228,227,696,721,722,697,700,719,720,710,709,707,702,715,713,717,701,714,718,703,704,716,698,705,706,708,712,723,711,699,736,735,730,732,731,724,725,727,729,733,734,726,728,738,737,739,533,740,741,511,510,576,577,571,570,573,572,575,574,111,680,446,447,578,596,594,595,593,513,178,175,177,176,174,184,179,183,180,182,181,170,171,172,168,169,173,681,685,687,686,684,688,459,683,682,137,144,139,140,141,142,143,138,8,10,9,2,11,12,13,14,15,16,17,18,3,4,22,19,20,21,23,24,25,5,26,27,28,29,6,33,30,31,32,34,7,35,40,41,36,37,38,39,1,42,532,535,534,460,461,462,136,187,186,188,185,408,416,415,407,411,544,542,543,541,558,559,218,219,220,221,223,217,222,423,422,406,420,419,421,417,418,540,550,552,553,551,549,470,469,475,464,468,467,471,474,472,473,465,466,517,516,515,514,555,557,556,554,599,659,603,604,640,639,641,642,644,646,647,649,648,665,592,664,597,643,650,602,598,651,652,645,653,660,661,601,662,600,663,47,48,49,50,51,52,43,53,54,55,44,45,46],"latestChangedDtsFile":"./types/index.d.ts"},"version":"4.9.5"} -\ No newline at end of file -diff --git a/dist/types/TransactionController.d.ts b/dist/types/TransactionController.d.ts -index f4c0fa6672b24d7756513bd46c565f9da0ffd61a..37793f450f9e52815dcebc2557ec910ad6ab6021 100644 ---- a/dist/types/TransactionController.d.ts -+++ b/dist/types/TransactionController.d.ts -@@ -115,7 +115,6 @@ export type PendingTransactionOptions = { - * @property transactionHistoryLimit - Transaction history limit. - * @property hooks - The controller hooks. - * @property hooks.afterSign - Additional logic to execute after signing a transaction. Return false to not change the status to signed. -- * @property hooks.beforeApproveOnInit - Additional logic to execute before starting an approval flow for a transaction during initialization. Return false to skip the transaction. - * @property hooks.beforeCheckPendingTransaction - Additional logic to execute before checking pending transactions. Return false to prevent the broadcast of the transaction. - * @property hooks.beforePublish - Additional logic to execute before publishing a transaction. Return false to prevent the broadcast of the transaction. - * @property hooks.getAdditionalSignArguments - Returns additional arguments required to sign a transaction. -@@ -148,7 +147,6 @@ export type TransactionControllerOptions = { - transactionHistoryLimit: number; - hooks: { - afterSign?: (transactionMeta: TransactionMeta, signedTx: TypedTransaction) => boolean; -- beforeApproveOnInit?: (transactionMeta: TransactionMeta) => boolean; - beforeCheckPendingTransaction?: (transactionMeta: TransactionMeta) => boolean; - beforePublish?: (transactionMeta: TransactionMeta) => boolean; - getAdditionalSignArguments?: (transactionMeta: TransactionMeta) => (TransactionMeta | undefined)[]; -@@ -365,7 +363,6 @@ export declare class TransactionController extends BaseController Promise; -+ constructor({ blockTracker, getChainId, getEthQuery, getTransactions, isResubmitEnabled, getGlobalLock, publishTransaction, hooks, }: { - blockTracker: BlockTracker; - getChainId: () => string; - getEthQuery: (networkClientId?: NetworkClientId) => EthQuery; -diff --git a/dist/types/helpers/PendingTransactionTracker.d.ts.map b/dist/types/helpers/PendingTransactionTracker.d.ts.map -index 14d6402240dc02077ae66e3d049b03f529bbde30..bdca97bd140595aa27e63cd7dcb89389c7170341 100644 ---- a/dist/types/helpers/PendingTransactionTracker.d.ts.map -+++ b/dist/types/helpers/PendingTransactionTracker.d.ts.map -@@ -1 +1 @@ --{"version":3,"file":"PendingTransactionTracker.d.ts","sourceRoot":"","sources":["../../../src/helpers/PendingTransactionTracker.ts"],"names":[],"mappings":";AACA,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,EACV,YAAY,EACZ,eAAe,EAChB,MAAM,8BAA8B,CAAC;AACtC,OAAO,YAAY,MAAM,QAAQ,CAAC;AAIlC,OAAO,KAAK,EAAE,eAAe,EAAsB,MAAM,UAAU,CAAC;AA6BpE,KAAK,MAAM,GAAG;IACZ,uBAAuB,EAAE,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IACnD,qBAAqB,EAAE,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IACjD,oBAAoB,EAAE,CAAC,MAAM,EAAE,eAAe,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;IAC9D,qBAAqB,EAAE,CAAC,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;CAChE,CAAC;AAKF,MAAM,WAAW,qCAAsC,SAAQ,YAAY;IAGzE,EAAE,CAAC,CAAC,SAAS,MAAM,MAAM,EACvB,SAAS,EAAE,CAAC,EACZ,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,GACrC,IAAI,CAAC;IAIR,IAAI,CAAC,CAAC,SAAS,MAAM,MAAM,EAAE,SAAS,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC;CACzE;AAED,qBAAa,yBAAyB;;IACpC,GAAG,EAAE,qCAAqC,CAAC;gBA8B/B,EACV,kBAAkB,EAClB,YAAY,EACZ,UAAU,EACV,WAAW,EACX,eAAe,EACf,iBAAiB,EACjB,aAAa,EACb,kBAAkB,EAClB,KAAK,GACN,EAAE;QACD,kBAAkB,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;QAC7D,YAAY,EAAE,YAAY,CAAC;QAC3B,UAAU,EAAE,MAAM,MAAM,CAAC;QACzB,WAAW,EAAE,CAAC,eAAe,CAAC,EAAE,eAAe,KAAK,QAAQ,CAAC;QAC7D,eAAe,EAAE,MAAM,eAAe,EAAE,CAAC;QACzC,iBAAiB,CAAC,EAAE,MAAM,OAAO,CAAC;QAClC,aAAa,EAAE,MAAM,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC;QACzC,kBAAkB,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;QAC3E,KAAK,CAAC,EAAE;YACN,6BAA6B,CAAC,EAAE,CAC9B,eAAe,EAAE,eAAe,KAC7B,OAAO,CAAC;YACb,aAAa,CAAC,EAAE,CAAC,eAAe,EAAE,eAAe,KAAK,OAAO,CAAC;SAC/D,CAAC;KACH;IAmBD,0BAA0B,aAQxB;IAEF;;;;OAIG;IACG,qBAAqB,CAAC,MAAM,EAAE,eAAe;IAwBnD,IAAI;CAqXL"} -\ No newline at end of file -+{"version":3,"file":"PendingTransactionTracker.d.ts","sourceRoot":"","sources":["../../../src/helpers/PendingTransactionTracker.ts"],"names":[],"mappings":";AACA,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,EACV,YAAY,EACZ,eAAe,EAChB,MAAM,8BAA8B,CAAC;AACtC,OAAO,YAAY,MAAM,QAAQ,CAAC;AAIlC,OAAO,KAAK,EAAE,eAAe,EAAsB,MAAM,UAAU,CAAC;AA6BpE,KAAK,MAAM,GAAG;IACZ,uBAAuB,EAAE,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IACnD,qBAAqB,EAAE,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IACjD,oBAAoB,EAAE,CAAC,MAAM,EAAE,eAAe,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;IAC9D,qBAAqB,EAAE,CAAC,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;CAChE,CAAC;AAKF,MAAM,WAAW,qCAAsC,SAAQ,YAAY;IAGzE,EAAE,CAAC,CAAC,SAAS,MAAM,MAAM,EACvB,SAAS,EAAE,CAAC,EACZ,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,GACrC,IAAI,CAAC;IAIR,IAAI,CAAC,CAAC,SAAS,MAAM,MAAM,EAAE,SAAS,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC;CACzE;AAED,qBAAa,yBAAyB;;IACpC,GAAG,EAAE,qCAAqC,CAAC;gBA4B/B,EACV,YAAY,EACZ,UAAU,EACV,WAAW,EACX,eAAe,EACf,iBAAiB,EACjB,aAAa,EACb,kBAAkB,EAClB,KAAK,GACN,EAAE;QACD,YAAY,EAAE,YAAY,CAAC;QAC3B,UAAU,EAAE,MAAM,MAAM,CAAC;QACzB,WAAW,EAAE,CAAC,eAAe,CAAC,EAAE,eAAe,KAAK,QAAQ,CAAC;QAC7D,eAAe,EAAE,MAAM,eAAe,EAAE,CAAC;QACzC,iBAAiB,CAAC,EAAE,MAAM,OAAO,CAAC;QAClC,aAAa,EAAE,MAAM,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC;QACzC,kBAAkB,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;QAC3E,KAAK,CAAC,EAAE;YACN,6BAA6B,CAAC,EAAE,CAC9B,eAAe,EAAE,eAAe,KAC7B,OAAO,CAAC;YACb,aAAa,CAAC,EAAE,CAAC,eAAe,EAAE,eAAe,KAAK,OAAO,CAAC;SAC/D,CAAC;KACH;IAkBD,0BAA0B,aAQxB;IAEF;;;;OAIG;IACG,qBAAqB,CAAC,MAAM,EAAE,eAAe;IAwBnD,IAAI;CAiXL"} -\ No newline at end of file diff --git a/.yarnrc.yml b/.yarnrc.yml index 252333917781..7176c6152327 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -43,6 +43,12 @@ npmAuditIgnoreAdvisories: # not appear to be used. - 1092461 + # Issue: Sentry SDK Prototype Pollution gadget in JavaScript SDKs + # URL: https://github.com/advisories/GHSA-593m-55hh-j8gv + # Not easily fixed in this version, will be fixed in v12.5.0 + # Minimally effects the extension due to usage of LavaMoat + SES lockdown. + - 1099839 + # Temp fix for https://github.com/MetaMask/metamask-extension/pull/16920 for the sake of 11.7.1 hotfix # This will be removed in this ticket https://github.com/MetaMask/metamask-extension/issues/22299 - 'ts-custom-error (deprecation)' diff --git a/CHANGELOG.md b/CHANGELOG.md index 9dd79055e0c3..c5fbcff7a94f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [12.4.0] +### Added +- Added a receive button to the home screen, allowing users to easily get their address or QR-code for receiving cryptocurrency ([#26148](https://github.com/MetaMask/metamask-extension/pull/26148)) +- Added smart transactions functionality for hardware wallet users ([#26251](https://github.com/MetaMask/metamask-extension/pull/26251)) +- Added new custom UI components for Snaps developers ([#26675](https://github.com/MetaMask/metamask-extension/pull/26675)) +- Add support for footers to Snap home pages ([#26463](https://github.com/MetaMask/metamask-extension/pull/26463)) +- [FLASK] Added Account Watcher as a preinstalled snap and added it to the menu list ([#26402](https://github.com/MetaMask/metamask-extension/pull/26402)) +- [FLASK] Added footers to Snap home pages ([#26463](https://github.com/MetaMask/metamask-extension/pull/26463)) +- Added icons for IoTeX network ([#26723](https://github.com/MetaMask/metamask-extension/pull/26723)) +- Added NEAR icon for chainId 397 and 398 ([#26459](https://github.com/MetaMask/metamask-extension/pull/26459)) + + +### Changed +- Redesign contract deployment transaction screen ([#26382](https://github.com/MetaMask/metamask-extension/pull/26382)) +- Improve performance, reliability and coverage of the phishing detection feature ([#25839](https://github.com/MetaMask/metamask-extension/pull/25839)) +- Updated Moonbeam and Moonriver network and token logos ([#26677](https://github.com/MetaMask/metamask-extension/pull/26677)) +- Updated UI for add network notification window ([#25777](https://github.com/MetaMask/metamask-extension/pull/25777)) +- Update visual styling of token lists ([#26300](https://github.com/MetaMask/metamask-extension/pull/26300)) +- Update spacing on Snap home page ([#26462](https://github.com/MetaMask/metamask-extension/pull/26462)) +- [FLASK] Integrated Snaps into the redesigned confirmation pages ([#26435](https://github.com/MetaMask/metamask-extension/pull/26435)) + +### Fixed +- Fixed network change toast width in wide screen mode ([#26532](https://github.com/MetaMask/metamask-extension/pull/26532)) +- Fixed missing deadline in swaps smart transaction status screen ([#25779](https://github.com/MetaMask/metamask-extension/pull/25779)) +- Improved Snap Address component UI/UX; stop using petnames in custom Snaps UIs ([#26477](https://github.com/MetaMask/metamask-extension/pull/26477)) +- Fixed bug that could prevent the Import NFT modal from closing after importing some tokens ([#26269](https://github.com/MetaMask/metamask-extension/pull/26269)) + ## [12.3.1] ### Fixed - Fix duplicate network validation ([#27463](https://github.com/MetaMask/metamask-extension/pull/27463)) @@ -5112,7 +5139,8 @@ Update styles and spacing on the critical error page ([#20350](https://github.c - Added the ability to restore accounts from seed words. -[Unreleased]: https://github.com/MetaMask/metamask-extension/compare/v12.3.1...HEAD +[Unreleased]: https://github.com/MetaMask/metamask-extension/compare/v12.4.0...HEAD +[12.4.0]: https://github.com/MetaMask/metamask-extension/compare/v12.3.1...v12.4.0 [12.3.1]: https://github.com/MetaMask/metamask-extension/compare/v12.3.0...v12.3.1 [12.3.0]: https://github.com/MetaMask/metamask-extension/compare/v12.2.4...v12.3.0 [12.2.4]: https://github.com/MetaMask/metamask-extension/compare/v12.2.3...v12.2.4 diff --git a/README.md b/README.md index 59c65864f1e3..531a1e4fc3b6 100644 --- a/README.md +++ b/README.md @@ -57,9 +57,9 @@ If you are not a MetaMask Internal Developer, or are otherwise developing on a f - If debugging unhandled exceptions, you'll need to add a value for `SENTRY_DSN` [Sentry Dsn](https://docs.sentry.io/product/sentry-basics/dsn-explainer/), see [Developing on MetaMask - Sentry](./development/README.md#sentry). - Optionally, replace the `PASSWORD` value with your development wallet password to avoid entering it each time you open the app. - Run `yarn install` to install the dependencies. -- Build the project to the `./dist/` folder with `yarn dist`. +- Build the project to the `./dist/` folder with `yarn dist` (for Chromium-based browsers) or `yarn dist:mv2` (for Firefox) - - Optionally, you may run `yarn start` to run dev mode. + - Optionally, to create a development build you can instead run `yarn start` (for Chromium-based browsers) or `yarn start:mv2` (for Firefox) - Uncompressed builds can be found in `/dist`, compressed builds can be found in `/builds` once they're built. - See the [build system readme](./development/build/README.md) for build system usage information. diff --git a/app/_locales/de/messages.json b/app/_locales/de/messages.json index cdc7fdd96669..5e1b32229bd1 100644 --- a/app/_locales/de/messages.json +++ b/app/_locales/de/messages.json @@ -1027,9 +1027,6 @@ "confirmRpcUrlDeletionMessage": { "message": "Sind Sie sicher, dass Sie die RPC-URL löschen möchten? Ihre Informationen werden für dieses Netzwerk nicht gespeichert." }, - "confirmTitleDescContractInteractionTransaction": { - "message": "Bestätigen Sie diese Transaktion nur, wenn Sie den Inhalt vollständig verstehen und der anfragenden Website vertrauen." - }, "confirmTitleDescPermitSignature": { "message": "Diese Website möchte die Genehmigung, Ihre Tokens auszugeben." }, @@ -4165,9 +4162,6 @@ "receive": { "message": "Empfangen" }, - "receiveTokensCamelCase": { - "message": "Tokens erhalten" - }, "recipientAddressPlaceholder": { "message": "Öffentliche Adresse (0x) oder ENS-Name eingeben" }, diff --git a/app/_locales/el/messages.json b/app/_locales/el/messages.json index 3db88285098a..d8f080e7752c 100644 --- a/app/_locales/el/messages.json +++ b/app/_locales/el/messages.json @@ -1027,9 +1027,6 @@ "confirmRpcUrlDeletionMessage": { "message": "Είστε σίγουροι ότι θέλετε να διαγράψετε τη διεύθυνση URL RPC; Οι πληροφορίες σας δεν θα αποθηκευτούν για αυτό το δίκτυο." }, - "confirmTitleDescContractInteractionTransaction": { - "message": "Επιβεβαιώστε αυτή τη συναλλαγή μόνο εάν κατανοείτε πλήρως το περιεχόμενο και εμπιστεύεστε τον ιστότοπο που τη ζητάει." - }, "confirmTitleDescPermitSignature": { "message": "Αυτός ο ιστότοπος ζητάει άδεια για να δαπανήσει τα tokens σας." }, @@ -4165,9 +4162,6 @@ "receive": { "message": "Λήψη" }, - "receiveTokensCamelCase": { - "message": "Λάβετε tokens" - }, "recipientAddressPlaceholder": { "message": "Εισάγετε τη δημόσια διεύθυνση (0x) ή το όνομα ENS" }, diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 75ff076121df..27424ebb89df 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -252,6 +252,9 @@ "addEthereumChainWarningModalTitle": { "message": "You are adding a new RPC provider for Ethereum Mainnet" }, + "addEthereumWatchOnlyAccount": { + "message": "Watch an Ethereum account (Beta)" + }, "addFriendsAndAddresses": { "message": "Add friends and addresses you trust" }, @@ -276,6 +279,10 @@ "addNetwork": { "message": "Add network" }, + "addNetworkConfirmationTitle": { + "message": "Add $1", + "description": "$1 represents network name" + }, "addNetworkTooltipWarning": { "message": "This network connection relies on third parties. This connection may be less reliable or enable third-parties to track activity. $1", "description": "$1 is Learn more link" @@ -344,6 +351,17 @@ "addressCopied": { "message": "Address copied!" }, + "addressMismatch": { + "message": "Site address mismatch" + }, + "addressMismatchOriginal": { + "message": "Current URL: $1", + "description": "$1 replaced by origin URL in confirmation request" + }, + "addressMismatchPunycode": { + "message": "Punycode version: $1", + "description": "$1 replaced by punycode version of the URL in confirmation request" + }, "advanced": { "message": "Advanced" }, @@ -409,6 +427,9 @@ "alertDisableTooltip": { "message": "This can be changed in \"Settings > Alerts\"" }, + "alertMessageAddressMismatchWarning": { + "message": "Attackers sometimes mimic sites by making small changes to the site address. Make sure you're interacting with the intended site before you continue." + }, "alertMessageGasEstimateFailed": { "message": "We’re unable to provide an accurate fee and this estimate might be high. We suggest you to input a custom gas limit, but there’s a risk the transaction will still fail." }, @@ -1033,8 +1054,17 @@ "confirmRpcUrlDeletionMessage": { "message": "Are you sure you want to delete the RPC URL? Your information will not be saved for this network." }, - "confirmTitleDescContractInteractionTransaction": { - "message": "Only confirm this transaction if you fully understand the content and trust the requesting site." + "confirmTitleApproveTransaction": { + "message": "Allowance request" + }, + "confirmTitleDeployContract": { + "message": "Deploy a contract" + }, + "confirmTitleDescApproveTransaction": { + "message": "This site wants permission to withdraw your NFTs" + }, + "confirmTitleDescDeployContract": { + "message": "This site wants you to deploy a contract" }, "confirmTitleDescPermitSignature": { "message": "This site wants permission to spend your tokens." @@ -1250,6 +1280,9 @@ "copyAddress": { "message": "Copy address to clipboard" }, + "copyAddressShort": { + "message": "Copy address" + }, "copyPrivateKey": { "message": "Copy private key" }, @@ -1549,24 +1582,6 @@ "developerOptions": { "message": "Developer Options" }, - "developerOptionsSentryButtonGenerateBackgroundError": { - "message": "Generate Background Error" - }, - "developerOptionsSentryButtonGenerateTrace": { - "message": "Generate Trace" - }, - "developerOptionsSentryButtonGenerateUIError": { - "message": "Generate UI Error" - }, - "developerOptionsSentryDescriptionGenerateBackgroundError": { - "message": "Generate an unhandled $1 in the service worker." - }, - "developerOptionsSentryDescriptionGenerateTrace": { - "message": " Generate a $1 Sentry trace." - }, - "developerOptionsSentryDescriptionGenerateUIError": { - "message": "Generate an unhandled $1 in this window." - }, "disabledGasOptionToolTipMessage": { "message": "“$1” is disabled because it does not meet the minimum of a 10% increase from the original gas fee.", "description": "$1 is gas estimate type which can be market or aggressive" @@ -1746,6 +1761,9 @@ "editNetworkLink": { "message": "edit the original network" }, + "editNetworksTitle": { + "message": "Edit networks" + }, "editNonceField": { "message": "Edit nonce" }, @@ -2872,6 +2890,9 @@ "more": { "message": "more" }, + "multichainAddEthereumChainConfirmationDescription": { + "message": "You're adding this network to MetaMask and giving this site permission to use it." + }, "multipleSnapConnectionWarning": { "message": "$1 wants to use $2 Snaps", "description": "$1 is the dapp and $2 is the number of snaps it wants to connect to." @@ -4221,9 +4242,6 @@ "receive": { "message": "Receive" }, - "receiveTokensCamelCase": { - "message": "Receive tokens" - }, "recipientAddressPlaceholder": { "message": "Enter public address (0x) or ENS name" }, @@ -4811,6 +4829,12 @@ "signingInWith": { "message": "Signing in with" }, + "simulationApproveHeading": { + "message": "Withdraw" + }, + "simulationDetailsApproveDesc": { + "message": "You're giving someone else permission to withdraw NFTs from your account." + }, "simulationDetailsFailed": { "message": "There was an error loading your estimation." }, @@ -5070,7 +5094,7 @@ "message": "Snaps connected" }, "snapsNoInsight": { - "message": "The snap didn't return any insight" + "message": "No insight to show" }, "snapsPrivacyWarningFirstMessage": { "message": "You acknowledge that any Snap that you install is a Third Party Service, unless otherwise identified, as defined in the Consensys $1. Your use of Third Party Services is governed by separate terms and conditions set forth by the Third Party Service provider. Consensys does not recommend the use of any Snap by any particular person for any particular reason. You access, rely upon or use the Third Party Service at your own risk. Consensys disclaims all responsibility and liability for any losses on account of your use of Third Party Services.", @@ -5153,6 +5177,9 @@ "spender": { "message": "Spender" }, + "spenderTooltipDesc": { + "message": "This is the address that will be able to withdraw your NFTs." + }, "spendingCap": { "message": "Spending cap" }, @@ -5878,6 +5905,9 @@ "testNetworks": { "message": "Test networks" }, + "testnets": { + "message": "Testnets" + }, "theme": { "message": "Theme" }, @@ -6427,6 +6457,13 @@ "message": "$1 The third party could spend your entire token balance without further notice or consent. Protect yourself by customizing a lower spending cap.", "description": "$1 is a warning icon with text 'Be careful' in 'warning' colour" }, + "watchEthereumAccountsDescription": { + "message": "Turning this option on will give you the ability to watch Ethereum accounts via a public address or ENS name. For feedback on this Beta feature please complete this $1.", + "description": "$1 is the link to a product feedback form" + }, + "watchEthereumAccountsToggle": { + "message": "Watch Ethereum Accounts (Beta)" + }, "weak": { "message": "Weak" }, diff --git a/app/_locales/en_GB/messages.json b/app/_locales/en_GB/messages.json index cbfe48f3d8ef..b7e15bc86b55 100644 --- a/app/_locales/en_GB/messages.json +++ b/app/_locales/en_GB/messages.json @@ -1039,9 +1039,6 @@ "confirmRpcUrlDeletionMessage": { "message": "Are you sure you want to delete the RPC URL? Your information will not be saved for this network." }, - "confirmTitleDescContractInteractionTransaction": { - "message": "Only confirm this transaction if you fully understand the content and trust the requesting site." - }, "confirmTitleDescPermitSignature": { "message": "This site wants permission to spend your tokens." }, @@ -4210,9 +4207,6 @@ "receive": { "message": "Receive" }, - "receiveTokensCamelCase": { - "message": "Receive tokens" - }, "recipientAddressPlaceholder": { "message": "Enter public address (0x) or ENS name" }, diff --git a/app/_locales/es/messages.json b/app/_locales/es/messages.json index 19031ace0ed8..a20b831091a0 100644 --- a/app/_locales/es/messages.json +++ b/app/_locales/es/messages.json @@ -1027,9 +1027,6 @@ "confirmRpcUrlDeletionMessage": { "message": "¿Está seguro de que desea eliminar la URL RPC? Su información no se guardará para esta red." }, - "confirmTitleDescContractInteractionTransaction": { - "message": "Solo confirme esta transacción si entiende completamente el contenido y confía en el sitio solicitante." - }, "confirmTitleDescPermitSignature": { "message": "Este sitio solicita permiso para gastar sus tokens." }, @@ -4165,9 +4162,6 @@ "receive": { "message": "Recibir" }, - "receiveTokensCamelCase": { - "message": "Recibir tokens" - }, "recipientAddressPlaceholder": { "message": "Ingrese la dirección pública (0x) o el nombre de ENS" }, diff --git a/app/_locales/fr/messages.json b/app/_locales/fr/messages.json index 2e7d3697e11d..28d08810ae82 100644 --- a/app/_locales/fr/messages.json +++ b/app/_locales/fr/messages.json @@ -1027,9 +1027,6 @@ "confirmRpcUrlDeletionMessage": { "message": "Voulez-vous vraiment supprimer l’URL du RPC ? Vos informations ne seront pas sauvegardées pour ce réseau." }, - "confirmTitleDescContractInteractionTransaction": { - "message": "Ne confirmez cette transaction que si vous comprenez parfaitement son contenu et si vous faites confiance au site demandeur." - }, "confirmTitleDescPermitSignature": { "message": "Ce site demande que vous lui accordiez l'autorisation de dépenser vos jetons." }, @@ -4165,9 +4162,6 @@ "receive": { "message": "Recevoir" }, - "receiveTokensCamelCase": { - "message": "Recevez des jetons" - }, "recipientAddressPlaceholder": { "message": "Saisissez l’adresse publique (0x) ou le nom de domaine ENS" }, diff --git a/app/_locales/hi/messages.json b/app/_locales/hi/messages.json index 1c1e9363d761..2371df612b77 100644 --- a/app/_locales/hi/messages.json +++ b/app/_locales/hi/messages.json @@ -1027,9 +1027,6 @@ "confirmRpcUrlDeletionMessage": { "message": "क्या आप वाकई RPC URL को हटाना चाहते हैं? आपकी जानकारी इस नेटवर्क के लिए सेव नहीं की जाएगी।" }, - "confirmTitleDescContractInteractionTransaction": { - "message": "यदि आप कंटेंट को पूरी तरह से समझते हैं और अनुरोध करने वाली साइट पर भरोसा करते हैं तो ही इस ट्रांसेक्शन को कन्फर्म करें" - }, "confirmTitleDescPermitSignature": { "message": "यह साइट आपके टोकन खर्च करने की अनुमति चाहती है।" }, @@ -4165,9 +4162,6 @@ "receive": { "message": "प्राप्त करें" }, - "receiveTokensCamelCase": { - "message": "टोकन प्राप्त करें" - }, "recipientAddressPlaceholder": { "message": "सार्वजनिक एड्रेस (0x) या ENS नाम डालें" }, diff --git a/app/_locales/id/messages.json b/app/_locales/id/messages.json index 13e7c6d1cbec..0f8ceacc9ea7 100644 --- a/app/_locales/id/messages.json +++ b/app/_locales/id/messages.json @@ -1027,9 +1027,6 @@ "confirmRpcUrlDeletionMessage": { "message": "Yakin ingin menghapus URL RPC? Informasi Anda tidak akan disimpan untuk jaringan ini." }, - "confirmTitleDescContractInteractionTransaction": { - "message": "Konfirmasikan transaksi ini hanya jika Anda benar-benar memahami isinya dan memercayai situs yang memintanya." - }, "confirmTitleDescPermitSignature": { "message": "Situs ini meminta izin untuk menggunakan token Anda." }, @@ -4165,9 +4162,6 @@ "receive": { "message": "Terima" }, - "receiveTokensCamelCase": { - "message": "Terima token" - }, "recipientAddressPlaceholder": { "message": "Masukkan alamat publik (0x) atau nama ENS" }, diff --git a/app/_locales/ja/messages.json b/app/_locales/ja/messages.json index 5935c29f4eab..44226098f705 100644 --- a/app/_locales/ja/messages.json +++ b/app/_locales/ja/messages.json @@ -1027,9 +1027,6 @@ "confirmRpcUrlDeletionMessage": { "message": "RPC URLを削除してよろしいですか?このネットワークの情報は保存されません。" }, - "confirmTitleDescContractInteractionTransaction": { - "message": "このトランザクションの内容を完全に理解し、要求元のサイトを信頼する場合にのみ確定してください。" - }, "confirmTitleDescPermitSignature": { "message": "このサイトがトークンの使用許可を求めています。" }, @@ -4165,9 +4162,6 @@ "receive": { "message": "受取" }, - "receiveTokensCamelCase": { - "message": "トークンの受取" - }, "recipientAddressPlaceholder": { "message": "パブリックアドレス (0x) またはENS名を入力してください" }, diff --git a/app/_locales/ko/messages.json b/app/_locales/ko/messages.json index a4b9ca10e7b0..7286eefb09d7 100644 --- a/app/_locales/ko/messages.json +++ b/app/_locales/ko/messages.json @@ -1027,9 +1027,6 @@ "confirmRpcUrlDeletionMessage": { "message": "정말로 RPC URL을 삭제하시겠습니까? 고객님의 정보는 이 네트워크에 저장되지 않습니다." }, - "confirmTitleDescContractInteractionTransaction": { - "message": "요청하는 사이트를 신뢰하고 그 내용을 완전히 이해하는 경우에만 이 트랜젝션을 컨펌하세요." - }, "confirmTitleDescPermitSignature": { "message": "해당 사이트에서 토큰 사용 승인을 요청합니다." }, @@ -4165,9 +4162,6 @@ "receive": { "message": "받기" }, - "receiveTokensCamelCase": { - "message": "토큰 받기" - }, "recipientAddressPlaceholder": { "message": "공개 주소(0x) 또는 ENS 제목 입력" }, diff --git a/app/_locales/pt/messages.json b/app/_locales/pt/messages.json index 5706a91b473a..3a930315bcbc 100644 --- a/app/_locales/pt/messages.json +++ b/app/_locales/pt/messages.json @@ -1027,9 +1027,6 @@ "confirmRpcUrlDeletionMessage": { "message": "Tem certeza de que deseja excluir o URL da RPC? Suas informações não serão salvas para essa rede." }, - "confirmTitleDescContractInteractionTransaction": { - "message": "Só confirme essa transação se você entende integralmente o conteúdo e confia no site solicitante." - }, "confirmTitleDescPermitSignature": { "message": "Este site quer permissão para gastar seus tokens." }, @@ -4165,9 +4162,6 @@ "receive": { "message": "Receber" }, - "receiveTokensCamelCase": { - "message": "Receber tokens" - }, "recipientAddressPlaceholder": { "message": "Insira o endereço público (0x) ou o nome ENS" }, diff --git a/app/_locales/ru/messages.json b/app/_locales/ru/messages.json index d08ae9c67865..f3f948d9a84e 100644 --- a/app/_locales/ru/messages.json +++ b/app/_locales/ru/messages.json @@ -1027,9 +1027,6 @@ "confirmRpcUrlDeletionMessage": { "message": "Уверены, что хотите удалить URL-адрес RPC? Ваша информация не будет сохранена для этой сети." }, - "confirmTitleDescContractInteractionTransaction": { - "message": "Подтверждайте эту транзакцию только в том случае, если вы полностью понимаете ее содержание и доверяете запрашивающему сайту." - }, "confirmTitleDescPermitSignature": { "message": "Этот сайт хочет получить разрешение на расходование ваших токенов." }, @@ -4165,9 +4162,6 @@ "receive": { "message": "Получить" }, - "receiveTokensCamelCase": { - "message": "Получить токены" - }, "recipientAddressPlaceholder": { "message": "Введите публичный адрес (0x) или имя ENS" }, diff --git a/app/_locales/tl/messages.json b/app/_locales/tl/messages.json index 65268e6199d6..7eecf1acfd13 100644 --- a/app/_locales/tl/messages.json +++ b/app/_locales/tl/messages.json @@ -1027,9 +1027,6 @@ "confirmRpcUrlDeletionMessage": { "message": "Sigurado ka ba na nais mong tanggalin ang RPC URL? Ang iyong impormasyon ay hindi mase-save sa network na ito." }, - "confirmTitleDescContractInteractionTransaction": { - "message": "Kumpirmahin lamang ang transaksyong ito kung ganap mong nauunawaan ang nilalaman at nagtitiwala sa site na humihiling." - }, "confirmTitleDescPermitSignature": { "message": "Ang site na ito ay nais ng permiso para gamitin ang iyong mga token." }, @@ -4165,9 +4162,6 @@ "receive": { "message": "Tumanggap" }, - "receiveTokensCamelCase": { - "message": "Tumanggap ng mga token" - }, "recipientAddressPlaceholder": { "message": "Ilagay ang pampublikong address (0x) o ENS name" }, diff --git a/app/_locales/tr/messages.json b/app/_locales/tr/messages.json index b58a54040cb1..cc40ce53b487 100644 --- a/app/_locales/tr/messages.json +++ b/app/_locales/tr/messages.json @@ -1027,9 +1027,6 @@ "confirmRpcUrlDeletionMessage": { "message": "RPC URL adresini silmek istediğinizden emin misiniz? Bilgileriniz bu ağ için kaydedilmeyecektir." }, - "confirmTitleDescContractInteractionTransaction": { - "message": "Bu işlemi sadece içeriği tam olarak anlıyorsanız ve talepte bulunan siteye güveniyorsanız onaylayın." - }, "confirmTitleDescPermitSignature": { "message": "Bu site token'lerinizi harcamak için izin istiyor." }, @@ -4165,9 +4162,6 @@ "receive": { "message": "Al" }, - "receiveTokensCamelCase": { - "message": "Token'leri al" - }, "recipientAddressPlaceholder": { "message": "Genel adres (0x) veya ENS adı girin" }, diff --git a/app/_locales/vi/messages.json b/app/_locales/vi/messages.json index bc956cf2e936..c4795a502a7e 100644 --- a/app/_locales/vi/messages.json +++ b/app/_locales/vi/messages.json @@ -1027,9 +1027,6 @@ "confirmRpcUrlDeletionMessage": { "message": "Bạn có chắc chắn muốn xóa URL RPC? Thông tin của bạn sẽ không được lưu cho mạng này." }, - "confirmTitleDescContractInteractionTransaction": { - "message": "Chỉ xác nhận giao dịch này nếu bạn hoàn toàn hiểu nội dung và tin tưởng trang web yêu cầu." - }, "confirmTitleDescPermitSignature": { "message": "Trang web này muốn được cấp quyền để chi tiêu số token của bạn." }, @@ -4165,9 +4162,6 @@ "receive": { "message": "Nhận" }, - "receiveTokensCamelCase": { - "message": "Nhận token" - }, "recipientAddressPlaceholder": { "message": "Nhập địa chỉ công khai (0x) hoặc tên ENS" }, diff --git a/app/_locales/zh_CN/messages.json b/app/_locales/zh_CN/messages.json index 7265f579a30f..c834c8a18230 100644 --- a/app/_locales/zh_CN/messages.json +++ b/app/_locales/zh_CN/messages.json @@ -1027,9 +1027,6 @@ "confirmRpcUrlDeletionMessage": { "message": "您确定要删除 RPC(远程过程调用)URL 吗?您的信息将不会保存到此网络。" }, - "confirmTitleDescContractInteractionTransaction": { - "message": "仅在您完全理解内容并信任请求网站的情况下,才能确认此交易。" - }, "confirmTitleDescPermitSignature": { "message": "此网站需要获得许可来使用您的代币。" }, @@ -4165,9 +4162,6 @@ "receive": { "message": "收款" }, - "receiveTokensCamelCase": { - "message": "收取代币" - }, "recipientAddressPlaceholder": { "message": "输入公钥 (0x) 或 ENS 名称" }, diff --git a/app/images/iotex-token.svg b/app/images/iotex-token.svg new file mode 100644 index 000000000000..49cba53d5d40 --- /dev/null +++ b/app/images/iotex-token.svg @@ -0,0 +1,269 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/images/iotex.svg b/app/images/iotex.svg new file mode 100644 index 000000000000..7594ba1bd24b --- /dev/null +++ b/app/images/iotex.svg @@ -0,0 +1,325 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/images/moonbeam-token.svg b/app/images/moonbeam-token.svg new file mode 100644 index 000000000000..ecb8ebc3c4f5 --- /dev/null +++ b/app/images/moonbeam-token.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/images/moonbeam.svg b/app/images/moonbeam.svg index 106338e4f1e0..85b8b5f8e0de 100644 --- a/app/images/moonbeam.svg +++ b/app/images/moonbeam.svg @@ -1,16 +1,45 @@ - - - - - - - - - - - - - - - - + + \ No newline at end of file diff --git a/app/images/moonriver-token.svg b/app/images/moonriver-token.svg new file mode 100644 index 000000000000..852a04de84d2 --- /dev/null +++ b/app/images/moonriver-token.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/images/moonriver.svg b/app/images/moonriver.svg index b52ac55335e1..3e80c62c032a 100644 --- a/app/images/moonriver.svg +++ b/app/images/moonriver.svg @@ -1,48 +1,62 @@ - - - - - - - - - - - - - - - - - - - - - - - + + \ No newline at end of file diff --git a/app/images/near.svg b/app/images/near.svg new file mode 100644 index 000000000000..215c3ba06135 --- /dev/null +++ b/app/images/near.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/scripts/background.js b/app/scripts/background.js index 11a0b059a25f..ccb71e9c6217 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -20,6 +20,8 @@ import PortStream from 'extension-port-stream'; import { ethErrors } from 'eth-rpc-errors'; import { DIALOG_APPROVAL_TYPES } from '@metamask/snaps-rpc-methods'; +import { NotificationServicesController } from '@metamask/notification-services-controller'; + import { ENVIRONMENT_TYPE_POPUP, ENVIRONMENT_TYPE_NOTIFICATION, @@ -51,6 +53,7 @@ import { FakeLedgerBridge, FakeTrezorBridge, } from '../../test/stub/keyring-bridge'; +import { getCurrentChainId } from '../../ui/selectors'; import migrations from './migrations'; import Migrator from './lib/migrator'; import ExtensionPlatform from './platforms/extension'; @@ -79,7 +82,6 @@ import { createOffscreen } from './offscreen'; /* eslint-enable import/first */ -import { TRIGGER_TYPES } from './controllers/metamask-notifications/constants/notification-schema'; import { COOKIE_ID_MARKETING_WHITELIST_ORIGINS } from './constants/marketing-site-whitelist'; // eslint-disable-next-line @metamask/design-tokens/color-no-hex @@ -262,18 +264,35 @@ function maybeDetectPhishing(theController) { } theController.phishingController.maybeUpdateState(); - const phishingTestResponse = - theController.phishingController.test(hostname); - if (!phishingTestResponse?.result) { + const phishingTestResponse = theController.phishingController.test( + details.url, + ); + + const blockedRequestResponse = + theController.phishingController.isBlockedRequest(details.url); + + // if the request is not blocked, and the phishing test is not blocked, return and don't show the phishing screen + if (!phishingTestResponse?.result && !blockedRequestResponse.result) { return {}; } + // Determine the block reason based on the type + let blockReason; + if (phishingTestResponse?.result && blockedRequestResponse.result) { + blockReason = `${phishingTestResponse.type} and ${blockedRequestResponse.type}`; + } else if (phishingTestResponse?.result) { + blockReason = phishingTestResponse.type; + } else { + blockReason = blockedRequestResponse.type; + } + theController.metaMetricsController.trackEvent({ // should we differentiate between background redirection and content script redirection? event: MetaMetricsEventName.PhishingPageDisplayed, category: MetaMetricsEventCategory.Phishing, properties: { url: hostname, + reason: blockReason, }, }); const querystring = new URLSearchParams({ hostname, href }); @@ -298,7 +317,10 @@ function maybeDetectPhishing(theController) { redirectTab(details.tabId, redirectHref); return {}; }, - { types: ['main_frame', 'sub_frame'], urls: ['http://*/*', 'https://*/*'] }, + { + types: ['main_frame', 'sub_frame', 'xmlhttprequest'], + urls: ['http://*/*', 'https://*/*'], + }, isManifestV2 ? ['blocking'] : [], ); } @@ -356,9 +378,6 @@ function saveTimestamp() { * @property {object} featureFlags - An object for optional feature flags. * @property {boolean} welcomeScreen - True if welcome screen should be shown. * @property {string} currentLocale - A locale string matching the user's preferred display language. - * @property {object} providerConfig - The current selected network provider. - * @property {string} providerConfig.rpcUrl - The address for the RPC API, if using an RPC API. - * @property {string} providerConfig.type - An identifier for the type of network selected, allows MetaMask to use custom provider strategies for known networks. * @property {string} networkStatus - Either "unknown", "available", "unavailable", or "blocked", depending on the status of the currently selected network. * @property {object} accounts - An object mapping lower-case hex addresses to objects with "balance" and "address" keys, both storing hex string values. * @property {object} accountsByChainId - An object mapping lower-case hex addresses to objects with "balance" and "address" keys, both storing hex string values keyed by chain id. @@ -732,7 +751,7 @@ export function setupController( setupEnsIpfsResolver({ getCurrentChainId: () => - controller.networkController.state.providerConfig.chainId, + getCurrentChainId({ metamask: controller.networkController.state }), getIpfsGateway: controller.preferencesController.getIpfsGateway.bind( controller.preferencesController, ), @@ -1050,26 +1069,30 @@ export function setupController( function getUnreadNotificationsCount() { try { - const { isMetamaskNotificationsEnabled, isFeatureAnnouncementsEnabled } = - controller.metamaskNotificationsController.state; + const { isNotificationServicesEnabled, isFeatureAnnouncementsEnabled } = + controller.notificationServicesController.state; const snapNotificationCount = Object.values( controller.notificationController.state.notifications, ).filter((notification) => notification.readDate === null).length; const featureAnnouncementCount = isFeatureAnnouncementsEnabled - ? controller.metamaskNotificationsController.state.metamaskNotificationsList.filter( + ? controller.notificationServicesController.state.metamaskNotificationsList.filter( (notification) => !notification.isRead && - notification.type === TRIGGER_TYPES.FEATURES_ANNOUNCEMENT, + notification.type === + NotificationServicesController.Constants.TRIGGER_TYPES + .FEATURES_ANNOUNCEMENT, ).length : 0; - const walletNotificationCount = isMetamaskNotificationsEnabled - ? controller.metamaskNotificationsController.state.metamaskNotificationsList.filter( + const walletNotificationCount = isNotificationServicesEnabled + ? controller.notificationServicesController.state.metamaskNotificationsList.filter( (notification) => !notification.isRead && - notification.type !== TRIGGER_TYPES.FEATURES_ANNOUNCEMENT, + notification.type !== + NotificationServicesController.Constants.TRIGGER_TYPES + .FEATURES_ANNOUNCEMENT, ).length : 0; diff --git a/app/scripts/constants/sentry-state.ts b/app/scripts/constants/sentry-state.ts index ae00352b4e72..db78f948c31d 100644 --- a/app/scripts/constants/sentry-state.ts +++ b/app/scripts/constants/sentry-state.ts @@ -99,6 +99,8 @@ export const SENTRY_BACKGROUND_STATE = { bridgeState: { bridgeFeatureFlags: { extensionSupport: false, + destNetworkAllowlist: [], + srcNetworkAllowlist: [], }, }, }, @@ -135,10 +137,10 @@ export const SENTRY_BACKGROUND_STATE = { LoggingController: { logs: false, }, - MetamaskNotificationsController: { + NotificationServicesController: { subscriptionAccountsSeen: false, isMetamaskNotificationsFeatureSeen: false, - isMetamaskNotificationsEnabled: false, + isNotificationServicesEnabled: false, isFeatureAnnouncementsEnabled: false, metamaskNotificationsList: false, metamaskNotificationsReadList: false, @@ -166,15 +168,6 @@ export const SENTRY_BACKGROUND_STATE = { NetworkController: { networkConfigurations: false, networksMetadata: true, - providerConfig: { - chainId: true, - id: true, - nickname: true, - rpcPrefs: false, - rpcUrl: false, - ticker: true, - type: true, - }, selectedNetworkClientId: false, }, NftController: { @@ -250,7 +243,7 @@ export const SENTRY_BACKGROUND_STATE = { useTransactionSimulations: true, enableMV3TimestampSave: true, }, - PushPlatformNotificationsController: { + NotificationServicesPushController: { fcmToken: false, }, MultichainRatesController: { @@ -285,9 +278,12 @@ export const SENTRY_BACKGROUND_STATE = { snapStates: false, snaps: false, }, - SnapInterface: { + SnapInterfaceController: { interfaces: false, }, + SnapInsightsController: { + insights: false, + }, SnapsRegistry: { database: false, lastUpdated: false, @@ -401,6 +397,7 @@ export const SENTRY_UI_STATE = { welcomeScreenSeen: true, confirmationExchangeRates: true, useSafeChainsListValidation: true, + watchEthereumAccountEnabled: false, bitcoinSupportEnabled: false, bitcoinTestnetSupportEnabled: false, ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps) diff --git a/app/scripts/controllers/bridge/bridge-controller.test.ts b/app/scripts/controllers/bridge/bridge-controller.test.ts index 7ccad836e2b6..9c9036b87f7b 100644 --- a/app/scripts/controllers/bridge/bridge-controller.test.ts +++ b/app/scripts/controllers/bridge/bridge-controller.test.ts @@ -1,5 +1,6 @@ import nock from 'nock'; import { BRIDGE_API_BASE_URL } from '../../../../shared/constants/bridge'; +import { CHAIN_IDS } from '../../../../shared/constants/network'; import BridgeController from './bridge-controller'; import { BridgeControllerMessenger } from './types'; import { DEFAULT_BRIDGE_CONTROLLER_STATE } from './constants'; @@ -22,21 +23,32 @@ describe('BridgeController', function () { bridgeController = new BridgeController({ messenger: messengerMock }); }); + beforeEach(() => { + jest.clearAllMocks(); + nock(BRIDGE_API_BASE_URL) + .get('/getAllFeatureFlags') + .reply(200, { + 'extension-support': true, + 'src-network-allowlist': [10, 534352], + 'dest-network-allowlist': [137, 42161], + }); + }); + it('constructor should setup correctly', function () { expect(bridgeController.state).toStrictEqual(EMPTY_INIT_STATE); }); it('setBridgeFeatureFlags should fetch and set the bridge feature flags', async function () { - nock(BRIDGE_API_BASE_URL).get('/getAllFeatureFlags').reply(200, { - 'extension-support': true, - }); - expect(bridgeController.state.bridgeState.bridgeFeatureFlags).toStrictEqual( - { extensionSupport: false }, - ); + const expectedFeatureFlagsResponse = { + extensionSupport: true, + destNetworkAllowlist: [CHAIN_IDS.POLYGON, CHAIN_IDS.ARBITRUM], + srcNetworkAllowlist: [CHAIN_IDS.OPTIMISM, CHAIN_IDS.SCROLL], + }; + expect(bridgeController.state).toStrictEqual(EMPTY_INIT_STATE); await bridgeController.setBridgeFeatureFlags(); expect(bridgeController.state.bridgeState.bridgeFeatureFlags).toStrictEqual( - { extensionSupport: true }, + expectedFeatureFlagsResponse, ); }); }); diff --git a/app/scripts/controllers/bridge/constants.ts b/app/scripts/controllers/bridge/constants.ts index b69f529bd339..f2932120f98d 100644 --- a/app/scripts/controllers/bridge/constants.ts +++ b/app/scripts/controllers/bridge/constants.ts @@ -5,5 +5,7 @@ export const BRIDGE_CONTROLLER_NAME = 'BridgeController'; export const DEFAULT_BRIDGE_CONTROLLER_STATE: BridgeControllerState = { bridgeFeatureFlags: { [BridgeFeatureFlagsKey.EXTENSION_SUPPORT]: false, + [BridgeFeatureFlagsKey.NETWORK_SRC_ALLOWLIST]: [], + [BridgeFeatureFlagsKey.NETWORK_DEST_ALLOWLIST]: [], }, }; diff --git a/app/scripts/controllers/bridge/types.ts b/app/scripts/controllers/bridge/types.ts index 1ab02b07f7a7..aa92a6597c69 100644 --- a/app/scripts/controllers/bridge/types.ts +++ b/app/scripts/controllers/bridge/types.ts @@ -2,20 +2,26 @@ import { ControllerStateChangeEvent, RestrictedControllerMessenger, } from '@metamask/base-controller'; +import { Hex } from '@metamask/utils'; import BridgeController from './bridge-controller'; import { BRIDGE_CONTROLLER_NAME } from './constants'; export enum BridgeFeatureFlagsKey { EXTENSION_SUPPORT = 'extensionSupport', + NETWORK_SRC_ALLOWLIST = 'srcNetworkAllowlist', + NETWORK_DEST_ALLOWLIST = 'destNetworkAllowlist', } export type BridgeFeatureFlags = { [BridgeFeatureFlagsKey.EXTENSION_SUPPORT]: boolean; + [BridgeFeatureFlagsKey.NETWORK_SRC_ALLOWLIST]: Hex[]; + [BridgeFeatureFlagsKey.NETWORK_DEST_ALLOWLIST]: Hex[]; }; export type BridgeControllerState = { bridgeFeatureFlags: BridgeFeatureFlags; }; + export enum BridgeBackgroundAction { SET_FEATURE_FLAGS = 'setBridgeFeatureFlags', } diff --git a/app/scripts/controllers/metamask-notifications/constants/constants.ts b/app/scripts/controllers/metamask-notifications/constants/constants.ts deleted file mode 100644 index 516b63b96fe3..000000000000 --- a/app/scripts/controllers/metamask-notifications/constants/constants.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const USER_STORAGE_VERSION = '1'; - -// Force cast. We don't really care about the type here since we treat it as a unique symbol -export const USER_STORAGE_VERSION_KEY: unique symbol = 'v' as never; diff --git a/app/scripts/controllers/metamask-notifications/constants/notification-schema.ts b/app/scripts/controllers/metamask-notifications/constants/notification-schema.ts deleted file mode 100644 index f642c9146f43..000000000000 --- a/app/scripts/controllers/metamask-notifications/constants/notification-schema.ts +++ /dev/null @@ -1,172 +0,0 @@ -export enum TRIGGER_TYPES { - FEATURES_ANNOUNCEMENT = 'features_announcement', - METAMASK_SWAP_COMPLETED = 'metamask_swap_completed', - ERC20_SENT = 'erc20_sent', - ERC20_RECEIVED = 'erc20_received', - ETH_SENT = 'eth_sent', - ETH_RECEIVED = 'eth_received', - ROCKETPOOL_STAKE_COMPLETED = 'rocketpool_stake_completed', - ROCKETPOOL_UNSTAKE_COMPLETED = 'rocketpool_unstake_completed', - LIDO_STAKE_COMPLETED = 'lido_stake_completed', - LIDO_WITHDRAWAL_REQUESTED = 'lido_withdrawal_requested', - LIDO_WITHDRAWAL_COMPLETED = 'lido_withdrawal_completed', - LIDO_STAKE_READY_TO_BE_WITHDRAWN = 'lido_stake_ready_to_be_withdrawn', - ERC721_SENT = 'erc721_sent', - ERC721_RECEIVED = 'erc721_received', - ERC1155_SENT = 'erc1155_sent', - ERC1155_RECEIVED = 'erc1155_received', -} - -export const TRIGGER_TYPES_WALLET_SET: Set = new Set([ - TRIGGER_TYPES.METAMASK_SWAP_COMPLETED, - TRIGGER_TYPES.ERC20_SENT, - TRIGGER_TYPES.ERC20_RECEIVED, - TRIGGER_TYPES.ETH_SENT, - TRIGGER_TYPES.ETH_RECEIVED, - TRIGGER_TYPES.ROCKETPOOL_STAKE_COMPLETED, - TRIGGER_TYPES.ROCKETPOOL_UNSTAKE_COMPLETED, - TRIGGER_TYPES.LIDO_STAKE_COMPLETED, - TRIGGER_TYPES.LIDO_WITHDRAWAL_REQUESTED, - TRIGGER_TYPES.LIDO_WITHDRAWAL_COMPLETED, - TRIGGER_TYPES.LIDO_STAKE_READY_TO_BE_WITHDRAWN, - TRIGGER_TYPES.ERC721_SENT, - TRIGGER_TYPES.ERC721_RECEIVED, - TRIGGER_TYPES.ERC1155_SENT, - TRIGGER_TYPES.ERC1155_RECEIVED, -]) satisfies Set>; - -export enum TRIGGER_TYPES_GROUPS { - RECEIVED = 'received', - SENT = 'sent', - DEFI = 'defi', -} - -export const NOTIFICATION_CHAINS = { - ETHEREUM: '1', - OPTIMISM: '10', - BSC: '56', - POLYGON: '137', - ARBITRUM: '42161', - AVALANCHE: '43114', - LINEA: '59144', -}; - -export const CHAIN_SYMBOLS = { - [NOTIFICATION_CHAINS.ETHEREUM]: 'ETH', - [NOTIFICATION_CHAINS.OPTIMISM]: 'ETH', - [NOTIFICATION_CHAINS.BSC]: 'BNB', - [NOTIFICATION_CHAINS.POLYGON]: 'POL', - [NOTIFICATION_CHAINS.ARBITRUM]: 'ETH', - [NOTIFICATION_CHAINS.AVALANCHE]: 'AVAX', - [NOTIFICATION_CHAINS.LINEA]: 'ETH', -}; - -export const SUPPORTED_CHAINS = [ - NOTIFICATION_CHAINS.ETHEREUM, - NOTIFICATION_CHAINS.OPTIMISM, - NOTIFICATION_CHAINS.BSC, - NOTIFICATION_CHAINS.POLYGON, - NOTIFICATION_CHAINS.ARBITRUM, - NOTIFICATION_CHAINS.AVALANCHE, - NOTIFICATION_CHAINS.LINEA, -]; - -export type Trigger = { - supported_chains: (typeof SUPPORTED_CHAINS)[number][]; -}; - -export const TRIGGERS: Partial> = { - [TRIGGER_TYPES.METAMASK_SWAP_COMPLETED]: { - supported_chains: [ - NOTIFICATION_CHAINS.ETHEREUM, - NOTIFICATION_CHAINS.OPTIMISM, - NOTIFICATION_CHAINS.BSC, - NOTIFICATION_CHAINS.POLYGON, - NOTIFICATION_CHAINS.ARBITRUM, - NOTIFICATION_CHAINS.AVALANCHE, - ], - }, - [TRIGGER_TYPES.ERC20_SENT]: { - supported_chains: [ - NOTIFICATION_CHAINS.ETHEREUM, - NOTIFICATION_CHAINS.OPTIMISM, - NOTIFICATION_CHAINS.BSC, - NOTIFICATION_CHAINS.POLYGON, - NOTIFICATION_CHAINS.ARBITRUM, - NOTIFICATION_CHAINS.AVALANCHE, - NOTIFICATION_CHAINS.LINEA, - ], - }, - [TRIGGER_TYPES.ERC20_RECEIVED]: { - supported_chains: [ - NOTIFICATION_CHAINS.ETHEREUM, - NOTIFICATION_CHAINS.OPTIMISM, - NOTIFICATION_CHAINS.BSC, - NOTIFICATION_CHAINS.POLYGON, - NOTIFICATION_CHAINS.ARBITRUM, - NOTIFICATION_CHAINS.AVALANCHE, - NOTIFICATION_CHAINS.LINEA, - ], - }, - [TRIGGER_TYPES.ERC721_SENT]: { - supported_chains: [ - NOTIFICATION_CHAINS.ETHEREUM, - NOTIFICATION_CHAINS.POLYGON, - ], - }, - [TRIGGER_TYPES.ERC721_RECEIVED]: { - supported_chains: [ - NOTIFICATION_CHAINS.ETHEREUM, - NOTIFICATION_CHAINS.POLYGON, - ], - }, - [TRIGGER_TYPES.ERC1155_SENT]: { - supported_chains: [ - NOTIFICATION_CHAINS.ETHEREUM, - NOTIFICATION_CHAINS.POLYGON, - ], - }, - [TRIGGER_TYPES.ERC1155_RECEIVED]: { - supported_chains: [ - NOTIFICATION_CHAINS.ETHEREUM, - NOTIFICATION_CHAINS.POLYGON, - ], - }, - [TRIGGER_TYPES.ETH_SENT]: { - supported_chains: [ - NOTIFICATION_CHAINS.ETHEREUM, - NOTIFICATION_CHAINS.OPTIMISM, - NOTIFICATION_CHAINS.BSC, - NOTIFICATION_CHAINS.POLYGON, - NOTIFICATION_CHAINS.ARBITRUM, - NOTIFICATION_CHAINS.AVALANCHE, - NOTIFICATION_CHAINS.LINEA, - ], - }, - [TRIGGER_TYPES.ETH_RECEIVED]: { - supported_chains: [ - NOTIFICATION_CHAINS.ETHEREUM, - NOTIFICATION_CHAINS.OPTIMISM, - NOTIFICATION_CHAINS.BSC, - NOTIFICATION_CHAINS.POLYGON, - NOTIFICATION_CHAINS.ARBITRUM, - NOTIFICATION_CHAINS.AVALANCHE, - NOTIFICATION_CHAINS.LINEA, - ], - }, - [TRIGGER_TYPES.ROCKETPOOL_STAKE_COMPLETED]: { - supported_chains: [NOTIFICATION_CHAINS.ETHEREUM], - }, - [TRIGGER_TYPES.ROCKETPOOL_UNSTAKE_COMPLETED]: { - supported_chains: [NOTIFICATION_CHAINS.ETHEREUM], - }, - [TRIGGER_TYPES.LIDO_STAKE_COMPLETED]: { - supported_chains: [NOTIFICATION_CHAINS.ETHEREUM], - }, - [TRIGGER_TYPES.LIDO_WITHDRAWAL_REQUESTED]: { - supported_chains: [NOTIFICATION_CHAINS.ETHEREUM], - }, - [TRIGGER_TYPES.LIDO_WITHDRAWAL_COMPLETED]: { - supported_chains: [NOTIFICATION_CHAINS.ETHEREUM], - }, -}; diff --git a/app/scripts/controllers/metamask-notifications/metamask-notifications.test.ts b/app/scripts/controllers/metamask-notifications/metamask-notifications.test.ts deleted file mode 100644 index 125b7f965bac..000000000000 --- a/app/scripts/controllers/metamask-notifications/metamask-notifications.test.ts +++ /dev/null @@ -1,809 +0,0 @@ -import { ControllerMessenger } from '@metamask/base-controller'; -import * as ControllerUtils from '@metamask/controller-utils'; -import { - KeyringControllerGetAccountsAction, - KeyringControllerState, - KeyringControllerStateChangeEvent, -} from '@metamask/keyring-controller'; -import { waitFor } from '@testing-library/react'; -import { - AuthenticationController, - UserStorageController, -} from '@metamask/profile-sync-controller'; -import { - PushPlatformNotificationsControllerEnablePushNotifications, - PushPlatformNotificationsControllerDisablePushNotifications, - PushPlatformNotificationsControllerUpdateTriggerPushNotifications, -} from '../push-platform-notifications/push-platform-notifications'; -import { - AllowedActions, - AllowedEvents, - MetamaskNotificationsController, - defaultState, -} from './metamask-notifications'; -import { - createMockFeatureAnnouncementAPIResult, - createMockFeatureAnnouncementRaw, -} from './mocks/mock-feature-announcements'; -import { - MOCK_USER_STORAGE_ACCOUNT, - createMockFullUserStorage, - createMockUserStorageWithTriggers, -} from './mocks/mock-notification-user-storage'; -import { - mockFetchFeatureAnnouncementNotifications, - mockBatchCreateTriggers, - mockBatchDeleteTriggers, - mockListNotifications, - mockMarkNotificationsAsRead, -} from './mocks/mockServices'; -import { createMockNotificationEthSent } from './mocks/mock-raw-notifications'; -import { processNotification } from './processors/process-notifications'; -import * as OnChainNotifications from './services/onchain-notifications'; -import { UserStorage } from './types/user-storage/user-storage'; -import * as MetamaskNotificationsUtils from './utils/utils'; - -const AuthMocks = AuthenticationController.Mocks; - -// Mock type used for testing purposes -// eslint-disable-next-line @typescript-eslint/no-explicit-any -type MockVar = any; - -describe('metamask-notifications - constructor()', () => { - test('initializes state & override state', () => { - const controller1 = new MetamaskNotificationsController({ - messenger: mockNotificationMessenger().messenger, - }); - expect(controller1.state).toEqual(defaultState); - - const controller2 = new MetamaskNotificationsController({ - messenger: mockNotificationMessenger().messenger, - state: { - ...defaultState, - isFeatureAnnouncementsEnabled: true, - isMetamaskNotificationsEnabled: true, - }, - }); - expect(controller2.state.isFeatureAnnouncementsEnabled).toBe(true); - expect(controller2.state.isMetamaskNotificationsEnabled).toBe(true); - }); - - test('Keyring Change Event but feature not enabled will not add or remove triggers', async () => { - const { messenger, globalMessenger, mockListAccounts } = arrangeMocks(); - - // initialize controller with 1 address - mockListAccounts.mockResolvedValueOnce(['addr1']); - const controller = new MetamaskNotificationsController({ messenger }); - - const mockUpdate = jest - .spyOn(controller, 'updateOnChainTriggersByAccount') - .mockResolvedValue({} as UserStorage); - const mockDelete = jest - .spyOn(controller, 'deleteOnChainTriggersByAccount') - .mockResolvedValue({} as UserStorage); - - // listAccounts has a new address - mockListAccounts.mockResolvedValueOnce(['addr1', 'addr2']); - await actPublishKeyringStateChange(globalMessenger); - - expect(mockUpdate).not.toBeCalled(); - expect(mockDelete).not.toBeCalled(); - }); - - test('Keyring Change Event with new triggers will update triggers correctly', async () => { - const { messenger, globalMessenger, mockListAccounts } = arrangeMocks(); - - // initialize controller with 1 address - const controller = new MetamaskNotificationsController({ - messenger, - state: { - isMetamaskNotificationsEnabled: true, - subscriptionAccountsSeen: ['addr1'], - }, - }); - - const mockUpdate = jest - .spyOn(controller, 'updateOnChainTriggersByAccount') - .mockResolvedValue({} as UserStorage); - const mockDelete = jest - .spyOn(controller, 'deleteOnChainTriggersByAccount') - .mockResolvedValue({} as UserStorage); - - async function act( - addresses: string[], - assertion: () => Promise | void, - ) { - mockListAccounts.mockResolvedValueOnce(addresses); - await actPublishKeyringStateChange(globalMessenger); - await assertion(); - - // Clear mocks for next act/assert - mockUpdate.mockClear(); - mockDelete.mockClear(); - } - - // Act - if list accounts has been seen, then will not update - await act(['addr1'], () => { - expect(mockUpdate).not.toBeCalled(); - expect(mockDelete).not.toBeCalled(); - }); - - // Act - if a new address in list, then will update - await act(['addr1', 'addr2'], () => { - expect(mockUpdate).toBeCalled(); - expect(mockDelete).not.toBeCalled(); - }); - - // Act - if the list doesn't have an address, then we need to delete - await act(['addr2'], () => { - expect(mockUpdate).not.toBeCalled(); - expect(mockDelete).toBeCalled(); - }); - - // If the address is added back to the list, because it is seen we won't update - await act(['addr1', 'addr2'], () => { - expect(mockUpdate).not.toBeCalled(); - expect(mockDelete).not.toBeCalled(); - }); - }); - - test('Initializes push notifications', async () => { - const { messenger, mockEnablePushNotifications } = arrangeMocks(); - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const _controller = new MetamaskNotificationsController({ - messenger, - state: { isMetamaskNotificationsEnabled: true }, - }); - - await waitFor(() => { - expect(mockEnablePushNotifications).toBeCalled(); - }); - }); - - test('Fails to initialize push notifications', async () => { - const { messenger, mockPerformGetStorage, mockEnablePushNotifications } = - arrangeMocks(); - - // test when user storage is empty - mockPerformGetStorage.mockResolvedValue(null); - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const _controller = new MetamaskNotificationsController({ - messenger, - state: { isMetamaskNotificationsEnabled: true }, - }); - - await waitFor(() => { - expect(mockPerformGetStorage).toBeCalled(); - }); - - expect(mockEnablePushNotifications).not.toBeCalled(); - }); - - function arrangeMocks() { - const messengerMocks = mockNotificationMessenger(); - jest - .spyOn(ControllerUtils, 'toChecksumHexAddress') - .mockImplementation((x) => x); - - return messengerMocks; - } - - async function actPublishKeyringStateChange( - messenger: ControllerMessenger, - ) { - await messenger.publish( - 'KeyringController:stateChange', - {} as KeyringControllerState, - [], - ); - } -}); - -// See /utils for more in-depth testing -describe('metamask-notifications - checkAccountsPresence()', () => { - test('Returns Record with accounts that have notifications enabled', async () => { - const { messenger, mockPerformGetStorage } = mockNotificationMessenger(); - mockPerformGetStorage.mockResolvedValue( - JSON.stringify(createMockFullUserStorage()), - ); - - const controller = new MetamaskNotificationsController({ messenger }); - const result = await controller.checkAccountsPresence([ - MOCK_USER_STORAGE_ACCOUNT, - 'fake_account', - ]); - expect(result).toEqual({ - [MOCK_USER_STORAGE_ACCOUNT]: true, - fake_account: false, - }); - }); -}); - -describe('metamask-notifications - setFeatureAnnouncementsEnabled()', () => { - test('flips state when the method is called', async () => { - const { messenger, mockIsSignedIn } = mockNotificationMessenger(); - mockIsSignedIn.mockReturnValue(true); - - const controller = new MetamaskNotificationsController({ - messenger, - state: { ...defaultState, isFeatureAnnouncementsEnabled: false }, - }); - - await controller.setFeatureAnnouncementsEnabled(true); - - expect(controller.state.isFeatureAnnouncementsEnabled).toBe(true); - }); -}); - -describe('metamask-notifications - createOnChainTriggers()', () => { - test('Create new triggers and push notifications if there is no User Storage (login for new user)', async () => { - const { - messenger, - mockInitializeUserStorage, - mockEnablePushNotifications, - mockCreateOnChainTriggers, - mockPerformGetStorage, - } = arrangeMocks(); - const controller = new MetamaskNotificationsController({ messenger }); - mockPerformGetStorage.mockResolvedValue(null); // Mock no storage found. - - const result = await controller.createOnChainTriggers(); - expect(result).toBeDefined(); - expect(mockInitializeUserStorage).toBeCalled(); // called since no user storage (this is an existing user) - expect(mockCreateOnChainTriggers).toBeCalled(); - expect(mockEnablePushNotifications).toBeCalled(); - }); - - test('Throws if not given a valid auth & storage key', async () => { - const mocks = arrangeMocks(); - const controller = new MetamaskNotificationsController({ - messenger: mocks.messenger, - }); - - const testScenarios = { - ...arrangeFailureAuthAssertions(mocks), - ...arrangeFailureUserStorageKeyAssertions(mocks), - }; - - for (const mockFailureAction of Object.values(testScenarios)) { - mockFailureAction(); - await expect(controller.createOnChainTriggers()).rejects.toThrow(); - } - }); - - function arrangeMocks() { - const messengerMocks = mockNotificationMessenger(); - const mockCreateOnChainTriggers = jest - .spyOn(OnChainNotifications, 'createOnChainTriggers') - .mockResolvedValue(); - const mockInitializeUserStorage = jest - .spyOn(MetamaskNotificationsUtils, 'initializeUserStorage') - .mockReturnValue(createMockUserStorageWithTriggers(['t1', 't2'])); - return { - ...messengerMocks, - mockCreateOnChainTriggers, - mockInitializeUserStorage, - }; - } -}); - -describe('metamask-notifications - deleteOnChainTriggersByAccount', () => { - test('Deletes and disables push notifications for a given account', async () => { - const { - messenger, - nockMockDeleteTriggersAPI, - mockDisablePushNotifications, - } = arrangeMocks(); - const controller = new MetamaskNotificationsController({ messenger }); - const result = await controller.deleteOnChainTriggersByAccount([ - MOCK_USER_STORAGE_ACCOUNT, - ]); - expect( - MetamaskNotificationsUtils.traverseUserStorageTriggers(result).length, - ).toBe(0); - expect(nockMockDeleteTriggersAPI.isDone()).toBe(true); - expect(mockDisablePushNotifications).toBeCalled(); - }); - - test('Does nothing if account does not exist in storage', async () => { - const { messenger, mockDisablePushNotifications } = arrangeMocks(); - const controller = new MetamaskNotificationsController({ messenger }); - const result = await controller.deleteOnChainTriggersByAccount([ - 'UNKNOWN_ACCOUNT', - ]); - expect( - MetamaskNotificationsUtils.traverseUserStorageTriggers(result).length, - ).not.toBe(0); - - expect(mockDisablePushNotifications).not.toBeCalled(); - }); - - test('Throws errors when invalid auth and storage', async () => { - const mocks = arrangeMocks(); - const controller = new MetamaskNotificationsController({ - messenger: mocks.messenger, - }); - - const testScenarios = { - ...arrangeFailureAuthAssertions(mocks), - ...arrangeFailureUserStorageKeyAssertions(mocks), - ...arrangeFailureUserStorageAssertions(mocks), - }; - - for (const mockFailureAction of Object.values(testScenarios)) { - mockFailureAction(); - await expect( - controller.deleteOnChainTriggersByAccount([MOCK_USER_STORAGE_ACCOUNT]), - ).rejects.toThrow(); - } - }); - - function arrangeMocks() { - const messengerMocks = mockNotificationMessenger(); - const nockMockDeleteTriggersAPI = mockBatchDeleteTriggers(); - return { ...messengerMocks, nockMockDeleteTriggersAPI }; - } -}); - -describe('metamask-notifications - updateOnChainTriggersByAccount()', () => { - test('Creates Triggers and Push Notification Links for a new account', async () => { - const { - messenger, - mockUpdateTriggerPushNotifications, - mockPerformSetStorage, - } = arrangeMocks(); - const MOCK_ACCOUNT = 'MOCK_ACCOUNT2'; - const controller = new MetamaskNotificationsController({ messenger }); - - const result = await controller.updateOnChainTriggersByAccount([ - MOCK_ACCOUNT, - ]); - expect( - MetamaskNotificationsUtils.traverseUserStorageTriggers(result, { - address: MOCK_ACCOUNT.toLowerCase(), - }).length > 0, - ).toBe(true); - - expect(mockUpdateTriggerPushNotifications).toBeCalled(); - expect(mockPerformSetStorage).toBeCalled(); - }); - - test('Throws errors when invalid auth and storage', async () => { - const mocks = arrangeMocks(); - const controller = new MetamaskNotificationsController({ - messenger: mocks.messenger, - }); - - const testScenarios = { - ...arrangeFailureAuthAssertions(mocks), - ...arrangeFailureUserStorageKeyAssertions(mocks), - ...arrangeFailureUserStorageAssertions(mocks), - }; - - for (const mockFailureAction of Object.values(testScenarios)) { - mockFailureAction(); - await expect( - controller.deleteOnChainTriggersByAccount([MOCK_USER_STORAGE_ACCOUNT]), - ).rejects.toThrow(); - } - }); - - function arrangeMocks() { - const messengerMocks = mockNotificationMessenger(); - const mockBatchTriggersAPI = mockBatchCreateTriggers(); - return { ...messengerMocks, mockBatchTriggersAPI }; - } -}); - -describe('metamask-notifications - fetchAndUpdateMetamaskNotifications()', () => { - test('Processes and shows feature announcements and wallet notifications', async () => { - const { - messenger, - mockFeatureAnnouncementAPIResult, - mockListNotificationsAPIResult, - } = arrangeMocks(); - - const controller = new MetamaskNotificationsController({ - messenger, - state: { ...defaultState, isFeatureAnnouncementsEnabled: true }, - }); - - const result = await controller.fetchAndUpdateMetamaskNotifications(); - - // Should have 1 feature announcement and 1 wallet notification - expect(result.length).toBe(2); - expect( - result.find( - (n) => n.id === mockFeatureAnnouncementAPIResult.items?.[0].fields.id, - ), - ).toBeDefined(); - expect(result.find((n) => n.id === mockListNotificationsAPIResult[0].id)); - - // State is also updated - expect(controller.state.metamaskNotificationsList.length).toBe(2); - }); - - test('Only fetches and processes feature announcements if not authenticated', async () => { - const { messenger, mockGetBearerToken, mockFeatureAnnouncementAPIResult } = - arrangeMocks(); - mockGetBearerToken.mockRejectedValue( - new Error('MOCK - failed to get access token'), - ); - - const controller = new MetamaskNotificationsController({ - messenger, - state: { ...defaultState, isFeatureAnnouncementsEnabled: true }, - }); - - // Should only have feature announcement - const result = await controller.fetchAndUpdateMetamaskNotifications(); - expect(result.length).toBe(1); - expect( - result.find( - (n) => n.id === mockFeatureAnnouncementAPIResult.items?.[0].fields.id, - ), - ).toBeDefined(); - - // State is also updated - expect(controller.state.metamaskNotificationsList.length).toBe(1); - }); - - function arrangeMocks() { - const messengerMocks = mockNotificationMessenger(); - - const mockFeatureAnnouncementAPIResult = - createMockFeatureAnnouncementAPIResult(); - const mockFeatureAnnouncementsAPI = - mockFetchFeatureAnnouncementNotifications({ - status: 200, - body: mockFeatureAnnouncementAPIResult, - }); - - const mockListNotificationsAPIResult = [createMockNotificationEthSent()]; - const mockListNotificationsAPI = mockListNotifications({ - status: 200, - body: mockListNotificationsAPIResult, - }); - return { - ...messengerMocks, - mockFeatureAnnouncementAPIResult, - mockFeatureAnnouncementsAPI, - mockListNotificationsAPIResult, - mockListNotificationsAPI, - }; - } -}); - -describe('metamask-notifications - markMetamaskNotificationsAsRead()', () => { - test('updates feature announcements as read', async () => { - const { messenger } = arrangeMocks(); - const controller = new MetamaskNotificationsController({ messenger }); - - await controller.markMetamaskNotificationsAsRead([ - processNotification(createMockFeatureAnnouncementRaw()), - processNotification(createMockNotificationEthSent()), - ]); - - // Should see 2 items in controller read state - expect(controller.state.metamaskNotificationsReadList.length).toBe(1); - }); - - test('should at least mark feature announcements locally if external updates fail', async () => { - const { messenger } = arrangeMocks({ onChainMarkAsReadFails: true }); - const controller = new MetamaskNotificationsController({ messenger }); - - await controller.markMetamaskNotificationsAsRead([ - processNotification(createMockFeatureAnnouncementRaw()), - processNotification(createMockNotificationEthSent()), - ]); - - // Should see 1 item in controller read state. - // This is because on-chain failed. - // We can debate & change implementation if it makes sense to mark as read locally if external APIs fail. - expect(controller.state.metamaskNotificationsReadList.length).toBe(1); - }); - - function arrangeMocks(options?: { onChainMarkAsReadFails: boolean }) { - const messengerMocks = mockNotificationMessenger(); - - const mockMarkAsReadAPI = mockMarkNotificationsAsRead({ - status: options?.onChainMarkAsReadFails ? 500 : 200, - }); - - return { - ...messengerMocks, - mockMarkAsReadAPI, - }; - } -}); - -describe('metamask-notifications - enableMetamaskNotifications()', () => { - it('create new notifications when switched on and no new notifications', async () => { - const mocks = arrangeMocks(); - mocks.mockListAccounts.mockResolvedValue(['0xAddr1']); - const controller = new MetamaskNotificationsController({ - messenger: mocks.messenger, - }); - - const promise = controller.enableMetamaskNotifications(); - - // Act - intermediate state - expect(controller.state.isUpdatingMetamaskNotifications).toBe(true); - - await promise; - - // Act - final state - expect(controller.state.isUpdatingMetamaskNotifications).toBe(false); - expect(controller.state.isMetamaskNotificationsEnabled).toBe(true); - - // Act - services called - expect(mocks.mockCreateOnChainTriggers).toBeCalled(); - }); - - it('not create new notifications when enabling an account already in storage', async () => { - const mocks = arrangeMocks(); - mocks.mockListAccounts.mockResolvedValue(['0xAddr1']); - const userStorage = createMockFullUserStorage({ address: '0xAddr1' }); - mocks.mockPerformGetStorage.mockResolvedValue(JSON.stringify(userStorage)); - const controller = new MetamaskNotificationsController({ - messenger: mocks.messenger, - }); - - await controller.enableMetamaskNotifications(); - - const existingTriggers = - MetamaskNotificationsUtils.getAllUUIDs(userStorage); - const upsertedTriggers = - mocks.mockCreateOnChainTriggers.mock.calls[0][3].map((t) => t.id); - - expect(existingTriggers).toEqual(upsertedTriggers); - }); - - function arrangeMocks() { - const messengerMocks = mockNotificationMessenger(); - - const mockCreateOnChainTriggers = jest - .spyOn(OnChainNotifications, 'createOnChainTriggers') - .mockResolvedValue(); - - return { ...messengerMocks, mockCreateOnChainTriggers }; - } -}); - -describe('metamask-notifications - disableMetamaskNotifications()', () => { - it('disable notifications and turn off push notifications', async () => { - const mocks = arrangeMocks(); - const controller = new MetamaskNotificationsController({ - messenger: mocks.messenger, - state: { isMetamaskNotificationsEnabled: true }, - }); - - const promise = controller.disableMetamaskNotifications(); - - // Act - intermediate state - expect(controller.state.isUpdatingMetamaskNotifications).toBe(true); - - await promise; - - // Act - final state - expect(controller.state.isUpdatingMetamaskNotifications).toBe(false); - expect(controller.state.isMetamaskNotificationsEnabled).toBe(false); - - expect(mocks.mockDisablePushNotifications).toBeCalled(); - - // We do not delete triggers when disabling notifications - // As other devices might be using those triggers to receive notifications - expect(mocks.mockDeleteOnChainTriggers).not.toBeCalled(); - }); - - function arrangeMocks() { - const messengerMocks = mockNotificationMessenger(); - - const mockDeleteOnChainTriggers = jest - .spyOn(OnChainNotifications, 'deleteOnChainTriggers') - .mockResolvedValue({} as UserStorage); - - return { ...messengerMocks, mockDeleteOnChainTriggers }; - } -}); - -// Type-Computation - we are extracting args and parameters from a generic type utility -// Thus this `AnyFunc` can be used to help constrain the generic parameters correctly -// eslint-disable-next-line @typescript-eslint/no-explicit-any -type AnyFunc = (...args: any[]) => any; -const typedMockAction = () => - jest.fn, Parameters>(); - -function mockNotificationMessenger() { - const globalMessenger = new ControllerMessenger< - AllowedActions, - AllowedEvents - >(); - - const messenger = globalMessenger.getRestricted({ - name: 'MetamaskNotificationsController', - allowedActions: [ - 'KeyringController:getAccounts', - 'KeyringController:getState', - 'AuthenticationController:getBearerToken', - 'AuthenticationController:isSignedIn', - 'PushPlatformNotificationsController:disablePushNotifications', - 'PushPlatformNotificationsController:enablePushNotifications', - 'PushPlatformNotificationsController:updateTriggerPushNotifications', - 'UserStorageController:getStorageKey', - 'UserStorageController:performGetStorage', - 'UserStorageController:performSetStorage', - 'UserStorageController:enableProfileSyncing', - ], - allowedEvents: [ - 'KeyringController:stateChange', - 'KeyringController:lock', - 'KeyringController:unlock', - 'PushPlatformNotificationsController:onNewNotifications', - ], - }); - - const mockListAccounts = - typedMockAction().mockResolvedValue([]); - - const mockGetBearerToken = - typedMockAction().mockResolvedValue( - AuthMocks.MOCK_ACCESS_TOKEN, - ); - - const mockIsSignedIn = - typedMockAction().mockReturnValue( - true, - ); - - const mockDisablePushNotifications = - typedMockAction(); - - const mockEnablePushNotifications = - typedMockAction(); - - const mockUpdateTriggerPushNotifications = - typedMockAction(); - - const mockGetStorageKey = - typedMockAction().mockResolvedValue( - 'MOCK_STORAGE_KEY', - ); - - const mockEnableProfileSyncing = - typedMockAction(); - - const mockPerformGetStorage = - typedMockAction().mockResolvedValue( - JSON.stringify(createMockFullUserStorage()), - ); - - const mockPerformSetStorage = - typedMockAction(); - - jest.spyOn(messenger, 'call').mockImplementation((...args) => { - const [actionType] = args; - - // This mock implementation does not have a nice discriminate union where types/parameters can be correctly inferred - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const [, ...params]: any[] = args; - - if (actionType === 'KeyringController:getAccounts') { - return mockListAccounts(); - } - - if (actionType === 'AuthenticationController:getBearerToken') { - return mockGetBearerToken(); - } - - if (actionType === 'AuthenticationController:isSignedIn') { - return mockIsSignedIn(); - } - - if ( - actionType === - 'PushPlatformNotificationsController:disablePushNotifications' - ) { - return mockDisablePushNotifications(params[0]); - } - - if ( - actionType === - 'PushPlatformNotificationsController:enablePushNotifications' - ) { - return mockEnablePushNotifications(params[0]); - } - - if ( - actionType === - 'PushPlatformNotificationsController:updateTriggerPushNotifications' - ) { - return mockUpdateTriggerPushNotifications(params[0]); - } - - if (actionType === 'UserStorageController:getStorageKey') { - return mockGetStorageKey(); - } - - if (actionType === 'UserStorageController:enableProfileSyncing') { - return mockEnableProfileSyncing(); - } - - if (actionType === 'UserStorageController:performGetStorage') { - return mockPerformGetStorage(params[0]); - } - - if (actionType === 'UserStorageController:performSetStorage') { - return mockPerformSetStorage(params[0], params[1]); - } - - if (actionType === 'KeyringController:getState') { - return { isUnlocked: true } as MockVar; - } - - throw new Error(`MOCK_FAIL - unsupported messenger call: ${actionType}`); - }); - - return { - globalMessenger, - messenger, - mockListAccounts, - mockGetBearerToken, - mockIsSignedIn, - mockDisablePushNotifications, - mockEnablePushNotifications, - mockUpdateTriggerPushNotifications, - mockGetStorageKey, - mockPerformGetStorage, - mockPerformSetStorage, - }; -} - -function arrangeFailureAuthAssertions( - mocks: ReturnType, -) { - const testScenarios = { - NotLoggedIn: () => mocks.mockIsSignedIn.mockReturnValue(false), - - // unlikely, but in case it returns null - NoBearerToken: () => - mocks.mockGetBearerToken.mockResolvedValueOnce(null as unknown as string), - - RejectedBearerToken: () => - mocks.mockGetBearerToken.mockRejectedValueOnce( - new Error('MOCK - no bearer token'), - ), - }; - - return testScenarios; -} - -function arrangeFailureUserStorageKeyAssertions( - mocks: ReturnType, -) { - const testScenarios = { - NoStorageKey: () => - mocks.mockGetStorageKey.mockResolvedValueOnce(null as unknown as string), // unlikely but in case it returns null - RejectedStorageKey: () => - mocks.mockGetStorageKey.mockRejectedValueOnce( - new Error('MOCK - no storage key'), - ), - }; - return testScenarios; -} - -function arrangeFailureUserStorageAssertions( - mocks: ReturnType, -) { - const testScenarios = { - NoUserStorage: () => - mocks.mockPerformGetStorage.mockResolvedValueOnce(null), - ThrowUserStorage: () => - mocks.mockPerformGetStorage.mockRejectedValueOnce( - new Error('MOCK - Unable to call storage api'), - ), - }; - return testScenarios; -} diff --git a/app/scripts/controllers/metamask-notifications/metamask-notifications.ts b/app/scripts/controllers/metamask-notifications/metamask-notifications.ts deleted file mode 100644 index a2fba0fe3cef..000000000000 --- a/app/scripts/controllers/metamask-notifications/metamask-notifications.ts +++ /dev/null @@ -1,1214 +0,0 @@ -import { - BaseController, - RestrictedControllerMessenger, - ControllerGetStateAction, - StateMetadata, -} from '@metamask/base-controller'; -import log from 'loglevel'; -import { toChecksumHexAddress } from '@metamask/controller-utils'; -import { - KeyringControllerGetAccountsAction, - KeyringControllerStateChangeEvent, - KeyringControllerGetStateAction, - KeyringControllerLockEvent, - KeyringControllerUnlockEvent, -} from '@metamask/keyring-controller'; -import { - AuthenticationController, - UserStorageController, -} from '@metamask/profile-sync-controller'; -import { - PushPlatformNotificationsControllerEnablePushNotifications, - PushPlatformNotificationsControllerDisablePushNotifications, - PushPlatformNotificationsControllerUpdateTriggerPushNotifications, - PushPlatformNotificationsControllerOnNewNotificationEvent, -} from '../push-platform-notifications/push-platform-notifications'; -import { - TRIGGER_TYPES, - TRIGGER_TYPES_GROUPS, -} from './constants/notification-schema'; -import { USER_STORAGE_VERSION_KEY } from './constants/constants'; -import type { UserStorage } from './types/user-storage/user-storage'; -import * as FeatureNotifications from './services/feature-announcements'; -import * as OnChainNotifications from './services/onchain-notifications'; -import type { - Notification, - MarkAsReadNotificationsParam, -} from './types/notification/notification'; -import { OnChainRawNotification } from './types/on-chain-notification/on-chain-notification'; -import { processNotification } from './processors/process-notifications'; -import * as MetamaskNotificationsUtils from './utils/utils'; -import type { NotificationUnion } from './types/types'; - -// Unique name for the controller -const controllerName = 'MetamaskNotificationsController'; - -/** - * State shape for MetamaskNotificationsController - */ -export type MetamaskNotificationsControllerState = { - /** - * We store and manage accounts that have been seen/visted through the - * account subscription. This allows us to track and add notifications for new accounts and not previous accounts added. - */ - subscriptionAccountsSeen: string[]; - - /** - * Flag that indicates if the metamask notifications feature has been seen - */ - isMetamaskNotificationsFeatureSeen: boolean; - - /** - * Flag that indicates if the metamask notifications are enabled - */ - isMetamaskNotificationsEnabled: boolean; - - /** - * Flag that indicates if the feature announcements are enabled - */ - isFeatureAnnouncementsEnabled: boolean; - - /** - * List of metamask notifications - */ - metamaskNotificationsList: Notification[]; - - /** - * List of read metamask notifications - */ - metamaskNotificationsReadList: string[]; - /** - * Flag that indicates that the creating notifications is in progress - */ - isUpdatingMetamaskNotifications: boolean; - /** - * Flag that indicates that the fetching notifications is in progress - * This is used to show a loading spinner in the UI - * when fetching notifications - */ - isFetchingMetamaskNotifications: boolean; - /** - * Flag that indicates that the updating notifications for a specific address is in progress - */ - isUpdatingMetamaskNotificationsAccount: string[]; - /** - * Flag that indicates that the checking accounts presence is in progress - */ - isCheckingAccountsPresence: boolean; -}; - -const metadata: StateMetadata = { - subscriptionAccountsSeen: { - persist: true, - anonymous: true, - }, - - isMetamaskNotificationsFeatureSeen: { - persist: true, - anonymous: false, - }, - isMetamaskNotificationsEnabled: { - persist: true, - anonymous: false, - }, - isFeatureAnnouncementsEnabled: { - persist: true, - anonymous: false, - }, - metamaskNotificationsList: { - persist: true, - anonymous: true, - }, - metamaskNotificationsReadList: { - persist: true, - anonymous: true, - }, - isUpdatingMetamaskNotifications: { - persist: false, - anonymous: false, - }, - isFetchingMetamaskNotifications: { - persist: false, - anonymous: false, - }, - isUpdatingMetamaskNotificationsAccount: { - persist: false, - anonymous: false, - }, - isCheckingAccountsPresence: { - persist: false, - anonymous: false, - }, -}; -export const defaultState: MetamaskNotificationsControllerState = { - subscriptionAccountsSeen: [], - isMetamaskNotificationsFeatureSeen: false, - isMetamaskNotificationsEnabled: false, - isFeatureAnnouncementsEnabled: false, - metamaskNotificationsList: [], - metamaskNotificationsReadList: [], - isUpdatingMetamaskNotifications: false, - isFetchingMetamaskNotifications: false, - isUpdatingMetamaskNotificationsAccount: [], - isCheckingAccountsPresence: false, -}; - -export declare type MetamaskNotificationsControllerUpdateMetamaskNotificationsList = - { - type: `${typeof controllerName}:updateMetamaskNotificationsList`; - handler: MetamaskNotificationsController['updateMetamaskNotificationsList']; - }; - -export declare type MetamaskNotificationsControllerDisableMetamaskNotifications = - { - type: `${typeof controllerName}:disableMetamaskNotifications`; - handler: MetamaskNotificationsController['disableMetamaskNotifications']; - }; - -export declare type MetamaskNotificationsControllerSelectIsMetamaskNotificationsEnabled = - { - type: `${typeof controllerName}:selectIsMetamaskNotificationsEnabled`; - handler: MetamaskNotificationsController['selectIsMetamaskNotificationsEnabled']; - }; - -export type MetamaskNotificationsControllerNotificationsListUpdatedEvent = { - type: `${typeof controllerName}:notificationsListUpdated`; - payload: [Notification[]]; -}; - -export type MetamaskNotificationsControllerMarkNotificationsAsRead = { - type: `${typeof controllerName}:markNotificationsAsRead`; - payload: [Notification[]]; -}; - -// Messenger Actions -export type Actions = - | MetamaskNotificationsControllerUpdateMetamaskNotificationsList - | MetamaskNotificationsControllerDisableMetamaskNotifications - | MetamaskNotificationsControllerSelectIsMetamaskNotificationsEnabled - | ControllerGetStateAction<'state', MetamaskNotificationsControllerState>; - -// Allowed Actions -export type AllowedActions = - // Keyring Controller Requests - | KeyringControllerGetAccountsAction - | KeyringControllerGetStateAction - // Auth Controller Requests - | AuthenticationController.AuthenticationControllerGetBearerToken - | AuthenticationController.AuthenticationControllerIsSignedIn - // User Storage Controller Requests - | UserStorageController.UserStorageControllerEnableProfileSyncing - | UserStorageController.UserStorageControllerGetStorageKey - | UserStorageController.UserStorageControllerPerformGetStorage - | UserStorageController.UserStorageControllerPerformSetStorage - // Push Notifications Controller Requests - | PushPlatformNotificationsControllerEnablePushNotifications - | PushPlatformNotificationsControllerDisablePushNotifications - | PushPlatformNotificationsControllerUpdateTriggerPushNotifications; - -// Events -export type Events = - | MetamaskNotificationsControllerNotificationsListUpdatedEvent - | MetamaskNotificationsControllerMarkNotificationsAsRead; - -// Allowed Events -export type AllowedEvents = - // Keyring Events - | KeyringControllerStateChangeEvent - | KeyringControllerLockEvent - | KeyringControllerUnlockEvent - // Push Notification Events - | PushPlatformNotificationsControllerOnNewNotificationEvent; - -// Type for the messenger of MetamaskNotificationsController -export type MetamaskNotificationsControllerMessenger = - RestrictedControllerMessenger< - typeof controllerName, - Actions | AllowedActions, - Events | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] - >; - -/** - * Controller that enables wallet notifications and feature announcements - */ -export class MetamaskNotificationsController extends BaseController< - typeof controllerName, - MetamaskNotificationsControllerState, - MetamaskNotificationsControllerMessenger -> { - // Flag to check is notifications have been setup when the browser/extension is initialized. - // We want to re-initialize push notifications when the browser/extension is refreshed - // To ensure we subscribe to the most up-to-date notifications - #isPushNotificationsSetup = false; - - #isUnlocked = false; - - #keyringController = { - setupLockedStateSubscriptions: (onUnlock: () => Promise) => { - const { isUnlocked } = this.messagingSystem.call( - 'KeyringController:getState', - ); - this.#isUnlocked = isUnlocked; - - this.messagingSystem.subscribe('KeyringController:unlock', () => { - this.#isUnlocked = true; - // messaging system cannot await promises - // we don't need to wait for a result on this. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - onUnlock(); - }); - - this.messagingSystem.subscribe('KeyringController:lock', () => { - this.#isUnlocked = false; - }); - }, - }; - - #auth = { - getBearerToken: async () => { - return await this.messagingSystem.call( - 'AuthenticationController:getBearerToken', - ); - }, - isSignedIn: () => { - return this.messagingSystem.call('AuthenticationController:isSignedIn'); - }, - }; - - #storage = { - enableProfileSyncing: async () => { - return await this.messagingSystem.call( - 'UserStorageController:enableProfileSyncing', - ); - }, - getStorageKey: () => { - return this.messagingSystem.call('UserStorageController:getStorageKey'); - }, - getNotificationStorage: async () => { - return await this.messagingSystem.call( - 'UserStorageController:performGetStorage', - 'notifications.notification_settings', - ); - }, - setNotificationStorage: async (state: string) => { - return await this.messagingSystem.call( - 'UserStorageController:performSetStorage', - 'notifications.notification_settings', - state, - ); - }, - }; - - #pushNotifications = { - enablePushNotifications: async (UUIDs: string[]) => { - try { - await this.messagingSystem.call( - 'PushPlatformNotificationsController:enablePushNotifications', - UUIDs, - ); - } catch (e) { - log.error('Silently failed to enable push notifications', e); - } - }, - disablePushNotifications: async (UUIDs: string[]) => { - try { - await this.messagingSystem.call( - 'PushPlatformNotificationsController:disablePushNotifications', - UUIDs, - ); - } catch (e) { - log.error('Silently failed to disable push notifications', e); - } - }, - updatePushNotifications: async (UUIDs: string[]) => { - try { - await this.messagingSystem.call( - 'PushPlatformNotificationsController:updateTriggerPushNotifications', - UUIDs, - ); - } catch (e) { - log.error('Silently failed to update push notifications', e); - } - }, - subscribe: () => { - this.messagingSystem.subscribe( - 'PushPlatformNotificationsController:onNewNotifications', - (notification) => { - this.updateMetamaskNotificationsList(notification); - }, - ); - }, - initializePushNotifications: async () => { - if (!this.state.isMetamaskNotificationsEnabled) { - return; - } - if (this.#isPushNotificationsSetup) { - return; - } - if (!this.#isUnlocked) { - return; - } - - const storage = await this.#getUserStorage(); - if (!storage) { - return; - } - - const uuids = MetamaskNotificationsUtils.getAllUUIDs(storage); - await this.#pushNotifications.enablePushNotifications(uuids); - this.#isPushNotificationsSetup = true; - }, - }; - - #accounts = { - /** - * Used to get list of addresses from keyring (wallet addresses) - * - * @returns addresses removed, added, and latest list of addresses - */ - listAccounts: async () => { - // Get previous and current account sets - const nonChecksumAccounts = await this.messagingSystem.call( - 'KeyringController:getAccounts', - ); - const accounts = nonChecksumAccounts.map((a) => toChecksumHexAddress(a)); - const currentAccountsSet = new Set(accounts); - const prevAccountsSet = new Set(this.state.subscriptionAccountsSeen); - - // Invalid value you cannot have zero accounts - // Only occurs when the Accounts controller is initializing. - if (accounts.length === 0) { - return { - accountsAdded: [], - accountsRemoved: [], - accounts: [], - }; - } - - // Calculate added and removed addresses - const accountsAdded = accounts.filter((a) => !prevAccountsSet.has(a)); - const accountsRemoved = [...prevAccountsSet.values()].filter( - (a) => !currentAccountsSet.has(a), - ); - - // Update accounts seen - this.update((state) => { - state.subscriptionAccountsSeen = [...prevAccountsSet, ...accountsAdded]; - }); - - return { - accountsAdded, - accountsRemoved, - accounts, - }; - }, - - /** - * Initializes the cache/previous list. This is handy so we have an accurate in-mem state of the previous list of accounts. - * - * @returns result from list accounts - */ - initialize: () => { - return this.#accounts.listAccounts(); - }, - - /** - * Subscription to any state change in the keyring controller (aka wallet accounts). - * We can call the `listAccounts` defined above to find out about any accounts added, removed - * And call effects to subscribe/unsubscribe to notifications. - */ - subscribe: () => { - this.messagingSystem.subscribe( - 'KeyringController:stateChange', - async () => { - if (!this.state.isMetamaskNotificationsEnabled) { - return; - } - - const { accountsAdded, accountsRemoved } = - await this.#accounts.listAccounts(); - - const promises: Promise[] = []; - if (accountsAdded.length > 0) { - promises.push(this.updateOnChainTriggersByAccount(accountsAdded)); - } - if (accountsRemoved.length > 0) { - promises.push(this.deleteOnChainTriggersByAccount(accountsRemoved)); - } - await Promise.all(promises); - }, - ); - }, - }; - - /** - * Creates a MetamaskNotificationsController instance. - * - * @param args - The arguments to this function. - * @param args.messenger - Messenger used to communicate with BaseV2 controller. - * @param args.state - Initial state to set on this controller. - */ - constructor({ - messenger, - state, - }: { - messenger: MetamaskNotificationsControllerMessenger; - state?: Partial; - }) { - super({ - messenger, - metadata, - name: controllerName, - state: { ...defaultState, ...state }, - }); - - this.#registerMessageHandlers(); - this.#clearLoadingStates(); - this.#keyringController.setupLockedStateSubscriptions( - this.#pushNotifications.initializePushNotifications, - ); - this.#accounts.initialize(); - this.#pushNotifications.initializePushNotifications(); - this.#accounts.subscribe(); - this.#pushNotifications.subscribe(); - } - - #registerMessageHandlers(): void { - this.messagingSystem.registerActionHandler( - `${controllerName}:updateMetamaskNotificationsList`, - this.updateMetamaskNotificationsList.bind(this), - ); - - this.messagingSystem.registerActionHandler( - `${controllerName}:disableMetamaskNotifications`, - this.disableMetamaskNotifications.bind(this), - ); - - this.messagingSystem.registerActionHandler( - `${controllerName}:selectIsMetamaskNotificationsEnabled`, - this.selectIsMetamaskNotificationsEnabled.bind(this), - ); - } - - #clearLoadingStates(): void { - this.update((state) => { - state.isUpdatingMetamaskNotifications = false; - state.isCheckingAccountsPresence = false; - state.isFetchingMetamaskNotifications = false; - state.isUpdatingMetamaskNotificationsAccount = []; - }); - } - - #assertAuthEnabled() { - if (!this.#auth.isSignedIn()) { - this.update((state) => { - state.isMetamaskNotificationsEnabled = false; - }); - throw new Error('User is not signed in.'); - } - } - - async #getValidStorageKeyAndBearerToken() { - this.#assertAuthEnabled(); - - const bearerToken = await this.#auth.getBearerToken(); - const storageKey = await this.#storage.getStorageKey(); - - if (!bearerToken || !storageKey) { - throw new Error('Missing BearerToken or storage key'); - } - - return { bearerToken, storageKey }; - } - - #performEnableProfileSyncing = async () => { - try { - await this.#storage.enableProfileSyncing(); - } catch (e) { - log.error('Failed to enable profile syncing', e); - throw new Error('Failed to enable profile syncing'); - } - }; - - #assertUserStorage( - storage: UserStorage | null, - ): asserts storage is UserStorage { - if (!storage) { - throw new Error('User Storage does not exist'); - } - } - - /** - * Retrieves and parses the user storage from the storage key. - * - * This method attempts to retrieve the user storage using the specified storage key, - * then parses the JSON string to an object. If the storage is not found or cannot be parsed, - * it throws an error. - * - * @returns The parsed user storage object or null - */ - async #getUserStorage(): Promise { - const userStorageString: string | null = - await this.#storage.getNotificationStorage(); - - if (!userStorageString) { - return null; - } - - try { - const userStorage: UserStorage = JSON.parse(userStorageString); - return userStorage; - } catch (error) { - log.error('Unable to parse User Storage'); - return null; - } - } - - /** - * @deprecated - This needs rework for it to be feasible. Currently this is a half-baked solution, as it fails once we add new triggers (introspection for filters is difficult). - * - * Checks for the complete presence of trigger types by group across all addresses in user storage. - * - * This method retrieves the user storage and uses `MetamaskNotificationsUtils` to verify if all expected trigger types for each group are present for every address. - * @returns A record indicating whether all expected trigger types for each group are present for every address. - * @throws {Error} If user storage does not exist. - */ - public async checkTriggersPresenceByGroup(): Promise< - Record - > { - const userStorage = await this.#getUserStorage(); - this.#assertUserStorage(userStorage); - - // Use MetamaskNotificationsUtils to check the presence of triggers - return MetamaskNotificationsUtils.checkTriggersPresenceByGroup(userStorage); - } - - /** - * Retrieves the current enabled state of MetaMask notifications. - * - * This method directly returns the boolean value of `isMetamaskNotificationsEnabled` - * from the controller's state, indicating whether MetaMask notifications are currently enabled. - * - * @returns The enabled state of MetaMask notifications. - */ - public selectIsMetamaskNotificationsEnabled(): boolean { - return this.state.isMetamaskNotificationsEnabled; - } - - /** - * Sets the state of notification creation process. - * - * This method updates the `isUpdatingMetamaskNotifications` state, which can be used to indicate - * whether the notification creation process is currently active or not. This is useful - * for UI elements that need to reflect the state of ongoing operations, such as loading - * indicators or disabled buttons during processing. - * - * @param isUpdatingMetamaskNotifications - A boolean value representing the new state of the notification creation process. - */ - #setIsUpdatingMetamaskNotifications( - isUpdatingMetamaskNotifications: boolean, - ) { - this.update((state) => { - state.isUpdatingMetamaskNotifications = isUpdatingMetamaskNotifications; - }); - } - - /** - * Updates the state to indicate whether fetching of MetaMask notifications is in progress. - * - * This method is used to set the `isFetchingMetamaskNotifications` state, which can be utilized - * to show or hide loading indicators in the UI when notifications are being fetched. - * - * @param isFetchingMetamaskNotifications - A boolean value representing the fetching state. - */ - #setIsFetchingMetamaskNotifications( - isFetchingMetamaskNotifications: boolean, - ) { - this.update((state) => { - state.isFetchingMetamaskNotifications = isFetchingMetamaskNotifications; - }); - } - - /** - * Updates the state to indicate that the checking of accounts presence is in progress. - * - * This method modifies the `isCheckingAccountsPresence` state, which can be used to manage UI elements - * that depend on the status of account presence checks, such as displaying loading indicators or disabling - * buttons while the check is ongoing. - * - * @param isCheckingAccountsPresence - A boolean value indicating whether the account presence check is currently active. - */ - #setIsCheckingAccountsPresence(isCheckingAccountsPresence: boolean) { - this.update((state) => { - state.isCheckingAccountsPresence = isCheckingAccountsPresence; - }); - } - - /** - * Updates the state to indicate that account updates are in progress. - * Removes duplicate accounts before updating the state. - * - * @param accounts - The accounts being updated. - */ - #updateUpdatingAccountsState(accounts: string[]) { - this.update((state) => { - const uniqueAccounts = new Set([ - ...state.isUpdatingMetamaskNotificationsAccount, - ...accounts, - ]); - state.isUpdatingMetamaskNotificationsAccount = Array.from(uniqueAccounts); - }); - } - - /** - * Clears the state indicating that account updates are complete. - * - * @param accounts - The accounts that have finished updating. - */ - #clearUpdatingAccountsState(accounts: string[]) { - this.update((state) => { - state.isUpdatingMetamaskNotificationsAccount = - state.isUpdatingMetamaskNotificationsAccount.filter( - (existingAccount) => !accounts.includes(existingAccount), - ); - }); - } - - public async checkAccountsPresence( - accounts: string[], - ): Promise> { - try { - this.#setIsCheckingAccountsPresence(true); - - // Retrieve user storage - const userStorage = await this.#getUserStorage(); - this.#assertUserStorage(userStorage); - - const presence = MetamaskNotificationsUtils.checkAccountsPresence( - userStorage, - accounts, - ); - return presence; - } catch (error) { - log.error('Failed to check accounts presence', error); - throw error; - } finally { - this.#setIsCheckingAccountsPresence(false); - } - } - - /** - * Sets the enabled state of feature announcements. - * - * **Action** - used in the notification settings to enable/disable feature announcements. - * - * @param featureAnnouncementsEnabled - A boolean value indicating the desired enabled state of the feature announcements. - * @async - * @throws {Error} If fails to update - */ - public async setFeatureAnnouncementsEnabled( - featureAnnouncementsEnabled: boolean, - ) { - try { - this.update((s) => { - s.isFeatureAnnouncementsEnabled = featureAnnouncementsEnabled; - }); - } catch (e) { - log.error('Unable to toggle feature announcements', e); - throw new Error('Unable to toggle feature announcements'); - } - } - - /** - * This creates/re-creates on-chain triggers defined in User Storage. - * - * **Action** - Used during Sign In / Enabling of notifications. - * - * @returns The updated or newly created user storage. - * @throws {Error} Throws an error if unauthenticated or from other operations. - */ - public async createOnChainTriggers(): Promise { - try { - this.#setIsUpdatingMetamaskNotifications(true); - - await this.#performEnableProfileSyncing(); - - const { bearerToken, storageKey } = - await this.#getValidStorageKeyAndBearerToken(); - - const { accounts } = await this.#accounts.listAccounts(); - - let userStorage = await this.#getUserStorage(); - - // If userStorage does not exist, create a new one - // All the triggers created are set as: "disabled" - if (userStorage?.[USER_STORAGE_VERSION_KEY] === undefined) { - userStorage = MetamaskNotificationsUtils.initializeUserStorage( - accounts.map((account) => ({ address: account })), - false, - ); - - // Write the userStorage - await this.#storage.setNotificationStorage(JSON.stringify(userStorage)); - } - - // Create the triggers - const triggers = - MetamaskNotificationsUtils.traverseUserStorageTriggers(userStorage); - await OnChainNotifications.createOnChainTriggers( - userStorage, - storageKey, - bearerToken, - triggers, - ); - - // Create push notifications triggers - const allUUIDS = MetamaskNotificationsUtils.getAllUUIDs(userStorage); - await this.#pushNotifications.enablePushNotifications(allUUIDS); - - // Write the new userStorage (triggers are now "enabled") - await this.#storage.setNotificationStorage(JSON.stringify(userStorage)); - - // Update the state of the controller - this.update((state) => { - state.isMetamaskNotificationsEnabled = true; - state.isFeatureAnnouncementsEnabled = true; - state.isMetamaskNotificationsFeatureSeen = true; - }); - - return userStorage; - } catch (err) { - log.error('Failed to create On Chain triggers', err); - throw new Error('Failed to create On Chain triggers'); - } finally { - this.#setIsUpdatingMetamaskNotifications(false); - } - } - - /** - * Enables all MetaMask notifications for the user. - * This is identical flow when initializing notifications for the first time. - * 1. Enable Profile Syncing - * 2. Get or Create Notification User Storage - * 3. Upsert Triggers - * 4. Update Push notifications - * - * @throws {Error} If there is an error during the process of enabling notifications. - */ - public async enableMetamaskNotifications() { - try { - this.#setIsUpdatingMetamaskNotifications(true); - await this.createOnChainTriggers(); - } catch (e) { - log.error('Unable to enable notifications', e); - throw new Error('Unable to enable notifications'); - } finally { - this.#setIsUpdatingMetamaskNotifications(false); - } - } - - /** - * Disables all MetaMask notifications for the user. - * This method ensures that the user is authenticated, retrieves all linked accounts, - * and disables on-chain triggers for each account. It also sets the global notification - * settings for MetaMask, feature announcements to false. - * - * @throws {Error} If the user is not authenticated or if there is an error during the process. - */ - public async disableMetamaskNotifications() { - try { - this.#setIsUpdatingMetamaskNotifications(true); - - // Disable Push Notifications - const userStorage = await this.#getUserStorage(); - this.#assertUserStorage(userStorage); - const UUIDs = MetamaskNotificationsUtils.getAllUUIDs(userStorage); - await this.#pushNotifications.disablePushNotifications(UUIDs); - - // Clear Notification States (toggles and list) - this.update((state) => { - state.isMetamaskNotificationsEnabled = false; - state.isFeatureAnnouncementsEnabled = false; - state.metamaskNotificationsList = []; - }); - } catch (e) { - log.error('Unable to disable notifications', e); - throw new Error('Unable to disable notifications'); - } finally { - this.#setIsUpdatingMetamaskNotifications(false); - } - } - - /** - * Deletes on-chain triggers associated with a specific account. - * This method performs several key operations: - * 1. Validates Auth & Storage - * 2. Finds and deletes all triggers associated with the account - * 3. Disables any related push notifications - * 4. Updates Storage to reflect new state. - * - * **Action** - When a user disables notifications for a given account in settings. - * - * @param accounts - The account for which on-chain triggers are to be deleted. - * @returns A promise that resolves to void or an object containing a success message. - * @throws {Error} Throws an error if unauthenticated or from other operations. - */ - public async deleteOnChainTriggersByAccount( - accounts: string[], - ): Promise { - try { - this.#updateUpdatingAccountsState(accounts); - // Get and Validate BearerToken and User Storage Key - const { bearerToken, storageKey } = - await this.#getValidStorageKeyAndBearerToken(); - - // Get & Validate User Storage - const userStorage = await this.#getUserStorage(); - this.#assertUserStorage(userStorage); - - // Get the UUIDs to delete - const UUIDs = accounts - .map((a) => - MetamaskNotificationsUtils.getUUIDsForAccount( - userStorage, - a.toLowerCase(), - ), - ) - .flat(); - - if (UUIDs.length === 0) { - return userStorage; - } - - // Delete these UUIDs (Mutates User Storage) - await OnChainNotifications.deleteOnChainTriggers( - userStorage, - storageKey, - bearerToken, - UUIDs, - ); - - // Delete these UUIDs from the push notifications - await this.#pushNotifications.disablePushNotifications(UUIDs); - - // Update User Storage - await this.#storage.setNotificationStorage(JSON.stringify(userStorage)); - return userStorage; - } catch (err) { - log.error('Failed to delete OnChain triggers', err); - throw new Error('Failed to delete OnChain triggers'); - } finally { - this.#clearUpdatingAccountsState(accounts); - } - } - - /** - * Updates/Creates on-chain triggers for a specific account. - * - * This method performs several key operations: - * 1. Validates Auth & Storage - * 2. Finds and creates any missing triggers associated with the account - * 3. Enables any related push notifications - * 4. Updates Storage to reflect new state. - * - * **Action** - When a user enables notifications for an account - * - * @param accounts - List of accounts you want to update. - * @returns A promise that resolves to the updated user storage. - * @throws {Error} Throws an error if unauthenticated or from other operations. - */ - public async updateOnChainTriggersByAccount( - accounts: string[], - ): Promise { - try { - this.#updateUpdatingAccountsState(accounts); - // Get and Validate BearerToken and User Storage Key - const { bearerToken, storageKey } = - await this.#getValidStorageKeyAndBearerToken(); - - // Get & Validate User Storage - const userStorage = await this.#getUserStorage(); - this.#assertUserStorage(userStorage); - - // Add any missing triggers - accounts.forEach((a) => - MetamaskNotificationsUtils.upsertAddressTriggers(a, userStorage), - ); - - const newTriggers = - MetamaskNotificationsUtils.traverseUserStorageTriggers(userStorage, { - mapTrigger: (t) => { - if (t.enabled === false) { - return t; - } - return undefined; - }, - }); - - // Create any missing triggers. - if (newTriggers.length > 0) { - // Write te updated userStorage (where triggers are disabled) - await this.#storage.setNotificationStorage(JSON.stringify(userStorage)); - - // Create the triggers - const triggers = MetamaskNotificationsUtils.traverseUserStorageTriggers( - userStorage, - { - mapTrigger: (t) => { - if ( - accounts.some( - (a) => a.toLowerCase() === t.address.toLowerCase(), - ) - ) { - return t; - } - return undefined; - }, - }, - ); - await OnChainNotifications.createOnChainTriggers( - userStorage, - storageKey, - bearerToken, - triggers, - ); - } - - // Update Push Notifications Triggers - const UUIDs = MetamaskNotificationsUtils.getAllUUIDs(userStorage); - await this.#pushNotifications.updatePushNotifications(UUIDs); - - // Update the userStorage (where triggers are enabled) - await this.#storage.setNotificationStorage(JSON.stringify(userStorage)); - return userStorage; - } catch (err) { - log.error('Failed to update OnChain triggers', err); - throw new Error('Failed to update OnChain triggers'); - } finally { - this.#clearUpdatingAccountsState(accounts); - } - } - - /** - * Fetches the list of metamask notifications. - * This includes OnChain notifications and Feature Announcements. - * - * **Action** - When a user views the notification list page/dropdown - * - * @throws {Error} Throws an error if unauthenticated or from other operations. - */ - public async fetchAndUpdateMetamaskNotifications(): Promise { - try { - this.#setIsFetchingMetamaskNotifications(true); - - // Raw Feature Notifications - const rawFeatureAnnouncementNotifications = this.state - .isFeatureAnnouncementsEnabled - ? await FeatureNotifications.getFeatureAnnouncementNotifications().catch( - () => [], - ) - : []; - - // Raw On Chain Notifications - const rawOnChainNotifications: OnChainRawNotification[] = []; - const userStorage = await this.#storage - .getNotificationStorage() - .then((s) => s && (JSON.parse(s) as UserStorage)) - .catch(() => null); - const bearerToken = await this.#auth.getBearerToken().catch(() => null); - if (userStorage && bearerToken) { - const notifications = - await OnChainNotifications.getOnChainNotifications( - userStorage, - bearerToken, - ).catch(() => []); - - rawOnChainNotifications.push(...notifications); - } - - const readIds = this.state.metamaskNotificationsReadList; - - // Combined Notifications - const isNotUndefined = (t?: T): t is T => Boolean(t); - const processAndFilter = (ns: NotificationUnion[]) => - ns - .map((n) => { - try { - return processNotification(n, readIds); - } catch { - // So we don't throw and show no notifications - return undefined; - } - }) - .filter(isNotUndefined); - - const featureAnnouncementNotifications = processAndFilter( - rawFeatureAnnouncementNotifications, - ); - const onChainNotifications = processAndFilter(rawOnChainNotifications); - - const metamaskNotifications: Notification[] = [ - ...featureAnnouncementNotifications, - ...onChainNotifications, - ]; - metamaskNotifications.sort( - (a, b) => - new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(), - ); - - // Update State - this.update((state) => { - state.metamaskNotificationsList = metamaskNotifications; - }); - - this.messagingSystem.publish( - `${controllerName}:notificationsListUpdated`, - this.state.metamaskNotificationsList, - ); - - this.#setIsFetchingMetamaskNotifications(false); - return metamaskNotifications; - } catch (err) { - this.#setIsFetchingMetamaskNotifications(false); - log.error('Failed to fetch notifications', err); - throw new Error('Failed to fetch notifications'); - } - } - - /** - * Marks specified metamask notifications as read. - * - * @param notifications - An array of notifications to be marked as read. Each notification should include its type and read status. - * @returns A promise that resolves when the operation is complete. - */ - public async markMetamaskNotificationsAsRead( - notifications: MarkAsReadNotificationsParam, - ): Promise { - let onchainNotificationIds: string[] = []; - let featureAnnouncementNotificationIds: string[] = []; - - try { - // Filter unread on/off chain notifications - const onChainNotifications = notifications.filter( - (notification) => - notification.type !== TRIGGER_TYPES.FEATURES_ANNOUNCEMENT && - !notification.isRead, - ); - - const featureAnnouncementNotifications = notifications.filter( - (notification) => - notification.type === TRIGGER_TYPES.FEATURES_ANNOUNCEMENT && - !notification.isRead, - ); - - // Mark On-Chain Notifications as Read - if (onChainNotifications.length > 0) { - const bearerToken = await this.#auth.getBearerToken(); - - if (bearerToken) { - onchainNotificationIds = onChainNotifications.map( - (notification) => notification.id, - ); - await OnChainNotifications.markNotificationsAsRead( - bearerToken, - onchainNotificationIds, - ).catch(() => { - onchainNotificationIds = []; - log.warn('Unable to mark onchain notifications as read'); - }); - } - } - - // Mark Off-Chain notifications as Read - if (featureAnnouncementNotifications.length > 0) { - featureAnnouncementNotificationIds = - featureAnnouncementNotifications.map( - (notification) => notification.id, - ); - } - } catch (err) { - log.warn('Something failed when marking notifications as read', err); - } - - // Update the state - this.update((state) => { - const currentReadList = state.metamaskNotificationsReadList; - const newReadIds = [...featureAnnouncementNotificationIds]; - state.metamaskNotificationsReadList = [ - ...new Set([...currentReadList, ...newReadIds]), - ]; - - state.metamaskNotificationsList = state.metamaskNotificationsList.map( - (notification: Notification) => { - if ( - newReadIds.includes(notification.id) || - onchainNotificationIds.includes(notification.id) - ) { - return { ...notification, isRead: true }; - } - return notification; - }, - ); - }); - - // Publish the event - this.messagingSystem.publish( - `${controllerName}:markNotificationsAsRead`, - this.state.metamaskNotificationsList, - ); - } - - /** - * Updates the list of MetaMask notifications by adding a new notification at the beginning of the list. - * This method ensures that the most recent notification is displayed first in the UI. - * - * @param notification - The new notification object to be added to the list. - * @returns A promise that resolves when the notification list has been successfully updated. - */ - public async updateMetamaskNotificationsList( - notification: Notification, - ): Promise { - if ( - this.state.metamaskNotificationsList.some((n) => n.id === notification.id) - ) { - return; - } - - const processedNotification = - processAndFilterSingleNotification(notification); - - if (processedNotification) { - this.update((state) => { - const existingNotificationIds = new Set( - state.metamaskNotificationsList.map((n) => n.id), - ); - // Add the new notification only if its ID is not already present in the list - if (!existingNotificationIds.has(notification.id)) { - state.metamaskNotificationsList = [ - notification, - ...state.metamaskNotificationsList, - ]; - this.messagingSystem.publish( - `${controllerName}:notificationsListUpdated`, - state.metamaskNotificationsList, - ); - } - }); - } - } -} - -const isNotUndefined = (t?: T): t is T => Boolean(t); -function processAndFilterSingleNotification(n: NotificationUnion) { - try { - const processedNotification = processNotification(n); - if (isNotUndefined(processedNotification)) { - return processedNotification; - } - } catch { - return undefined; - } - return undefined; -} diff --git a/app/scripts/controllers/metamask-notifications/mocks/mock-feature-announcements.ts b/app/scripts/controllers/metamask-notifications/mocks/mock-feature-announcements.ts deleted file mode 100644 index 71e2b637e5bb..000000000000 --- a/app/scripts/controllers/metamask-notifications/mocks/mock-feature-announcements.ts +++ /dev/null @@ -1,201 +0,0 @@ -import { ContentfulResult } from '../services/feature-announcements'; -import { FeatureAnnouncementRawNotification } from '../types/feature-announcement/feature-announcement'; -import { TRIGGER_TYPES } from '../constants/notification-schema'; - -export function createMockFeatureAnnouncementAPIResult(): ContentfulResult { - return { - sys: { - type: 'Array', - }, - total: 17, - skip: 0, - limit: 1, - items: [ - { - metadata: { - tags: [], - }, - sys: { - space: { - sys: { - type: 'Link', - linkType: 'Space', - id: 'jdkgyfmyd9sw', - }, - }, - id: '1ABRmHaNCgmxROKXXLXsMu', - type: 'Entry', - createdAt: '2024-04-09T13:24:01.872Z', - updatedAt: '2024-04-09T13:24:01.872Z', - environment: { - sys: { - id: 'master', - type: 'Link', - linkType: 'Environment', - }, - }, - revision: 1, - contentType: { - sys: { - type: 'Link', - linkType: 'ContentType', - id: 'productAnnouncement', - }, - }, - locale: 'en-US', - }, - fields: { - title: 'Don’t miss out on airdrops and new NFT mints!', - id: 'dont-miss-out-on-airdrops-and-new-nft-mints', - category: 'ANNOUNCEMENT', - shortDescription: - 'Check your airdrop eligibility and see trending NFT drops. Head over to the Explore tab to get started. ', - image: { - sys: { - type: 'Link', - linkType: 'Asset', - id: '5jqq8sFeLc6XEoeWlpI3aB', - }, - }, - longDescription: { - data: {}, - content: [ - { - data: {}, - content: [ - { - data: {}, - marks: [], - value: - 'You can now verify if any of your connected addresses are eligible for airdrops and other ERC-20 claims in a secure and convenient way. We’ve also added trending NFT mints based on creators you’ve minted from before or other tokens you hold. Head over to the Explore tab to get started. \n', - nodeType: 'text', - }, - ], - nodeType: 'paragraph', - }, - ], - nodeType: 'document', - }, - link: { - sys: { - type: 'Link', - linkType: 'Entry', - id: '62xKYM2ydo4F1mS5q97K5q', - }, - }, - }, - }, - ], - includes: { - Entry: [ - { - metadata: { - tags: [], - }, - sys: { - space: { - sys: { - type: 'Link', - linkType: 'Space', - id: 'jdkgyfmyd9sw', - }, - }, - id: '62xKYM2ydo4F1mS5q97K5q', - type: 'Entry', - createdAt: '2024-04-09T13:23:03.636Z', - updatedAt: '2024-04-09T13:23:03.636Z', - environment: { - sys: { - id: 'master', - type: 'Link', - linkType: 'Environment', - }, - }, - revision: 1, - contentType: { - sys: { - type: 'Link', - linkType: 'ContentType', - id: 'link', - }, - }, - locale: 'en-US', - }, - fields: { - extensionLinkText: 'Try now', - extensionLinkRoute: 'home.html', - }, - }, - ], - Asset: [ - { - metadata: { - tags: [], - }, - sys: { - space: { - sys: { - type: 'Link', - linkType: 'Space', - id: 'jdkgyfmyd9sw', - }, - }, - id: '5jqq8sFeLc6XEoeWlpI3aB', - type: 'Asset', - createdAt: '2024-04-09T13:23:13.327Z', - updatedAt: '2024-04-09T13:23:13.327Z', - environment: { - sys: { - id: 'master', - type: 'Link', - linkType: 'Environment', - }, - }, - revision: 1, - locale: 'en-US', - }, - fields: { - title: 'PDAPP notification image Airdrops & NFT mints', - description: '', - file: { - url: '//images.ctfassets.net/jdkgyfmyd9sw/5jqq8sFeLc6XEoeWlpI3aB/73ee0f1afa9916c3a7538b0bbee09c26/PDAPP_notification_image_Airdrops___NFT_mints.png', - details: { - size: 797731, - image: { - width: 2880, - height: 1921, - }, - }, - fileName: 'PDAPP notification image_Airdrops & NFT mints.png', - contentType: 'image/png', - }, - }, - }, - ], - }, - } as unknown as ContentfulResult; -} - -export function createMockFeatureAnnouncementRaw(): FeatureAnnouncementRawNotification { - return { - type: TRIGGER_TYPES.FEATURES_ANNOUNCEMENT, - createdAt: '2999-04-09T13:24:01.872Z', - data: { - id: 'dont-miss-out-on-airdrops-and-new-nft-mints', - category: 'ANNOUNCEMENT', - title: 'Don’t miss out on airdrops and new NFT mints!', - longDescription: `

You can now verify if any of your connected addresses are eligible for airdrops and other ERC-20 claims in a secure and convenient way. We’ve also added trending NFT mints based on creators you’ve minted from before or other tokens you hold. Head over to the Explore tab to get started.

`, - shortDescription: - 'Check your airdrop eligibility and see trending NFT drops. Head over to the Explore tab to get started.', - image: { - title: 'PDAPP notification image Airdrops & NFT mints', - description: '', - url: '//images.ctfassets.net/jdkgyfmyd9sw/5jqq8sFeLc6XEoeWlpI3aB/73ee0f1afa9916c3a7538b0bbee09c26/PDAPP_notification_image_Airdrops___NFT_mints.png', - }, - extensionLink: { - extensionLinkText: 'Try now', - extensionLinkRoute: 'home.html', - }, - }, - }; -} diff --git a/app/scripts/controllers/metamask-notifications/mocks/mock-notification-trigger.ts b/app/scripts/controllers/metamask-notifications/mocks/mock-notification-trigger.ts deleted file mode 100644 index d4ce4a61114f..000000000000 --- a/app/scripts/controllers/metamask-notifications/mocks/mock-notification-trigger.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { v4 as uuidv4 } from 'uuid'; -import { NotificationTrigger } from '../utils/utils'; - -export function createMockNotificationTrigger( - override?: Partial, -): NotificationTrigger { - return { - id: uuidv4(), - address: '0xFAKE_ADDRESS', - chainId: '1', - kind: 'eth_sent', - enabled: true, - ...override, - }; -} diff --git a/app/scripts/controllers/metamask-notifications/mocks/mock-notification-user-storage.ts b/app/scripts/controllers/metamask-notifications/mocks/mock-notification-user-storage.ts deleted file mode 100644 index a799a8d35b52..000000000000 --- a/app/scripts/controllers/metamask-notifications/mocks/mock-notification-user-storage.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { TRIGGER_TYPES } from '../constants/notification-schema'; -import { USER_STORAGE_VERSION_KEY } from '../constants/constants'; -import { UserStorage } from '../types/user-storage/user-storage'; -import { initializeUserStorage } from '../utils/utils'; - -export const MOCK_USER_STORAGE_ACCOUNT = - '0x0000000000000000000000000000000000000000'; -export const MOCK_USER_STORAGE_CHAIN = '1'; - -export function createMockUserStorage( - override?: Partial, -): UserStorage { - return { - [USER_STORAGE_VERSION_KEY]: '1', - [MOCK_USER_STORAGE_ACCOUNT]: { - [MOCK_USER_STORAGE_CHAIN]: { - '111-111-111-111': { - k: TRIGGER_TYPES.ERC20_RECEIVED, - e: true, - }, - '222-222-222-222': { - k: TRIGGER_TYPES.ERC20_SENT, - e: true, - }, - }, - }, - ...override, - }; -} - -export function createMockUserStorageWithTriggers( - triggers: string[] | { id: string; e: boolean; k?: TRIGGER_TYPES }[], -): UserStorage { - const userStorage: UserStorage = { - [USER_STORAGE_VERSION_KEY]: '1', - [MOCK_USER_STORAGE_ACCOUNT]: { - [MOCK_USER_STORAGE_CHAIN]: {}, - }, - }; - - // insert triggerIds - triggers.forEach((t) => { - let tId: string; - let e: boolean; - let k: TRIGGER_TYPES; - if (typeof t === 'string') { - tId = t; - e = true; - k = TRIGGER_TYPES.ERC20_RECEIVED; - } else { - tId = t.id; - e = t.e; - k = t.k ?? TRIGGER_TYPES.ERC20_RECEIVED; - } - - userStorage[MOCK_USER_STORAGE_ACCOUNT][MOCK_USER_STORAGE_CHAIN][tId] = { - k, - e, - }; - }); - - return userStorage; -} - -export function createMockFullUserStorage( - props: { triggersEnabled?: boolean; address?: string } = {}, -): UserStorage { - return initializeUserStorage( - [{ address: props.address ?? MOCK_USER_STORAGE_ACCOUNT }], - props.triggersEnabled ?? true, - ); -} diff --git a/app/scripts/controllers/metamask-notifications/mocks/mock-raw-notifications.ts b/app/scripts/controllers/metamask-notifications/mocks/mock-raw-notifications.ts deleted file mode 100644 index f78b4a53a21f..000000000000 --- a/app/scripts/controllers/metamask-notifications/mocks/mock-raw-notifications.ts +++ /dev/null @@ -1,609 +0,0 @@ -import { TRIGGER_TYPES } from '../constants/notification-schema'; -import type { OnChainRawNotification } from '../types/on-chain-notification/on-chain-notification'; - -export function createMockNotificationEthSent() { - const mockNotification: OnChainRawNotification = { - type: TRIGGER_TYPES.ETH_SENT, - id: '3fa85f64-5717-4562-b3fc-2c963f66afa6', - trigger_id: '3fa85f64-5717-4562-b3fc-2c963f66afa6', - chain_id: 1, - block_number: 17485840, - block_timestamp: '2022-03-01T00:00:00Z', - tx_hash: '0x881D40237659C251811CEC9c364ef91dC08D300C', - unread: true, - created_at: '2022-03-01T00:00:00Z', - address: '0x881D40237659C251811CEC9c364ef91dC08D300C', - data: { - kind: 'eth_sent', - network_fee: { - gas_price: '207806259583', - native_token_price_in_usd: '0.83', - }, - from: '0x881D40237659C251811CEC9c364ef91dC08D300C', - to: '0x881D40237659C251811CEC9c364ef91dC08D300D', - amount: { - usd: '670.64', - eth: '0.005', - }, - }, - }; - - return mockNotification; -} - -export function createMockNotificationEthReceived() { - const mockNotification: OnChainRawNotification = { - type: TRIGGER_TYPES.ETH_RECEIVED, - id: '3fa85f64-5717-4562-b3fc-2c963f66afa6', - trigger_id: '3fa85f64-5717-4562-b3fc-2c963f66afa6', - chain_id: 1, - block_number: 17485840, - block_timestamp: '2022-03-01T00:00:00Z', - tx_hash: '0x881D40237659C251811CEC9c364ef91dC08D300C', - unread: true, - created_at: '2022-03-01T00:00:00Z', - address: '0x881D40237659C251811CEC9c364ef91dC08D300C', - data: { - kind: 'eth_received', - network_fee: { - gas_price: '207806259583', - native_token_price_in_usd: '0.83', - }, - from: '0x881D40237659C251811CEC9c364ef91dC08D300C', - to: '0x881D40237659C251811CEC9c364ef91dC08D300D', - amount: { - usd: '670.64', - eth: '808.000000000000000000', - }, - }, - }; - - return mockNotification; -} - -export function createMockNotificationERC20Sent() { - const mockNotification: OnChainRawNotification = { - type: TRIGGER_TYPES.ERC20_SENT, - id: '3fa85f64-5717-4562-b3fc-2c963f66afa6', - trigger_id: '3fa85f64-5717-4562-b3fc-2c963f66afa6', - chain_id: 1, - block_number: 17485840, - block_timestamp: '2022-03-01T00:00:00Z', - tx_hash: '0x881D40237659C251811CEC9c364ef91dC08D300C', - unread: true, - created_at: '2022-03-01T00:00:00Z', - address: '0x881D40237659C251811CEC9c364ef91dC08D300C', - data: { - kind: 'erc20_sent', - network_fee: { - gas_price: '207806259583', - native_token_price_in_usd: '0.83', - }, - to: '0xecc19e177d24551aa7ed6bc6fe566eca726cc8a9', - from: '0x1231deb6f5749ef6ce6943a275a1d3e7486f4eae', - token: { - usd: '1.00', - name: 'USDC', - image: - 'https://raw.githubusercontent.com/MetaMask/contract-metadata/master/images/usdc.svg', - amount: '4956250000', - symbol: 'USDC', - address: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', - decimals: '6', - }, - }, - }; - - return mockNotification; -} - -export function createMockNotificationERC20Received() { - const mockNotification: OnChainRawNotification = { - type: TRIGGER_TYPES.ERC20_RECEIVED, - id: '3fa85f64-5717-4562-b3fc-2c963f66afa6', - trigger_id: '3fa85f64-5717-4562-b3fc-2c963f66afa6', - chain_id: 1, - block_number: 17485840, - block_timestamp: '2022-03-01T00:00:00Z', - tx_hash: '0x881D40237659C251811CEC9c364ef91dC08D300C', - unread: true, - created_at: '2022-03-01T00:00:00Z', - address: '0x881D40237659C251811CEC9c364ef91dC08D300C', - data: { - kind: 'erc20_received', - network_fee: { - gas_price: '207806259583', - native_token_price_in_usd: '0.83', - }, - to: '0xeae7380dd4cef6fbd1144f49e4d1e6964258a4f4', - from: '0x51c72848c68a965f66fa7a88855f9f7784502a7f', - token: { - usd: '0.00', - name: 'SHIBA INU', - image: - 'https://raw.githubusercontent.com/MetaMask/contract-metadata/master/images/shib.svg', - amount: '8382798736999999457296646144', - symbol: 'SHIB', - address: '0x95ad61b0a150d79219dcf64e1e6cc01f0b64c4ce', - decimals: '18', - }, - }, - }; - - return mockNotification; -} - -export function createMockNotificationERC721Sent() { - const mockNotification: OnChainRawNotification = { - type: TRIGGER_TYPES.ERC721_SENT, - block_number: 18576643, - block_timestamp: '1700043467', - chain_id: 1, - created_at: '2023-11-15T11:08:17.895407Z', - address: '0x881D40237659C251811CEC9c364ef91dC08D300C', - data: { - to: '0xf47f628fe3bd2595e9ab384bfffc3859b448e451', - nft: { - // This is not a hex color - // eslint-disable-next-line @metamask/design-tokens/color-no-hex - name: 'Captainz #8680', - image: - 'https://i.seadn.io/s/raw/files/ae0fc06714ff7fb40217340d8a242c0e.gif?w=500&auto=format', - token_id: '8680', - collection: { - name: 'The Captainz', - image: - 'https://i.seadn.io/gcs/files/6df4d75778066bce740050615bc84e21.png?w=500&auto=format', - symbol: 'Captainz', - address: '0x769272677fab02575e84945f03eca517acc544cc', - }, - }, - from: '0x24a0bb54b7e7a8e406e9b28058a9fd6c49e6df4f', - kind: 'erc721_sent', - network_fee: { - gas_price: '24550653274', - native_token_price_in_usd: '1986.61', - }, - }, - id: 'a4193058-9814-537e-9df4-79dcac727fb6', - trigger_id: '028485be-b994-422b-a93b-03fcc01ab715', - tx_hash: - '0x0833c69fb41cf972a0f031fceca242939bc3fcf82b964b74606649abcad371bd', - unread: true, - }; - - return mockNotification; -} - -export function createMockNotificationERC721Received() { - const mockNotification: OnChainRawNotification = { - type: TRIGGER_TYPES.ERC721_RECEIVED, - block_number: 18571446, - block_timestamp: '1699980623', - chain_id: 1, - created_at: '2023-11-14T17:40:52.319281Z', - address: '0x881D40237659C251811CEC9c364ef91dC08D300C', - data: { - to: '0xba7f3daa8adfdad686574406ab9bd5d2f0a49d2e', - nft: { - // This is not a hex color - // eslint-disable-next-line @metamask/design-tokens/color-no-hex - name: 'The Plague #2722', - image: - 'https://i.seadn.io/s/raw/files/a96f90ec8ebf55a2300c66a0c46d6a16.png?w=500&auto=format', - token_id: '2722', - collection: { - name: 'The Plague NFT', - image: - 'https://i.seadn.io/gcs/files/4577987a5ca45ca5118b2e31559ee4d1.jpg?w=500&auto=format', - symbol: 'FROG', - address: '0xc379e535caff250a01caa6c3724ed1359fe5c29b', - }, - }, - from: '0x24a0bb54b7e7a8e406e9b28058a9fd6c49e6df4f', - kind: 'erc721_received', - network_fee: { - gas_price: '53701898538', - native_token_price_in_usd: '2047.01', - }, - }, - id: '00a79d24-befa-57ed-a55a-9eb8696e1654', - trigger_id: 'd24ac26a-8579-49ec-9947-d04d63592ebd', - tx_hash: - '0xe554c9e29e6eeca8ba94da4d047334ba08b8eb9ca3b801dd69cec08dfdd4ae43', - unread: true, - }; - - return mockNotification; -} - -export function createMockNotificationERC1155Sent() { - const mockNotification: OnChainRawNotification = { - type: TRIGGER_TYPES.ERC1155_SENT, - block_number: 18615206, - block_timestamp: '1700510003', - chain_id: 1, - created_at: '2023-11-20T20:44:10.110706Z', - address: '0x881D40237659C251811CEC9c364ef91dC08D300C', - data: { - to: '0x15bd77ccacf2da39b84f0c31fee2e451225bb190', - nft: { - name: 'IlluminatiNFT DAO', - image: - 'https://i.seadn.io/gcs/files/79a77cb37c7b2f1069f752645d29fea7.jpg?w=500&auto=format', - token_id: '1', - collection: { - name: 'IlluminatiNFT DAO', - image: - 'https://i.seadn.io/gae/LTKz3om2eCQfn3M6PkqEmY7KhLtdMCOm0QVch2318KJq7-KyToCH7NBTMo4UuJ0AZI-oaBh1HcgrAEIEWYbXY3uMcYpuGXunaXEh?w=500&auto=format', - symbol: 'TRUTH', - address: '0xe25f0fe686477f9df3c2876c4902d3b85f75f33a', - }, - }, - from: '0x0000000000000000000000000000000000000000', - kind: 'erc1155_sent', - network_fee: { - gas_price: '33571446596', - native_token_price_in_usd: '2038.88', - }, - }, - id: 'a09ff9d1-623a-52ab-a3d4-c7c8c9a58362', - trigger_id: 'e2130f7d-78b8-4c34-999a-3f3d3bb5b03c', - tx_hash: - '0x03381aba290facbaf71c123e263c8dc3dd550aac00ef589cce395182eaeff76f', - unread: true, - }; - - return mockNotification; -} - -export function createMockNotificationERC1155Received() { - const mockNotification: OnChainRawNotification = { - type: TRIGGER_TYPES.ERC1155_RECEIVED, - block_number: 18615206, - block_timestamp: '1700510003', - chain_id: 1, - created_at: '2023-11-20T20:44:10.110706Z', - address: '0x881D40237659C251811CEC9c364ef91dC08D300C', - data: { - to: '0x15bd77ccacf2da39b84f0c31fee2e451225bb190', - nft: { - name: 'IlluminatiNFT DAO', - image: - 'https://i.seadn.io/gcs/files/79a77cb37c7b2f1069f752645d29fea7.jpg?w=500&auto=format', - token_id: '1', - collection: { - name: 'IlluminatiNFT DAO', - image: - 'https://i.seadn.io/gae/LTKz3om2eCQfn3M6PkqEmY7KhLtdMCOm0QVch2318KJq7-KyToCH7NBTMo4UuJ0AZI-oaBh1HcgrAEIEWYbXY3uMcYpuGXunaXEh?w=500&auto=format', - symbol: 'TRUTH', - address: '0xe25f0fe686477f9df3c2876c4902d3b85f75f33a', - }, - }, - from: '0x0000000000000000000000000000000000000000', - kind: 'erc1155_received', - network_fee: { - gas_price: '33571446596', - native_token_price_in_usd: '2038.88', - }, - }, - id: 'b6b93c84-e8dc-54ed-9396-7ea50474843a', - trigger_id: '710c8abb-43a9-42a5-9d86-9dd258726c82', - tx_hash: - '0x03381aba290facbaf71c123e263c8dc3dd550aac00ef589cce395182eaeff76f', - unread: true, - }; - - return mockNotification; -} - -export function createMockNotificationMetaMaskSwapsCompleted() { - const mockNotification: OnChainRawNotification = { - type: TRIGGER_TYPES.METAMASK_SWAP_COMPLETED, - block_number: 18377666, - block_timestamp: '1697637275', - chain_id: 1, - created_at: '2023-10-18T13:58:49.854596Z', - address: '0x881D40237659C251811CEC9c364ef91dC08D300C', - data: { - kind: 'metamask_swap_completed', - rate: '1558.27', - token_in: { - usd: '1576.73', - image: - 'https://token.api.cx.metamask.io/assets/nativeCurrencyLogos/ethereum.svg', - amount: '9000000000000000', - symbol: 'ETH', - address: '0x0000000000000000000000000000000000000000', - decimals: '18', - name: 'Ethereum', - }, - token_out: { - usd: '1.00', - image: - 'https://raw.githubusercontent.com/MetaMask/contract-metadata/master/images/usdt.svg', - amount: '14024419', - symbol: 'USDT', - address: '0xdac17f958d2ee523a2206206994597c13d831ec7', - decimals: '6', - name: 'USDT', - }, - network_fee: { - gas_price: '15406129273', - native_token_price_in_usd: '1576.73', - }, - }, - id: '7ddfe6a1-ac52-5ffe-aa40-f04242db4b8b', - trigger_id: 'd2eaa2eb-2e6e-4fd5-8763-b70ea571b46c', - tx_hash: - '0xf69074290f3aa11bce567aabc9ca0df7a12559dfae1b80ba1a124e9dfe19ecc5', - unread: true, - }; - - return mockNotification; -} - -export function createMockNotificationRocketPoolStakeCompleted() { - const mockNotification: OnChainRawNotification = { - type: TRIGGER_TYPES.ROCKETPOOL_STAKE_COMPLETED, - block_number: 18585057, - block_timestamp: '1700145059', - chain_id: 1, - created_at: '2023-11-20T12:02:48.796824Z', - address: '0x881D40237659C251811CEC9c364ef91dC08D300C', - data: { - kind: 'rocketpool_stake_completed', - stake_in: { - usd: '2031.86', - name: 'Ethereum', - image: - 'https://token.api.cx.metamask.io/assets/nativeCurrencyLogos/ethereum.svg', - amount: '190690478063438272', - symbol: 'ETH', - address: '0x0000000000000000000000000000000000000000', - decimals: '18', - }, - stake_out: { - usd: '2226.49', - name: 'Rocket Pool ETH', - image: - 'https://raw.githubusercontent.com/MetaMask/contract-metadata/master/images/rETH.svg', - amount: '175024360778165879', - symbol: 'RETH', - address: '0xae78736Cd615f374D3085123A210448E74Fc6393', - decimals: '18', - }, - network_fee: { - gas_price: '36000000000', - native_token_price_in_usd: '2031.86', - }, - }, - id: 'c2a2f225-b2fb-5d6c-ba56-e27a5c71ffb9', - trigger_id: '5110ff97-acff-40c0-83b4-11d487b8c7b0', - tx_hash: - '0xcfc0693bf47995907b0f46ef0644cf16dd9a0de797099b2e00fd481e1b2117d3', - unread: true, - }; - - return mockNotification; -} - -export function createMockNotificationRocketPoolUnStakeCompleted() { - const mockNotification: OnChainRawNotification = { - type: TRIGGER_TYPES.ROCKETPOOL_UNSTAKE_COMPLETED, - block_number: 18384336, - block_timestamp: '1697718011', - chain_id: 1, - created_at: '2023-10-19T13:11:10.623042Z', - address: '0x881D40237659C251811CEC9c364ef91dC08D300C', - data: { - kind: 'rocketpool_unstake_completed', - stake_in: { - usd: '1686.34', - image: - 'https://raw.githubusercontent.com/MetaMask/contract-metadata/master/images/rETH.svg', - amount: '66608041413696770', - symbol: 'RETH', - address: '0xae78736Cd615f374D3085123A210448E74Fc6393', - decimals: '18', - name: 'Rocketpool Eth', - }, - stake_out: { - usd: '1553.75', - image: - 'https://token.api.cx.metamask.io/assets/nativeCurrencyLogos/ethereum.svg', - amount: '72387843427700824', - symbol: 'ETH', - address: '0x0000000000000000000000000000000000000000', - decimals: '18', - name: 'Ethereum', - }, - network_fee: { - gas_price: '5656322987', - native_token_price_in_usd: '1553.75', - }, - }, - id: 'd8c246e7-a0a4-5f1d-b079-2b1707665fbc', - trigger_id: '291ec897-f569-4837-b6c0-21001b198dff', - tx_hash: - '0xc7972a7e409abfc62590ec90e633acd70b9b74e76ad02305be8bf133a0e22d5f', - unread: true, - }; - - return mockNotification; -} - -export function createMockNotificationLidoStakeCompleted() { - const mockNotification: OnChainRawNotification = { - type: TRIGGER_TYPES.LIDO_STAKE_COMPLETED, - block_number: 18487118, - block_timestamp: '1698961091', - chain_id: 1, - created_at: '2023-11-02T22:28:49.970865Z', - address: '0x881D40237659C251811CEC9c364ef91dC08D300C', - data: { - kind: 'lido_stake_completed', - stake_in: { - usd: '1806.33', - name: 'Ethereum', - image: - 'https://token.api.cx.metamask.io/assets/nativeCurrencyLogos/ethereum.svg', - amount: '330303634023928032', - symbol: 'ETH', - address: '0x0000000000000000000000000000000000000000', - decimals: '18', - }, - stake_out: { - usd: '1801.30', - name: 'Liquid staked Ether 2.0', - image: - 'https://raw.githubusercontent.com/MetaMask/contract-metadata/master/images/stETH.svg', - amount: '330303634023928032', - symbol: 'STETH', - address: '0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84', - decimals: '18', - }, - network_fee: { - gas_price: '26536359866', - native_token_price_in_usd: '1806.33', - }, - }, - id: '9d9b1467-b3ee-5492-8ca2-22382657b690', - trigger_id: 'ec10d66a-f78f-461f-83c9-609aada8cc50', - tx_hash: - '0x8cc0fa805f7c3b1743b14f3b91c6b824113b094f26d4ccaf6a71ad8547ce6a0f', - unread: true, - }; - - return mockNotification; -} - -export function createMockNotificationLidoWithdrawalRequested() { - const mockNotification: OnChainRawNotification = { - type: TRIGGER_TYPES.LIDO_WITHDRAWAL_REQUESTED, - block_number: 18377760, - block_timestamp: '1697638415', - chain_id: 1, - created_at: '2023-10-18T15:04:02.482526Z', - address: '0x881D40237659C251811CEC9c364ef91dC08D300C', - data: { - kind: 'lido_withdrawal_requested', - stake_in: { - usd: '1568.54', - image: - 'https://raw.githubusercontent.com/MetaMask/contract-metadata/master/images/stETH.svg', - amount: '97180668792218669859', - symbol: 'STETH', - address: '0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84', - decimals: '18', - name: 'Staked Eth', - }, - stake_out: { - usd: '1576.73', - image: - 'https://token.api.cx.metamask.io/assets/nativeCurrencyLogos/ethereum.svg', - amount: '97180668792218669859', - symbol: 'ETH', - address: '0x0000000000000000000000000000000000000000', - decimals: '18', - name: 'Ethereum', - }, - network_fee: { - gas_price: '11658906980', - native_token_price_in_usd: '1576.73', - }, - }, - id: '29ddc718-78c6-5f91-936f-2bef13a605f0', - trigger_id: 'ef003925-3379-4ba7-9e2d-8218690cadc8', - tx_hash: - '0x58b5f82e084cb750ea174e02b20fbdfd2ba8d78053deac787f34fc38e5d427aa', - unread: true, - }; - - return mockNotification; -} - -export function createMockNotificationLidoWithdrawalCompleted() { - const mockNotification: OnChainRawNotification = { - type: TRIGGER_TYPES.LIDO_WITHDRAWAL_COMPLETED, - block_number: 18378208, - block_timestamp: '1697643851', - chain_id: 1, - created_at: '2023-10-18T16:35:03.147606Z', - address: '0x881D40237659C251811CEC9c364ef91dC08D300C', - data: { - kind: 'lido_withdrawal_completed', - stake_in: { - usd: '1570.23', - image: - 'https://raw.githubusercontent.com/MetaMask/contract-metadata/master/images/stETH.svg', - amount: '35081997661451346', - symbol: 'STETH', - address: '0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84', - decimals: '18', - name: 'Staked Eth', - }, - stake_out: { - usd: '1571.74', - image: - 'https://token.api.cx.metamask.io/assets/nativeCurrencyLogos/ethereum.svg', - amount: '35081997661451346', - symbol: 'ETH', - address: '0x0000000000000000000000000000000000000000', - decimals: '18', - name: 'Ethereum', - }, - network_fee: { - gas_price: '12699495150', - native_token_price_in_usd: '1571.74', - }, - }, - id: 'f4ef0b7f-5612-537f-9144-0b5c63ae5391', - trigger_id: 'd73df14d-ce73-4f38-bad3-ab028154042c', - tx_hash: - '0xe6d210d2e601ef3dd1075c48e71452cf35f2daae3886911e964e3babad8ac657', - unread: true, - }; - - return mockNotification; -} - -export function createMockNotificationLidoReadyToBeWithdrawn() { - const mockNotification: OnChainRawNotification = { - type: TRIGGER_TYPES.LIDO_STAKE_READY_TO_BE_WITHDRAWN, - block_number: 18378208, - block_timestamp: '1697643851', - chain_id: 1, - created_at: '2023-10-18T16:35:03.147606Z', - address: '0x881D40237659C251811CEC9c364ef91dC08D300C', - data: { - kind: 'lido_stake_ready_to_be_withdrawn', - request_id: '123456789', - staked_eth: { - address: '0x881D40237659C251811CEC9c364ef91dC08D300F', - symbol: 'ETH', - name: 'Ethereum', - amount: '2.5', - decimals: '18', - image: - 'https://token.api.cx.metamask.io/assets/nativeCurrencyLogos/ethereum.svg', - usd: '10000.00', - }, - }, - id: 'f4ef0b7f-5612-537f-9144-0b5c63ae5391', - trigger_id: 'd73df14d-ce73-4f38-bad3-ab028154042c', - tx_hash: - '0xe6d210d2e601ef3dd1075c48e71452cf35f2daae3886911e964e3babad8ac657', - unread: true, - }; - - return mockNotification; -} - -export function createMockRawOnChainNotifications(): OnChainRawNotification[] { - return [1, 2, 3].map((id) => { - const notification = createMockNotificationEthSent(); - notification.id += `-${id}`; - return notification; - }); -} diff --git a/app/scripts/controllers/metamask-notifications/mocks/mockResponses.ts b/app/scripts/controllers/metamask-notifications/mocks/mockResponses.ts deleted file mode 100644 index 940646c10640..000000000000 --- a/app/scripts/controllers/metamask-notifications/mocks/mockResponses.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { FEATURE_ANNOUNCEMENT_API } from '../services/feature-announcements'; -import { - NOTIFICATION_API_LIST_ENDPOINT, - NOTIFICATION_API_MARK_ALL_AS_READ_ENDPOINT, - TRIGGER_API_BATCH_ENDPOINT, -} from '../services/onchain-notifications'; -import { createMockFeatureAnnouncementAPIResult } from './mock-feature-announcements'; -import { createMockRawOnChainNotifications } from './mock-raw-notifications'; - -type MockResponse = { - url: string; - requestMethod: 'GET' | 'POST' | 'PUT' | 'DELETE'; - response: unknown; -}; - -export const CONTENTFUL_RESPONSE = createMockFeatureAnnouncementAPIResult(); - -export function getMockFeatureAnnouncementResponse() { - return { - url: FEATURE_ANNOUNCEMENT_API, - requestMethod: 'GET', - response: CONTENTFUL_RESPONSE, - } satisfies MockResponse; -} - -export function getMockBatchCreateTriggersResponse() { - return { - url: TRIGGER_API_BATCH_ENDPOINT, - requestMethod: 'POST', - response: null, - } satisfies MockResponse; -} - -export function getMockBatchDeleteTriggersResponse() { - return { - url: TRIGGER_API_BATCH_ENDPOINT, - requestMethod: 'DELETE', - response: null, - } satisfies MockResponse; -} - -export const MOCK_RAW_ON_CHAIN_NOTIFICATIONS = - createMockRawOnChainNotifications(); - -export function getMockListNotificationsResponse() { - return { - url: NOTIFICATION_API_LIST_ENDPOINT, - requestMethod: 'POST', - response: MOCK_RAW_ON_CHAIN_NOTIFICATIONS, - } satisfies MockResponse; -} - -export function getMockMarkNotificationsAsReadResponse() { - return { - url: NOTIFICATION_API_MARK_ALL_AS_READ_ENDPOINT, - requestMethod: 'POST', - response: null, - } satisfies MockResponse; -} diff --git a/app/scripts/controllers/metamask-notifications/mocks/mockServices.ts b/app/scripts/controllers/metamask-notifications/mocks/mockServices.ts deleted file mode 100644 index 84de50ec8555..000000000000 --- a/app/scripts/controllers/metamask-notifications/mocks/mockServices.ts +++ /dev/null @@ -1,71 +0,0 @@ -import nock from 'nock'; -import { - getMockBatchCreateTriggersResponse, - getMockBatchDeleteTriggersResponse, - getMockFeatureAnnouncementResponse, - getMockListNotificationsResponse, - getMockMarkNotificationsAsReadResponse, -} from './mockResponses'; - -type MockReply = { - status: nock.StatusCode; - body?: nock.Body; -}; - -export function mockFetchFeatureAnnouncementNotifications( - mockReply?: MockReply, -) { - const mockResponse = getMockFeatureAnnouncementResponse(); - const reply = mockReply ?? { status: 200, body: mockResponse.response }; - const mockEndpoint = nock(mockResponse.url) - .get('') - .query(true) - .reply(reply.status, reply.body); - - return mockEndpoint; -} - -export function mockBatchCreateTriggers(mockReply?: MockReply) { - const mockResponse = getMockBatchCreateTriggersResponse(); - const reply = mockReply ?? { status: 204 }; - - const mockEndpoint = nock(mockResponse.url) - .post('') - .reply(reply.status, reply.body); - - return mockEndpoint; -} - -export function mockBatchDeleteTriggers(mockReply?: MockReply) { - const mockResponse = getMockBatchDeleteTriggersResponse(); - const reply = mockReply ?? { status: 204 }; - - const mockEndpoint = nock(mockResponse.url) - .delete('') - .reply(reply.status, reply.body); - - return mockEndpoint; -} - -export function mockListNotifications(mockReply?: MockReply) { - const mockResponse = getMockListNotificationsResponse(); - const reply = mockReply ?? { status: 200, body: mockResponse.response }; - - const mockEndpoint = nock(mockResponse.url) - .post('') - .query(true) - .reply(reply.status, reply.body); - - return mockEndpoint; -} - -export function mockMarkNotificationsAsRead(mockReply?: MockReply) { - const mockResponse = getMockMarkNotificationsAsReadResponse(); - const reply = mockReply ?? { status: 200 }; - - const mockEndpoint = nock(mockResponse.url) - .post('') - .reply(reply.status, reply.body); - - return mockEndpoint; -} diff --git a/app/scripts/controllers/metamask-notifications/processors/process-feature-announcement.test.ts b/app/scripts/controllers/metamask-notifications/processors/process-feature-announcement.test.ts deleted file mode 100644 index 8785ad47197a..000000000000 --- a/app/scripts/controllers/metamask-notifications/processors/process-feature-announcement.test.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { TRIGGER_TYPES } from '../constants/notification-schema'; -import { createMockFeatureAnnouncementRaw } from '../mocks/mock-feature-announcements'; -import { - isFeatureAnnouncementRead, - processFeatureAnnouncement, -} from './process-feature-announcement'; - -describe('process-feature-announcement - isFeatureAnnouncementRead()', () => { - const MOCK_NOTIFICATION_ID = 'MOCK_NOTIFICATION_ID'; - - test('Returns true if a given notificationId is within list of read platform notifications', () => { - const notification = { - id: MOCK_NOTIFICATION_ID, - createdAt: new Date().toString(), - }; - - const result1 = isFeatureAnnouncementRead(notification, [ - 'id-1', - 'id-2', - MOCK_NOTIFICATION_ID, - ]); - expect(result1).toBe(true); - - const result2 = isFeatureAnnouncementRead(notification, ['id-1', 'id-2']); - expect(result2).toBe(false); - }); - - test('Returns isRead if notification is older than 90 days', () => { - const mockDate = new Date(); - mockDate.setDate(mockDate.getDate() - 100); - - const notification = { - id: MOCK_NOTIFICATION_ID, - createdAt: mockDate.toString(), - }; - - const result = isFeatureAnnouncementRead(notification, []); - expect(result).toBe(true); - }); -}); - -describe('process-feature-announcement - processFeatureAnnouncement()', () => { - test('Processes a Raw Feature Announcement to a shared Notification Type', () => { - const rawNotification = createMockFeatureAnnouncementRaw(); - const result = processFeatureAnnouncement(rawNotification); - - expect(result.id).toBe(rawNotification.data.id); - expect(result.type).toBe(TRIGGER_TYPES.FEATURES_ANNOUNCEMENT); - expect(result.isRead).toBe(false); - expect(result.data).toBeDefined(); - }); -}); diff --git a/app/scripts/controllers/metamask-notifications/processors/process-feature-announcement.ts b/app/scripts/controllers/metamask-notifications/processors/process-feature-announcement.ts deleted file mode 100644 index 260cd8b33a60..000000000000 --- a/app/scripts/controllers/metamask-notifications/processors/process-feature-announcement.ts +++ /dev/null @@ -1,32 +0,0 @@ -import type { FeatureAnnouncementRawNotification } from '../types/feature-announcement/feature-announcement'; -import type { Notification } from '../types/notification/notification'; - -const ONE_DAY_MS = 1000 * 60 * 60 * 24; - -function shouldAutoExpire(oldDate: Date) { - const differenceInTime = Date.now() - oldDate.getTime(); - const differenceInDays = differenceInTime / ONE_DAY_MS; - return differenceInDays >= 90; -} - -export function isFeatureAnnouncementRead( - notification: Pick, - readPlatformNotificationsList: string[], -): boolean { - if (readPlatformNotificationsList.includes(notification.id)) { - return true; - } - return shouldAutoExpire(new Date(notification.createdAt)); -} - -export function processFeatureAnnouncement( - notification: FeatureAnnouncementRawNotification, -): Notification { - return { - type: notification.type, - id: notification.data.id, - createdAt: new Date(notification.createdAt).toISOString(), - data: notification.data, - isRead: false, - }; -} diff --git a/app/scripts/controllers/metamask-notifications/processors/process-notifications.test.ts b/app/scripts/controllers/metamask-notifications/processors/process-notifications.test.ts deleted file mode 100644 index c1ed0c03b67e..000000000000 --- a/app/scripts/controllers/metamask-notifications/processors/process-notifications.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { TRIGGER_TYPES } from '../constants/notification-schema'; -import { createMockFeatureAnnouncementRaw } from '../mocks/mock-feature-announcements'; -import { createMockNotificationEthSent } from '../mocks/mock-raw-notifications'; -import { processNotification } from './process-notifications'; - -describe('process-notifications - processNotification()', () => { - // More thorough tests are found in the specific process - test('Maps Feature Announcement to shared Notification Type', () => { - const result = processNotification(createMockFeatureAnnouncementRaw()); - expect(result).toBeDefined(); - }); - - // More thorough tests are found in the specific process - test('Maps On Chain Notification to shared Notification Type', () => { - const result = processNotification(createMockNotificationEthSent()); - expect(result).toBeDefined(); - }); - - test('Throws on invalid notification to process', () => { - const rawNotification = createMockNotificationEthSent(); - - // Testing Mock with invalid notification type - rawNotification.type = 'FAKE_NOTIFICATION_TYPE' as TRIGGER_TYPES.ETH_SENT; - - expect(() => processNotification(rawNotification)).toThrow(); - }); -}); diff --git a/app/scripts/controllers/metamask-notifications/processors/process-notifications.ts b/app/scripts/controllers/metamask-notifications/processors/process-notifications.ts deleted file mode 100644 index ee09cd232e83..000000000000 --- a/app/scripts/controllers/metamask-notifications/processors/process-notifications.ts +++ /dev/null @@ -1,44 +0,0 @@ -import type { Notification } from '../types/notification/notification'; -import type { OnChainRawNotification } from '../types/on-chain-notification/on-chain-notification'; -import type { FeatureAnnouncementRawNotification } from '../types/feature-announcement/feature-announcement'; -import { TRIGGER_TYPES } from '../constants/notification-schema'; -import type { NotificationUnion } from '../types/types'; -import { processOnChainNotification } from './process-onchain-notifications'; -import { - isFeatureAnnouncementRead, - processFeatureAnnouncement, -} from './process-feature-announcement'; - -const isOnChainNotification = ( - n: NotificationUnion, -): n is OnChainRawNotification => Object.values(TRIGGER_TYPES).includes(n.type); - -const isFeatureAnnouncement = ( - n: NotificationUnion, -): n is FeatureAnnouncementRawNotification => - n.type === TRIGGER_TYPES.FEATURES_ANNOUNCEMENT; - -export function processNotification( - notification: NotificationUnion, - readNotifications: string[] = [], -): Notification { - const exhaustedAllCases = (_: never) => { - throw new Error( - `No processor found for notification kind ${notification.type}`, - ); - }; - - if (isFeatureAnnouncement(notification)) { - const n = processFeatureAnnouncement( - notification as FeatureAnnouncementRawNotification, - ); - n.isRead = isFeatureAnnouncementRead(n, readNotifications); - return n; - } - - if (isOnChainNotification(notification)) { - return processOnChainNotification(notification as OnChainRawNotification); - } - - return exhaustedAllCases(notification as never); -} diff --git a/app/scripts/controllers/metamask-notifications/processors/process-onchain-notifications.test.ts b/app/scripts/controllers/metamask-notifications/processors/process-onchain-notifications.test.ts deleted file mode 100644 index 8c703c0bedda..000000000000 --- a/app/scripts/controllers/metamask-notifications/processors/process-onchain-notifications.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { - createMockNotificationEthSent, - createMockNotificationEthReceived, - createMockNotificationERC20Sent, - createMockNotificationERC20Received, - createMockNotificationERC721Sent, - createMockNotificationERC721Received, - createMockNotificationERC1155Sent, - createMockNotificationERC1155Received, - createMockNotificationMetaMaskSwapsCompleted, - createMockNotificationRocketPoolStakeCompleted, - createMockNotificationRocketPoolUnStakeCompleted, - createMockNotificationLidoStakeCompleted, - createMockNotificationLidoWithdrawalRequested, - createMockNotificationLidoWithdrawalCompleted, - createMockNotificationLidoReadyToBeWithdrawn, -} from '../mocks/mock-raw-notifications'; -import { OnChainRawNotification } from '../types/on-chain-notification/on-chain-notification'; -import { processOnChainNotification } from './process-onchain-notifications'; - -const rawNotifications = [ - createMockNotificationEthSent(), - createMockNotificationEthReceived(), - createMockNotificationERC20Sent(), - createMockNotificationERC20Received(), - createMockNotificationERC721Sent(), - createMockNotificationERC721Received(), - createMockNotificationERC1155Sent(), - createMockNotificationERC1155Received(), - createMockNotificationMetaMaskSwapsCompleted(), - createMockNotificationRocketPoolStakeCompleted(), - createMockNotificationRocketPoolUnStakeCompleted(), - createMockNotificationLidoStakeCompleted(), - createMockNotificationLidoWithdrawalRequested(), - createMockNotificationLidoWithdrawalCompleted(), - createMockNotificationLidoReadyToBeWithdrawn(), -]; - -const rawNotificationTestSuite = rawNotifications.map( - (n): [string, OnChainRawNotification] => [n.type, n], -); - -describe('process-onchain-notifications - processOnChainNotification()', () => { - // @ts-expect-error This is missing from the Mocha type definitions - test.each(rawNotificationTestSuite)( - 'Converts Raw On-Chain Notification (%s) to a shared Notification Type', - (_: string, rawNotification: OnChainRawNotification) => { - const result = processOnChainNotification(rawNotification); - expect(result.id).toBe(rawNotification.id); - expect(result.type).toBe(rawNotification.type); - }, - ); -}); diff --git a/app/scripts/controllers/metamask-notifications/processors/process-onchain-notifications.ts b/app/scripts/controllers/metamask-notifications/processors/process-onchain-notifications.ts deleted file mode 100644 index 793bfb1b1f6f..000000000000 --- a/app/scripts/controllers/metamask-notifications/processors/process-onchain-notifications.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { OnChainRawNotification } from '../types/on-chain-notification/on-chain-notification'; -import type { Notification } from '../types/notification/notification'; - -export function processOnChainNotification( - notification: OnChainRawNotification, -): Notification { - return { - ...notification, - id: notification.id, - createdAt: new Date(notification.created_at).toISOString(), - isRead: !notification.unread, - }; -} diff --git a/app/scripts/controllers/metamask-notifications/services/feature-announcements.test.ts b/app/scripts/controllers/metamask-notifications/services/feature-announcements.test.ts deleted file mode 100644 index d8a178d065c5..000000000000 --- a/app/scripts/controllers/metamask-notifications/services/feature-announcements.test.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { TRIGGER_TYPES } from '../constants/notification-schema'; -import { createMockFeatureAnnouncementAPIResult } from '../mocks/mock-feature-announcements'; -import { mockFetchFeatureAnnouncementNotifications } from '../mocks/mockServices'; -import { getFeatureAnnouncementNotifications } from './feature-announcements'; - -jest.mock('@contentful/rich-text-html-renderer', () => ({ - documentToHtmlString: jest - .fn() - .mockImplementation((richText) => `

${richText}

`), -})); - -describe('Feature Announcement Notifications', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - it('should return an empty array if fetch fails', async () => { - const mockEndpoint = mockFetchFeatureAnnouncementNotifications({ - status: 500, - }); - - const notifications = await getFeatureAnnouncementNotifications(); - mockEndpoint.done(); - expect(notifications).toEqual([]); - }); - - it('should return an empty array if data is not available', async () => { - const mockEndpoint = mockFetchFeatureAnnouncementNotifications({ - status: 200, - body: { items: [] }, - }); - - const notifications = await getFeatureAnnouncementNotifications(); - mockEndpoint.done(); - expect(notifications).toEqual([]); - }); - - it('should fetch entries from Contentful and return formatted notifications', async () => { - const mockEndpoint = mockFetchFeatureAnnouncementNotifications({ - status: 200, - body: createMockFeatureAnnouncementAPIResult(), - }); - - const notifications = await getFeatureAnnouncementNotifications(); - expect(notifications).toHaveLength(1); - mockEndpoint.done(); - - const resultNotification = notifications[0]; - expect(resultNotification).toEqual( - expect.objectContaining({ - id: 'dont-miss-out-on-airdrops-and-new-nft-mints', - type: TRIGGER_TYPES.FEATURES_ANNOUNCEMENT, - createdAt: expect.any(String), - isRead: expect.any(Boolean), - }), - ); - - expect(resultNotification.data).toBeDefined(); - }); -}); diff --git a/app/scripts/controllers/metamask-notifications/services/feature-announcements.ts b/app/scripts/controllers/metamask-notifications/services/feature-announcements.ts deleted file mode 100644 index 8b3c3870627e..000000000000 --- a/app/scripts/controllers/metamask-notifications/services/feature-announcements.ts +++ /dev/null @@ -1,129 +0,0 @@ -import { documentToHtmlString } from '@contentful/rich-text-html-renderer'; -import log from 'loglevel'; -import type { Entry, Asset } from 'contentful'; -import type { - FeatureAnnouncementRawNotification, - TypeFeatureAnnouncement, -} from '../types/feature-announcement/feature-announcement'; -import type { Notification } from '../types/notification/notification'; -import { processFeatureAnnouncement } from '../processors/process-feature-announcement'; -import { TRIGGER_TYPES } from '../constants/notification-schema'; -import { ImageFields } from '../types/feature-announcement/type-feature-announcement'; -import { TypeExtensionLinkFields } from '../types/feature-announcement/type-extension-link'; - -const spaceId = process.env.CONTENTFUL_ACCESS_SPACE_ID || ':space_id'; -const accessToken = process.env.CONTENTFUL_ACCESS_TOKEN || ''; -export const FEATURE_ANNOUNCEMENT_API = `https://cdn.contentful.com/spaces/${spaceId}/environments/master/entries`; -export const FEATURE_ANNOUNCEMENT_URL = `${FEATURE_ANNOUNCEMENT_API}?access_token=${accessToken}&content_type=productAnnouncement&include=10&fields.clients=extension`; - -export type ContentfulResult = { - includes?: { - Entry?: Entry[]; - Asset?: Asset[]; - }; - items?: TypeFeatureAnnouncement[]; -}; - -async function fetchFromContentful( - url: string, - retries = 1, - retryDelay = 1000, -): Promise { - let lastError: Error | null = null; - - for (let i = 0; i < retries; i++) { - try { - const response = await fetch(url); - if (!response.ok) { - throw new Error(`Fetch failed with status: ${response.status}`); - } - return await response.json(); - } catch (error) { - if (error instanceof Error) { - lastError = error; - } - if (i < retries - 1) { - await new Promise((resolve) => setTimeout(resolve, retryDelay)); - } - } - } - - log.error( - `Error fetching from Contentful after ${retries} retries: ${lastError}`, - ); - return null; -} - -async function fetchFeatureAnnouncementNotifications(): Promise< - FeatureAnnouncementRawNotification[] -> { - const data = await fetchFromContentful(FEATURE_ANNOUNCEMENT_URL); - - if (!data) { - return []; - } - - const findIncludedItem = (sysId: string) => { - const item = - data?.includes?.Entry?.find((i: Entry) => i?.sys?.id === sysId) || - data?.includes?.Asset?.find((i: Asset) => i?.sys?.id === sysId); - return item ? item?.fields : null; - }; - - const contentfulNotifications = data?.items ?? []; - const rawNotifications: FeatureAnnouncementRawNotification[] = - contentfulNotifications.map((n: TypeFeatureAnnouncement) => { - const { fields } = n; - const imageFields = fields.image - ? (findIncludedItem(fields.image.sys.id) as ImageFields['fields']) - : undefined; - const extensionLinkFields = fields.extensionLink - ? (findIncludedItem( - fields.extensionLink.sys.id, - ) as TypeExtensionLinkFields['fields']) - : undefined; - - const notification: FeatureAnnouncementRawNotification = { - type: TRIGGER_TYPES.FEATURES_ANNOUNCEMENT, - createdAt: new Date(n.sys.createdAt).toString(), - data: { - id: fields.id, - category: fields.category, - title: fields.title, - longDescription: documentToHtmlString(fields.longDescription), - shortDescription: fields.shortDescription, - image: { - title: imageFields?.title, - description: imageFields?.description, - url: imageFields?.file?.url ?? '', - }, - extensionLink: extensionLinkFields && { - extensionLinkText: extensionLinkFields?.extensionLinkText, - extensionLinkRoute: extensionLinkFields?.extensionLinkRoute, - }, - }, - }; - - return notification; - }); - - return rawNotifications; -} - -export async function getFeatureAnnouncementNotifications(): Promise< - Notification[] -> { - if ( - process.env.CONTENTFUL_ACCESS_SPACE_ID && - process.env.CONTENTFUL_ACCESS_TOKEN - ) { - const rawNotifications = await fetchFeatureAnnouncementNotifications(); - const notifications = rawNotifications.map((notification) => - processFeatureAnnouncement(notification), - ); - - return notifications; - } - - return []; -} diff --git a/app/scripts/controllers/metamask-notifications/services/onchain-notifications.test.ts b/app/scripts/controllers/metamask-notifications/services/onchain-notifications.test.ts deleted file mode 100644 index eb205c7e52aa..000000000000 --- a/app/scripts/controllers/metamask-notifications/services/onchain-notifications.test.ts +++ /dev/null @@ -1,278 +0,0 @@ -import { TRIGGER_TYPES } from '../constants/notification-schema'; -import { - mockBatchCreateTriggers, - mockBatchDeleteTriggers, - mockListNotifications, - mockMarkNotificationsAsRead, -} from '../mocks/mockServices'; -import { - MOCK_USER_STORAGE_ACCOUNT, - MOCK_USER_STORAGE_CHAIN, - createMockUserStorageWithTriggers, -} from '../mocks/mock-notification-user-storage'; -import { UserStorage } from '../types/user-storage/user-storage'; -import * as MetamaskNotificationsUtils from '../utils/utils'; -import * as OnChainNotifications from './onchain-notifications'; - -const MOCK_STORAGE_KEY = 'MOCK_USER_STORAGE_KEY'; -const MOCK_BEARER_TOKEN = 'MOCK_BEARER_TOKEN'; -const MOCK_TRIGGER_ID = 'TRIGGER_ID_1'; - -describe('On Chain Notifications - createOnChainTriggers()', () => { - test('Should create new triggers', async () => { - const mocks = arrangeMocks(); - - // The initial trigger to create should not be enabled - assertUserStorageTriggerStatus(mocks.mockUserStorage, false); - - await OnChainNotifications.createOnChainTriggers( - mocks.mockUserStorage, - MOCK_STORAGE_KEY, - MOCK_BEARER_TOKEN, - mocks.triggers, - ); - - mocks.mockEndpoint.done(); - - // once we created triggers, we expect the trigger to be enabled - assertUserStorageTriggerStatus(mocks.mockUserStorage, true); - }); - - test('Does not call endpoint if there are no triggers to create', async () => { - const mocks = arrangeMocks(); - await OnChainNotifications.createOnChainTriggers( - mocks.mockUserStorage, - MOCK_STORAGE_KEY, - MOCK_BEARER_TOKEN, - [], // there are no triggers we've provided that need to be created - ); - - expect(mocks.mockEndpoint.isDone()).toBe(false); - }); - - test('Should throw error if endpoint fails', async () => { - const mockUserStorage = createMockUserStorageWithTriggers([ - { id: MOCK_TRIGGER_ID, k: TRIGGER_TYPES.ETH_SENT, e: false }, - ]); - const triggers = - MetamaskNotificationsUtils.traverseUserStorageTriggers(mockUserStorage); - const mockBadEndpoint = mockBatchCreateTriggers({ - status: 500, - body: { error: 'mock api failure' }, - }); - - // The initial trigger to create should not be enabled - assertUserStorageTriggerStatus(mockUserStorage, false); - - await expect( - OnChainNotifications.createOnChainTriggers( - mockUserStorage, - MOCK_STORAGE_KEY, - MOCK_BEARER_TOKEN, - triggers, - ), - ).rejects.toThrow(); - - mockBadEndpoint.done(); - - // since failed, expect triggers to not be enabled - assertUserStorageTriggerStatus(mockUserStorage, false); - }); - - function assertUserStorageTriggerStatus( - userStorage: UserStorage, - enabled: boolean, - ) { - expect( - userStorage[MOCK_USER_STORAGE_ACCOUNT][MOCK_USER_STORAGE_CHAIN][ - MOCK_TRIGGER_ID - ].e, - ).toBe(enabled); - } - - function arrangeMocks() { - const mockUserStorage = createMockUserStorageWithTriggers([ - { id: MOCK_TRIGGER_ID, k: TRIGGER_TYPES.ETH_SENT, e: false }, - ]); - const triggers = - MetamaskNotificationsUtils.traverseUserStorageTriggers(mockUserStorage); - const mockEndpoint = mockBatchCreateTriggers(); - - return { - mockUserStorage, - triggers, - mockEndpoint, - }; - } -}); - -describe('On Chain Notifications - deleteOnChainTriggers()', () => { - test('Should delete a trigger from API and in user storage', async () => { - const { mockUserStorage, triggerId1, triggerId2 } = arrangeUserStorage(); - const mockEndpoint = mockBatchDeleteTriggers(); - - // Assert that triggers exists - [triggerId1, triggerId2].forEach((t) => { - expect(getTriggerFromUserStorage(mockUserStorage, t)).toBeDefined(); - }); - - await OnChainNotifications.deleteOnChainTriggers( - mockUserStorage, - MOCK_STORAGE_KEY, - MOCK_BEARER_TOKEN, - [triggerId2], - ); - - mockEndpoint.done(); - - // Assert trigger deletion - expect( - getTriggerFromUserStorage(mockUserStorage, triggerId1), - ).toBeDefined(); - expect( - getTriggerFromUserStorage(mockUserStorage, triggerId2), - ).toBeUndefined(); - }); - - test('Should delete all triggers and account in user storage', async () => { - const { mockUserStorage, triggerId1, triggerId2 } = arrangeUserStorage(); - const mockEndpoint = mockBatchDeleteTriggers(); - - await OnChainNotifications.deleteOnChainTriggers( - mockUserStorage, - MOCK_STORAGE_KEY, - MOCK_BEARER_TOKEN, - [triggerId1, triggerId2], // delete all triggers for an account - ); - - mockEndpoint.done(); - - // assert that the underlying user is also deleted since all underlying triggers are deleted - expect(mockUserStorage[MOCK_USER_STORAGE_ACCOUNT]).toBeUndefined(); - }); - - test('Should throw error if endpoint fails to delete', async () => { - const { mockUserStorage, triggerId1, triggerId2 } = arrangeUserStorage(); - const mockBadEndpoint = mockBatchDeleteTriggers({ - status: 500, - body: { error: 'mock api failure' }, - }); - - await expect( - OnChainNotifications.deleteOnChainTriggers( - mockUserStorage, - MOCK_STORAGE_KEY, - MOCK_BEARER_TOKEN, - [triggerId1, triggerId2], - ), - ).rejects.toThrow(); - - mockBadEndpoint.done(); - - // Assert that triggers were not deleted from user storage - [triggerId1, triggerId2].forEach((t) => { - expect(getTriggerFromUserStorage(mockUserStorage, t)).toBeDefined(); - }); - }); - - function getTriggerFromUserStorage( - userStorage: UserStorage, - triggerId: string, - ) { - return userStorage[MOCK_USER_STORAGE_ACCOUNT][MOCK_USER_STORAGE_CHAIN][ - triggerId - ]; - } - - function arrangeUserStorage() { - const triggerId1 = 'TRIGGER_ID_1'; - const triggerId2 = 'TRIGGER_ID_2'; - const mockUserStorage = createMockUserStorageWithTriggers([ - triggerId1, - triggerId2, - ]); - - return { - mockUserStorage, - triggerId1, - triggerId2, - }; - } -}); - -describe('On Chain Notifications - getOnChainNotifications()', () => { - test('Should return a list of notifications', async () => { - const mockEndpoint = mockListNotifications(); - const mockUserStorage = createMockUserStorageWithTriggers([ - 'trigger_1', - 'trigger_2', - ]); - - const result = await OnChainNotifications.getOnChainNotifications( - mockUserStorage, - MOCK_BEARER_TOKEN, - ); - - mockEndpoint.done(); - expect(result.length > 0).toBe(true); - }); - - test('Should return an empty list if not triggers found in user storage', async () => { - const mockEndpoint = mockListNotifications(); - const mockUserStorage = createMockUserStorageWithTriggers([]); // no triggers - - const result = await OnChainNotifications.getOnChainNotifications( - mockUserStorage, - MOCK_BEARER_TOKEN, - ); - - expect(mockEndpoint.isDone()).toBe(false); - expect(result.length === 0).toBe(true); - }); - - test('Should return an empty list of notifications if endpoint fails to fetch triggers', async () => { - const mockEndpoint = mockListNotifications({ - status: 500, - body: { error: 'mock api failure' }, - }); - const mockUserStorage = createMockUserStorageWithTriggers([ - 'trigger_1', - 'trigger_2', - ]); - - const result = await OnChainNotifications.getOnChainNotifications( - mockUserStorage, - MOCK_BEARER_TOKEN, - ); - - mockEndpoint.done(); - expect(result.length === 0).toBe(true); - }); -}); - -describe('On Chain Notifications - markNotificationsAsRead()', () => { - test('Should successfully call endpoint to mark notifications as read', async () => { - const mockEndpoint = mockMarkNotificationsAsRead(); - await OnChainNotifications.markNotificationsAsRead(MOCK_BEARER_TOKEN, [ - 'notification_1', - 'notification_2', - ]); - - mockEndpoint.done(); - }); - - test('Should throw error if fails to call endpoint to mark notifications as read', async () => { - const mockBadEndpoint = mockMarkNotificationsAsRead({ - status: 500, - body: { error: 'mock api failure' }, - }); - await expect( - OnChainNotifications.markNotificationsAsRead(MOCK_BEARER_TOKEN, [ - 'notification_1', - 'notification_2', - ]), - ).rejects.toThrow(); - - mockBadEndpoint.done(); - }); -}); diff --git a/app/scripts/controllers/metamask-notifications/services/onchain-notifications.ts b/app/scripts/controllers/metamask-notifications/services/onchain-notifications.ts deleted file mode 100644 index d4a95bf1aa4e..000000000000 --- a/app/scripts/controllers/metamask-notifications/services/onchain-notifications.ts +++ /dev/null @@ -1,290 +0,0 @@ -import log from 'loglevel'; -import { UserStorageController } from '@metamask/profile-sync-controller'; -import type { UserStorage } from '../types/user-storage/user-storage'; -import type { OnChainRawNotification } from '../types/on-chain-notification/on-chain-notification'; -import { - traverseUserStorageTriggers, - toggleUserStorageTriggerStatus, - makeApiCall, -} from '../utils/utils'; -import { TRIGGER_TYPES } from '../constants/notification-schema'; -import type { components } from '../types/on-chain-notification/schema'; - -const { createSHA256Hash } = UserStorageController; - -export type NotificationTrigger = { - id: string; - chainId: string; - kind: string; - address: string; -}; - -export const TRIGGER_API = process.env.TRIGGERS_SERVICE_URL; -export const NOTIFICATION_API = process.env.NOTIFICATIONS_SERVICE_URL; -export const TRIGGER_API_BATCH_ENDPOINT = `${TRIGGER_API}/api/v1/triggers/batch`; -export const NOTIFICATION_API_LIST_ENDPOINT = `${NOTIFICATION_API}/api/v1/notifications`; -export const NOTIFICATION_API_LIST_ENDPOINT_PAGE_QUERY = (page: number) => - `${NOTIFICATION_API_LIST_ENDPOINT}?page=${page}&per_page=100`; -export const NOTIFICATION_API_MARK_ALL_AS_READ_ENDPOINT = `${NOTIFICATION_API}/api/v1/notifications/mark-as-read`; - -/** - * Creates on-chain triggers based on the provided notification triggers. - * This method generates a unique token for each trigger using the trigger ID and storage key, - * proving ownership of the trigger being updated. It then makes an API call to create these triggers. - * Upon successful creation, it updates the userStorage to reflect the new trigger status. - * - * @param userStorage - The user's storage object where triggers and their statuses are stored. - * @param storageKey - A key used along with the trigger ID to generate a unique token for each trigger. - * @param bearerToken - The JSON Web Token used for authentication in the API call. - * @param triggers - An array of notification triggers to be created. Each trigger includes an ID, chain ID, kind, and address. - * @returns A promise that resolves to void. Throws an error if the API call fails or if there's an issue creating the triggers. - */ -export async function createOnChainTriggers( - userStorage: UserStorage, - storageKey: string, - bearerToken: string, - triggers: NotificationTrigger[], -): Promise { - type RequestPayloadTrigger = { - id: string; - // this is the trigger token, generated by using the uuid + storage key. It proves you own the trigger you are updating - token: string; - config: { - kind: string; - chain_id: number; - address: string; - }; - }; - const triggersToCreate: RequestPayloadTrigger[] = triggers.map((t) => ({ - id: t.id, - token: createSHA256Hash(t.id + storageKey), - config: { - kind: t.kind, - chain_id: Number(t.chainId), - address: t.address, - }, - })); - - if (triggersToCreate.length === 0) { - return; - } - - const response = await makeApiCall( - bearerToken, - TRIGGER_API_BATCH_ENDPOINT, - 'POST', - triggersToCreate, - ); - - if (!response.ok) { - const errorData = await response.json().catch(() => undefined); - log.error('Error creating triggers:', errorData); - throw new Error('OnChain Notifications - unable to create triggers'); - } - - // If the trigger creation was fine - // then update the userStorage - for (const trigger of triggersToCreate) { - toggleUserStorageTriggerStatus( - userStorage, - trigger.config.address, - String(trigger.config.chain_id), - trigger.id, - true, - ); - } -} - -/** - * Deletes on-chain triggers based on the provided UUIDs. - * This method generates a unique token for each trigger using the UUID and storage key, - * proving ownership of the trigger being deleted. It then makes an API call to delete these triggers. - * Upon successful deletion, it updates the userStorage to remove the deleted trigger statuses. - * - * @param userStorage - The user's storage object where triggers and their statuses are stored. - * @param storageKey - A key used along with the UUID to generate a unique token for each trigger. - * @param bearerToken - The JSON Web Token used for authentication in the API call. - * @param uuids - An array of UUIDs representing the triggers to be deleted. - * @returns A promise that resolves to the updated UserStorage object. Throws an error if the API call fails or if there's an issue deleting the triggers. - */ -export async function deleteOnChainTriggers( - userStorage: UserStorage, - storageKey: string, - bearerToken: string, - uuids: string[], -): Promise { - const triggersToDelete = uuids.map((uuid) => ({ - id: uuid, - token: createSHA256Hash(uuid + storageKey), - })); - - try { - const response = await makeApiCall( - bearerToken, - TRIGGER_API_BATCH_ENDPOINT, - 'DELETE', - triggersToDelete, - ); - - if (!response.ok) { - throw new Error( - `Failed to delete on-chain notifications for uuids ${uuids.join(', ')}`, - ); - } - - // Update the state of the deleted trigger to false - for (const uuid of uuids) { - for (const address in userStorage) { - if (Object.hasOwn(userStorage, address)) { - for (const chainId in userStorage[address]) { - if (userStorage?.[address]?.[chainId]?.[uuid]) { - delete userStorage[address][chainId][uuid]; - } - } - } - } - } - - // Follow-up cleanup, if an address had no triggers whatsoever, then we can delete the address - const isEmpty = (obj = {}) => Object.keys(obj).length === 0; - for (const address in userStorage) { - if (Object.hasOwn(userStorage, address)) { - for (const chainId in userStorage[address]) { - // Chain isEmpty Check - if (isEmpty(userStorage?.[address]?.[chainId])) { - delete userStorage[address][chainId]; - } - } - - // Address isEmpty Check - if (isEmpty(userStorage?.[address])) { - delete userStorage[address]; - } - } - } - } catch (err) { - log.error( - `Error deleting on-chain notifications for uuids ${uuids.join(', ')}:`, - err, - ); - throw err; - } - - return userStorage; -} - -/** - * Fetches on-chain notifications for the given user storage and BearerToken. - * This method iterates through the userStorage to find enabled triggers and fetches notifications for those triggers. - * It makes paginated API calls to the notifications service, transforming and aggregating the notifications into a single array. - * The process stops either when all pages have been fetched or when a page has less than 100 notifications, indicating the end of the data. - * - * @param userStorage - The user's storage object containing trigger information. - * @param bearerToken - The JSON Web Token used for authentication in the API call. - * @returns A promise that resolves to an array of OnChainRawNotification objects. If no triggers are enabled or an error occurs, it may return an empty array. - */ -export async function getOnChainNotifications( - userStorage: UserStorage, - bearerToken: string, -): Promise { - const triggerIds = traverseUserStorageTriggers(userStorage, { - mapTrigger: (t) => { - if (!t.enabled) { - return undefined; - } - return t.id; - }, - }); - - if (triggerIds.length === 0) { - return []; - } - - const onChainNotifications: OnChainRawNotification[] = []; - const PAGE_LIMIT = 2; - for (let page = 1; page <= PAGE_LIMIT; page++) { - try { - const response = await makeApiCall( - bearerToken, - NOTIFICATION_API_LIST_ENDPOINT_PAGE_QUERY(page), - 'POST', - { trigger_ids: triggerIds }, - ); - - const notifications = (await response.json()) as OnChainRawNotification[]; - - // Transform and sort notifications - const transformedNotifications = notifications - .map( - ( - n: components['schemas']['Notification'], - ): OnChainRawNotification | undefined => { - if (!n.data?.kind) { - return undefined; - } - - return { - ...n, - type: n.data.kind as TRIGGER_TYPES, - } as OnChainRawNotification; - }, - ) - .filter((n): n is OnChainRawNotification => Boolean(n)); - - onChainNotifications.push(...transformedNotifications); - - // if less than 100 notifications on page, then means we reached end - if (notifications.length < 100) { - page = PAGE_LIMIT + 1; - break; - } - } catch (err) { - log.error( - `Error fetching on-chain notifications for trigger IDs ${triggerIds.join( - ', ', - )}:`, - err, - ); - // do nothing - } - } - - return onChainNotifications; -} - -/** - * Marks the specified notifications as read. - * This method sends a POST request to the notifications service to mark the provided notification IDs as read. - * If the operation is successful, it completes without error. If the operation fails, it throws an error with details. - * - * @param bearerToken - The JSON Web Token used for authentication in the API call. - * @param notificationIds - An array of notification IDs to be marked as read. - * @returns A promise that resolves to void. The promise will reject if there's an error during the API call or if the response status is not 200. - */ -export async function markNotificationsAsRead( - bearerToken: string, - notificationIds: string[], -): Promise { - if (notificationIds.length === 0) { - return; - } - - try { - const response = await makeApiCall( - bearerToken, - NOTIFICATION_API_MARK_ALL_AS_READ_ENDPOINT, - 'POST', - { ids: notificationIds }, - ); - - if (response.status !== 200) { - const errorData = await response.json().catch(() => undefined); - throw new Error( - `Error marking notifications as read: ${errorData?.message}`, - ); - } - } catch (err) { - log.error('Error marking notifications as read:', err); - throw err; - } -} diff --git a/app/scripts/controllers/metamask-notifications/types/feature-announcement/feature-announcement.ts b/app/scripts/controllers/metamask-notifications/types/feature-announcement/feature-announcement.ts deleted file mode 100644 index 6155c9dcd156..000000000000 --- a/app/scripts/controllers/metamask-notifications/types/feature-announcement/feature-announcement.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type { TRIGGER_TYPES } from '../../constants/notification-schema'; -import type { TypeFeatureAnnouncement } from './type-feature-announcement'; - -export type { TypeFeatureAnnouncement }; -export type { TypeFeatureAnnouncementFields } from './type-feature-announcement'; - -export type FeatureAnnouncementRawNotificationData = Omit< - TypeFeatureAnnouncement['fields'], - 'image' | 'longDescription' | 'extensionLink' -> & { - longDescription: string; - image: { - title?: string; - description?: string; - url: string; - }; - extensionLink?: { - extensionLinkText: string; - extensionLinkRoute: string; - }; -}; - -export type FeatureAnnouncementRawNotification = { - type: TRIGGER_TYPES.FEATURES_ANNOUNCEMENT; - createdAt: string; - data: FeatureAnnouncementRawNotificationData; -}; diff --git a/app/scripts/controllers/metamask-notifications/types/feature-announcement/type-extension-link.ts b/app/scripts/controllers/metamask-notifications/types/feature-announcement/type-extension-link.ts deleted file mode 100644 index bfc3d40267ae..000000000000 --- a/app/scripts/controllers/metamask-notifications/types/feature-announcement/type-extension-link.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { Entry } from 'contentful'; - -export type TypeExtensionLinkFields = { - fields: { - extensionLinkText: string; - extensionLinkRoute: string; - }; - contentTypeId: 'extensionLink'; -}; - -export type TypeExtensionLink = Entry< - TypeExtensionLinkFields, - 'WITHOUT_UNRESOLVABLE_LINKS' ->; diff --git a/app/scripts/controllers/metamask-notifications/types/feature-announcement/type-feature-announcement.ts b/app/scripts/controllers/metamask-notifications/types/feature-announcement/type-feature-announcement.ts deleted file mode 100644 index 8cff6cbd7d9d..000000000000 --- a/app/scripts/controllers/metamask-notifications/types/feature-announcement/type-feature-announcement.ts +++ /dev/null @@ -1,41 +0,0 @@ -import type { Entry, EntryFieldTypes } from 'contentful'; -import type { TypeExtensionLinkFields } from './type-extension-link'; - -export type ImageFields = { - fields: { - title?: string; - description?: string; - file?: { - url: string; - fileName: string; - contentType: string; - details: { - size: number; - image?: { - width: number; - height: number; - }; - }; - }; - }; - contentTypeId: 'Image'; -}; - -export type TypeFeatureAnnouncementFields = { - fields: { - title: EntryFieldTypes.Text; - id: EntryFieldTypes.Symbol; - category: EntryFieldTypes.Text; // E.g. Announcement, etc. - shortDescription: EntryFieldTypes.Text; - image: EntryFieldTypes.EntryLink; - longDescription: EntryFieldTypes.RichText; - extensionLink?: EntryFieldTypes.EntryLink; - clients?: EntryFieldTypes.Text<'extension' | 'mobile' | 'portfolio'>; - }; - contentTypeId: 'productAnnouncement'; -}; - -export type TypeFeatureAnnouncement = Entry< - TypeFeatureAnnouncementFields, - 'WITHOUT_UNRESOLVABLE_LINKS' ->; diff --git a/app/scripts/controllers/metamask-notifications/types/notification/notification.ts b/app/scripts/controllers/metamask-notifications/types/notification/notification.ts deleted file mode 100644 index 3c556ab30191..000000000000 --- a/app/scripts/controllers/metamask-notifications/types/notification/notification.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { Notification } from '../types'; - -export type { Notification } from '../types'; - -// NFT -export type NFT = { - token_id: string; - image: string; - collection?: { - name: string; - image: string; - }; -}; - -export type MarkAsReadNotificationsParam = Pick< - Notification, - 'id' | 'type' | 'isRead' ->[]; diff --git a/app/scripts/controllers/metamask-notifications/types/on-chain-notification/on-chain-notification.ts b/app/scripts/controllers/metamask-notifications/types/on-chain-notification/on-chain-notification.ts deleted file mode 100644 index 022702933bea..000000000000 --- a/app/scripts/controllers/metamask-notifications/types/on-chain-notification/on-chain-notification.ts +++ /dev/null @@ -1,50 +0,0 @@ -import type { TRIGGER_TYPES } from '../../constants/notification-schema'; -import type { Compute } from '../type-utils'; -import type { components } from './schema'; - -export type Data_MetamaskSwapCompleted = - components['schemas']['Data_MetamaskSwapCompleted']; -export type Data_LidoStakeReadyToBeWithdrawn = - components['schemas']['Data_LidoStakeReadyToBeWithdrawn']; -export type Data_LidoStakeCompleted = - components['schemas']['Data_LidoStakeCompleted']; -export type Data_LidoWithdrawalRequested = - components['schemas']['Data_LidoWithdrawalRequested']; -export type Data_LidoWithdrawalCompleted = - components['schemas']['Data_LidoWithdrawalCompleted']; -export type Data_RocketPoolStakeCompleted = - components['schemas']['Data_RocketPoolStakeCompleted']; -export type Data_RocketPoolUnstakeCompleted = - components['schemas']['Data_RocketPoolUnstakeCompleted']; -export type Data_ETHSent = components['schemas']['Data_ETHSent']; -export type Data_ETHReceived = components['schemas']['Data_ETHReceived']; -export type Data_ERC20Sent = components['schemas']['Data_ERC20Sent']; -export type Data_ERC20Received = components['schemas']['Data_ERC20Received']; -export type Data_ERC721Sent = components['schemas']['Data_ERC721Sent']; -export type Data_ERC721Received = components['schemas']['Data_ERC721Received']; - -type Notification = components['schemas']['Notification']; -type NotificationDataKinds = NonNullable['kind']; -type ConvertToEnum = { - [K in TRIGGER_TYPES]: Kind extends `${K}` ? K : never; -}[TRIGGER_TYPES]; - -/** - * Type-Computation. - * 1. Adds a `type` field to the notification, it converts the schema type into the ENUM we use. - * 2. It ensures that the `data` field is the correct Notification data for this `type` - * - The `Compute` utility merges the intersections (`&`) for a prettier type. - */ -export type OnChainRawNotification = { - [K in NotificationDataKinds]: Compute< - Omit & { - type: ConvertToEnum; - data: Extract; - } - >; -}[NotificationDataKinds]; - -export type OnChainRawNotificationsWithNetworkFields = Extract< - OnChainRawNotification, - { data: { network_fee: unknown } } ->; diff --git a/app/scripts/controllers/metamask-notifications/types/on-chain-notification/schema.d.ts b/app/scripts/controllers/metamask-notifications/types/on-chain-notification/schema.d.ts deleted file mode 100644 index 05c725e3690d..000000000000 --- a/app/scripts/controllers/metamask-notifications/types/on-chain-notification/schema.d.ts +++ /dev/null @@ -1,303 +0,0 @@ -/** - * This file was auto-generated by openapi-typescript. - * Do not make direct changes to the file. - * Script: `npx openapi-typescript -o ./schema.d.ts` - */ - -export type paths = { - '/api/v1/notifications': { - /** List all notifications ordered by most recent */ - post: { - parameters: { - query?: { - /** @description Page number for pagination */ - page?: number; - /** @description Number of notifications per page for pagination */ - per_page?: number; - }; - }; - requestBody?: { - content: { - 'application/json': { - trigger_ids: string[]; - chain_ids?: number[]; - kinds?: string[]; - unread?: boolean; - }; - }; - }; - responses: { - /** @description Successfully fetched a list of notifications */ - 200: { - content: { - 'application/json': components['schemas']['Notification'][]; - }; - }; - }; - }; - }; - '/api/v1/notifications/mark-as-read': { - /** Mark notifications as read */ - post: { - requestBody: { - content: { - 'application/json': { - ids?: string[]; - }; - }; - }; - responses: { - /** @description Successfully marked notifications as read */ - 200: { - content: never; - }; - }; - }; - }; -}; - -export type webhooks = Record; - -export type components = { - schemas: { - Notification: { - /** Format: uuid */ - id: string; - /** Format: uuid */ - trigger_id: string; - /** @example 1 */ - chain_id: number; - /** @example 17485840 */ - block_number: number; - block_timestamp: string; - /** - * Format: address - * - * @example 0x881D40237659C251811CEC9c364ef91dC08D300C - */ - tx_hash: string; - /** @example false */ - unread: boolean; - /** Format: date-time */ - created_at: string; - /** Format: address */ - address: string; - data?: - | components['schemas']['Data_MetamaskSwapCompleted'] - | components['schemas']['Data_LidoStakeReadyToBeWithdrawn'] - | components['schemas']['Data_LidoStakeCompleted'] - | components['schemas']['Data_LidoWithdrawalRequested'] - | components['schemas']['Data_LidoWithdrawalCompleted'] - | components['schemas']['Data_RocketPoolStakeCompleted'] - | components['schemas']['Data_RocketPoolUnstakeCompleted'] - | components['schemas']['Data_ETHSent'] - | components['schemas']['Data_ETHReceived'] - | components['schemas']['Data_ERC20Sent'] - | components['schemas']['Data_ERC20Received'] - | components['schemas']['Data_ERC721Sent'] - | components['schemas']['Data_ERC721Received'] - | components['schemas']['Data_ERC1155Sent'] - | components['schemas']['Data_ERC1155Received']; - }; - Data_MetamaskSwapCompleted: { - /** @enum {string} */ - kind: 'metamask_swap_completed'; - network_fee: components['schemas']['NetworkFee']; - /** Format: decimal */ - rate: string; - token_in: components['schemas']['Token']; - token_out: components['schemas']['Token']; - }; - Data_LidoStakeCompleted: { - /** @enum {string} */ - kind: 'lido_stake_completed'; - network_fee: components['schemas']['NetworkFee']; - stake_in: components['schemas']['Stake']; - stake_out: components['schemas']['Stake']; - }; - Data_LidoWithdrawalRequested: { - /** @enum {string} */ - kind: 'lido_withdrawal_requested'; - network_fee: components['schemas']['NetworkFee']; - stake_in: components['schemas']['Stake']; - stake_out: components['schemas']['Stake']; - }; - Data_LidoStakeReadyToBeWithdrawn: { - /** @enum {string} */ - kind: 'lido_stake_ready_to_be_withdrawn'; - /** Format: decimal */ - request_id: string; - staked_eth: components['schemas']['Stake']; - }; - Data_LidoWithdrawalCompleted: { - /** @enum {string} */ - kind: 'lido_withdrawal_completed'; - network_fee: components['schemas']['NetworkFee']; - stake_in: components['schemas']['Stake']; - stake_out: components['schemas']['Stake']; - }; - Data_RocketPoolStakeCompleted: { - /** @enum {string} */ - kind: 'rocketpool_stake_completed'; - network_fee: components['schemas']['NetworkFee']; - stake_in: components['schemas']['Stake']; - stake_out: components['schemas']['Stake']; - }; - Data_RocketPoolUnstakeCompleted: { - /** @enum {string} */ - kind: 'rocketpool_unstake_completed'; - network_fee: components['schemas']['NetworkFee']; - stake_in: components['schemas']['Stake']; - stake_out: components['schemas']['Stake']; - }; - Data_ETHSent: { - /** @enum {string} */ - kind: 'eth_sent'; - network_fee: components['schemas']['NetworkFee']; - /** Format: address */ - from: string; - /** Format: address */ - to: string; - amount: { - /** Format: decimal */ - usd: string; - /** Format: decimal */ - eth: string; - }; - }; - Data_ETHReceived: { - /** @enum {string} */ - kind: 'eth_received'; - network_fee: components['schemas']['NetworkFee']; - /** Format: address */ - from: string; - /** Format: address */ - to: string; - amount: { - /** Format: decimal */ - usd: string; - /** Format: decimal */ - eth: string; - }; - }; - Data_ERC20Sent: { - /** @enum {string} */ - kind: 'erc20_sent'; - network_fee: components['schemas']['NetworkFee']; - /** Format: address */ - from: string; - /** Format: address */ - to: string; - token: components['schemas']['Token']; - }; - Data_ERC20Received: { - /** @enum {string} */ - kind: 'erc20_received'; - network_fee: components['schemas']['NetworkFee']; - /** Format: address */ - from: string; - /** Format: address */ - to: string; - token: components['schemas']['Token']; - }; - Data_ERC721Sent: { - /** @enum {string} */ - kind: 'erc721_sent'; - network_fee: components['schemas']['NetworkFee']; - /** Format: address */ - from: string; - /** Format: address */ - to: string; - nft: components['schemas']['NFT']; - }; - Data_ERC721Received: { - /** @enum {string} */ - kind: 'erc721_received'; - network_fee: components['schemas']['NetworkFee']; - /** Format: address */ - from: string; - /** Format: address */ - to: string; - nft: components['schemas']['NFT']; - }; - Data_ERC1155Sent: { - /** @enum {string} */ - kind: 'erc1155_sent'; - network_fee: components['schemas']['NetworkFee']; - /** Format: address */ - from: string; - /** Format: address */ - to: string; - nft?: components['schemas']['NFT']; - }; - Data_ERC1155Received: { - /** @enum {string} */ - kind: 'erc1155_received'; - network_fee: components['schemas']['NetworkFee']; - /** Format: address */ - from: string; - /** Format: address */ - to: string; - nft?: components['schemas']['NFT']; - }; - NetworkFee: { - /** Format: decimal */ - gas_price: string; - /** Format: decimal */ - native_token_price_in_usd: string; - }; - Token: { - /** Format: address */ - address: string; - symbol: string; - name: string; - /** Format: decimal */ - amount: string; - /** Format: int32 */ - decimals: string; - /** Format: uri */ - image: string; - /** Format: decimal */ - usd: string; - }; - NFT: { - name: string; - token_id: string; - /** Format: uri */ - image: string; - collection: { - /** Format: address */ - address: string; - name: string; - symbol: string; - /** Format: uri */ - image: string; - }; - }; - Stake: { - /** Format: address */ - address: string; - symbol: string; - name: string; - /** Format: decimal */ - amount: string; - /** Format: int32 */ - decimals: string; - /** Format: uri */ - image: string; - /** Format: decimal */ - usd: string; - }; - }; - responses: never; - parameters: never; - requestBodies: never; - headers: never; - pathItems: never; -}; - -export type $defs = Record; - -export type external = Record; - -export type operations = Record; diff --git a/app/scripts/controllers/metamask-notifications/types/type-utils.ts b/app/scripts/controllers/metamask-notifications/types/type-utils.ts deleted file mode 100644 index f05a763f5d2c..000000000000 --- a/app/scripts/controllers/metamask-notifications/types/type-utils.ts +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Computes and combines intersection types for a more "prettier" type (more human readable) - */ -export type Compute = T extends T ? { [K in keyof T]: T[K] } : never; diff --git a/app/scripts/controllers/metamask-notifications/types/types.ts b/app/scripts/controllers/metamask-notifications/types/types.ts deleted file mode 100644 index 1d0d7026a430..000000000000 --- a/app/scripts/controllers/metamask-notifications/types/types.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { FeatureAnnouncementRawNotification } from './feature-announcement/feature-announcement'; -import type { Compute } from './type-utils'; -import type { OnChainRawNotification } from './on-chain-notification/on-chain-notification'; - -export type NotificationUnion = - | FeatureAnnouncementRawNotification - | OnChainRawNotification; - -/** - * The shape of a "generic" notification. - * Other than the fields listed below, tt will also contain: - * - `type` field (declared in the Raw shapes) - * - `data` field (declared in the Raw shapes) - */ -export type Notification = Compute< - NotificationUnion & { - id: string; - createdAt: string; - isRead: boolean; - } ->; diff --git a/app/scripts/controllers/metamask-notifications/types/user-storage/user-storage.ts b/app/scripts/controllers/metamask-notifications/types/user-storage/user-storage.ts deleted file mode 100644 index 086d4cc7e4d4..000000000000 --- a/app/scripts/controllers/metamask-notifications/types/user-storage/user-storage.ts +++ /dev/null @@ -1,32 +0,0 @@ -import type { - SUPPORTED_CHAINS, - TRIGGER_TYPES, -} from '../../constants/notification-schema'; -import type { - USER_STORAGE_VERSION_KEY, - USER_STORAGE_VERSION, -} from '../../constants/constants'; - -export type UserStorage = { - /** - * The Version 'v' of the User Storage. - * NOTE - will allow us to support upgrade/downgrades in the future - */ - [USER_STORAGE_VERSION_KEY]: typeof USER_STORAGE_VERSION; - [address: string]: { - [chain in (typeof SUPPORTED_CHAINS)[number]]: { - [uuid: string]: { - /** Trigger Kind 'k' */ - k: TRIGGER_TYPES; - /** - * Trigger Enabled 'e' - * This is mostly an 'acknowledgement' to determine if a trigger has been made - * For example if we fail to create a trigger, we can set to false & retry (on re-log in, or elsewhere) - * - * Most of the time this is 'true', as triggers when deleted are also removed from User Storage - */ - e: boolean; - }; - }; - }; -}; diff --git a/app/scripts/controllers/metamask-notifications/utils/utils.test.ts b/app/scripts/controllers/metamask-notifications/utils/utils.test.ts deleted file mode 100644 index b533f6c4559b..000000000000 --- a/app/scripts/controllers/metamask-notifications/utils/utils.test.ts +++ /dev/null @@ -1,314 +0,0 @@ -import { USER_STORAGE_VERSION_KEY } from '../constants/constants'; -import { - NOTIFICATION_CHAINS, - TRIGGER_TYPES, -} from '../constants/notification-schema'; -import { - MOCK_USER_STORAGE_ACCOUNT, - MOCK_USER_STORAGE_CHAIN, - createMockFullUserStorage, - createMockUserStorageWithTriggers, -} from '../mocks/mock-notification-user-storage'; -import { UserStorage } from '../types/user-storage/user-storage'; -import * as MetamaskNotificationsUtils from './utils'; - -describe('metamask-notifications/utils - initializeUserStorage()', () => { - test('Creates a new user storage object based on the accounts provided', () => { - const mockAddress = 'MOCK_ADDRESS'; - const userStorage = MetamaskNotificationsUtils.initializeUserStorage( - [{ address: mockAddress }], - true, - ); - - // Addresses in User Storage are lowercase to prevent multiple entries of same address - const userStorageAddress = mockAddress.toLowerCase(); - expect(userStorage[userStorageAddress]).toBeDefined(); - }); - - test('Returns User Storage with no addresses if none provided', () => { - function assertEmptyStorage(storage: UserStorage) { - expect(Object.keys(storage).length === 1).toBe(true); - expect(USER_STORAGE_VERSION_KEY in storage).toBe(true); - } - - const userStorageTest1 = MetamaskNotificationsUtils.initializeUserStorage( - [], - true, - ); - assertEmptyStorage(userStorageTest1); - - const userStorageTest2 = MetamaskNotificationsUtils.initializeUserStorage( - [{ address: undefined }], - true, - ); - assertEmptyStorage(userStorageTest2); - }); -}); - -describe('metamask-notifications/utils - traverseUserStorageTriggers()', () => { - test('Traverses User Storage to return triggers', () => { - const storage = createMockFullUserStorage(); - const triggersObjArray = - MetamaskNotificationsUtils.traverseUserStorageTriggers(storage); - expect(triggersObjArray.length > 0).toBe(true); - expect(typeof triggersObjArray[0] === 'object').toBe(true); - }); - - test('Traverses and maps User Storage using mapper', () => { - const storage = createMockFullUserStorage(); - - // as the type suggests, the mapper returns a string, so expect this to be a string - const triggersStrArray = - MetamaskNotificationsUtils.traverseUserStorageTriggers(storage, { - mapTrigger: (t) => t.id, - }); - expect(triggersStrArray.length > 0).toBe(true); - expect(typeof triggersStrArray[0] === 'string').toBe(true); - - // if the mapper returns a falsy value, it is filtered out - const emptyTriggersArray = - MetamaskNotificationsUtils.traverseUserStorageTriggers(storage, { - mapTrigger: (_t): string | undefined => undefined, - }); - expect(emptyTriggersArray.length === 0).toBe(true); - }); -}); - -describe('metamask-notifications/utils - checkAccountsPresence()', () => { - test('Returns record of addresses that are in storage', () => { - const storage = createMockFullUserStorage(); - const result = MetamaskNotificationsUtils.checkAccountsPresence(storage, [ - MOCK_USER_STORAGE_ACCOUNT, - ]); - expect(result).toEqual({ - [MOCK_USER_STORAGE_ACCOUNT.toLowerCase()]: true, - }); - }); - - test('Returns record of addresses in storage and not fully in storage', () => { - const storage = createMockFullUserStorage(); - const MOCK_MISSING_ADDRESS = '0x2'; - const result = MetamaskNotificationsUtils.checkAccountsPresence(storage, [ - MOCK_USER_STORAGE_ACCOUNT, - MOCK_MISSING_ADDRESS, - ]); - expect(result).toEqual({ - [MOCK_USER_STORAGE_ACCOUNT.toLowerCase()]: true, - [MOCK_MISSING_ADDRESS.toLowerCase()]: false, - }); - }); - - test('Returns record where accounts are not fully present, due to missing chains', () => { - const storage = createMockFullUserStorage(); - delete storage[MOCK_USER_STORAGE_ACCOUNT][NOTIFICATION_CHAINS.ETHEREUM]; - - const result = MetamaskNotificationsUtils.checkAccountsPresence(storage, [ - MOCK_USER_STORAGE_ACCOUNT, - ]); - expect(result).toEqual({ - [MOCK_USER_STORAGE_ACCOUNT.toLowerCase()]: false, // false due to missing chains - }); - }); - - test('Returns record where accounts are not fully present, due to missing triggers', () => { - const storage = createMockFullUserStorage(); - const MOCK_TRIGGER_TO_DELETE = Object.keys( - storage[MOCK_USER_STORAGE_ACCOUNT][NOTIFICATION_CHAINS.ETHEREUM], - )[0]; - delete storage[MOCK_USER_STORAGE_ACCOUNT][NOTIFICATION_CHAINS.ETHEREUM][ - MOCK_TRIGGER_TO_DELETE - ]; - - const result = MetamaskNotificationsUtils.checkAccountsPresence(storage, [ - MOCK_USER_STORAGE_ACCOUNT, - ]); - expect(result).toEqual({ - [MOCK_USER_STORAGE_ACCOUNT.toLowerCase()]: false, // false due to missing triggers - }); - }); -}); - -describe('metamask-notifications/utils - inferEnabledKinds()', () => { - test('Returns all kinds from a User Storage Obj', () => { - const partialStorage = createMockUserStorageWithTriggers([ - { id: '1', e: true, k: TRIGGER_TYPES.ERC1155_RECEIVED }, - { id: '2', e: true, k: TRIGGER_TYPES.ERC1155_SENT }, - { id: '3', e: true, k: TRIGGER_TYPES.ERC1155_SENT }, // should remove duplicates - ]); - - const result = MetamaskNotificationsUtils.inferEnabledKinds(partialStorage); - expect(result.length).toBe(2); - expect(result.includes(TRIGGER_TYPES.ERC1155_RECEIVED)).toBe(true); - expect(result.includes(TRIGGER_TYPES.ERC1155_SENT)).toBe(true); - }); -}); - -describe('metamask-notifications/utils - getUUIDsForAccount()', () => { - test('Returns all trigger IDs in user storage from a given address', () => { - const partialStorage = createMockUserStorageWithTriggers(['t1', 't2']); - - const result = MetamaskNotificationsUtils.getUUIDsForAccount( - partialStorage, - MOCK_USER_STORAGE_ACCOUNT, - ); - expect(result.length).toBe(2); - expect(result.includes('t1')).toBe(true); - expect(result.includes('t2')).toBe(true); - }); - test('Returns an empty array if the address does not exist or has any triggers', () => { - const partialStorage = createMockUserStorageWithTriggers(['t1', 't2']); - const result = MetamaskNotificationsUtils.getUUIDsForAccount( - partialStorage, - 'ACCOUNT_THAT_DOES_NOT_EXIST_IN_STORAGE', - ); - expect(result.length).toBe(0); - }); -}); - -describe('metamask-notifications/utils - getAllUUIDs()', () => { - test('Returns all triggerIds in User Storage', () => { - const partialStorage = createMockUserStorageWithTriggers(['t1', 't2']); - const result1 = MetamaskNotificationsUtils.getAllUUIDs(partialStorage); - expect(result1.length).toBe(2); - expect(result1.includes('t1')).toBe(true); - expect(result1.includes('t2')).toBe(true); - - const fullStorage = createMockFullUserStorage(); - const result2 = MetamaskNotificationsUtils.getAllUUIDs(fullStorage); - expect(result2.length).toBeGreaterThan(2); // we expect there to be more than 2 triggers. We have multiple chains to there should be quite a few UUIDs. - }); -}); - -describe('metamask-notifications/utils - getUUIDsForKinds()', () => { - test('Returns all triggerIds that match the kind', () => { - const partialStorage = createMockUserStorageWithTriggers([ - { id: 't1', e: true, k: TRIGGER_TYPES.ERC1155_RECEIVED }, - { id: 't2', e: true, k: TRIGGER_TYPES.ERC1155_SENT }, - ]); - const result = MetamaskNotificationsUtils.getUUIDsForKinds(partialStorage, [ - TRIGGER_TYPES.ERC1155_RECEIVED, - ]); - expect(result).toEqual(['t1']); - }); - - test('Returns empty list if no triggers are found matching the kinds', () => { - const partialStorage = createMockUserStorageWithTriggers([ - { id: 't1', e: true, k: TRIGGER_TYPES.ERC1155_RECEIVED }, - { id: 't2', e: true, k: TRIGGER_TYPES.ERC1155_SENT }, - ]); - const result = MetamaskNotificationsUtils.getUUIDsForKinds(partialStorage, [ - TRIGGER_TYPES.ETH_SENT, // A kind we have not created a trigger for - ]); - expect(result.length).toBe(0); - }); -}); - -describe('metamask-notifications/utils - getUUIDsForAccountByKinds()', () => { - const createPartialStorage = () => - createMockUserStorageWithTriggers([ - { id: 't1', e: true, k: TRIGGER_TYPES.ERC1155_RECEIVED }, - { id: 't2', e: true, k: TRIGGER_TYPES.ERC1155_SENT }, - ]); - - test('Returns triggers with correct account and matching kinds', () => { - const partialStorage = createPartialStorage(); - const result = MetamaskNotificationsUtils.getUUIDsForAccountByKinds( - partialStorage, - MOCK_USER_STORAGE_ACCOUNT, - [TRIGGER_TYPES.ERC1155_RECEIVED], - ); - expect(result.length).toBe(1); - }); - - test('Returns empty when using incorrect account', () => { - const partialStorage = createPartialStorage(); - const result = MetamaskNotificationsUtils.getUUIDsForAccountByKinds( - partialStorage, - 'ACCOUNT_THAT_DOES_NOT_EXIST_IN_STORAGE', - [TRIGGER_TYPES.ERC1155_RECEIVED], - ); - expect(result.length).toBe(0); - }); - - test('Returns empty when using incorrect kind', () => { - const partialStorage = createPartialStorage(); - const result = MetamaskNotificationsUtils.getUUIDsForAccountByKinds( - partialStorage, - MOCK_USER_STORAGE_ACCOUNT, - [TRIGGER_TYPES.ETH_SENT], // this trigger was not created in partial storage - ); - expect(result.length).toBe(0); - }); -}); - -describe('metamask-notifications/utils - upsertAddressTriggers()', () => { - test('Updates and adds new triggers for a new address', () => { - const MOCK_NEW_ADDRESS = 'MOCK_NEW_ADDRESS'.toLowerCase(); // addresses stored in user storage are lower-case - const storage = createMockFullUserStorage(); - - // Before - expect(storage[MOCK_NEW_ADDRESS]).toBeUndefined(); - - MetamaskNotificationsUtils.upsertAddressTriggers(MOCK_NEW_ADDRESS, storage); - - // After - expect(storage[MOCK_NEW_ADDRESS]).toBeDefined(); - const newTriggers = MetamaskNotificationsUtils.getUUIDsForAccount( - storage, - MOCK_NEW_ADDRESS, - ); - expect(newTriggers.length > 0).toBe(true); - }); -}); - -describe('metamask-notifications/utils - upsertTriggerTypeTriggers()', () => { - test('Updates and adds a new trigger to an address', () => { - const partialStorage = createMockUserStorageWithTriggers([ - { id: 't1', e: true, k: TRIGGER_TYPES.ERC1155_RECEIVED }, - { id: 't2', e: true, k: TRIGGER_TYPES.ERC1155_SENT }, - ]); - - // Before - expect( - MetamaskNotificationsUtils.getUUIDsForAccount( - partialStorage, - MOCK_USER_STORAGE_ACCOUNT, - ).length, - ).toBe(2); - - MetamaskNotificationsUtils.upsertTriggerTypeTriggers( - TRIGGER_TYPES.ETH_SENT, - partialStorage, - ); - - // After - expect( - MetamaskNotificationsUtils.getUUIDsForAccount( - partialStorage, - MOCK_USER_STORAGE_ACCOUNT, - ).length, - ).toBe(3); - }); -}); - -describe('metamask-notifications/utils - toggleUserStorageTriggerStatus()', () => { - test('Updates Triggers from disabled to enabled', () => { - // Triggers are initially set to false false. - const partialStorage = createMockUserStorageWithTriggers([ - { id: 't1', k: TRIGGER_TYPES.ERC1155_RECEIVED, e: false }, - { id: 't2', k: TRIGGER_TYPES.ERC1155_SENT, e: false }, - ]); - - MetamaskNotificationsUtils.toggleUserStorageTriggerStatus( - partialStorage, - MOCK_USER_STORAGE_ACCOUNT, - MOCK_USER_STORAGE_CHAIN, - 't1', - true, - ); - - expect( - partialStorage[MOCK_USER_STORAGE_ACCOUNT][MOCK_USER_STORAGE_CHAIN].t1.e, - ).toBe(true); - }); -}); diff --git a/app/scripts/controllers/metamask-notifications/utils/utils.ts b/app/scripts/controllers/metamask-notifications/utils/utils.ts deleted file mode 100644 index 547a38b217d5..000000000000 --- a/app/scripts/controllers/metamask-notifications/utils/utils.ts +++ /dev/null @@ -1,619 +0,0 @@ -import log from 'loglevel'; -import { v4 as uuidv4 } from 'uuid'; -import type { UserStorage } from '../types/user-storage/user-storage'; -import { - USER_STORAGE_VERSION_KEY, - USER_STORAGE_VERSION, -} from '../constants/constants'; -import { - TRIGGER_TYPES, - TRIGGER_TYPES_GROUPS, - TRIGGERS, -} from '../constants/notification-schema'; - -export type NotificationTrigger = { - id: string; - chainId: string; - kind: string; - address: string; - enabled: boolean; -}; - -type MapTriggerFn = ( - trigger: NotificationTrigger, -) => Result | undefined; - -type TraverseTriggerOpts = { - address?: string; - mapTrigger?: MapTriggerFn; -}; - -/** - * Extracts and returns the ID from a notification trigger. - * This utility function is primarily used as a mapping function in `traverseUserStorageTriggers` - * to convert a full trigger object into its ID string. - * - * @param trigger - The notification trigger from which the ID is extracted. - * @returns The ID of the provided notification trigger. - */ -const triggerToId = (trigger: NotificationTrigger): string => trigger.id; - -/** - * A utility function that returns the input trigger without any transformation. - * This function is used as the default mapping function in `traverseUserStorageTriggers` - * when no custom mapping function is provided. - * - * @param trigger - The notification trigger to be returned as is. - * @returns The same notification trigger that was passed in. - */ -const triggerIdentity = (trigger: NotificationTrigger): NotificationTrigger => - trigger; - -/** - * Maps a given trigger type to its corresponding trigger group. - * - * This method categorizes each trigger type into one of the predefined groups: - * RECEIVED, SENT, or DEFI. These groups help in organizing triggers based on their nature. - * For instance, triggers related to receiving assets are categorized under RECEIVED, - * triggers for sending assets under SENT, and triggers related to decentralized finance (DeFi) - * operations under DEFI. This categorization aids in managing and responding to different types - * of notifications more effectively. - * - * @param type - The trigger type to be categorized. - * @returns The group to which the trigger type belongs. - */ -const groupTriggerTypes = (type: TRIGGER_TYPES): TRIGGER_TYPES_GROUPS => { - switch (type) { - case TRIGGER_TYPES.ERC20_RECEIVED: - case TRIGGER_TYPES.ETH_RECEIVED: - case TRIGGER_TYPES.ERC721_RECEIVED: - case TRIGGER_TYPES.ERC1155_RECEIVED: - return TRIGGER_TYPES_GROUPS.RECEIVED; - case TRIGGER_TYPES.ERC20_SENT: - case TRIGGER_TYPES.ETH_SENT: - case TRIGGER_TYPES.ERC721_SENT: - case TRIGGER_TYPES.ERC1155_SENT: - return TRIGGER_TYPES_GROUPS.SENT; - case TRIGGER_TYPES.METAMASK_SWAP_COMPLETED: - case TRIGGER_TYPES.ROCKETPOOL_STAKE_COMPLETED: - case TRIGGER_TYPES.ROCKETPOOL_UNSTAKE_COMPLETED: - case TRIGGER_TYPES.LIDO_STAKE_COMPLETED: - case TRIGGER_TYPES.LIDO_WITHDRAWAL_REQUESTED: - case TRIGGER_TYPES.LIDO_WITHDRAWAL_COMPLETED: - default: - return TRIGGER_TYPES_GROUPS.DEFI; - } -}; - -/** - * Create a completely new user storage object with the given accounts and state. - * This method initializes the user storage with a version key and iterates over each account to populate it with triggers. - * Each trigger is associated with supported chains, and for each chain, a unique identifier (UUID) is generated. - * The trigger object contains a kind (`k`) indicating the type of trigger and an enabled state (`e`). - * The kind and enabled state are stored with abbreviated keys to reduce the JSON size. - * - * This is used primarily for creating a new user storage (e.g. when first signing in/enabling notification profile syncing), - * caution is needed in case you need to remove triggers that you don't want (due to notification setting filters) - * - * @param accounts - An array of account objects, each optionally containing an address. - * @param state - A boolean indicating the initial enabled state for all triggers in the user storage. - * @returns A `UserStorage` object populated with triggers for each account and chain. - */ -export function initializeUserStorage( - accounts: { address?: string }[], - state: boolean, -): UserStorage { - const userStorage: UserStorage = { - [USER_STORAGE_VERSION_KEY]: USER_STORAGE_VERSION, - }; - - accounts.forEach((account) => { - const address = account.address?.toLowerCase(); - if (!address) { - return; - } - if (!userStorage[address]) { - userStorage[address] = {}; - } - - Object.entries(TRIGGERS).forEach( - ([trigger, { supported_chains: supportedChains }]) => { - supportedChains.forEach((chain) => { - if (!userStorage[address]?.[chain]) { - userStorage[address][chain] = {}; - } - - userStorage[address][chain][uuidv4()] = { - k: trigger as TRIGGER_TYPES, // use 'k' instead of 'kind' to reduce the json weight - e: state, // use 'e' instead of 'enabled' to reduce the json weight - }; - }); - }, - ); - }); - - return userStorage; -} - -/** - * Iterates over user storage to find and optionally transform notification triggers. - * This method allows for flexible retrieval and transformation of triggers based on provided options. - * - * @param userStorage - The user storage object containing notification triggers. - * @param options - Optional parameters to filter and map triggers: - * - `address`: If provided, only triggers for this address are considered. - * - `mapTrigger`: A function to transform each trigger. If not provided, triggers are returned as is. - * @returns An array of triggers, potentially transformed by the `mapTrigger` function. - */ -export function traverseUserStorageTriggers< - ResultTriggers = NotificationTrigger, ->( - userStorage: UserStorage, - options?: TraverseTriggerOpts, -): ResultTriggers[] { - const triggers: ResultTriggers[] = []; - const mapTrigger = - options?.mapTrigger ?? (triggerIdentity as MapTriggerFn); - - for (const address in userStorage) { - if (address === (USER_STORAGE_VERSION_KEY as unknown as string)) { - continue; - } - if (options?.address && address !== options.address) { - continue; - } - - for (const chainId in userStorage[address]) { - if (Object.hasOwn(userStorage[address], chainId)) { - for (const uuid in userStorage[address][chainId]) { - if (uuid) { - const mappedTrigger = mapTrigger({ - id: uuid, - kind: userStorage[address]?.[chainId]?.[uuid]?.k, - chainId, - address, - enabled: userStorage[address]?.[chainId]?.[uuid]?.e ?? false, - }); - if (mappedTrigger) { - triggers.push(mappedTrigger); - } - } - } - } - } - } - - return triggers; -} - -/** - * @deprecated - This needs rework for it to be feasible. Currently this is a half-baked solution, as it fails once we add new triggers (introspection for filters is difficult). - * - * Checks for the complete presence of trigger types by group across all addresses in the user storage. - * This method ensures that each address has at least one trigger of each type expected for every group. - * It leverages `traverseUserStorageTriggers` to iterate over triggers and check their presence. - * @param userStorage - The user storage object containing notification triggers. - * @returns A record indicating whether all expected trigger types for each group are present for every address. - */ -export function checkTriggersPresenceByGroup( - userStorage: UserStorage, -): Record { - // Initialize a record to track the complete presence of triggers for each group - const completeGroupPresence: Record = { - [TRIGGER_TYPES_GROUPS.RECEIVED]: true, - [TRIGGER_TYPES_GROUPS.SENT]: true, - [TRIGGER_TYPES_GROUPS.DEFI]: true, - }; - - // Map to track the required trigger types for each group - const requiredTriggersByGroup: Record< - TRIGGER_TYPES_GROUPS, - Set - > = { - [TRIGGER_TYPES_GROUPS.RECEIVED]: new Set([ - TRIGGER_TYPES.ERC20_RECEIVED, - TRIGGER_TYPES.ETH_RECEIVED, - TRIGGER_TYPES.ERC721_RECEIVED, - TRIGGER_TYPES.ERC1155_RECEIVED, - ]), - [TRIGGER_TYPES_GROUPS.SENT]: new Set([ - TRIGGER_TYPES.ERC20_SENT, - TRIGGER_TYPES.ETH_SENT, - TRIGGER_TYPES.ERC721_SENT, - TRIGGER_TYPES.ERC1155_SENT, - ]), - [TRIGGER_TYPES_GROUPS.DEFI]: new Set([ - TRIGGER_TYPES.METAMASK_SWAP_COMPLETED, - TRIGGER_TYPES.ROCKETPOOL_STAKE_COMPLETED, - TRIGGER_TYPES.ROCKETPOOL_UNSTAKE_COMPLETED, - TRIGGER_TYPES.LIDO_STAKE_COMPLETED, - TRIGGER_TYPES.LIDO_WITHDRAWAL_REQUESTED, - TRIGGER_TYPES.LIDO_WITHDRAWAL_COMPLETED, - TRIGGER_TYPES.LIDO_STAKE_READY_TO_BE_WITHDRAWN, - ]), - }; - - // Object to keep track of encountered triggers for each group by address - const encounteredTriggers: Record< - string, - Record> - > = {}; - - // Use traverseUserStorageTriggers to iterate over all triggers - traverseUserStorageTriggers(userStorage, { - mapTrigger: (trigger) => { - const group = groupTriggerTypes(trigger.kind as TRIGGER_TYPES); - if (!encounteredTriggers[trigger.address]) { - encounteredTriggers[trigger.address] = { - [TRIGGER_TYPES_GROUPS.RECEIVED]: new Set(), - [TRIGGER_TYPES_GROUPS.SENT]: new Set(), - [TRIGGER_TYPES_GROUPS.DEFI]: new Set(), - }; - } - encounteredTriggers[trigger.address][group].add( - trigger.kind as TRIGGER_TYPES, - ); - return undefined; // We don't need to transform the trigger, just record its presence - }, - }); - - // Check if all required triggers for each group are present for every address - Object.keys(encounteredTriggers).forEach((address) => { - Object.entries(requiredTriggersByGroup).forEach( - ([group, requiredTriggers]) => { - const hasAllTriggers = Array.from(requiredTriggers).every( - (triggerType) => - encounteredTriggers[address][group as TRIGGER_TYPES_GROUPS].has( - triggerType, - ), - ); - if (!hasAllTriggers) { - completeGroupPresence[group as TRIGGER_TYPES_GROUPS] = false; - } - }, - ); - }); - - return completeGroupPresence; -} - -/** - * Verifies the presence of specified accounts and their chains in the user storage. - * This method checks if each provided account exists in the user storage and if all its supported chains are present. - * - * @param userStorage - The user storage object containing notification triggers. - * @param accounts - An array of account addresses to check for presence. - * @returns A record where each key is an account address and each value is a boolean indicating whether the account and all its supported chains are present in the user storage. - */ -export function checkAccountsPresence( - userStorage: UserStorage, - accounts: string[], -): Record { - const presenceRecord: Record = {}; - - // Initialize presence record for all accounts as false - accounts.forEach((account) => { - presenceRecord[account.toLowerCase()] = isAccountEnabled( - account, - userStorage, - ); - }); - - return presenceRecord; -} - -function isAccountEnabled( - accountAddress: string, - userStorage: UserStorage, -): boolean { - const accountObject = userStorage[accountAddress?.toLowerCase()]; - - // If the account address is not present in the userStorage, return true - if (!accountObject) { - return false; - } - - // Check if all available chains are present - for (const [triggerKind, triggerConfig] of Object.entries(TRIGGERS)) { - for (const chain of triggerConfig.supported_chains) { - if (!accountObject[chain]) { - return false; - } - - const triggerExists = Object.values(accountObject[chain]).some( - (obj) => obj.k === triggerKind, - ); - if (!triggerExists) { - return false; - } - - // Check if any trigger is disabled - for (const uuid in accountObject[chain]) { - if (!accountObject[chain][uuid].e) { - return false; - } - } - } - } - - return true; -} - -/** - * Infers and returns an array of enabled notification trigger kinds from the user storage. - * This method counts the occurrences of each kind of trigger and returns the kinds that are present. - * - * @param userStorage - The user storage object containing notification triggers. - * @returns An array of trigger kinds (`TRIGGER_TYPES`) that are enabled in the user storage. - */ -export function inferEnabledKinds(userStorage: UserStorage): TRIGGER_TYPES[] { - const allSupportedKinds = new Set(); - - traverseUserStorageTriggers(userStorage, { - mapTrigger: (t) => { - allSupportedKinds.add(t.kind as TRIGGER_TYPES); - }, - }); - - return Array.from(allSupportedKinds); -} - -/** - * Retrieves all UUIDs associated with a specific account address from the user storage. - * This function utilizes `traverseUserStorageTriggers` with a mapping function to extract - * just the UUIDs of the notification triggers for the given address. - * - * @param userStorage - The user storage object containing notification triggers. - * @param address - The specific account address to retrieve UUIDs for. - * @returns An array of UUID strings associated with the given account address. - */ -export function getUUIDsForAccount( - userStorage: UserStorage, - address: string, -): string[] { - return traverseUserStorageTriggers(userStorage, { - address, - mapTrigger: triggerToId, - }); -} - -/** - * Retrieves all UUIDs from the user storage, regardless of the account address or chain ID. - * This method leverages `traverseUserStorageTriggers` with a specific mapping function (`triggerToId`) - * to extract only the UUIDs from all notification triggers present in the user storage. - * - * @param userStorage - The user storage object containing notification triggers. - * @returns An array of UUID strings from all notification triggers in the user storage. - */ -export function getAllUUIDs(userStorage: UserStorage): string[] { - return traverseUserStorageTriggers(userStorage, { - mapTrigger: triggerToId, - }); -} - -/** - * Retrieves UUIDs for notification triggers that match any of the specified kinds. - * This method filters triggers based on their kind and returns an array of UUIDs for those that match the allowed kinds. - * It utilizes `traverseUserStorageTriggers` with a custom mapping function that checks if a trigger's kind is in the allowed list. - * - * @param userStorage - The user storage object containing notification triggers. - * @param allowedKinds - An array of kinds (as strings) to filter the triggers by. - * @returns An array of UUID strings for triggers that match the allowed kinds. - */ -export function getUUIDsForKinds( - userStorage: UserStorage, - allowedKinds: string[], -): string[] { - const kindsSet = new Set(allowedKinds); - - return traverseUserStorageTriggers(userStorage, { - mapTrigger: (t) => (kindsSet.has(t.kind) ? t.id : undefined), - }); -} - -/** - * Retrieves notification triggers for a specific account address that match any of the specified kinds. - * This method filters triggers both by the account address and their kind, returning triggers that match the allowed kinds for the specified address. - * It leverages `traverseUserStorageTriggers` with a custom mapping function to filter and return only the relevant triggers. - * - * @param userStorage - The user storage object containing notification triggers. - * @param address - The specific account address for which to retrieve triggers. - * @param allowedKinds - An array of trigger kinds (`TRIGGER_TYPES`) to filter the triggers by. - * @returns An array of `NotificationTrigger` objects that match the allowed kinds for the specified account address. - */ -export function getUUIDsForAccountByKinds( - userStorage: UserStorage, - address: string, - allowedKinds: TRIGGER_TYPES[], -): NotificationTrigger[] { - const allowedKindsSet = new Set(allowedKinds); - return traverseUserStorageTriggers(userStorage, { - address, - mapTrigger: (trigger) => { - if (allowedKindsSet.has(trigger.kind as TRIGGER_TYPES)) { - return trigger; - } - return undefined; - }, - }); -} - -/** - * Upserts (updates or inserts) notification triggers for a given account across all supported chains. - * This method ensures that each supported trigger type exists for each chain associated with the account. - * If a trigger type does not exist for a chain, it creates a new trigger with a unique UUID. - * - * @param _account - The account address for which to upsert triggers. The address is normalized to lowercase. - * @param userStorage - The user storage object to be updated with new or existing triggers. - * @returns The updated user storage object with upserted triggers for the specified account. - */ -export function upsertAddressTriggers( - _account: string, - userStorage: UserStorage, -): UserStorage { - // Ensure the account exists in userStorage - const account = _account.toLowerCase(); - userStorage[account] = userStorage[account] || {}; - - // Iterate over each trigger and its supported chains - for (const [trigger, { supported_chains: supportedChains }] of Object.entries( - TRIGGERS, - )) { - for (const chain of supportedChains) { - // Ensure the chain exists for the account - userStorage[account][chain] = userStorage[account][chain] || {}; - - // Check if the trigger exists for the chain - const existingTrigger = Object.values(userStorage[account][chain]).find( - (obj) => obj.k === trigger, - ); - - if (!existingTrigger) { - // If the trigger doesn't exist, create a new one with a new UUID - const uuid = uuidv4(); - userStorage[account][chain][uuid] = { - k: trigger as TRIGGER_TYPES, - e: false, - }; - } - } - } - - return userStorage; -} - -/** - * Upserts (updates or inserts) notification triggers of a specific type across all accounts and chains in user storage. - * This method ensures that a trigger of the specified type exists for each account and chain. If a trigger of the specified type - * does not exist for an account and chain, it creates a new trigger with a unique UUID. - * - * @param triggerType - The type of trigger to upsert across all accounts and chains. - * @param userStorage - The user storage object to be updated with new or existing triggers of the specified type. - * @returns The updated user storage object with upserted triggers of the specified type for all accounts and chains. - */ -export function upsertTriggerTypeTriggers( - triggerType: TRIGGER_TYPES, - userStorage: UserStorage, -): UserStorage { - // Iterate over each account in userStorage - Object.entries(userStorage).forEach(([account, chains]) => { - if (account === (USER_STORAGE_VERSION_KEY as unknown as string)) { - return; - } - - // Iterate over each chain for the account - Object.entries(chains).forEach(([chain, triggers]) => { - // Check if the trigger type exists for the chain - const existingTrigger = Object.values(triggers).find( - (obj) => obj.k === triggerType, - ); - - if (!existingTrigger) { - // If the trigger type doesn't exist, create a new one with a new UUID - const uuid = uuidv4(); - userStorage[account][chain][uuid] = { - k: triggerType, - e: false, - }; - } - }); - }); - - return userStorage; -} - -/** - * Toggles the enabled status of a user storage trigger. - * - * @param userStorage - The user storage object. - * @param address - The user's address. - * @param chainId - The chain ID. - * @param uuid - The unique identifier for the trigger. - * @param enabled - The new enabled status. - * @returns The updated user storage object. - */ -export function toggleUserStorageTriggerStatus( - userStorage: UserStorage, - address: string, - chainId: string, - uuid: string, - enabled: boolean, -): UserStorage { - if (userStorage?.[address]?.[chainId]?.[uuid]) { - userStorage[address][chainId][uuid].e = enabled; - } - - return userStorage; -} - -/** - * Attempts to fetch a resource from the network, retrying the request up to a specified number of times - * in case of failure, with a delay between attempts. - * - * @param url - The resource URL. - * @param options - The options for the fetch request. - * @param retries - Maximum number of retry attempts. Defaults to 3. - * @param retryDelay - Delay between retry attempts in milliseconds. Defaults to 1000. - * @returns A Promise resolving to the Response object. - * @throws Will throw an error if the request fails after the specified number of retries. - */ -async function fetchWithRetry( - url: string, - options: RequestInit, - retries = 3, - retryDelay = 1000, -): Promise { - for (let attempt = 1; attempt <= retries; attempt++) { - try { - const response = await fetch(url, options); - if (!response.ok) { - throw new Error(`Fetch failed with status: ${response.status}`); - } - return response; - } catch (error) { - log.error(`Attempt ${attempt} failed for fetch:`, error); - if (attempt < retries) { - await new Promise((resolve) => setTimeout(resolve, retryDelay)); - } else { - throw new Error( - `Fetching failed after ${retries} retries. Last error: ${ - error instanceof Error ? error.message : 'Unknown error' - }`, - ); - } - } - } - - throw new Error('Unexpected error in fetchWithRetry'); -} - -/** - * Performs an API call with automatic retries on failure. - * - * @param bearerToken - The JSON Web Token for authorization. - * @param endpoint - The URL of the API endpoint to call. - * @param method - The HTTP method ('POST' or 'DELETE'). - * @param body - The body of the request. It should be an object that can be serialized to JSON. - * @param retries - The number of retry attempts in case of failure (default is 3). - * @param retryDelay - The delay between retries in milliseconds (default is 1000). - * @returns A Promise that resolves to the response of the fetch request. - */ -export async function makeApiCall( - bearerToken: string, - endpoint: string, - method: 'POST' | 'DELETE', - body: T, - retries = 1, - retryDelay = 1000, -): Promise { - const options: RequestInit = { - method, - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${bearerToken}`, - }, - body: JSON.stringify(body), - }; - - return fetchWithRetry(endpoint, options, retries, retryDelay); -} diff --git a/app/scripts/controllers/metametrics.test.js b/app/scripts/controllers/metametrics.test.js index 1df31a8d2f31..de75170b8e58 100644 --- a/app/scripts/controllers/metametrics.test.js +++ b/app/scripts/controllers/metametrics.test.js @@ -7,8 +7,9 @@ import { METAMETRICS_BACKGROUND_PAGE_OBJECT, MetaMetricsUserTrait, } from '../../../shared/constants/metametrics'; -import { CHAIN_IDS, CURRENCY_SYMBOLS } from '../../../shared/constants/network'; +import { CHAIN_IDS } from '../../../shared/constants/network'; import * as Utils from '../lib/util'; +import { mockNetworkState } from '../../../test/stub/networks'; import MetaMetricsController from './metametrics'; const segment = createSegmentMock(2, 10000); @@ -1037,17 +1038,11 @@ describe('MetaMetricsController', function () { }, }, allTokens: MOCK_ALL_TOKENS, - networkConfigurations: { - 'network-configuration-id-1': { - chainId: CHAIN_IDS.MAINNET, - ticker: CURRENCY_SYMBOLS.ETH, - }, - 'network-configuration-id-2': { - chainId: CHAIN_IDS.GOERLI, - ticker: CURRENCY_SYMBOLS.TEST_ETH, - }, - 'network-configuration-id-3': { chainId: '0xaf' }, - }, + ...mockNetworkState( + { chainId: CHAIN_IDS.MAINNET }, + { chainId: CHAIN_IDS.GOERLI }, + { chainId: '0xaf' }, + ), internalAccounts: { accounts: { mock1: {}, @@ -1131,16 +1126,17 @@ describe('MetaMetricsController', function () { it('should return only changed traits object on subsequent calls', function () { const metaMetricsController = getMetaMetricsController(); + const networkState = mockNetworkState( + { chainId: CHAIN_IDS.MAINNET }, + { chainId: CHAIN_IDS.GOERLI }, + ); metaMetricsController._buildUserTraitsObject({ addressBook: { [CHAIN_IDS.MAINNET]: [{ address: '0x' }], [CHAIN_IDS.GOERLI]: [{ address: '0x' }, { address: '0x0' }], }, allTokens: {}, - networkConfigurations: { - 'network-configuration-id-1': { chainId: CHAIN_IDS.MAINNET }, - 'network-configuration-id-2': { chainId: CHAIN_IDS.GOERLI }, - }, + ...networkState, ledgerTransportType: 'web-hid', openSeaEnabled: true, internalAccounts: { @@ -1166,10 +1162,7 @@ describe('MetaMetricsController', function () { '0xabcde': [{ '0x12345': { address: '0xtestAddress' } }], }, }, - networkConfigurations: { - 'network-configuration-id-1': { chainId: CHAIN_IDS.MAINNET }, - 'network-configuration-id-2': { chainId: CHAIN_IDS.GOERLI }, - }, + ...networkState, ledgerTransportType: 'web-hid', openSeaEnabled: false, internalAccounts: { @@ -1197,16 +1190,17 @@ describe('MetaMetricsController', function () { it('should return null if no traits changed', function () { const metaMetricsController = getMetaMetricsController(); + const networkState = mockNetworkState( + { chainId: CHAIN_IDS.MAINNET }, + { chainId: CHAIN_IDS.GOERLI }, + ); metaMetricsController._buildUserTraitsObject({ addressBook: { [CHAIN_IDS.MAINNET]: [{ address: '0x' }], [CHAIN_IDS.GOERLI]: [{ address: '0x' }, { address: '0x0' }], }, allTokens: {}, - networkConfigurations: { - 'network-configuration-id-1': { chainId: CHAIN_IDS.MAINNET }, - 'network-configuration-id-2': { chainId: CHAIN_IDS.GOERLI }, - }, + ...networkState, ledgerTransportType: 'web-hid', openSeaEnabled: true, internalAccounts: { @@ -1228,10 +1222,7 @@ describe('MetaMetricsController', function () { [CHAIN_IDS.GOERLI]: [{ address: '0x' }, { address: '0x0' }], }, allTokens: {}, - networkConfigurations: { - 'network-configuration-id-1': { chainId: CHAIN_IDS.MAINNET }, - 'network-configuration-id-2': { chainId: CHAIN_IDS.GOERLI }, - }, + ...networkState, ledgerTransportType: 'web-hid', openSeaEnabled: true, internalAccounts: { diff --git a/app/scripts/controllers/mmi-controller.test.ts b/app/scripts/controllers/mmi-controller.test.ts index 2c9f749d2490..3a9e6cddba6a 100644 --- a/app/scripts/controllers/mmi-controller.test.ts +++ b/app/scripts/controllers/mmi-controller.test.ts @@ -23,6 +23,7 @@ import { ControllerMessenger } from '@metamask/base-controller'; import { mmiKeyringBuilderFactory } from '../mmi-keyring-builder-factory'; import MetaMetricsController from './metametrics'; import { ETH_EOA_METHODS } from '../../../shared/constants/eth-methods'; +import { mockNetworkState } from '../../../test/stub/networks'; jest.mock('@metamask-institutional/portfolio-dashboard', () => ({ handleMmiPortfolio: jest.fn(), @@ -98,13 +99,7 @@ describe('MMIController', function () { 'NetworkController:infuraIsUnblocked', ], }), - state: { - providerConfig: { - type: NETWORK_TYPES.SEPOLIA, - chainId: CHAIN_IDS.SEPOLIA, - ticker: TEST_NETWORK_TICKER_MAP[NETWORK_TYPES.SEPOLIA], - }, - }, + state: mockNetworkState({chainId: CHAIN_IDS.SEPOLIA}), infuraProjectId: 'mock-infura-project-id', }); diff --git a/app/scripts/controllers/mmi-controller.ts b/app/scripts/controllers/mmi-controller.ts index 5530f876786d..1ba853a1b846 100644 --- a/app/scripts/controllers/mmi-controller.ts +++ b/app/scripts/controllers/mmi-controller.ts @@ -38,6 +38,7 @@ import { ConnectionRequest, } from '../../../shared/constants/mmi-controller'; import AccountTracker from '../lib/account-tracker'; +import { getCurrentChainId } from '../../../ui/selectors'; import MetaMetricsController from './metametrics'; import { getPermissionBackgroundApiMethods } from './permissions'; import { PreferencesController } from './preferences'; @@ -853,11 +854,11 @@ export default class MMIController extends EventEmitter { ); } const selectedChainId = parseInt( - this.networkController.state.providerConfig.chainId, + getCurrentChainId({ metamask: this.networkController.state }), 16, ); if (selectedChainId !== chainId && chainId === 1) { - await this.networkController.setProviderType('mainnet'); + await this.networkController.setActiveNetwork('mainnet'); } else if (selectedChainId !== chainId) { const { networkConfigurations } = this.networkController.state; diff --git a/app/scripts/controllers/permissions/specifications.js b/app/scripts/controllers/permissions/specifications.js index e88c12a74e1a..2d25ab16b1e4 100644 --- a/app/scripts/controllers/permissions/specifications.js +++ b/app/scripts/controllers/permissions/specifications.js @@ -25,7 +25,7 @@ import { */ export const PermissionNames = Object.freeze({ ...RestrictedMethods, - permittedChains: 'permittedChains', + permittedChains: 'endowment:permitted-chains', }); /** diff --git a/app/scripts/controllers/permissions/specifications.test.js b/app/scripts/controllers/permissions/specifications.test.js index 29f9f4f1b8ce..b27ec07a45b1 100644 --- a/app/scripts/controllers/permissions/specifications.test.js +++ b/app/scripts/controllers/permissions/specifications.test.js @@ -9,6 +9,7 @@ import { CaveatFactories, getCaveatSpecifications, getPermissionSpecifications, + PermissionNames, unrestrictedMethods, } from './specifications'; @@ -239,9 +240,9 @@ describe('PermissionController specifications', () => { expect( permissionSpecifications[RestrictedMethods.eth_accounts].targetName, ).toStrictEqual(RestrictedMethods.eth_accounts); - expect(permissionSpecifications.permittedChains.targetName).toStrictEqual( - 'permittedChains', - ); + expect( + permissionSpecifications[PermissionNames.permittedChains].targetName, + ).toStrictEqual('endowment:permitted-chains'); }); describe('eth_accounts', () => { diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index fb0c1498d65c..1db839386ed1 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -58,6 +58,7 @@ export default class PreferencesController { useRequestQueue: true, openSeaEnabled: true, // todo set this to true securityAlertsEnabled: true, + watchEthereumAccountEnabled: false, bitcoinSupportEnabled: false, bitcoinTestnetSupportEnabled: false, ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps) @@ -290,6 +291,20 @@ export default class PreferencesController { } ///: END:ONLY_INCLUDE_IF + ///: BEGIN:ONLY_INCLUDE_IF(build-flask) + /** + * Setter for the `watchEthereumAccountEnabled` property. + * + * @param {boolean} watchEthereumAccountEnabled - Whether or not the user wants to + * enable the "Watch Ethereum account (Beta)" button. + */ + setWatchEthereumAccountEnabled(watchEthereumAccountEnabled) { + this.store.updateState({ + watchEthereumAccountEnabled, + }); + } + ///: END:ONLY_INCLUDE_IF + /** * Setter for the `bitcoinSupportEnabled` property. * diff --git a/app/scripts/controllers/preferences.test.js b/app/scripts/controllers/preferences.test.js index f0058200b946..60784db8984f 100644 --- a/app/scripts/controllers/preferences.test.js +++ b/app/scripts/controllers/preferences.test.js @@ -5,23 +5,26 @@ import { ControllerMessenger } from '@metamask/base-controller'; import { TokenListController } from '@metamask/assets-controllers'; import { AccountsController } from '@metamask/accounts-controller'; import { CHAIN_IDS } from '../../../shared/constants/network'; +import { mockNetworkState } from '../../../test/stub/networks'; import PreferencesController from './preferences'; -const NETWORK_CONFIGURATION_DATA = { - 'test-networkConfigurationId-1': { +const NETWORK_CONFIGURATION_DATA = mockNetworkState( + { + id: 'test-networkConfigurationId-1', rpcUrl: 'https://testrpc.com', chainId: CHAIN_IDS.GOERLI, nickname: '0X5', rpcPrefs: { blockExplorerUrl: 'https://etherscan.io' }, }, - 'test-networkConfigurationId-2': { + { + id: 'test-networkConfigurationId-2', rpcUrl: 'http://localhost:8545', chainId: '0x539', ticker: 'ETH', nickname: 'Localhost 8545', rpcPrefs: {}, }, -}; +).networkConfigurations; describe('preferences controller', () => { let controllerMessenger; diff --git a/app/scripts/controllers/push-platform-notifications/utils/get-notification-image.ts b/app/scripts/controllers/push-notifications/get-notification-image.ts similarity index 100% rename from app/scripts/controllers/push-platform-notifications/utils/get-notification-image.ts rename to app/scripts/controllers/push-notifications/get-notification-image.ts diff --git a/app/scripts/controllers/push-platform-notifications/utils/get-notification-message.test.ts b/app/scripts/controllers/push-notifications/get-notification-message.test.ts similarity index 96% rename from app/scripts/controllers/push-platform-notifications/utils/get-notification-message.test.ts rename to app/scripts/controllers/push-notifications/get-notification-message.test.ts index fdbe7e345104..9d8a6a1e2fdf 100644 --- a/app/scripts/controllers/push-platform-notifications/utils/get-notification-message.test.ts +++ b/app/scripts/controllers/push-notifications/get-notification-message.test.ts @@ -1,4 +1,7 @@ -import { +import { NotificationServicesController } from '@metamask/notification-services-controller'; +import { createNotificationMessage } from './get-notification-message'; + +const { createMockNotificationERC1155Received, createMockNotificationERC1155Sent, createMockNotificationERC20Received, @@ -14,9 +17,9 @@ import { createMockNotificationMetaMaskSwapsCompleted, createMockNotificationRocketPoolStakeCompleted, createMockNotificationRocketPoolUnStakeCompleted, -} from '../../metamask-notifications/mocks/mock-raw-notifications'; -import { processNotification } from '../../metamask-notifications/processors/process-notifications'; -import { createNotificationMessage } from './get-notification-message'; +} = NotificationServicesController.Mocks; + +const { processNotification } = NotificationServicesController.Processors; describe('notification-message tests', () => { test('displays erc20 sent notification', () => { diff --git a/app/scripts/controllers/push-notifications/get-notification-message.ts b/app/scripts/controllers/push-notifications/get-notification-message.ts new file mode 100644 index 000000000000..dd47b766ce1a --- /dev/null +++ b/app/scripts/controllers/push-notifications/get-notification-message.ts @@ -0,0 +1,71 @@ +import type { NotificationServicesController } from '@metamask/notification-services-controller'; +import { NotificationsServicesPushController } from '@metamask/notification-services-controller'; +import { t as translate } from '../../translate'; + +const t = (...args: Parameters) => translate(...args) ?? ''; + +const translations: NotificationsServicesPushController.Utils.TranslationKeys = + { + pushPlatformNotificationsFundsSentTitle: () => + t('pushPlatformNotificationsFundsSentTitle'), + pushPlatformNotificationsFundsSentDescriptionDefault: () => + t('pushPlatformNotificationsFundsSentDescriptionDefault'), + pushPlatformNotificationsFundsSentDescription: (amount, symbol) => + t('pushPlatformNotificationsFundsSentDescription', amount, symbol), + pushPlatformNotificationsFundsReceivedTitle: () => + t('pushPlatformNotificationsFundsReceivedTitle'), + pushPlatformNotificationsFundsReceivedDescriptionDefault: () => + t('pushPlatformNotificationsFundsReceivedDescriptionDefault'), + pushPlatformNotificationsFundsReceivedDescription: (amount, symbol) => + t('pushPlatformNotificationsFundsReceivedDescription', amount, symbol), + pushPlatformNotificationsSwapCompletedTitle: () => + t('pushPlatformNotificationsSwapCompletedTitle'), + pushPlatformNotificationsSwapCompletedDescription: () => + t('pushPlatformNotificationsSwapCompletedDescription'), + pushPlatformNotificationsNftSentTitle: () => + t('pushPlatformNotificationsNftSentTitle'), + pushPlatformNotificationsNftSentDescription: () => + t('pushPlatformNotificationsNftSentDescription'), + pushPlatformNotificationsNftReceivedTitle: () => + t('pushPlatformNotificationsNftReceivedTitle'), + pushPlatformNotificationsNftReceivedDescription: () => + t('pushPlatformNotificationsNftReceivedDescription'), + pushPlatformNotificationsStakingRocketpoolStakeCompletedTitle: () => + t('pushPlatformNotificationsStakingRocketpoolStakeCompletedTitle'), + pushPlatformNotificationsStakingRocketpoolStakeCompletedDescription: () => + t('pushPlatformNotificationsStakingRocketpoolStakeCompletedDescription'), + pushPlatformNotificationsStakingRocketpoolUnstakeCompletedTitle: () => + t('pushPlatformNotificationsStakingRocketpoolUnstakeCompletedTitle'), + pushPlatformNotificationsStakingRocketpoolUnstakeCompletedDescription: () => + t( + 'pushPlatformNotificationsStakingRocketpoolUnstakeCompletedDescription', + ), + pushPlatformNotificationsStakingLidoStakeCompletedTitle: () => + t('pushPlatformNotificationsStakingLidoStakeCompletedTitle'), + pushPlatformNotificationsStakingLidoStakeCompletedDescription: () => + t('pushPlatformNotificationsStakingLidoStakeCompletedDescription'), + pushPlatformNotificationsStakingLidoStakeReadyToBeWithdrawnTitle: () => + t('pushPlatformNotificationsStakingLidoStakeReadyToBeWithdrawnTitle'), + pushPlatformNotificationsStakingLidoStakeReadyToBeWithdrawnDescription: + () => + t( + 'pushPlatformNotificationsStakingLidoStakeReadyToBeWithdrawnDescription', + ), + pushPlatformNotificationsStakingLidoWithdrawalRequestedTitle: () => + t('pushPlatformNotificationsStakingLidoWithdrawalRequestedTitle'), + pushPlatformNotificationsStakingLidoWithdrawalRequestedDescription: () => + t('pushPlatformNotificationsStakingLidoWithdrawalRequestedDescription'), + pushPlatformNotificationsStakingLidoWithdrawalCompletedTitle: () => + t('pushPlatformNotificationsStakingLidoWithdrawalCompletedTitle'), + pushPlatformNotificationsStakingLidoWithdrawalCompletedDescription: () => + t('pushPlatformNotificationsStakingLidoWithdrawalCompletedDescription'), + }; + +export function createNotificationMessage( + n: NotificationServicesController.Types.INotification, +) { + return NotificationsServicesPushController.Utils.createOnChainPushNotificationMessage( + n, + translations, + ); +} diff --git a/app/scripts/controllers/push-notifications/index.ts b/app/scripts/controllers/push-notifications/index.ts new file mode 100644 index 000000000000..c71ffdd250db --- /dev/null +++ b/app/scripts/controllers/push-notifications/index.ts @@ -0,0 +1,54 @@ +// We are defining that this file uses a webworker global scope. +// eslint-disable-next-line spaced-comment +/// + +import { NotificationServicesController } from '@metamask/notification-services-controller'; +import ExtensionPlatform from '../../platforms/extension'; +import { getNotificationImage } from './get-notification-image'; +import { createNotificationMessage } from './get-notification-message'; + +type INotification = NotificationServicesController.Types.INotification; + +const sw = self as unknown as ServiceWorkerGlobalScope; +const extensionPlatform = new ExtensionPlatform(); + +export async function onPushNotificationReceived( + notification: INotification, +): Promise { + const notificationMessage = createNotificationMessage(notification); + if (!notificationMessage) { + return; + } + + const registration = sw?.registration; + if (!registration) { + return; + } + + const iconUrl = await getNotificationImage(); + + await registration.showNotification(notificationMessage.title, { + body: notificationMessage.description, + icon: iconUrl, + tag: notification?.id, + data: notification, + }); +} + +export async function onPushNotificationClicked( + event: NotificationEvent, + notification?: INotification, +) { + // Close notification + event.notification.close(); + + // Get Data + const data: INotification = notification ?? event?.notification?.data; + + // Navigate + const destination = `${extensionPlatform.getExtensionURL( + null, + null, + )}#notifications/${data.id}`; + event.waitUntil(sw.clients.openWindow(destination)); +} diff --git a/app/scripts/controllers/push-platform-notifications/mocks/mockResponse.ts b/app/scripts/controllers/push-platform-notifications/mocks/mockResponse.ts deleted file mode 100644 index 3ff965b3444f..000000000000 --- a/app/scripts/controllers/push-platform-notifications/mocks/mockResponse.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { REGISTRATION_TOKENS_ENDPOINT } from '../services/endpoints'; -import { LinksResult } from '../services/services'; - -type MockResponse = { - url: string | RegExp; - requestMethod: 'GET' | 'POST' | 'PUT'; - response: unknown; -}; - -export const MOCK_REG_TOKEN = 'REG_TOKEN'; -export const MOCK_LINKS_RESPONSE: LinksResult = { - trigger_ids: ['1', '2', '3'], - registration_tokens: [ - { token: 'reg_token_1', platform: 'portfolio' }, - { token: 'reg_token_2', platform: 'extension' }, - ], -}; - -export function getMockRetrievePushNotificationLinksResponse() { - return { - url: REGISTRATION_TOKENS_ENDPOINT, - requestMethod: 'GET', - response: MOCK_LINKS_RESPONSE, - } satisfies MockResponse; -} - -export function getMockUpdatePushNotificationLinksResponse() { - return { - url: REGISTRATION_TOKENS_ENDPOINT, - requestMethod: 'POST', - response: null, - } satisfies MockResponse; -} - -export const MOCK_FCM_RESPONSE = { - name: '', - token: 'fcm-token', - web: { - endpoint: '', - p256dh: '', - auth: '', - applicationPubKey: '', - }, -}; - -export function getMockCreateFCMRegistrationTokenResponse() { - return { - url: /^https:\/\/fcmregistrations\.googleapis\.com\/v1\/projects\/.*$/u, - requestMethod: 'POST', - response: MOCK_FCM_RESPONSE, - } satisfies MockResponse; -} - -export function getMockDeleteFCMRegistrationTokenResponse() { - return { - url: /^https:\/\/fcmregistrations\.googleapis\.com\/v1\/projects\/.*$/u, - requestMethod: 'POST', - response: {}, - } satisfies MockResponse; -} diff --git a/app/scripts/controllers/push-platform-notifications/mocks/mockServices.ts b/app/scripts/controllers/push-platform-notifications/mocks/mockServices.ts deleted file mode 100644 index e0c3cb98d0c2..000000000000 --- a/app/scripts/controllers/push-platform-notifications/mocks/mockServices.ts +++ /dev/null @@ -1,36 +0,0 @@ -import nock from 'nock'; -import { - getMockRetrievePushNotificationLinksResponse, - getMockUpdatePushNotificationLinksResponse, -} from './mockResponse'; - -type MockReply = { - status: nock.StatusCode; - body?: nock.Body; -}; - -export function mockEndpointGetPushNotificationLinks(mockReply?: MockReply) { - const mockResponse = getMockRetrievePushNotificationLinksResponse(); - const reply = mockReply ?? { - status: 200, - body: mockResponse.response, - }; - - const mockEndpoint = nock(mockResponse.url) - .get('') - .reply(reply.status, reply.body); - - return mockEndpoint; -} - -export function mockEndpointUpdatePushNotificationLinks(mockReply?: MockReply) { - const mockResponse = getMockUpdatePushNotificationLinksResponse(); - const reply = mockReply ?? { - status: 200, - body: mockResponse.response, - }; - - const mockEndpoint = nock(mockResponse.url).post('').reply(reply.status); - - return mockEndpoint; -} diff --git a/app/scripts/controllers/push-platform-notifications/push-platform-notifications.test.ts b/app/scripts/controllers/push-platform-notifications/push-platform-notifications.test.ts deleted file mode 100644 index 2387c4dcfd9c..000000000000 --- a/app/scripts/controllers/push-platform-notifications/push-platform-notifications.test.ts +++ /dev/null @@ -1,171 +0,0 @@ -import { ControllerMessenger } from '@metamask/base-controller'; -import { AuthenticationController } from '@metamask/profile-sync-controller'; -import { isManifestV3 } from '../../../../shared/modules/mv3.utils'; -import type { - PushPlatformNotificationsControllerMessenger, - PushPlatformNotificationsControllerState, -} from './push-platform-notifications'; -import { PushPlatformNotificationsController } from './push-platform-notifications'; -import * as services from './services/services'; - -const MOCK_JWT = 'mockJwt'; -const MOCK_FCM_TOKEN = 'mockFcmToken'; -const MOCK_TRIGGERS = ['uuid1', 'uuid2']; - -const describeOnlyMV3 = isManifestV3 - ? describe - : (title: string, fn: (this: Mocha.Suite) => void) => - describe.skip( - `${title} skipped: No MV2 tests, this functionality is not enabled`, - fn, - ); - -describeOnlyMV3('PushPlatformNotificationsController', () => { - describe('enablePushNotifications', () => { - afterEach(() => { - jest.clearAllMocks(); - }); - - it('should update the state with the fcmToken', async () => { - await withController(async ({ controller, messenger }) => { - mockAuthBearerTokenCall(messenger); - jest - .spyOn(services, 'activatePushNotifications') - .mockResolvedValue(MOCK_FCM_TOKEN); - - const unsubscribeMock = jest.fn(); - jest - .spyOn(services, 'listenToPushNotifications') - .mockResolvedValue(unsubscribeMock); - - await controller.enablePushNotifications(MOCK_TRIGGERS); - expect(controller.state.fcmToken).toBe(MOCK_FCM_TOKEN); - - expect(services.listenToPushNotifications).toHaveBeenCalled(); - }); - }); - - it('should fail if a jwt token is not provided', async () => { - await withController(async ({ messenger, controller }) => { - mockAuthBearerTokenCall(messenger).mockResolvedValue( - null as unknown as string, - ); - await expect(controller.enablePushNotifications([])).rejects.toThrow(); - }); - }); - }); - - describe('disablePushNotifications', () => { - afterEach(() => { - jest.clearAllMocks(); - }); - - it('should update the state removing the fcmToken', async () => { - await withController(async ({ messenger, controller }) => { - mockAuthBearerTokenCall(messenger); - await controller.disablePushNotifications(MOCK_TRIGGERS); - expect(controller.state.fcmToken).toBe(''); - }); - }); - - it('should fail if a jwt token is not provided', async () => { - await withController(async ({ messenger, controller }) => { - mockAuthBearerTokenCall(messenger).mockResolvedValue( - null as unknown as string, - ); - await expect(controller.disablePushNotifications([])).rejects.toThrow(); - }); - }); - }); - - describe('updateTriggerPushNotifications', () => { - afterEach(() => { - jest.clearAllMocks(); - }); - - it('should call updateTriggerPushNotifications with the correct parameters', async () => { - await withController(async ({ messenger, controller }) => { - mockAuthBearerTokenCall(messenger); - const spy = jest - .spyOn(services, 'updateTriggerPushNotifications') - .mockResolvedValue({ - isTriggersLinkedToPushNotifications: true, - }); - - await controller.updateTriggerPushNotifications(MOCK_TRIGGERS); - - expect(spy).toHaveBeenCalledWith( - controller.state.fcmToken, - MOCK_JWT, - MOCK_TRIGGERS, - ); - }); - }); - }); -}); - -// Test helper functions - -type WithControllerCallback = ({ - controller, - initialState, - messenger, -}: { - controller: PushPlatformNotificationsController; - initialState: PushPlatformNotificationsControllerState; - messenger: PushPlatformNotificationsControllerMessenger; -}) => Promise | ReturnValue; - -function buildMessenger() { - return new ControllerMessenger< - AuthenticationController.AuthenticationControllerGetBearerToken, - never - >(); -} - -function buildPushPlatformNotificationsControllerMessenger( - messenger = buildMessenger(), -) { - return messenger.getRestricted({ - name: 'PushPlatformNotificationsController', - allowedActions: ['AuthenticationController:getBearerToken'], - allowedEvents: [], - }) as PushPlatformNotificationsControllerMessenger; -} - -async function withController( - fn: WithControllerCallback, -): Promise { - const messenger = buildPushPlatformNotificationsControllerMessenger(); - const controller = new PushPlatformNotificationsController({ - messenger, - state: { fcmToken: '' }, - }); - - return await fn({ - controller, - initialState: controller.state, - messenger, - }); -} - -function mockAuthBearerTokenCall( - messenger: PushPlatformNotificationsControllerMessenger, -) { - type Fn = - AuthenticationController.AuthenticationControllerGetBearerToken['handler']; - const mockAuthGetBearerToken = jest - .fn, Parameters>() - .mockResolvedValue(MOCK_JWT); - - jest.spyOn(messenger, 'call').mockImplementation((...args) => { - const [actionType] = args; - if (actionType === 'AuthenticationController:getBearerToken') { - return mockAuthGetBearerToken(); - } - - throw new Error('MOCK - unsupported messenger call mock'); - }); - - return mockAuthGetBearerToken; -} diff --git a/app/scripts/controllers/push-platform-notifications/push-platform-notifications.ts b/app/scripts/controllers/push-platform-notifications/push-platform-notifications.ts deleted file mode 100644 index 51cc81c14e7c..000000000000 --- a/app/scripts/controllers/push-platform-notifications/push-platform-notifications.ts +++ /dev/null @@ -1,279 +0,0 @@ -import { - BaseController, - RestrictedControllerMessenger, - ControllerGetStateAction, -} from '@metamask/base-controller'; -import { AuthenticationController } from '@metamask/profile-sync-controller'; - -import log from 'loglevel'; - -import { isManifestV3 } from '../../../../shared/modules/mv3.utils'; -import type { Notification } from '../metamask-notifications/types/notification/notification'; -import { - activatePushNotifications, - deactivatePushNotifications, - listenToPushNotifications, - updateTriggerPushNotifications, -} from './services/services'; - -const controllerName = 'PushPlatformNotificationsController'; - -export type PushPlatformNotificationsControllerState = { - fcmToken: string; -}; - -export declare type PushPlatformNotificationsControllerEnablePushNotifications = - { - type: `${typeof controllerName}:enablePushNotifications`; - handler: PushPlatformNotificationsController['enablePushNotifications']; - }; - -export declare type PushPlatformNotificationsControllerDisablePushNotifications = - { - type: `${typeof controllerName}:disablePushNotifications`; - handler: PushPlatformNotificationsController['disablePushNotifications']; - }; -export declare type PushPlatformNotificationsControllerUpdateTriggerPushNotifications = - { - type: `${typeof controllerName}:updateTriggerPushNotifications`; - handler: PushPlatformNotificationsController['updateTriggerPushNotifications']; - }; - -export type PushPlatformNotificationsControllerMessengerActions = - | PushPlatformNotificationsControllerEnablePushNotifications - | PushPlatformNotificationsControllerDisablePushNotifications - | PushPlatformNotificationsControllerUpdateTriggerPushNotifications - | ControllerGetStateAction<'state', PushPlatformNotificationsControllerState>; - -type AllowedActions = - AuthenticationController.AuthenticationControllerGetBearerToken; - -export type PushPlatformNotificationsControllerOnNewNotificationEvent = { - type: `${typeof controllerName}:onNewNotifications`; - payload: [Notification]; -}; - -export type PushPlatformNotificationsControllerPushNotificationClicked = { - type: `${typeof controllerName}:pushNotificationClicked`; - payload: [Notification]; -}; - -type AllowedEvents = - | PushPlatformNotificationsControllerOnNewNotificationEvent - | PushPlatformNotificationsControllerPushNotificationClicked; - -export type PushPlatformNotificationsControllerMessenger = - RestrictedControllerMessenger< - typeof controllerName, - PushPlatformNotificationsControllerMessengerActions | AllowedActions, - AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] - >; - -const metadata = { - fcmToken: { - persist: true, - anonymous: true, - }, -}; - -/** - * Manages push notifications for the application, including enabling, disabling, and updating triggers for push notifications. - * This controller integrates with Firebase Cloud Messaging (FCM) to handle the registration and management of push notifications. - * It is responsible for registering and unregistering the service worker that listens for push notifications, - * managing the FCM token, and communicating with the server to register or unregister the device for push notifications. - * Additionally, it provides functionality to update the server with new UUIDs that should trigger push notifications. - * - * @augments {BaseController} - */ -export class PushPlatformNotificationsController extends BaseController< - typeof controllerName, - PushPlatformNotificationsControllerState, - PushPlatformNotificationsControllerMessenger -> { - #pushListenerUnsubscribe: (() => void) | undefined = undefined; - - constructor({ - messenger, - state, - }: { - messenger: PushPlatformNotificationsControllerMessenger; - state: PushPlatformNotificationsControllerState; - }) { - super({ - messenger, - metadata, - name: controllerName, - state: { - fcmToken: state?.fcmToken || '', - }, - }); - - this.#registerMessageHandlers(); - } - - #registerMessageHandlers(): void { - this.messagingSystem.registerActionHandler( - 'PushPlatformNotificationsController:enablePushNotifications', - this.enablePushNotifications.bind(this), - ); - this.messagingSystem.registerActionHandler( - 'PushPlatformNotificationsController:disablePushNotifications', - this.disablePushNotifications.bind(this), - ); - this.messagingSystem.registerActionHandler( - 'PushPlatformNotificationsController:updateTriggerPushNotifications', - this.updateTriggerPushNotifications.bind(this), - ); - } - - async #getAndAssertBearerToken() { - const bearerToken = await this.messagingSystem.call( - 'AuthenticationController:getBearerToken', - ); - if (!bearerToken) { - log.error( - 'Failed to enable push notifications: BearerToken token is missing.', - ); - throw new Error('BearerToken token is missing'); - } - - return bearerToken; - } - - /** - * Enables push notifications for the application. - * - * This method sets up the necessary infrastructure for handling push notifications by: - * 1. Registering the service worker to listen for messages. - * 2. Fetching the Firebase Cloud Messaging (FCM) token from Firebase. - * 3. Sending the FCM token to the server responsible for sending notifications, to register the device. - * - * @param UUIDs - An array of UUIDs to enable push notifications for. - */ - public async enablePushNotifications(UUIDs: string[]) { - // TEMP: disabling push notifications if browser does not support MV3. - // Will need work to support firefox on MV2 - if (!isManifestV3) { - return; - } - - const bearerToken = await this.#getAndAssertBearerToken(); - - try { - // Activate Push Notifications - const regToken = await activatePushNotifications(bearerToken, UUIDs); - - if (!regToken) { - return; - } - - // Listen to push notifications - this.#pushListenerUnsubscribe ??= await listenToPushNotifications( - (n) => - this.messagingSystem.publish( - 'PushPlatformNotificationsController:onNewNotifications', - n, - ), - (n) => - this.messagingSystem.publish( - 'PushPlatformNotificationsController:pushNotificationClicked', - n, - ), - ); - - // Update state - this.update((state) => { - state.fcmToken = regToken; - }); - } catch (error) { - log.error('Failed to enable push notifications:', error); - throw new Error('Failed to enable push notifications'); - } - } - - /** - * Disables push notifications for the application. - * This method handles the process of disabling push notifications by: - * 1. Unregistering the service worker to stop listening for messages. - * 2. Sending a request to the server to unregister the device using the FCM token. - * 3. Removing the FCM token from the state to complete the process. - * - * @param UUIDs - An array of UUIDs for which push notifications should be disabled. - */ - public async disablePushNotifications(UUIDs: string[]) { - // TEMP: disabling push notifications if browser does not support MV3. - // Will need work to support firefox on MV2 - if (!isManifestV3) { - return; - } - - const bearerToken = await this.#getAndAssertBearerToken(); - let isPushNotificationsDisabled: boolean; - - try { - // Send a request to the server to unregister the token/device - isPushNotificationsDisabled = await deactivatePushNotifications( - this.state.fcmToken, - bearerToken, - UUIDs, - ); - } catch (error) { - const errorMessage = `Failed to disable push notifications: ${error}`; - log.error(errorMessage); - throw new Error(errorMessage); - } - - // Remove the FCM token from the state - if (!isPushNotificationsDisabled) { - return; - } - - // Unsubscribe from push notifications - this.#pushListenerUnsubscribe?.(); - - // Update State - if (isPushNotificationsDisabled) { - this.update((state) => { - state.fcmToken = ''; - }); - } - } - - /** - * Updates the triggers for push notifications. - * This method is responsible for updating the server with the new set of UUIDs that should trigger push notifications. - * It uses the current FCM token and a BearerToken for authentication. - * - * @param UUIDs - An array of UUIDs that should trigger push notifications. - */ - public async updateTriggerPushNotifications(UUIDs: string[]) { - // TEMP: disabling push notifications if browser does not support MV3. - // Will need work to support firefox on MV2 - if (!isManifestV3) { - return; - } - - const bearerToken = await this.#getAndAssertBearerToken(); - - try { - const { fcmToken } = await updateTriggerPushNotifications( - this.state.fcmToken, - bearerToken, - UUIDs, - ); - - // update the state with the new FCM token - if (fcmToken) { - this.update((state) => { - state.fcmToken = fcmToken; - }); - } - } catch (error) { - const errorMessage = `Failed to update triggers for push notifications: ${error}`; - log.error(errorMessage); - throw new Error(errorMessage); - } - } -} diff --git a/app/scripts/controllers/push-platform-notifications/services/endpoints.ts b/app/scripts/controllers/push-platform-notifications/services/endpoints.ts deleted file mode 100644 index ff6520eb03a5..000000000000 --- a/app/scripts/controllers/push-platform-notifications/services/endpoints.ts +++ /dev/null @@ -1,2 +0,0 @@ -const url = process.env.PUSH_NOTIFICATIONS_SERVICE_URL; -export const REGISTRATION_TOKENS_ENDPOINT = `${url}/v1/link`; diff --git a/app/scripts/controllers/push-platform-notifications/services/services.test.ts b/app/scripts/controllers/push-platform-notifications/services/services.test.ts deleted file mode 100644 index 413c2d28caf8..000000000000 --- a/app/scripts/controllers/push-platform-notifications/services/services.test.ts +++ /dev/null @@ -1,232 +0,0 @@ -import * as FirebaseApp from 'firebase/app'; -import * as FirebaseMessaging from 'firebase/messaging'; -import * as FirebaseMessagingSW from 'firebase/messaging/sw'; -import { - mockEndpointGetPushNotificationLinks, - mockEndpointUpdatePushNotificationLinks, -} from '../mocks/mockServices'; -import * as services from './services'; - -jest.mock('firebase/app'); -jest.mock('firebase/messaging'); -jest.mock('firebase/messaging/sw'); - -const MOCK_REG_TOKEN = 'REG_TOKEN'; -const MOCK_NEW_REG_TOKEN = 'NEW_REG_TOKEN'; -const MOCK_TRIGGERS = ['1', '2', '3']; -const MOCK_JWT = 'MOCK_JWT'; - -describe('PushPlatformNotificationsServices', () => { - describe('getPushNotificationLinks', () => { - it('Should return reg token links', async () => { - const mockGetLinksEndpoint = mockEndpointGetPushNotificationLinks(); - const res = await services.getPushNotificationLinks(MOCK_JWT); - - expect(mockGetLinksEndpoint.isDone()).toBe(true); - expect(res).toBeDefined(); - expect(res?.trigger_ids).toBeDefined(); - expect(res?.registration_tokens).toBeDefined(); - }); - - it('Should return null if api call fails', async () => { - const mockGetLinksEndpoint = mockEndpointGetPushNotificationLinks({ - status: 500, - }); - const res = await services.getPushNotificationLinks(MOCK_JWT); - - expect(mockGetLinksEndpoint.isDone()).toBe(true); - expect(res).toBeNull(); - }); - }); - - describe('updateLinksAPI', () => { - it('Should return true if links are updated', async () => { - const mockUpdateLinksEndpoint = mockEndpointUpdatePushNotificationLinks(); - - const res = await services.updateLinksAPI(MOCK_JWT, MOCK_TRIGGERS, [ - { token: MOCK_NEW_REG_TOKEN, platform: 'extension' }, - ]); - - expect(mockUpdateLinksEndpoint.isDone()).toBe(true); - expect(res).toBe(true); - }); - - it('Should return false if links are not updated', async () => { - mockEndpointUpdatePushNotificationLinks({ status: 500 }); - - const res = await services.updateLinksAPI(MOCK_JWT, MOCK_TRIGGERS, [ - { token: MOCK_NEW_REG_TOKEN, platform: 'extension' }, - ]); - - expect(res).toBe(false); - }); - }); - - describe('activatePushNotifications()', () => { - it('should append registration token when enabling push', async () => { - arrangeEndpoints(); - arrangeFCMMocks(); - - const res = await services.activatePushNotifications( - MOCK_JWT, - MOCK_TRIGGERS, - ); - - expect(res).toBe(MOCK_NEW_REG_TOKEN); - }); - - it('should fail if unable to get existing notification links', async () => { - mockEndpointGetPushNotificationLinks({ status: 500 }); - - const res = await services.activatePushNotifications( - MOCK_JWT, - MOCK_TRIGGERS, - ); - expect(res).toBeNull(); - }); - - it('should fail if unable to create new reg token', async () => { - arrangeEndpoints(); - const fcmMocks = arrangeFCMMocks(); - fcmMocks.getToken.mockRejectedValue(new Error('MOCK ERROR')); - const res = await services.activatePushNotifications( - MOCK_JWT, - MOCK_TRIGGERS, - ); - expect(res).toBeNull(); - }); - - it('should silently fail and return if failed to activate push notifications', async () => { - mockEndpointGetPushNotificationLinks(); - mockEndpointUpdatePushNotificationLinks({ status: 500 }); - arrangeFCMMocks(); - const res = await services.activatePushNotifications( - MOCK_JWT, - MOCK_TRIGGERS, - ); - - // We return the registration token, but we haven't updating the links. - // This can be redone at a later invocation (e.g. when the extension is re-initialized or notification setting changes) - expect(res).toBe(MOCK_NEW_REG_TOKEN); - }); - }); - - describe('deactivatePushNotifications()', () => { - it('should fail if unable to get existing notification links', async () => { - mockEndpointGetPushNotificationLinks({ status: 500 }); - - const res = await services.deactivatePushNotifications( - MOCK_REG_TOKEN, - MOCK_JWT, - MOCK_TRIGGERS, - ); - - expect(res).toBe(false); - }); - - it('should fail if unable to update links', async () => { - mockEndpointGetPushNotificationLinks(); - mockEndpointUpdatePushNotificationLinks({ status: 500 }); - - const res = await services.deactivatePushNotifications( - MOCK_REG_TOKEN, - MOCK_JWT, - MOCK_TRIGGERS, - ); - - expect(res).toBe(false); - }); - - it('should fail if unable to delete reg token', async () => { - arrangeEndpoints(); - const fcmMocks = arrangeFCMMocks(); - fcmMocks.deleteToken.mockRejectedValue(new Error('MOCK FAIL')); - - const res = await services.deactivatePushNotifications( - MOCK_REG_TOKEN, - MOCK_JWT, - MOCK_TRIGGERS, - ); - - expect(res).toBe(false); - }); - }); - - describe('updateTriggerPushNotifications()', () => { - it('should update triggers for push notifications', async () => { - arrangeEndpoints(); - - const res = await services.updateTriggerPushNotifications( - MOCK_REG_TOKEN, - MOCK_JWT, - MOCK_TRIGGERS, - ); - - expect(res).toEqual({ - isTriggersLinkedToPushNotifications: true, - fcmToken: expect.any(String), - }); - }); - - it('should fail if unable to update triggers', async () => { - mockEndpointGetPushNotificationLinks(); - mockEndpointUpdatePushNotificationLinks({ status: 500 }); - - const res = await services.updateTriggerPushNotifications( - MOCK_REG_TOKEN, - MOCK_JWT, - MOCK_TRIGGERS, - ); - - expect(res).toEqual({ - isTriggersLinkedToPushNotifications: false, - fcmToken: expect.any(String), - }); - }); - }); - - function arrangeEndpoints() { - const mockGetLinksEndpoint = mockEndpointGetPushNotificationLinks(); - const mockUpdateLinksEndpoint = mockEndpointUpdatePushNotificationLinks(); - - return { - mockGetLinksEndpoint, - mockUpdateLinksEndpoint, - }; - } - - function arrangeFCMMocks() { - const mockFirebaseApp: FirebaseApp.FirebaseApp = { - name: '', - automaticDataCollectionEnabled: false, - options: {}, - }; - const mockFirebaseMessaging: FirebaseMessagingSW.Messaging = { - app: mockFirebaseApp, - }; - - jest.spyOn(FirebaseApp, 'getApp').mockReturnValue(mockFirebaseApp); - jest.spyOn(FirebaseApp, 'initializeApp').mockReturnValue(mockFirebaseApp); - - const getMessaging = jest - .spyOn(FirebaseMessagingSW, 'getMessaging') - .mockReturnValue(mockFirebaseMessaging); - const onBackgroundMessage = jest - .spyOn(FirebaseMessagingSW, 'onBackgroundMessage') - .mockReturnValue(() => jest.fn()); - - const getToken = jest - .spyOn(FirebaseMessaging, 'getToken') - .mockResolvedValue(MOCK_NEW_REG_TOKEN); - const deleteToken = jest - .spyOn(FirebaseMessaging, 'deleteToken') - .mockResolvedValue(true); - - return { - getMessaging, - onBackgroundMessage, - getToken, - deleteToken, - }; - } -}); diff --git a/app/scripts/controllers/push-platform-notifications/services/services.ts b/app/scripts/controllers/push-platform-notifications/services/services.ts deleted file mode 100644 index 392cd22ad051..000000000000 --- a/app/scripts/controllers/push-platform-notifications/services/services.ts +++ /dev/null @@ -1,345 +0,0 @@ -import { getToken, deleteToken } from 'firebase/messaging'; -import type { FirebaseApp } from 'firebase/app'; -import { getApp, initializeApp } from 'firebase/app'; -import { getMessaging, onBackgroundMessage } from 'firebase/messaging/sw'; -import type { Messaging, MessagePayload } from 'firebase/messaging/sw'; -import log from 'loglevel'; -import { - onNotificationClick, - onPushNotification, -} from '../utils/get-notification-message'; -import { - Notification, - NotificationUnion, -} from '../../metamask-notifications/types/types'; -import { processNotification } from '../../metamask-notifications/processors/process-notifications'; -import { REGISTRATION_TOKENS_ENDPOINT } from './endpoints'; - -const sw = self as unknown as ServiceWorkerGlobalScope; - -export type RegToken = { - token: string; - platform: 'extension' | 'mobile' | 'portfolio'; -}; - -export type LinksResult = { - trigger_ids: string[]; - registration_tokens: RegToken[]; -}; - -/** - * Attempts to retrieve an existing Firebase app instance. If no instance exists, it initializes a new app with the provided configuration. - * - * @returns The Firebase app instance. - */ -async function createFirebaseApp(): Promise { - try { - return getApp(); - } catch { - const firebaseConfig = { - apiKey: process.env.FIREBASE_API_KEY, - authDomain: process.env.FIREBASE_AUTH_DOMAIN, - storageBucket: process.env.FIREBASE_STORAGE_BUCKET, - projectId: process.env.FIREBASE_PROJECT_ID, - messagingSenderId: process.env.FIREBASE_MESSAGING_SENDER_ID, - appId: process.env.FIREBASE_APP_ID, - measurementId: process.env.FIREBASE_MEASUREMENT_ID, - }; - return initializeApp(firebaseConfig); - } -} - -/** - * Retrieves the Firebase Messaging service instance. - * - * This function first ensures a Firebase app instance is created or retrieved by calling `createFirebaseApp`. - * It then initializes and returns the Firebase Messaging service associated with the Firebase app. - * - * @returns A promise that resolves with the Firebase Messaging service instance. - */ -async function getFirebaseMessaging(): Promise { - const app = await createFirebaseApp(); - return getMessaging(app); -} - -/** - * Creates a registration token for Firebase Cloud Messaging. - * - * @returns A promise that resolves with the registration token or null if an error occurs. - */ -async function createRegToken(): Promise { - try { - const messaging = await getFirebaseMessaging(); - const token = await getToken(messaging, { - serviceWorkerRegistration: sw.registration, - vapidKey: process.env.VAPID_KEY, - }); - return token; - } catch { - return null; - } -} - -/** - * Deletes the Firebase Cloud Messaging registration token. - * - * @returns A promise that resolves with true if the token was successfully deleted, false otherwise. - */ -async function deleteRegToken(): Promise { - try { - const messaging = await getFirebaseMessaging(); - await deleteToken(messaging); - return true; - } catch (error) { - return false; - } -} - -/** - * Fetches push notification links from a remote endpoint using a BearerToken for authorization. - * - * @param bearerToken - The JSON Web Token used for authorization. - * @returns A promise that resolves with the links result or null if an error occurs. - */ -export async function getPushNotificationLinks( - bearerToken: string, -): Promise { - try { - const response = await fetch(REGISTRATION_TOKENS_ENDPOINT, { - headers: { Authorization: `Bearer ${bearerToken}` }, - }); - if (!response.ok) { - log.error('Failed to fetch the push notification links'); - throw new Error('Failed to fetch the push notification links'); - } - return response.json() as Promise; - } catch (error) { - log.error('Failed to fetch the push notification links', error); - return null; - } -} - -/** - * Updates the push notification links on a remote API. - * - * @param bearerToken - The JSON Web Token used for authorization. - * @param triggers - An array of trigger identifiers. - * @param regTokens - An array of registration tokens. - * @returns A promise that resolves with true if the update was successful, false otherwise. - */ -export async function updateLinksAPI( - bearerToken: string, - triggers: string[], - regTokens: RegToken[], -): Promise { - try { - const body: LinksResult = { - trigger_ids: triggers, - registration_tokens: regTokens, - }; - const response = await fetch(REGISTRATION_TOKENS_ENDPOINT, { - method: 'POST', - headers: { - Authorization: `Bearer ${bearerToken}`, - 'Content-Type': 'application/json', - }, - body: JSON.stringify(body), - }); - return response.status === 200; - } catch { - return false; - } -} - -/** - * Enables push notifications by registering the device and linking triggers. - * - * @param bearerToken - The JSON Web Token used for authorization. - * @param triggers - An array of trigger identifiers. - * @returns A promise that resolves with an object containing the success status and the BearerToken token. - */ -export async function activatePushNotifications( - bearerToken: string, - triggers: string[], -): Promise { - const notificationLinks = await getPushNotificationLinks(bearerToken); - - if (!notificationLinks) { - return null; - } - - const regToken = await createRegToken().catch(() => null); - if (!regToken) { - return null; - } - - const newRegTokens = new Set(notificationLinks.registration_tokens); - newRegTokens.add({ token: regToken, platform: 'extension' }); - - await updateLinksAPI(bearerToken, triggers, Array.from(newRegTokens)); - return regToken; -} - -export async function listenToPushNotifications( - onNewNotification: (notification: Notification) => void, - onNotificationClicked: (notification: Notification) => void, -): Promise<() => void> { - /* - Push notifications require 2 listeners that need tracking (when creating and for tearing down): - 1. handling receiving a push notification (and the content we want to display) - 2. handling when a user clicks on a push notification - */ - - // Firebase - const messaging = await getFirebaseMessaging(); - const unsubscribePushNotifications = onBackgroundMessage( - messaging, - async (payload: MessagePayload): Promise => { - try { - const data = payload?.data?.data - ? JSON.parse(payload?.data?.data) - : undefined; - - // if the payload does not contain data, do nothing - if (!data) { - return; - } - - const notificationData = { - ...data, - type: data?.type ?? data?.data?.kind, - } as NotificationUnion; - - const notification = processNotification(notificationData); - onNewNotification(notification); - - await onPushNotification(notification); - } catch (error) { - // Do Nothing, cannot parse a bad notification - log.error('Unable to send push notification:', { - notification: payload?.data?.data, - error, - }); - throw new Error('Unable to send push notification'); - } - }, - ); - - // Notification Click Listener - const notificationClickHandler = (event: NotificationEvent) => { - onNotificationClick(event, onNotificationClicked); - }; - sw.addEventListener('notificationclick', notificationClickHandler); - const unsubscribeNotificationClicks = () => - sw.removeEventListener('notificationclick', notificationClickHandler); - - const unsubscribe = () => { - unsubscribePushNotifications(); - unsubscribeNotificationClicks(); - }; - - return unsubscribe; -} - -/** - * Handle Clicking Notifications. - */ - -/** - * Disables push notifications by removing the registration token and unlinking triggers. - * - * @param regToken - The registration token to be removed. - * @param bearerToken - The JSON Web Token used for authorization. - * @param triggers - An array of trigger identifiers to be unlinked. - * @returns A promise that resolves with true if notifications were successfully disabled, false otherwise. - */ -export async function deactivatePushNotifications( - regToken: string, - bearerToken: string, - triggers: string[], -): Promise { - // if we don't have a reg token, then we can early return - if (!regToken) { - return true; - } - - const notificationLinks = await getPushNotificationLinks(bearerToken); - if (!notificationLinks) { - return false; - } - - const filteredRegTokens = notificationLinks.registration_tokens.filter( - (r) => r.token !== regToken, - ); - - const isTokenRemovedFromAPI = await updateLinksAPI( - bearerToken, - triggers, - filteredRegTokens, - ); - if (!isTokenRemovedFromAPI) { - return false; - } - - const isTokenRemovedFromFCM = await deleteRegToken(); - if (!isTokenRemovedFromFCM) { - return false; - } - - return true; -} - -/** - * Updates the triggers linked to push notifications for a given registration token. - * If the provided registration token does not exist or is not in the current set of registration tokens, - * a new registration token is created and used for the update. - * - * @param regToken - The registration token to update triggers for. If null or not found, a new token will be created. - * @param bearerToken - The JSON Web Token used for authorization. - * @param triggers - An array of new trigger identifiers to link. - * @returns A promise that resolves with an object containing: - * - isTriggersLinkedToPushNotifications: boolean indicating if the triggers were successfully updated. - * - fcmToken: the new or existing Firebase Cloud Messaging token used for the update, if applicable. - */ -export async function updateTriggerPushNotifications( - regToken: string, - bearerToken: string, - triggers: string[], -): Promise<{ - isTriggersLinkedToPushNotifications: boolean; - fcmToken?: string | null; -}> { - const notificationLinks = await getPushNotificationLinks(bearerToken); - if (!notificationLinks) { - return { isTriggersLinkedToPushNotifications: false }; - } - // Create new registration token if doesn't exist - const hasRegToken = Boolean( - regToken && - notificationLinks.registration_tokens.some((r) => r.token === regToken), - ); - - let newRegToken: string | null = null; - if (!hasRegToken) { - await deleteRegToken(); - newRegToken = await createRegToken(); - if (!newRegToken) { - throw new Error('Failed to create a new registration token'); - } - notificationLinks.registration_tokens.push({ - token: newRegToken, - platform: 'extension', - }); - } - - const isTriggersLinkedToPushNotifications = await updateLinksAPI( - bearerToken, - triggers, - notificationLinks.registration_tokens, - ); - - return { - isTriggersLinkedToPushNotifications, - fcmToken: newRegToken ?? null, - }; -} diff --git a/app/scripts/controllers/push-platform-notifications/types/firebase.ts b/app/scripts/controllers/push-platform-notifications/types/firebase.ts deleted file mode 100644 index 91dc2c2e4d6f..000000000000 --- a/app/scripts/controllers/push-platform-notifications/types/firebase.ts +++ /dev/null @@ -1,46 +0,0 @@ -export declare type Messaging = { - app: FirebaseApp; -}; - -export declare type FirebaseApp = { - readonly name: string; - readonly options: FirebaseOptions; - automaticDataCollectionEnabled: boolean; -}; - -export declare type FirebaseOptions = { - apiKey?: string; - authDomain?: string; - databaseURL?: string; - projectId?: string; - storageBucket?: string; - messagingSenderId?: string; - appId?: string; - measurementId?: string; -}; - -export type NotificationPayload = { - title?: string; - body?: string; - image?: string; - icon?: string; -}; - -export type FcmOptions = { - link?: string; - analyticsLabel?: string; -}; - -export type MessagePayload = { - notification?: NotificationPayload; - data?: { [key: string]: string }; - fcmOptions?: FcmOptions; - from: string; - collapseKey: string; - messageId: string; -}; - -export type GetTokenOptions = { - vapidKey?: string; - serviceWorkerRegistration?: ServiceWorkerRegistration; -}; diff --git a/app/scripts/controllers/push-platform-notifications/utils/get-notification-data.test.ts b/app/scripts/controllers/push-platform-notifications/utils/get-notification-data.test.ts deleted file mode 100644 index 2ffbe89fdd9e..000000000000 --- a/app/scripts/controllers/push-platform-notifications/utils/get-notification-data.test.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { - formatAmount, - getAmount, - getLeadingZeroCount, -} from './get-notification-data'; - -describe('getNotificationData - formatAmount() tests', () => { - test('Should format large numbers', () => { - expect(formatAmount(1000)).toBe('1K'); - expect(formatAmount(1500)).toBe('1.5K'); - expect(formatAmount(1000000)).toBe('1M'); - expect(formatAmount(1000000000)).toBe('1B'); - expect(formatAmount(1000000000000)).toBe('1T'); - expect(formatAmount(1234567)).toBe('1.23M'); - }); - - test('Should format smaller numbers (<1000) with custom decimal place', () => { - const formatOptions = { decimalPlaces: 18 }; - expect(formatAmount(100.0012, formatOptions)).toBe('100.0012'); - expect(formatAmount(100.001200001, formatOptions)).toBe('100.001200001'); - expect(formatAmount(1e-18, formatOptions)).toBe('0.000000000000000001'); - expect(formatAmount(1e-19, formatOptions)).toBe('0'); // number is smaller than decimals given, hence 0 - }); - - test('Should format small numbers (<1000) up to 4 decimals otherwise uses ellipses', () => { - const formatOptions = { shouldEllipse: true }; - expect(formatAmount(100.1, formatOptions)).toBe('100.1'); - expect(formatAmount(100.01, formatOptions)).toBe('100.01'); - expect(formatAmount(100.001, formatOptions)).toBe('100.001'); - expect(formatAmount(100.0001, formatOptions)).toBe('100.0001'); - expect(formatAmount(100.00001, formatOptions)).toBe('100.0000...'); // since number is has >4 decimals, it will be truncated - expect(formatAmount(0.00001, formatOptions)).toBe('0.0000...'); // since number is has >4 decimals, it will be truncated - }); - - test('Should format small numbers (<1000) to custom decimal places and ellipse', () => { - const formatOptions = { decimalPlaces: 2, shouldEllipse: true }; - expect(formatAmount(100.1, formatOptions)).toBe('100.1'); - expect(formatAmount(100.01, formatOptions)).toBe('100.01'); - expect(formatAmount(100.001, formatOptions)).toBe('100.00...'); - expect(formatAmount(100.0001, formatOptions)).toBe('100.00...'); - expect(formatAmount(100.00001, formatOptions)).toBe('100.00...'); // since number is has >2 decimals, it will be truncated - expect(formatAmount(0.00001, formatOptions)).toBe('0.00...'); // since number is has >2 decimals, it will be truncated - }); -}); - -describe('getNotificationData - getAmount() tests', () => { - test('Should get formatted amount for larger numbers', () => { - expect(getAmount('1', '2')).toBe('0.01'); - expect(getAmount('10', '2')).toBe('0.1'); - expect(getAmount('100', '2')).toBe('1'); - expect(getAmount('1000', '2')).toBe('10'); - expect(getAmount('10000', '2')).toBe('100'); - expect(getAmount('100000', '2')).toBe('1K'); - expect(getAmount('1000000', '2')).toBe('10K'); - }); - test('Should get formatted amount for small/decimal numbers', () => { - const formatOptions = { shouldEllipse: true }; - expect(getAmount('100000', '5', formatOptions)).toBe('1'); - expect(getAmount('100001', '5', formatOptions)).toBe('1.0000...'); - expect(getAmount('10000', '5', formatOptions)).toBe('0.1'); - expect(getAmount('1000', '5', formatOptions)).toBe('0.01'); - expect(getAmount('100', '5', formatOptions)).toBe('0.001'); - expect(getAmount('10', '5', formatOptions)).toBe('0.0001'); - expect(getAmount('1', '5', formatOptions)).toBe('0.0000...'); - }); -}); - -describe('getNotificationData - getLeadingZeroCount() tests', () => { - test('Should handle all test cases', () => { - expect(getLeadingZeroCount(0)).toBe(0); - expect(getLeadingZeroCount(-1)).toBe(0); - expect(getLeadingZeroCount(1e-1)).toBe(0); - - expect(getLeadingZeroCount('1.01')).toBe(1); - expect(getLeadingZeroCount('3e-2')).toBe(1); - expect(getLeadingZeroCount('100.001e1')).toBe(1); - - expect(getLeadingZeroCount('0.00120043')).toBe(2); - }); -}); diff --git a/app/scripts/controllers/push-platform-notifications/utils/get-notification-data.ts b/app/scripts/controllers/push-platform-notifications/utils/get-notification-data.ts deleted file mode 100644 index f95149c54c19..000000000000 --- a/app/scripts/controllers/push-platform-notifications/utils/get-notification-data.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { BigNumber } from 'bignumber.js'; -import { calcTokenAmount } from '../../../../../shared/lib/transactions-controller-utils'; - -type FormatOptions = { - decimalPlaces?: number; - shouldEllipse?: boolean; -}; -const defaultFormatOptions = { - decimalPlaces: 4, -}; - -/** - * Calculates the number of leading zeros in the fractional part of a number. - * - * This function converts a number or a string representation of a number into - * its decimal form and then counts the number of leading zeros present in the - * fractional part of the number. This is useful for determining the precision - * of very small numbers. - * - * @param num - The number to analyze, which can be in the form - * of a number or a string. - * @returns The count of leading zeros in the fractional part of the number. - */ -export const getLeadingZeroCount = (num: number | string) => { - const numToString = new BigNumber(num, 10).toString(10); - const fractionalPart = numToString.split('.')[1] ?? ''; - return fractionalPart.match(/^0*/u)?.[0]?.length || 0; -}; - -/** - * This formats a number using Intl - * It abbreviates large numbers (using K, M, B, T) - * And abbreviates small numbers in 2 ways: - * - Will format to the given number of decimal places - * - Will format up to 4 decimal places - * - Will ellipse the number if longer than given decimal places - * - * @param numericAmount - The number to format - * @param opts - The options to use when formatting - * @returns The formatted number - */ -export const formatAmount = (numericAmount: number, opts?: FormatOptions) => { - // create options with defaults - const options = { ...defaultFormatOptions, ...opts }; - - const leadingZeros = getLeadingZeroCount(numericAmount); - const isDecimal = numericAmount.toString().includes('.') || leadingZeros > 0; - const isLargeNumber = numericAmount > 999; - - const handleShouldEllipse = (decimalPlaces: number) => - Boolean(options?.shouldEllipse) && leadingZeros >= decimalPlaces; - - if (isLargeNumber) { - return Intl.NumberFormat('en-US', { - notation: 'compact', - compactDisplay: 'short', - maximumFractionDigits: 2, - }).format(numericAmount); - } - - if (isDecimal) { - const ellipse = handleShouldEllipse(options.decimalPlaces); - const formattedValue = Intl.NumberFormat('en-US', { - minimumFractionDigits: ellipse ? options.decimalPlaces : undefined, - maximumFractionDigits: options.decimalPlaces, - }).format(numericAmount); - - return ellipse ? `${formattedValue}...` : formattedValue; - } - - // Default to showing the raw amount - return numericAmount.toString(); -}; - -export const getAmount = ( - amount: string, - decimals: string, - options?: FormatOptions, -) => { - if (!amount || !decimals) { - return ''; - } - - const numericAmount = calcTokenAmount( - amount, - parseFloat(decimals), - ).toNumber(); - - return formatAmount(numericAmount, options); -}; diff --git a/app/scripts/controllers/push-platform-notifications/utils/get-notification-message.ts b/app/scripts/controllers/push-platform-notifications/utils/get-notification-message.ts deleted file mode 100644 index 514c5b40fadc..000000000000 --- a/app/scripts/controllers/push-platform-notifications/utils/get-notification-message.ts +++ /dev/null @@ -1,264 +0,0 @@ -// We are defining that this file uses a webworker global scope. -// eslint-disable-next-line spaced-comment -/// - -import { CHAIN_SYMBOLS } from '../../metamask-notifications/constants/notification-schema'; -import type { TRIGGER_TYPES } from '../../metamask-notifications/constants/notification-schema'; -import type { OnChainRawNotification } from '../../metamask-notifications/types/on-chain-notification/on-chain-notification'; -import { t } from '../../../translate'; -import type { Notification } from '../../metamask-notifications/types/types'; -import ExtensionPlatform from '../../../platforms/extension'; -import { getAmount, formatAmount } from './get-notification-data'; -import { getNotificationImage } from './get-notification-image'; - -type PushNotificationMessage = { - title: string; - description: string; -}; - -type NotificationMessage = { - title: string | null; - defaultDescription: string | null; - getDescription?: (n: N) => string | null; -}; - -type NotificationMessageDict = { - [K in TRIGGER_TYPES]?: NotificationMessage< - Extract - >; -}; - -const sw = self as unknown as ServiceWorkerGlobalScope; -const extensionPlatform = new ExtensionPlatform(); - -function getChainSymbol(chainId: number) { - return CHAIN_SYMBOLS[chainId] ?? null; -} - -export async function onPushNotification( - notification: Notification, -): Promise { - const notificationMessage = createNotificationMessage(notification); - if (!notificationMessage) { - return; - } - - const registration = sw?.registration; - if (!registration) { - return; - } - - const iconUrl = await getNotificationImage(); - - await registration.showNotification(notificationMessage.title, { - body: notificationMessage.description, - icon: iconUrl, - tag: notification?.id, - data: notification, - }); -} - -export async function onNotificationClick( - event: NotificationEvent, - emitEvent?: (n: Notification) => void, -) { - // Close notification - event.notification.close(); - - // Get Data - const data: Notification = event?.notification?.data; - emitEvent?.(data); - - // Navigate - const destination = `${extensionPlatform.getExtensionURL( - null, - null, - )}#notifications/${data.id}`; - event.waitUntil(sw.clients.openWindow(destination)); -} - -export function isOnChainNotification(n: unknown): n is OnChainRawNotification { - const assumed = n as OnChainRawNotification; - - // We don't have a validation/parsing library to check all possible types of an on chain notification - // It is safe enough just to check "some" fields, and catch any errors down the line if the shape is bad. - const isValidEnoughToBeOnChainNotification = [ - assumed?.id, - assumed?.data, - assumed?.trigger_id, - ].every((field) => field !== undefined); - return isValidEnoughToBeOnChainNotification; -} - -const notificationMessageDict: NotificationMessageDict = { - erc20_sent: { - title: t('pushPlatformNotificationsFundsSentTitle'), - defaultDescription: t( - 'pushPlatformNotificationsFundsSentDescriptionDefault', - ), - getDescription: (n) => { - const symbol = n?.data?.token?.symbol; - const tokenAmount = n?.data?.token?.amount; - const tokenDecimals = n?.data?.token?.decimals; - if (!symbol || !tokenAmount || !tokenDecimals) { - return null; - } - - const amount = getAmount(tokenAmount, tokenDecimals, { - shouldEllipse: true, - }); - return t('pushPlatformNotificationsFundsSentDescription', amount, symbol); - }, - }, - eth_sent: { - title: t('pushPlatformNotificationsFundsSentTitle'), - defaultDescription: t( - 'pushPlatformNotificationsFundsSentDescriptionDefault', - ), - getDescription: (n) => { - const symbol = getChainSymbol(n?.chain_id); - const tokenAmount = n?.data?.amount?.eth; - if (!symbol || !tokenAmount) { - return null; - } - - const amount = formatAmount(parseFloat(tokenAmount), { - shouldEllipse: true, - }); - return t('pushPlatformNotificationsFundsSentDescription', amount, symbol); - }, - }, - erc20_received: { - title: t('pushPlatformNotificationsFundsReceivedTitle'), - defaultDescription: t( - 'pushPlatformNotificationsFundsReceivedDescriptionDefault', - ), - getDescription: (n) => { - const symbol = n?.data?.token?.symbol; - const tokenAmount = n?.data?.token?.amount; - const tokenDecimals = n?.data?.token?.decimals; - if (!symbol || !tokenAmount || !tokenDecimals) { - return null; - } - - const amount = getAmount(tokenAmount, tokenDecimals, { - shouldEllipse: true, - }); - return t( - 'pushPlatformNotificationsFundsReceivedDescription', - amount, - symbol, - ); - }, - }, - eth_received: { - title: t('pushPlatformNotificationsFundsReceivedTitle'), - defaultDescription: t( - 'pushPlatformNotificationsFundsReceivedDescriptionDefault', - ), - getDescription: (n) => { - const symbol = getChainSymbol(n?.chain_id); - const tokenAmount = n?.data?.amount?.eth; - if (!symbol || !tokenAmount) { - return null; - } - - const amount = formatAmount(parseFloat(tokenAmount), { - shouldEllipse: true, - }); - return t( - 'pushPlatformNotificationsFundsReceivedDescription', - amount, - symbol, - ); - }, - }, - metamask_swap_completed: { - title: t('pushPlatformNotificationsSwapCompletedTitle'), - defaultDescription: t('pushPlatformNotificationsSwapCompletedDescription'), - }, - erc721_sent: { - title: t('pushPlatformNotificationsNftSentTitle'), - defaultDescription: t('pushPlatformNotificationsNftSentDescription'), - }, - erc1155_sent: { - title: t('pushPlatformNotificationsNftSentTitle'), - defaultDescription: t('pushPlatformNotificationsNftSentDescription'), - }, - erc721_received: { - title: t('pushPlatformNotificationsNftReceivedTitle'), - defaultDescription: t('pushPlatformNotificationsNftReceivedDescription'), - }, - erc1155_received: { - title: t('pushPlatformNotificationsNftReceivedTitle'), - defaultDescription: t('pushPlatformNotificationsNftReceivedDescription'), - }, - rocketpool_stake_completed: { - title: t('pushPlatformNotificationsStakingRocketpoolStakeCompletedTitle'), - defaultDescription: t( - 'pushPlatformNotificationsStakingRocketpoolStakeCompletedDescription', - ), - }, - rocketpool_unstake_completed: { - title: t('pushPlatformNotificationsStakingRocketpoolUnstakeCompletedTitle'), - defaultDescription: t( - 'pushPlatformNotificationsStakingRocketpoolUnstakeCompletedDescription', - ), - }, - lido_stake_completed: { - title: t('pushPlatformNotificationsStakingLidoStakeCompletedTitle'), - defaultDescription: t( - 'pushPlatformNotificationsStakingLidoStakeCompletedDescription', - ), - }, - lido_stake_ready_to_be_withdrawn: { - title: t( - 'pushPlatformNotificationsStakingLidoStakeReadyToBeWithdrawnTitle', - ), - defaultDescription: t( - 'pushPlatformNotificationsStakingLidoStakeReadyToBeWithdrawnDescription', - ), - }, - lido_withdrawal_requested: { - title: t('pushPlatformNotificationsStakingLidoWithdrawalRequestedTitle'), - defaultDescription: t( - 'pushPlatformNotificationsStakingLidoWithdrawalRequestedDescription', - ), - }, - lido_withdrawal_completed: { - title: t('pushPlatformNotificationsStakingLidoWithdrawalCompletedTitle'), - defaultDescription: t( - 'pushPlatformNotificationsStakingLidoWithdrawalCompletedDescription', - ), - }, -}; - -export function createNotificationMessage( - n: Notification, -): PushNotificationMessage | null { - if (!n?.type) { - return null; - } - const notificationMessage = notificationMessageDict[n.type] as - | NotificationMessage - | undefined; - - if (!notificationMessage) { - return null; - } - - let description: string | null = null; - try { - description = - notificationMessage?.getDescription?.(n) ?? - notificationMessage.defaultDescription ?? - null; - } catch (e) { - description = notificationMessage.defaultDescription ?? null; - } - - return { - title: notificationMessage.title ?? '', // Ensure title is always a string - description: description ?? '', // Fallback to empty string if null - }; -} diff --git a/app/scripts/controllers/swaps/index.ts b/app/scripts/controllers/swaps/index.ts index 0b623068d8f3..c39e8c14d3d7 100644 --- a/app/scripts/controllers/swaps/index.ts +++ b/app/scripts/controllers/swaps/index.ts @@ -481,7 +481,6 @@ export default class SwapsController extends BaseController< ): Promise<[string | null, Record] | Record> { const { marketData } = this._getTokenRatesState(); const chainId = this._getCurrentChainId(); - const tokenConversionRates = marketData?.[chainId] ?? {}; const { customGasPrice, customMaxPriorityFeePerGas } = @@ -1087,7 +1086,7 @@ export default class SwapsController extends BaseController< swapsNetworkConfig?.stxMaxFeeMultiplier || FALLBACK_SMART_TRANSACTIONS_MAX_FEE_MULTIPLIER; _state.swapsState.swapsStxStatusDeadline = - swapsNetworkConfig?.swapsStxStatusDeadline || + swapsNetworkConfig?.stxStatusDeadline || FALLBACK_SMART_TRANSACTIONS_DEADLINE; }); } diff --git a/app/scripts/controllers/swaps/swaps.test.ts b/app/scripts/controllers/swaps/swaps.test.ts index 8b2fbd22d032..e62f08e9d15f 100644 --- a/app/scripts/controllers/swaps/swaps.test.ts +++ b/app/scripts/controllers/swaps/swaps.test.ts @@ -1171,6 +1171,7 @@ describe('SwapsController', function () { const swapsQuotePrefetchingRefreshTime = 0; const swapsStxBatchStatusRefreshTime = 0; const swapsStxGetTransactionsRefreshTime = 0; + swapsController.__test__updateState({ swapsState: { ...swapsController.state.swapsState, diff --git a/app/scripts/lib/backup.test.js b/app/scripts/lib/backup.test.js index 0de359da7758..947761449a36 100644 --- a/app/scripts/lib/backup.test.js +++ b/app/scripts/lib/backup.test.js @@ -3,6 +3,7 @@ */ import { EthAccountType } from '@metamask/keyring-api'; import { ETH_EOA_METHODS } from '../../../shared/constants/eth-methods'; +import { mockNetworkState } from '../../../test/stub/networks'; import Backup from './backup'; function getMockPreferencesController() { @@ -105,15 +106,17 @@ const jsonData = JSON.stringify({ }, }, network: { - networkConfigurations: { - 'network-configuration-id-1': { + ...mockNetworkState( + { + id: 'network-configuration-id-1', chainId: '0x539', nickname: 'Localhost 8545', rpcPrefs: {}, rpcUrl: 'http://localhost:8545', ticker: 'ETH', }, - 'network-configuration-id-2': { + { + id: 'network-configuration-id-2', chainId: '0x38', nickname: 'Binance Smart Chain Mainnet', rpcPrefs: { @@ -122,7 +125,8 @@ const jsonData = JSON.stringify({ rpcUrl: 'https://bsc-dataseed1.binance.org', ticker: 'BNB', }, - 'network-configuration-id-3': { + { + id: 'network-configuration-id-3', chainId: '0x61', nickname: 'Binance Smart Chain Testnet', rpcPrefs: { @@ -131,7 +135,8 @@ const jsonData = JSON.stringify({ rpcUrl: 'https://data-seed-prebsc-1-s1.binance.org:8545', ticker: 'tBNB', }, - 'network-configuration-id-4': { + { + id: 'network-configuration-id-4', chainId: '0x89', nickname: 'Polygon Mainnet', rpcPrefs: { @@ -140,7 +145,7 @@ const jsonData = JSON.stringify({ rpcUrl: 'https://polygon-rpc.com', ticker: 'MATIC', }, - }, + ), }, preferences: { useBlockie: false, diff --git a/app/scripts/lib/createDupeReqFilterStream.ts b/app/scripts/lib/createDupeReqFilterStream.ts index ded95c5fa99f..04601db30124 100644 --- a/app/scripts/lib/createDupeReqFilterStream.ts +++ b/app/scripts/lib/createDupeReqFilterStream.ts @@ -54,7 +54,8 @@ export default function createDupeReqFilterStream() { transform(chunk: JsonRpcRequest, _, cb) { // JSON-RPC notifications have no ids; our only recourse is to let them through. const hasNoId = chunk.id === undefined; - const requestNotYetSeen = seenRequestIds.add(chunk.id); + const requestNotYetSeen = + chunk.id !== null && seenRequestIds.add(chunk.id); if (hasNoId || requestNotYetSeen) { cb(null, chunk); diff --git a/app/scripts/lib/createRPCMethodTrackingMiddleware.js b/app/scripts/lib/createRPCMethodTrackingMiddleware.js index abfb436d09f7..3e799cc4828f 100644 --- a/app/scripts/lib/createRPCMethodTrackingMiddleware.js +++ b/app/scripts/lib/createRPCMethodTrackingMiddleware.js @@ -341,6 +341,7 @@ export default function createRPCMethodTrackingMiddleware({ if (shouldTrackEvent === false || typeof eventType === 'undefined') { return callback(); } + const location = res.error?.data?.location; let event; if (res.error?.code === errorCodes.provider.userRejectedRequest) { @@ -367,10 +368,10 @@ export default function createRPCMethodTrackingMiddleware({ securityAlertResponse, }); } - const properties = { ...eventProperties, ...blockaidMetricProps, + location, // if security_alert_response from blockaidMetricProps is Benign, force set security_alert_reason to empty string security_alert_reason: blockaidMetricProps.security_alert_response === diff --git a/app/scripts/lib/createRPCMethodTrackingMiddleware.test.js b/app/scripts/lib/createRPCMethodTrackingMiddleware.test.js index b55b52425216..af3c1ee4768b 100644 --- a/app/scripts/lib/createRPCMethodTrackingMiddleware.test.js +++ b/app/scripts/lib/createRPCMethodTrackingMiddleware.test.js @@ -245,7 +245,10 @@ describe('createRPCMethodTrackingMiddleware', () => { }; const res = { - error: { code: errorCodes.provider.userRejectedRequest }, + error: { + code: errorCodes.provider.userRejectedRequest, + data: { location: 'some_location' }, + }, }; const { next, executeMiddlewareStack } = getNext(); const handler = createHandler(); @@ -257,6 +260,7 @@ describe('createRPCMethodTrackingMiddleware', () => { event: MetaMetricsEventName.SignatureRejected, properties: { signature_type: MESSAGE_TYPE.PERSONAL_SIGN, + location: 'some_location', }, referrer: { url: 'some.dapp' }, }); diff --git a/app/scripts/lib/createTracingMiddleware.test.ts b/app/scripts/lib/createTracingMiddleware.test.ts new file mode 100644 index 000000000000..ceb87e0f3ac5 --- /dev/null +++ b/app/scripts/lib/createTracingMiddleware.test.ts @@ -0,0 +1,46 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +import { MESSAGE_TYPE } from '../../../shared/constants/app'; +import createTracingMiddleware from './createTracingMiddleware'; + +const REQUEST_MOCK = { + id: 'testId', + method: MESSAGE_TYPE.ETH_SEND_TRANSACTION, +} as any; + +const RESPONSE_MOCK = {}; +const NEXT_MOCK = jest.fn(); + +describe('createTracingMiddleware', () => { + let request: any; + + beforeEach(() => { + jest.resetAllMocks(); + + request = { ...REQUEST_MOCK }; + + global.sentry = { + getMetaMetricsEnabled: () => Promise.resolve(true), + }; + }); + + it('adds trace context to request if method is send transaction', async () => { + await createTracingMiddleware()(request, RESPONSE_MOCK, NEXT_MOCK); + + expect(request.traceContext).toBeDefined(); + }); + + it('does not add trace context to request if method not supported', async () => { + request.method = MESSAGE_TYPE.ETH_SIGN_TYPED_DATA_V4; + + await createTracingMiddleware()(request, RESPONSE_MOCK, NEXT_MOCK); + + expect(request.traceContext).toBeUndefined(); + }); + + it('calls next', async () => { + await createTracingMiddleware()(request, RESPONSE_MOCK, NEXT_MOCK); + + expect(NEXT_MOCK).toHaveBeenCalledTimes(1); + }); +}); diff --git a/app/scripts/lib/createTracingMiddleware.ts b/app/scripts/lib/createTracingMiddleware.ts new file mode 100644 index 000000000000..99e0a73795b1 --- /dev/null +++ b/app/scripts/lib/createTracingMiddleware.ts @@ -0,0 +1,31 @@ +// Request and repsones are currently untyped. +/* eslint-disable @typescript-eslint/no-explicit-any */ + +import { MESSAGE_TYPE } from '../../../shared/constants/app'; +import { trace, TraceName } from '../../../shared/lib/trace'; + +export default function createTracingMiddleware() { + return async function tracingMiddleware( + req: any, + _res: any, + next: () => void, + ) { + const { id, method } = req; + + if (method === MESSAGE_TYPE.ETH_SEND_TRANSACTION) { + req.traceContext = await trace({ + name: TraceName.Transaction, + id, + tags: { source: 'dapp' }, + }); + + await trace({ + name: TraceName.Middleware, + id, + parentContext: req.traceContext, + }); + } + + next(); + }; +} diff --git a/app/scripts/lib/ppom/ppom-middleware.test.ts b/app/scripts/lib/ppom/ppom-middleware.test.ts index 04f9b5075cb3..4adfeb3b8a6c 100644 --- a/app/scripts/lib/ppom/ppom-middleware.test.ts +++ b/app/scripts/lib/ppom/ppom-middleware.test.ts @@ -1,5 +1,4 @@ import { type Hex, JsonRpcResponseStruct } from '@metamask/utils'; -import * as ControllerUtils from '@metamask/controller-utils'; import { CHAIN_IDS } from '../../../../shared/constants/network'; import { @@ -7,6 +6,7 @@ import { BlockaidResultType, } from '../../../../shared/constants/security-provider'; import { flushPromises } from '../../../../test/lib/timer-helpers'; +import { mockNetworkState } from '../../../../test/stub/networks'; import { createPPOMMiddleware, PPOMMiddlewareRequest } from './ppom-middleware'; import { generateSecurityAlertId, @@ -65,7 +65,7 @@ const createMiddleware = ( } const networkController = { - state: { providerConfig: { chainId: chainId || CHAIN_IDS.MAINNET } }, + state: mockNetworkState({ chainId: chainId || CHAIN_IDS.MAINNET }), }; const appStateController = { @@ -238,7 +238,7 @@ describe('PPOMMiddleware', () => { await middlewareFunction( req, - { ...JsonRpcResponseStruct }, + { ...JsonRpcResponseStruct.TYPE }, () => undefined, ); @@ -258,32 +258,13 @@ describe('PPOMMiddleware', () => { '0x935e73edb9ff52e23bac7f7e043a1ecd06d05477', 'Example password', ], - jsonrpc: '2.0', + jsonrpc: '2.0' as const, id: 2974202441, origin: 'https://metamask.github.io', networkClientId: 'mainnet', tabId: 1048745900, securityAlertResponse: undefined, }; - jest.spyOn(ControllerUtils, 'detectSIWE').mockReturnValue({ - isSIWEMessage: true, - parsedMessage: { - address: '0x935e73edb9ff52e23bac7f7e049a1ecd06d05477', - chainId: 1, - domain: 'metamask.github.io', - expirationTime: null, - issuedAt: '2021-09-30T16:25:24.000Z', - nonce: '32891757', - notBefore: '2022-03-17T12:45:13.610Z', - requestId: 'some_id', - scheme: null, - statement: - 'I accept the MetaMask Terms of Service: https://community.metamask.io/tos', - uri: 'https://metamask.github.io', - version: '1', - resources: null, - }, - }); await middlewareFunction(req, undefined, () => undefined); diff --git a/app/scripts/lib/ppom/ppom-middleware.ts b/app/scripts/lib/ppom/ppom-middleware.ts index caa2bcd5f472..76bc5daaf88c 100644 --- a/app/scripts/lib/ppom/ppom-middleware.ts +++ b/app/scripts/lib/ppom/ppom-middleware.ts @@ -14,6 +14,8 @@ import { SIGNING_METHODS } from '../../../../shared/constants/transaction'; import { PreferencesController } from '../../controllers/preferences'; import { AppStateController } from '../../controllers/app-state'; import { LOADING_SECURITY_ALERT_RESPONSE } from '../../../../shared/constants/security-provider'; +import { getProviderConfig } from '../../../../ui/ducks/metamask/metamask'; +import { trace, TraceContext, TraceName } from '../../../../shared/lib/trace'; import { generateSecurityAlertId, handlePPOMError, @@ -32,6 +34,7 @@ export type PPOMMiddlewareRequest< Params extends JsonRpcParams = JsonRpcParams, > = Required> & { securityAlertResponse?: SecurityAlertResponse | undefined; + traceContext?: TraceContext; }; /** @@ -75,7 +78,9 @@ export function createPPOMMiddleware< const securityAlertsEnabled = preferencesController.store.getState()?.securityAlertsEnabled; - const { chainId } = networkController.state.providerConfig; + const { chainId } = getProviderConfig({ + metamask: networkController.state, + }); const isSupportedChain = await isChainSupported(chainId); if ( @@ -86,13 +91,14 @@ export function createPPOMMiddleware< return; } - const { isSIWEMessage } = detectSIWE({ data: req?.params?.[0] }); - if (isSIWEMessage) { - return; - } - - if (req.method === MESSAGE_TYPE.ETH_SEND_TRANSACTION) { - const { to: toAddress } = req?.params?.[0] ?? {}; + const data = req.params[0]; + if (typeof data === 'string') { + const { isSIWEMessage } = detectSIWE({ data }); + if (isSIWEMessage) { + return; + } + } else if (req.method === MESSAGE_TYPE.ETH_SEND_TRANSACTION) { + const { to: toAddress } = data ?? {}; const internalAccounts = accountsController.listAccounts(); const isToInternalAccount = internalAccounts.some( ({ address }) => address?.toLowerCase() === toAddress?.toLowerCase(), @@ -104,18 +110,22 @@ export function createPPOMMiddleware< const securityAlertId = generateSecurityAlertId(); - validateRequestWithPPOM({ - ppomController, - request: req, - securityAlertId, - chainId, - }).then((securityAlertResponse) => { - updateSecurityAlertResponse( - req.method, - securityAlertId, - securityAlertResponse, - ); - }); + trace( + { name: TraceName.PPOMValidation, parentContext: req.traceContext }, + () => + validateRequestWithPPOM({ + ppomController, + request: req, + securityAlertId, + chainId, + }).then((securityAlertResponse) => { + updateSecurityAlertResponse( + req.method, + securityAlertId, + securityAlertResponse, + ); + }), + ); const loadingSecurityAlertResponse: SecurityAlertResponse = { ...LOADING_SECURITY_ALERT_RESPONSE, diff --git a/app/scripts/lib/ppom/ppom-util.ts b/app/scripts/lib/ppom/ppom-util.ts index 9c6491522782..193717d35ef1 100644 --- a/app/scripts/lib/ppom/ppom-util.ts +++ b/app/scripts/lib/ppom/ppom-util.ts @@ -20,6 +20,7 @@ import { SecurityAlertResponse } from './types'; import { getSecurityAlertsAPISupportedChainIds, isSecurityAlertsAPIEnabled, + SecurityAlertsAPIRequest, validateWithSecurityAlertsAPI, } from './security-alerts-api'; @@ -205,7 +206,7 @@ async function findConfirmationBySecurityAlertId( async function validateWithController( ppomController: PPOMController, - request: JsonRpcRequest, + request: SecurityAlertsAPIRequest | JsonRpcRequest, ): Promise { const response = (await ppomController.usePPOM((ppom: PPOM) => ppom.validateJsonRpc(request), @@ -220,7 +221,7 @@ async function validateWithController( async function validateWithAPI( ppomController: PPOMController, chainId: string, - request: JsonRpcRequest, + request: SecurityAlertsAPIRequest | JsonRpcRequest, ): Promise { try { const response = await validateWithSecurityAlertsAPI(chainId, request); diff --git a/app/scripts/lib/ppom/security-alerts-api.ts b/app/scripts/lib/ppom/security-alerts-api.ts index 9cbb7ed1a2b8..258526f1b7c4 100644 --- a/app/scripts/lib/ppom/security-alerts-api.ts +++ b/app/scripts/lib/ppom/security-alerts-api.ts @@ -1,14 +1,20 @@ -import { Hex } from '@metamask/utils'; +import { Hex, JsonRpcRequest } from '@metamask/utils'; import { SecurityAlertResponse } from './types'; const ENDPOINT_VALIDATE = 'validate'; const ENDPOINT_SUPPORTED_CHAINS = 'supportedChains'; -export type SecurityAlertsAPIRequest = { +type SecurityAlertsAPIRequestBody = { method: string; params: unknown[]; }; +export type SecurityAlertsAPIRequest = Omit< + JsonRpcRequest, + 'method' | 'params' +> & + SecurityAlertsAPIRequestBody; + export function isSecurityAlertsAPIEnabled() { const isEnabled = process.env.SECURITY_ALERTS_API_ENABLED; return isEnabled?.toString() === 'true'; @@ -16,7 +22,9 @@ export function isSecurityAlertsAPIEnabled() { export async function validateWithSecurityAlertsAPI( chainId: string, - body: SecurityAlertsAPIRequest, + body: + | SecurityAlertsAPIRequestBody + | Pick, ): Promise { const endpoint = `${ENDPOINT_VALIDATE}/${chainId}`; return request(endpoint, { diff --git a/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js b/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js index 0e93bd67623e..bd7434e61481 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js @@ -73,7 +73,7 @@ describe('addEthereumChainHandler', () => { jest.clearAllMocks(); }); - describe('with permittedChains permissioning inactive', () => { + describe('with `endowment:permitted-chains` permissioning inactive', () => { it('creates a new network configuration for the given chainid and switches to it if none exists', async () => { const mocks = makeMocks({ permissionsFeatureFlagIsActive: false, @@ -282,8 +282,8 @@ describe('addEthereumChainHandler', () => { }); }); - describe('with permittedChains permissioning active', () => { - it('creates a new network configuration for the given chainid, requests permittedChains permission and switches to it if no networkConfigurations with the same chainId exist', async () => { + describe('with `endowment:permitted-chains` permissioning active', () => { + it('creates a new network configuration for the given chainid, requests `endowment:permitted-chains` permission and switches to it if no networkConfigurations with the same chainId exist', async () => { const mocks = makeMocks({ permissionedChainIds: [], permissionsFeatureFlagIsActive: true, @@ -326,7 +326,7 @@ describe('addEthereumChainHandler', () => { describe('if a networkConfiguration for the given chainId already exists', () => { describe('if the proposed networkConfiguration has a different rpcUrl from the one already in state', () => { - it('create a new networkConfiguration and switches to it without requesting permissions, if the requested chainId has permittedChains permission granted for requesting origin', async () => { + it('create a new networkConfiguration and switches to it without requesting permissions, if the requested chainId has `endowment:permitted-chains` permission granted for requesting origin', async () => { const mocks = makeMocks({ permissionedChainIds: [CHAIN_IDS.MAINNET], permissionsFeatureFlagIsActive: true, diff --git a/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js b/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js index 2677425cce12..e49964314b5c 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js @@ -167,7 +167,7 @@ describe('switchEthereumChainHandler', () => { describe('with permittedChains permissioning active', () => { const permissionsFeatureFlagIsActive = true; - it('should call requestPermittedChainsPermission and setActiveNetwork when chainId is not in permittedChains', async () => { + it('should call requestPermittedChainsPermission and setActiveNetwork when chainId is not in `endowment:permitted-chains`', async () => { const mockrequestPermittedChainsPermission = jest .fn() .mockResolvedValue(); @@ -200,7 +200,7 @@ describe('switchEthereumChainHandler', () => { ); }); - it('should call setActiveNetwork without calling requestPermittedChainsPermission when requested chainId is in permittedChains', async () => { + it('should call setActiveNetwork without calling requestPermittedChainsPermission when requested chainId is in `endowment:permitted-chains`', async () => { const mocks = makeMocks({ permissionsFeatureFlagIsActive, permissionedChainIds: [CHAIN_IDS.MAINNET], diff --git a/app/scripts/lib/setupSentry.js b/app/scripts/lib/setupSentry.js index eaad01672722..575d09990add 100644 --- a/app/scripts/lib/setupSentry.js +++ b/app/scripts/lib/setupSentry.js @@ -3,12 +3,11 @@ import { createModuleLogger, createProjectLogger } from '@metamask/utils'; import { logger } from '@sentry/utils'; import browser from 'webextension-polyfill'; import { isManifestV3 } from '../../../shared/modules/mv3.utils'; -import { filterEvents } from './sentry-filter-events'; import extractEthjsErrorMessage from './extractEthjsErrorMessage'; - -let installType = 'unknown'; +import { filterEvents } from './sentry-filter-events'; const projectLogger = createProjectLogger('sentry'); +let installType = 'unknown'; export const log = createModuleLogger( projectLogger, @@ -36,6 +35,39 @@ export const ERROR_URL_ALLOWLIST = { SEGMENT: 'segment.io', }; +export default function setupSentry() { + if (!RELEASE) { + throw new Error('Missing release'); + } + + if (!getSentryTarget()) { + log('Skipped initialization'); + return undefined; + } + + log('Initializing'); + + // Normally this would be awaited, but getSelf should be available by the time the report is finalized. + // If it's not, we still get the extensionId, but the installType will default to "unknown" + browser.management + .getSelf() + .then((extensionInfo) => { + if (extensionInfo.installType) { + installType = extensionInfo.installType; + } + }) + .catch((error) => { + log('Error getting extension installType', error); + }); + integrateLogging(); + setSentryClient(); + + return { + ...Sentry, + getMetaMetricsEnabled, + }; +} + function getClientOptions() { const environment = getSentryEnvironment(); const sentryTarget = getSentryTarget(); @@ -44,8 +76,8 @@ function getClientOptions() { beforeBreadcrumb: beforeBreadcrumb(), beforeSend: (report) => rewriteReport(report), debug: METAMASK_DEBUG, - dsn: sentryTarget, dist: isManifestV3 ? 'mv3' : 'mv2', + dsn: sentryTarget, environment, integrations: [ Sentry.dedupeIntegration(), @@ -66,26 +98,6 @@ function getClientOptions() { }; } -export default function setupSentry() { - if (!RELEASE) { - throw new Error('Missing release'); - } - - if (!getSentryTarget()) { - log('Skipped initialization'); - return undefined; - } - - log('Initializing'); - - integrateLogging(); - setSentryClient(); - - return { - ...Sentry, - getMetaMetricsEnabled, - }; -} /** * Returns whether MetaMetrics is enabled, given the application state. * @@ -224,18 +236,6 @@ function setSentryClient() { const clientOptions = getClientOptions(); const { dsn, environment, release } = clientOptions; - // Normally this would be awaited, but getSelf should be available by the time the report is finalized. - // If it's not, we still get the extensionId, but the installType will default to "unknown" - browser.management - .getSelf() - .then((extensionInfo) => { - if (extensionInfo.installType) { - installType = extensionInfo.installType; - } - }) - .catch((error) => { - console.log('Error getting extension installType', error); - }); /** * Sentry throws on initialization as it wants to avoid polluting the global namespace and * potentially clashing with a website also using Sentry, but this could only happen in the content script. diff --git a/app/scripts/lib/snap-keyring/account-watcher-snap.ts b/app/scripts/lib/snap-keyring/account-watcher-snap.ts new file mode 100644 index 000000000000..48ace4e595af --- /dev/null +++ b/app/scripts/lib/snap-keyring/account-watcher-snap.ts @@ -0,0 +1,10 @@ +// BEGIN:ONLY_INCLUDE_IF(build-flask) +import { SnapId } from '@metamask/snaps-sdk'; +import AccountWatcherSnap from '@metamask/account-watcher/dist/preinstalled-snap.json'; + +export const ACCOUNT_WATCHER_SNAP_ID: SnapId = + AccountWatcherSnap.snapId as SnapId; + +export const ACCOUNT_WATCHER_NAME: string = + AccountWatcherSnap.manifest.proposedName; +// END:ONLY_INCLUDE_IF diff --git a/app/scripts/lib/snap-keyring/utils/isBlockedUrl.test.ts b/app/scripts/lib/snap-keyring/utils/isBlockedUrl.test.ts index b40e52afe170..f2216ff9694a 100644 --- a/app/scripts/lib/snap-keyring/utils/isBlockedUrl.test.ts +++ b/app/scripts/lib/snap-keyring/utils/isBlockedUrl.test.ts @@ -10,17 +10,24 @@ describe('isBlockedUrl', () => { allowedEvents: [], }); const phishingController = new PhishingController({ + // @ts-expect-error TODO: Resolve/patch mismatch between messenger types messenger: phishingControllerMessenger, state: { phishingLists: [ { - blocklist: ['https://metamask.test'], + blocklist: [ + 'metamask.test', + 'QmYwAPJzv5CZsnAzt8auVTL6aKqgfZY5vHBYdbyz4ySxTm', + 'ipfs://QmXbVAkGZMz6p8nJ3wXBng4JwvBqZWkFwnDMevL7Tz5w8y', + 'QmT78zSuBmuS4z925WJg3vNLRiT4Mj6apb5iS4iykKs4n8', + ], allowlist: [], fuzzylist: [], tolerance: 0, version: 1, lastUpdated: 0, name: ListNames.MetaMask, + c2DomainBlocklist: [], }, ], }, @@ -32,6 +39,16 @@ describe('isBlockedUrl', () => { ['https://metamask.io', false], ['https://metamask.test', true], ['sftp://metamask.io', true], + ['ipfs://QmYwAPJzv5CZsnAzt8auVTL6aKqgfZY5vHBYdbyz4ySxTm', true], + ['ipfs://QmXbVAkGZMz6p8nJ3wXBng4JwvBqZWkFgnDMevL7Tz5w8y', true], + [ + 'https://ipfs.io/ipfs/QmT78zSuBmuS4z925WJg3vNLRiT4Mj6apb5iS4iykKs4n8', + true, + ], + [ + 'https://ipfs.io/ipfs/QmT78zSuBmuS4zdsf925WJsadfsdfg3vNLRiT4Mj6apb5iS4iykKs4n8', + false, + ], ['', true], ['1', true], [undefined, true], diff --git a/app/scripts/lib/transaction/metrics.ts b/app/scripts/lib/transaction/metrics.ts index 39bf23b7e74c..ea9c864c106a 100644 --- a/app/scripts/lib/transaction/metrics.ts +++ b/app/scripts/lib/transaction/metrics.ts @@ -1,27 +1,15 @@ -import { isHexString } from 'ethereumjs-util'; import EthQuery, { Provider } from '@metamask/eth-query'; -import { BigNumber } from 'bignumber.js'; import { FetchGasFeeEstimateOptions } from '@metamask/gas-fee-controller'; +import { BigNumber } from 'bignumber.js'; +import { isHexString } from 'ethereumjs-util'; +import { SmartTransaction } from '@metamask/smart-transactions-controller/dist/types'; import { TransactionMeta, TransactionType, } from '@metamask/transaction-controller'; -import { SmartTransaction } from '@metamask/smart-transactions-controller/dist/types'; import { ORIGIN_METAMASK } from '../../../../shared/constants/app'; -import { - determineTransactionAssetType, - isEIP1559Transaction, -} from '../../../../shared/modules/transaction.utils'; -import { - hexWEIToDecETH, - hexWEIToDecGWEI, -} from '../../../../shared/modules/conversion.utils'; -import { - TokenStandard, - TransactionApprovalAmountType, - TransactionMetaMetricsEvent, -} from '../../../../shared/constants/transaction'; +import { GasRecommendations } from '../../../../shared/constants/gas'; import { MetaMetricsEventCategory, MetaMetricsEventFragment, @@ -30,22 +18,37 @@ import { MetaMetricsPageObject, MetaMetricsReferrerObject, } from '../../../../shared/constants/metametrics'; -import { GasRecommendations } from '../../../../shared/constants/gas'; +import { + TokenStandard, + TransactionApprovalAmountType, + TransactionMetaMetricsEvent, +} from '../../../../shared/constants/transaction'; import { calcGasTotal, getSwapsTokensReceivedFromTxMeta, TRANSACTION_ENVELOPE_TYPE_NAMES, } from '../../../../shared/lib/transactions-controller-utils'; +import { + hexWEIToDecETH, + hexWEIToDecGWEI, +} from '../../../../shared/modules/conversion.utils'; +import { getSmartTransactionMetricsProperties } from '../../../../shared/modules/metametrics'; +import { + determineTransactionAssetType, + isEIP1559Transaction, +} from '../../../../shared/modules/transaction.utils'; import { getBlockaidMetricsProps, getSwapAndSendMetricsProps, } from '../../../../ui/helpers/utils/metrics'; -import { getSmartTransactionMetricsProperties } from '../../../../shared/modules/metametrics'; +import { + REDESIGN_DEV_TRANSACTION_TYPES, + REDESIGN_USER_TRANSACTION_TYPES, +} from '../../../../ui/pages/confirmations/utils'; import { getSnapAndHardwareInfoForMetrics, type SnapAndHardwareMessenger, } from '../snap-keyring/metrics'; -import { REDESIGN_TRANSACTION_TYPES } from '../../../../ui/pages/confirmations/utils'; export type TransactionMetricsRequest = { createEventFragment: ( @@ -1004,9 +1007,14 @@ async function buildEventFragmentProperties({ transactionMetricsRequest.getRedesignedTransactionsEnabled(); if ( - (isRedesignedConfirmationsDeveloperSettingEnabled || - isRedesignedTransactionsUserSettingEnabled) && - REDESIGN_TRANSACTION_TYPES.includes(transactionMeta.type as TransactionType) + (isRedesignedConfirmationsDeveloperSettingEnabled && + REDESIGN_DEV_TRANSACTION_TYPES.includes( + transactionMeta.type as TransactionType, + )) || + (isRedesignedTransactionsUserSettingEnabled && + REDESIGN_USER_TRANSACTION_TYPES.includes( + transactionMeta.type as TransactionType, + )) ) { uiCustomizations.push( MetaMetricsEventUiCustomization.RedesignedConfirmation, diff --git a/app/scripts/lib/transaction/smart-transactions.test.ts b/app/scripts/lib/transaction/smart-transactions.test.ts index 357be3d4b33a..b3cea1b12c88 100644 --- a/app/scripts/lib/transaction/smart-transactions.test.ts +++ b/app/scripts/lib/transaction/smart-transactions.test.ts @@ -110,7 +110,8 @@ describe('submitSmartTransactionHook', () => { smartTransactionsController: createSmartTransactionsControllerMock(), transactionController: createTransactionControllerMock(), isSmartTransaction: true, - isHardwareWallet: false, + signedTransactionInHex: + '0x02f8b104058504a817c8008504a817c80082b427949ba60bbf4ba1de43f3b4983a539feebfbd5fd97680b844095ea7b30000000000000000000000002f318c334780961fb129d2a6c30d0763d9a5c9700000000000000000000000000000000000000000000000000000000000011170c080a0fdd2cb46203b5e7bba99cc56a37da3e5e3f36163a5bd9c51cddfd8d7028f5dd0a054c35cfa10b3350a3fd3a0e7b4aeb0b603d528c07a8cfdf4a78505d9864edef4', controllerMessenger: createSmartTransactionsControllerMessengerMock(), featureFlags: { extensionActive: true, @@ -149,13 +150,6 @@ describe('submitSmartTransactionHook', () => { expect(result).toEqual({ transactionHash: undefined }); }); - it('falls back to regular transaction submit if a hardware wallet is used', async () => { - const request: SubmitSmartTransactionRequestMocked = createRequest(); - request.isHardwareWallet = true; - const result = await submitSmartTransactionHook(request); - expect(result).toEqual({ transactionHash: undefined }); - }); - it('falls back to regular transaction submit if /getFees throws an error', async () => { const request: SubmitSmartTransactionRequestMocked = createRequest(); jest @@ -211,7 +205,7 @@ describe('submitSmartTransactionHook', () => { ); }); - it('submits a smart transaction', async () => { + it('submits a smart transaction with an already signed transaction', async () => { const request: SubmitSmartTransactionRequestMocked = createRequest(); setImmediate(() => { request.smartTransactionsController.eventEmitter.emit( @@ -235,6 +229,89 @@ describe('submitSmartTransactionHook', () => { }); const result = await submitSmartTransactionHook(request); expect(result).toEqual({ transactionHash: txHash }); + const { txParams } = request.transactionMeta; + expect( + request.smartTransactionsController.submitSignedTransactions, + ).toHaveBeenCalledWith({ + signedTransactions: [request.signedTransactionInHex], + signedCanceledTransactions: [], + txParams, + transactionMeta: request.transactionMeta, + }); + addRequestCallback(); + expect(request.controllerMessenger.call).toHaveBeenCalledTimes(4); + expect(request.controllerMessenger.call).toHaveBeenCalledWith( + 'ApprovalController:startFlow', + ); + expect(request.controllerMessenger.call).toHaveBeenCalledWith( + 'ApprovalController:addRequest', + { + id: 'approvalId', + origin: 'http://localhost', + type: 'smartTransaction:showSmartTransactionStatusPage', + requestState: { + smartTransaction: { + status: 'pending', + uuid, + creationTime: expect.any(Number), + }, + isDapp: true, + txId, + }, + }, + true, + ); + expect(request.controllerMessenger.call).toHaveBeenCalledWith( + 'ApprovalController:updateRequestState', + { + id: 'approvalId', + requestState: { + smartTransaction: { + status: 'success', + statusMetadata: { + minedHash: + '0x0302b75dfb9fd9eb34056af031efcaee2a8cbd799ea054a85966165cd82a7356', + }, + }, + isDapp: true, + txId, + }, + }, + ); + + expect(request.controllerMessenger.call).toHaveBeenCalledWith( + 'ApprovalController:endFlow', + { + id: 'approvalId', + }, + ); + }); + + it('signs and submits a smart transaction', async () => { + const request: SubmitSmartTransactionRequestMocked = createRequest(); + request.signedTransactionInHex = undefined; + setImmediate(() => { + request.smartTransactionsController.eventEmitter.emit( + `uuid:smartTransaction`, + { + status: 'pending', + statusMetadata: { + minedHash: '', + }, + }, + ); + request.smartTransactionsController.eventEmitter.emit( + `uuid:smartTransaction`, + { + status: 'success', + statusMetadata: { + minedHash: txHash, + }, + }, + ); + }); + const result = await submitSmartTransactionHook(request); + expect(result).toEqual({ transactionHash: txHash }); const { txParams, chainId } = request.transactionMeta; expect( request.transactionController.approveTransactionsWithSameNonce, @@ -333,24 +410,14 @@ describe('submitSmartTransactionHook', () => { }); const result = await submitSmartTransactionHook(request); expect(result).toEqual({ transactionHash: txHash }); - const { txParams, chainId } = request.transactionMeta; + const { txParams } = request.transactionMeta; expect( request.transactionController.approveTransactionsWithSameNonce, - ).toHaveBeenCalledWith( - [ - { - ...txParams, - maxFeePerGas: '0x2fd8a58d7', - maxPriorityFeePerGas: '0xaa0f8a94', - chainId, - }, - ], - { hasNonce: true }, - ); + ).not.toHaveBeenCalled(); expect( request.smartTransactionsController.submitSignedTransactions, ).toHaveBeenCalledWith({ - signedTransactions: [createSignedTransaction()], + signedTransactions: [request.signedTransactionInHex], signedCanceledTransactions: [], txParams, transactionMeta: request.transactionMeta, diff --git a/app/scripts/lib/transaction/smart-transactions.ts b/app/scripts/lib/transaction/smart-transactions.ts index 822439839d35..9226f003232b 100644 --- a/app/scripts/lib/transaction/smart-transactions.ts +++ b/app/scripts/lib/transaction/smart-transactions.ts @@ -60,10 +60,10 @@ export type FeatureFlags = { export type SubmitSmartTransactionRequest = { transactionMeta: TransactionMeta; + signedTransactionInHex?: string; smartTransactionsController: SmartTransactionsController; transactionController: TransactionController; isSmartTransaction: boolean; - isHardwareWallet: boolean; controllerMessenger: SmartTransactionsControllerMessenger; featureFlags: FeatureFlags; }; @@ -91,33 +91,33 @@ class SmartTransactionHook { #isSmartTransaction: boolean; - #isHardwareWallet: boolean; - #smartTransactionsController: SmartTransactionsController; #transactionController: TransactionController; #transactionMeta: TransactionMeta; + #signedTransactionInHex?: string; + #txParams: TransactionParams; constructor(request: SubmitSmartTransactionRequest) { const { transactionMeta, + signedTransactionInHex, smartTransactionsController, transactionController, isSmartTransaction, - isHardwareWallet, controllerMessenger, featureFlags, } = request; this.#approvalFlowId = ''; this.#approvalFlowEnded = false; this.#transactionMeta = transactionMeta; + this.#signedTransactionInHex = signedTransactionInHex; this.#smartTransactionsController = smartTransactionsController; this.#transactionController = transactionController; this.#isSmartTransaction = isSmartTransaction; - this.#isHardwareWallet = isHardwareWallet; this.#controllerMessenger = controllerMessenger; this.#featureFlags = featureFlags; this.#isDapp = transactionMeta.origin !== ORIGIN_METAMASK; @@ -137,7 +137,6 @@ class SmartTransactionHook { const useRegularTransactionSubmit = { transactionHash: undefined }; if ( !this.#isSmartTransaction || - this.#isHardwareWallet || isUnsupportedTransactionTypeForSmartTransaction ) { return useRegularTransactionSubmit; @@ -297,17 +296,18 @@ class SmartTransactionHook { }: { getFeesResponse: Fees; }) { - const signedTransactions = await this.#createSignedTransactions( - getFeesResponse.tradeTxFees?.fees ?? [], - false, - ); - const signedCanceledTransactions = await this.#createSignedTransactions( - getFeesResponse.tradeTxFees?.cancelFees || [], - true, - ); + let signedTransactions; + if (this.#signedTransactionInHex) { + signedTransactions = [this.#signedTransactionInHex]; + } else { + signedTransactions = await this.#createSignedTransactions( + getFeesResponse.tradeTxFees?.fees ?? [], + false, + ); + } return await this.#smartTransactionsController.submitSignedTransactions({ signedTransactions, - signedCanceledTransactions, + signedCanceledTransactions: [], txParams: this.#txParams, transactionMeta: this.#transactionMeta, }); diff --git a/app/scripts/lib/transaction/util.test.ts b/app/scripts/lib/transaction/util.test.ts index a27844f53b41..16077e0f08ed 100644 --- a/app/scripts/lib/transaction/util.test.ts +++ b/app/scripts/lib/transaction/util.test.ts @@ -1,4 +1,4 @@ -import { EthAccountType, InternalAccount } from '@metamask/keyring-api'; +import { InternalAccount } from '@metamask/keyring-api'; import { TransactionParams } from '@metamask/eth-json-rpc-middleware'; import { TransactionController, @@ -18,6 +18,7 @@ import { } from '../../../../shared/constants/security-provider'; import { SecurityAlertResponse } from '../ppom/types'; import { flushPromises } from '../../../../test/lib/timer-helpers'; +import { createMockInternalAccount } from '../../../../test/jest/mocks'; import { AddDappTransactionRequest, AddTransactionOptions, @@ -40,24 +41,9 @@ jest.mock('uuid', () => { const SECURITY_ALERT_ID_MOCK = '123'; const INTERNAL_ACCOUNT_ADDRESS = '0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b'; - -const mockAccount = { - type: EthAccountType.Eoa, - id: '3afa663e-0600-4d93-868a-61c2e553013b', +const INTERNAL_ACCOUNT = createMockInternalAccount({ address: INTERNAL_ACCOUNT_ADDRESS, - methods: [], - options: {}, -}; -const mockInternalAccount = { - ...mockAccount, - metadata: { - name: `Account 1`, - importTime: Date.now(), - keyring: { - type: '', - }, - }, -}; +}); const TRANSACTION_PARAMS_MOCK: TransactionParams = { from: '0x1', @@ -490,7 +476,7 @@ describe('Transaction Utils', () => { expect(validateRequestWithPPOMMock).toHaveBeenCalledTimes(0); }); - it('send to users own acccount', async () => { + it('send to users own account', async () => { const sendRequest = { ...request, transactionParams: { @@ -502,7 +488,7 @@ describe('Transaction Utils', () => { ...sendRequest, securityAlertsEnabled: false, chainId: '0x1', - internalAccounts: [mockInternalAccount], + internalAccounts: [INTERNAL_ACCOUNT], }); expect( diff --git a/app/scripts/lib/transaction/util.ts b/app/scripts/lib/transaction/util.ts index 988ec66ee5bb..8b71e33119f8 100644 --- a/app/scripts/lib/transaction/util.ts +++ b/app/scripts/lib/transaction/util.ts @@ -24,6 +24,7 @@ import { LOADING_SECURITY_ALERT_RESPONSE, SECURITY_PROVIDER_EXCLUDED_TRANSACTION_TYPES, } from '../../../../shared/constants/security-provider'; +import { endTrace, TraceName } from '../../../../shared/lib/trace'; export type AddTransactionOptions = NonNullable< Parameters[1] @@ -65,7 +66,7 @@ export async function addDappTransaction( ): Promise { const { dappRequest } = request; const { id: actionId, method, origin } = dappRequest; - const { securityAlertResponse } = dappRequest; + const { securityAlertResponse, traceContext } = dappRequest; const transactionOptions: AddTransactionOptions = { actionId, @@ -76,12 +77,21 @@ export async function addDappTransaction( securityAlertResponse, }; + endTrace({ name: TraceName.Middleware, id: actionId }); + const { waitForHash } = await addTransactionOrUserOperation({ ...request, - transactionOptions, + transactionOptions: { + ...transactionOptions, + traceContext, + }, }); - return (await waitForHash()) as string; + const hash = (await waitForHash()) as string; + + endTrace({ name: TraceName.Transaction, id: actionId }); + + return hash; } export async function addTransaction( diff --git a/app/scripts/lib/tx-verification/tx-verification-middleware.test.ts b/app/scripts/lib/tx-verification/tx-verification-middleware.test.ts index 110a2dc3040e..ea0aea6216d0 100644 --- a/app/scripts/lib/tx-verification/tx-verification-middleware.test.ts +++ b/app/scripts/lib/tx-verification/tx-verification-middleware.test.ts @@ -4,13 +4,14 @@ import { EXPERIENCES_TYPE, FIRST_PARTY_CONTRACT_NAMES, } from '../../../../shared/constants/first-party-contracts'; +import { mockNetworkState } from '../../../../test/stub/networks'; import { createTxVerificationMiddleware, TxParams, } from './tx-verification-middleware'; const getMockNetworkController = (chainId: `0x${string}` = '0x1') => - ({ state: { providerConfig: { chainId } } } as unknown as NetworkController); + ({ state: mockNetworkState({ chainId }) } as NetworkController); const mockTrustedSigners: Partial> = { [EXPERIENCES_TYPE.METAMASK_BRIDGE]: diff --git a/app/scripts/lib/tx-verification/tx-verification-middleware.ts b/app/scripts/lib/tx-verification/tx-verification-middleware.ts index 30782a98721b..322e22902c93 100644 --- a/app/scripts/lib/tx-verification/tx-verification-middleware.ts +++ b/app/scripts/lib/tx-verification/tx-verification-middleware.ts @@ -22,6 +22,7 @@ import { TRUSTED_SIGNERS, } from '../../../../shared/constants/verification'; import { MESSAGE_TYPE } from '../../../../shared/constants/app'; +import { getCurrentChainId } from '../../../../ui/selectors'; export type TxParams = { chainId?: `0x${string}`; @@ -62,7 +63,7 @@ export function createTxVerificationMiddleware( const chainId = typeof params.chainId === 'string' ? (params.chainId.toLowerCase() as Hex) - : networkController.state.providerConfig.chainId; + : getCurrentChainId({ metamask: networkController.state }); const experienceType = getExperience( params.to.toLowerCase() as Hex, diff --git a/app/scripts/metamask-controller.actions.test.js b/app/scripts/metamask-controller.actions.test.js index 33fa8c9baa8a..b5c4ef72dc63 100644 --- a/app/scripts/metamask-controller.actions.test.js +++ b/app/scripts/metamask-controller.actions.test.js @@ -89,10 +89,6 @@ describe('MetaMaskController', function () { blocklist: ['127.0.0.1'], name: ListNames.MetaMask, }, - phishfort_hotlist: { - blocklist: [], - name: ListNames.Phishfort, - }, }), ) .get(METAMASK_HOTLIST_DIFF_FILE) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index ebffc180f95f..7925a9f4118f 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -82,6 +82,7 @@ import { SnapController, IframeExecutionService, SnapInterfaceController, + SnapInsightsController, OffscreenExecutionService, } from '@metamask/snaps-controllers'; import { @@ -146,6 +147,10 @@ import { AuthenticationController, UserStorageController, } from '@metamask/profile-sync-controller'; +import { + NotificationsServicesPushController, + NotificationServicesController, +} from '@metamask/notification-services-controller'; import { methodsRequiringNetworkSwitch } from '../../shared/constants/methods-tags'; ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) @@ -161,7 +166,6 @@ import { import { CHAIN_IDS, NETWORK_TYPES, - TEST_NETWORK_TICKER_MAP, NetworkStatus, } from '../../shared/constants/network'; import { getAllowedSmartTransactionsChainIds } from '../../shared/constants/smartTransactions'; @@ -216,7 +220,9 @@ import { TOKEN_TRANSFER_LOG_TOPIC_HASH, TRANSFER_SINFLE_LOG_TOPIC_HASH, } from '../../shared/lib/transactions-controller-utils'; -import { endTrace } from '../../shared/lib/trace'; +import { getCurrentChainId } from '../../ui/selectors'; +import { getProviderConfig } from '../../ui/ducks/metamask/metamask'; +import { endTrace, trace } from '../../shared/lib/trace'; import { BalancesController as MultichainBalancesController } from './lib/accounts/BalancesController'; import { ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) @@ -321,8 +327,6 @@ import { WeakRefObjectMap } from './lib/WeakRefObjectMap'; import { METAMASK_COOKIE_HANDLER } from './constants/stream'; // Notification controllers -import { PushPlatformNotificationsController } from './controllers/push-platform-notifications/push-platform-notifications'; -import { MetamaskNotificationsController } from './controllers/metamask-notifications/metamask-notifications'; import { createTxVerificationMiddleware } from './lib/tx-verification/tx-verification-middleware'; import { updateSecurityAlertResponse } from './lib/ppom/ppom-util'; import createEvmMethodsToNonEvmAccountReqFilterMiddleware from './lib/createEvmMethodsToNonEvmAccountReqFilterMiddleware'; @@ -331,6 +335,11 @@ import { decodeTransactionData } from './lib/transaction/decode/util'; import { BridgeBackgroundAction } from './controllers/bridge/types'; import BridgeController from './controllers/bridge/bridge-controller'; import { BRIDGE_CONTROLLER_NAME } from './controllers/bridge/constants'; +import { + onPushNotificationClicked, + onPushNotificationReceived, +} from './controllers/push-notifications'; +import createTracingMiddleware from './lib/createTracingMiddleware'; export const METAMASK_CONTROLLER_EVENTS = { // Fired after state changes that impact the extension badge (unapproved msg count) @@ -340,9 +349,9 @@ export const METAMASK_CONTROLLER_EVENTS = { APPROVAL_STATE_CHANGE: 'ApprovalController:stateChange', QUEUED_REQUEST_STATE_CHANGE: 'QueuedRequestController:stateChange', METAMASK_NOTIFICATIONS_LIST_UPDATED: - 'MetamaskNotificationsController:notificationsListUpdated', + 'NotificationServicesController:notificationsListUpdated', METAMASK_NOTIFICATIONS_MARK_AS_READ: - 'MetamaskNotificationsController:markNotificationsAsRead', + 'NotificationServicesController:markNotificationsAsRead', NOTIFICATIONS_STATE_CHANGE: 'NotificationController:stateChange', }; @@ -496,14 +505,9 @@ export default class MetamaskController extends EventEmitter { id: 'networkConfigurationId', }; initialNetworkControllerState = { - providerConfig: { - ...networkConfig, - type: 'rpc', - }, + selectedNetworkClientId: networkConfig.id, networkConfigurations: { - networkConfigurationId: { - ...networkConfig, - }, + [networkConfig.id]: networkConfig, }, }; } else if ( @@ -511,11 +515,7 @@ export default class MetamaskController extends EventEmitter { process.env.METAMASK_ENVIRONMENT === 'test' ) { initialNetworkControllerState = { - providerConfig: { - type: NETWORK_TYPES.SEPOLIA, - chainId: CHAIN_IDS.SEPOLIA, - ticker: TEST_NETWORK_TICKER_MAP[NETWORK_TYPES.SEPOLIA], - }, + selectedNetworkClientId: NETWORK_TYPES.SEPOLIA, }; } this.networkController = new NetworkController({ @@ -576,7 +576,7 @@ export default class MetamaskController extends EventEmitter { }); this.tokenListController = new TokenListController({ - chainId: this.networkController.state.providerConfig.chainId, + chainId: getCurrentChainId({ metamask: this.networkController.state }), preventPollingOnNetworkRestart: !this.#isTokenListPollingRequired( this.preferencesController.store.getState(), ), @@ -586,7 +586,7 @@ export default class MetamaskController extends EventEmitter { this.assetsContractController = new AssetsContractController( { - chainId: this.networkController.state.providerConfig.chainId, + chainId: getCurrentChainId({ metamask: this.networkController.state }), onPreferencesStateChange: (listener) => this.preferencesController.store.subscribe(listener), onNetworkDidChange: (cb) => @@ -626,7 +626,7 @@ export default class MetamaskController extends EventEmitter { state: initState.TokensController, provider: this.provider, messenger: tokensControllerMessenger, - chainId: this.networkController.state.providerConfig.chainId, + chainId: getCurrentChainId({ metamask: this.networkController.state }), }); const nftControllerMessenger = this.controllerMessenger.getRestricted({ @@ -646,7 +646,7 @@ export default class MetamaskController extends EventEmitter { this.nftController = new NftController({ state: initState.NftController, messenger: nftControllerMessenger, - chainId: this.networkController.state.providerConfig.chainId, + chainId: getCurrentChainId({ metamask: this.networkController.state }), getERC721AssetName: this.assetsContractController.getERC721AssetName.bind( this.assetsContractController, ), @@ -701,7 +701,7 @@ export default class MetamaskController extends EventEmitter { this.nftDetectionController = new NftDetectionController({ messenger: nftDetectionControllerMessenger, - chainId: this.networkController.state.providerConfig.chainId, + chainId: getCurrentChainId({ metamask: this.networkController.state }), getOpenSeaApiKey: () => this.nftController.openSeaApiKey, getBalancesInSingleCall: this.assetsContractController.getBalancesInSingleCall.bind( @@ -725,11 +725,13 @@ export default class MetamaskController extends EventEmitter { 'NetworkController:networkDidChange', ), getNetworkIdentifier: () => { - const { type, rpcUrl } = this.networkController.state.providerConfig; + const { type, rpcUrl } = getProviderConfig({ + metamask: this.networkController.state, + }); return type === NETWORK_TYPES.RPC ? rpcUrl : type; }, getCurrentChainId: () => - this.networkController.state.providerConfig.chainId, + getCurrentChainId({ metamask: this.networkController.state }), version: process.env.METAMASK_VERSION, environment: process.env.METAMASK_ENVIRONMENT, extension: this.extension, @@ -777,10 +779,13 @@ export default class MetamaskController extends EventEmitter { legacyAPIEndpoint: `${gasApiBaseUrl}/networks//gasPrices`, EIP1559APIEndpoint: `${gasApiBaseUrl}/networks//suggestedGasFees`, getCurrentNetworkLegacyGasAPICompatibility: () => { - const { chainId } = this.networkController.state.providerConfig; + const chainId = getCurrentChainId({ + metamask: this.networkController.state, + }); return chainId === CHAIN_IDS.BSC; }, - getChainId: () => this.networkController.state.providerConfig.chainId, + getChainId: () => + getCurrentChainId({ metamask: this.networkController.state }), }); this.appStateController = new AppStateController({ @@ -838,6 +843,7 @@ export default class MetamaskController extends EventEmitter { messenger: this.controllerMessenger.getRestricted({ name: 'PPOMController', allowedEvents: ['NetworkController:stateChange'], + allowedActions: ['NetworkController:getNetworkClientById'], }), storageBackend: new IndexedDBPPOMStorage('PPOMDB', 1), provider: this.provider, @@ -846,7 +852,7 @@ export default class MetamaskController extends EventEmitter { ppomInit: () => PPOMModule.default(process.env.PPOM_URI), }, state: initState.PPOMController, - chainId: this.networkController.state.providerConfig.chainId, + chainId: getCurrentChainId({ metamask: this.networkController.state }), securityAlertsEnabled: this.preferencesController.store.getState().securityAlertsEnabled, onPreferencesChange: this.preferencesController.store.subscribe.bind( @@ -951,6 +957,8 @@ export default class MetamaskController extends EventEmitter { this.ensController = new EnsController({ messenger: this.controllerMessenger.getRestricted({ name: 'EnsController', + allowedActions: ['NetworkController:getNetworkClientById'], + allowedEvents: [], }), provider: this.provider, onNetworkDidChange: networkControllerMessenger.subscribe.bind( @@ -1416,6 +1424,27 @@ export default class MetamaskController extends EventEmitter { messenger: snapInterfaceControllerMessenger, }); + const snapInsightsControllerMessenger = + this.controllerMessenger.getRestricted({ + name: 'SnapInsightsController', + allowedActions: [ + `${this.snapController.name}:handleRequest`, + `${this.snapController.name}:getAll`, + `${this.permissionController.name}:getPermissions`, + `${this.snapInterfaceController.name}:deleteInterface`, + ], + allowedEvents: [ + `TransactionController:unapprovedTransactionAdded`, + `TransactionController:transactionStatusUpdated`, + `SignatureController:stateChange`, + ], + }); + + this.snapInsightsController = new SnapInsightsController({ + state: initState.SnapInsightsController, + messenger: snapInsightsControllerMessenger, + }); + // Notification Controllers this.authenticationController = new AuthenticationController.Controller({ state: initState.AuthenticationController, @@ -1454,18 +1483,35 @@ export default class MetamaskController extends EventEmitter { }), }); - const pushPlatformNotificationsControllerMessenger = + const notificationServicesPushControllerMessenger = this.controllerMessenger.getRestricted({ - name: 'PushPlatformNotificationsController', + name: 'NotificationServicesPushController', allowedActions: ['AuthenticationController:getBearerToken'], + allowedEvents: [], }); - this.pushPlatformNotificationsController = - new PushPlatformNotificationsController({ - state: initState.PushPlatformNotificationsController, - messenger: pushPlatformNotificationsControllerMessenger, + this.notificationServicesPushController = + new NotificationsServicesPushController.Controller({ + messenger: notificationServicesPushControllerMessenger, + state: initState.NotificationsServicesPushController, + env: { + apiKey: process.env.FIREBASE_API_KEY ?? '', + authDomain: process.env.FIREBASE_AUTH_DOMAIN ?? '', + storageBucket: process.env.FIREBASE_STORAGE_BUCKET ?? '', + projectId: process.env.FIREBASE_PROJECT_ID ?? '', + messagingSenderId: process.env.FIREBASE_MESSAGING_SENDER_ID ?? '', + appId: process.env.FIREBASE_APP_ID ?? '', + measurementId: process.env.FIREBASE_MEASUREMENT_ID ?? '', + vapidKey: process.env.VAPID_KEY ?? '', + }, + config: { + isPushEnabled: isManifestV3, + platform: 'extension', + onPushNotificationReceived, + onPushNotificationClicked, + }, }); - pushPlatformNotificationsControllerMessenger.subscribe( - 'PushPlatformNotificationsController:onNewNotifications', + notificationServicesPushControllerMessenger.subscribe( + 'NotificationServicesPushController:onNewNotifications', (notification) => { this.metaMetricsController.trackEvent({ category: MetaMetricsEventCategory.PushNotifications, @@ -1478,8 +1524,8 @@ export default class MetamaskController extends EventEmitter { }); }, ); - pushPlatformNotificationsControllerMessenger.subscribe( - 'PushPlatformNotificationsController:pushNotificationClicked', + notificationServicesPushControllerMessenger.subscribe( + 'NotificationServicesPushController:pushNotificationClicked', (notification) => { this.metaMetricsController.trackEvent({ category: MetaMetricsEventCategory.PushNotifications, @@ -1493,52 +1539,53 @@ export default class MetamaskController extends EventEmitter { }, ); - this.metamaskNotificationsController = new MetamaskNotificationsController({ - messenger: this.controllerMessenger.getRestricted({ - name: 'MetamaskNotificationsController', - allowedActions: [ - 'KeyringController:getAccounts', - 'KeyringController:getState', - 'AuthenticationController:getBearerToken', - 'AuthenticationController:isSignedIn', - 'UserStorageController:enableProfileSyncing', - 'UserStorageController:getStorageKey', - 'UserStorageController:performGetStorage', - 'UserStorageController:performSetStorage', - 'PushPlatformNotificationsController:enablePushNotifications', - 'PushPlatformNotificationsController:disablePushNotifications', - 'PushPlatformNotificationsController:updateTriggerPushNotifications', - ], - allowedEvents: [ - 'KeyringController:stateChange', - 'KeyringController:lock', - 'KeyringController:unlock', - 'PushPlatformNotificationsController:onNewNotifications', - ], - }), - state: initState.MetamaskNotificationsController, - }); - - // Temporary add missing methods (due to notification controller migration) - this.controllerMessenger.registerActionHandler( - 'NotificationServicesController:disableNotificationServices', - () => this.metamaskNotificationsController.disableMetamaskNotifications(), - ); - this.controllerMessenger.registerActionHandler( - 'NotificationServicesController:selectIsNotificationServicesEnabled', - () => - this.metamaskNotificationsController.selectIsMetamaskNotificationsEnabled(), - ); + this.notificationServicesController = + new NotificationServicesController.Controller({ + messenger: this.controllerMessenger.getRestricted({ + name: 'NotificationServicesController', + allowedActions: [ + 'KeyringController:getAccounts', + 'KeyringController:getState', + 'AuthenticationController:getBearerToken', + 'AuthenticationController:isSignedIn', + 'UserStorageController:enableProfileSyncing', + 'UserStorageController:getStorageKey', + 'UserStorageController:performGetStorage', + 'UserStorageController:performSetStorage', + 'NotificationServicesPushController:enablePushNotifications', + 'NotificationServicesPushController:disablePushNotifications', + 'NotificationServicesPushController:updateTriggerPushNotifications', + ], + allowedEvents: [ + 'KeyringController:stateChange', + 'KeyringController:lock', + 'KeyringController:unlock', + 'NotificationServicesPushController:onNewNotifications', + ], + }), + state: initState.NotificationServicesController, + env: { + isPushIntegrated: isManifestV3, + featureAnnouncements: { + platform: 'extension', + spaceId: process.env.CONTENTFUL_ACCESS_SPACE_ID ?? '', + accessToken: process.env.CONTENTFUL_ACCESS_TOKEN ?? '', + }, + }, + }); // account tracker watches balances, nonces, and any code at their address this.accountTracker = new AccountTracker({ provider: this.provider, blockTracker: this.blockTracker, getCurrentChainId: () => - this.networkController.state.providerConfig.chainId, + getCurrentChainId({ metamask: this.networkController.state }), getNetworkIdentifier: (providerConfig) => { const { type, rpcUrl } = - providerConfig ?? this.networkController.state.providerConfig; + providerConfig ?? + getProviderConfig({ + metamask: this.networkController.state, + }); return type === NETWORK_TYPES.RPC ? rpcUrl : type; }, preferencesController: this.preferencesController, @@ -1712,7 +1759,7 @@ export default class MetamaskController extends EventEmitter { getPermittedAccounts: this.getPermittedAccounts.bind(this), getSavedGasFees: () => this.preferencesController.store.getState().advancedGasFee[ - this.networkController.state.providerConfig.chainId + getCurrentChainId({ metamask: this.networkController.state }) ], incomingTransactions: { includeTokenTransfers: false, @@ -1720,7 +1767,7 @@ export default class MetamaskController extends EventEmitter { Boolean( this.preferencesController.store.getState() .incomingTransactionsPreferences?.[ - this.networkController.state.providerConfig.chainId + getCurrentChainId({ metamask: this.networkController.state }) ] && this.onboardingController.store.getState().completedOnboarding, ), queryEntireHistory: false, @@ -1747,6 +1794,7 @@ export default class MetamaskController extends EventEmitter { }, provider: this.provider, testGasFeeFlows: process.env.TEST_GAS_FEE_FLOWS, + trace, hooks: { ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) afterSign: (txMeta, signedEthTx) => @@ -1821,7 +1869,7 @@ export default class MetamaskController extends EventEmitter { }), getAllState: this.getState.bind(this), getCurrentChainId: () => - this.networkController.state.providerConfig.chainId, + getCurrentChainId({ metamask: this.networkController.state }), }); this.signatureController.hub.on( @@ -2232,6 +2280,7 @@ export default class MetamaskController extends EventEmitter { SnapsRegistry: this.snapsRegistry, NotificationController: this.notificationController, SnapInterfaceController: this.snapInterfaceController, + SnapInsightsController: this.snapInsightsController, ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) CustodyController: this.custodyController.store, InstitutionalFeaturesController: @@ -2244,9 +2293,9 @@ export default class MetamaskController extends EventEmitter { // Notification Controllers AuthenticationController: this.authenticationController, UserStorageController: this.userStorageController, - MetamaskNotificationsController: this.metamaskNotificationsController, - PushPlatformNotificationsController: - this.pushPlatformNotificationsController, + NotificationServicesController: this.notificationServicesController, + NotificationServicesPushController: + this.notificationServicesPushController, ...resetOnRestartStore, }); @@ -2284,6 +2333,7 @@ export default class MetamaskController extends EventEmitter { SnapsRegistry: this.snapsRegistry, NotificationController: this.notificationController, SnapInterfaceController: this.snapInterfaceController, + SnapInsightsController: this.snapInsightsController, ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) CustodyController: this.custodyController.store, InstitutionalFeaturesController: @@ -2295,10 +2345,10 @@ export default class MetamaskController extends EventEmitter { // Notification Controllers AuthenticationController: this.authenticationController, UserStorageController: this.userStorageController, - MetamaskNotificationsController: this.metamaskNotificationsController, + NotificationServicesController: this.notificationServicesController, QueuedRequestController: this.queuedRequestController, - PushPlatformNotificationsController: - this.pushPlatformNotificationsController, + NotificationServicesPushController: + this.notificationServicesPushController, ...resetOnRestartStore, }, controllerMessenger: this.controllerMessenger, @@ -2626,7 +2676,7 @@ export default class MetamaskController extends EventEmitter { 'PhishingController:maybeUpdateState', ); }, - isOnPhishingList: (origin) => { + isOnPhishingList: (sender) => { const { usePhishDetect } = this.preferencesController.store.getState(); @@ -2636,7 +2686,7 @@ export default class MetamaskController extends EventEmitter { return this.controllerMessenger.call( 'PhishingController:testOrigin', - origin, + sender.url, ).result; }, createInterface: this.controllerMessenger.call.bind( @@ -3045,8 +3095,8 @@ export default class MetamaskController extends EventEmitter { // Notification Controllers authenticationController, userStorageController, - metamaskNotificationsController, - pushPlatformNotificationsController, + notificationServicesController, + notificationServicesPushController, } = this; return { @@ -3091,7 +3141,10 @@ export default class MetamaskController extends EventEmitter { getUseRequestQueue: this.preferencesController.getUseRequestQueue.bind( this.preferencesController, ), - getProviderConfig: () => this.networkController.state.providerConfig, + getProviderConfig: () => + getProviderConfig({ + metamask: this.networkController.state, + }), setSecurityAlertsEnabled: preferencesController.setSecurityAlertsEnabled.bind( preferencesController, @@ -3102,6 +3155,12 @@ export default class MetamaskController extends EventEmitter { preferencesController, ), ///: END:ONLY_INCLUDE_IF + ///: BEGIN:ONLY_INCLUDE_IF(build-flask) + setWatchEthereumAccountEnabled: + preferencesController.setWatchEthereumAccountEnabled.bind( + preferencesController, + ), + ///: END:ONLY_INCLUDE_IF setBitcoinSupportEnabled: preferencesController.setBitcoinSupportEnabled.bind( preferencesController, @@ -3201,9 +3260,6 @@ export default class MetamaskController extends EventEmitter { verifyPassword: this.verifyPassword.bind(this), // network management - setProviderType: (type) => { - return this.networkController.setProviderType(type); - }, setActiveNetwork: (networkConfigurationId) => { return this.networkController.setActiveNetwork(networkConfigurationId); }, @@ -3826,54 +3882,54 @@ export default class MetamaskController extends EventEmitter { userStorageController, ), - // MetamaskNotificationsController + // NotificationServicesController checkAccountsPresence: - metamaskNotificationsController.checkAccountsPresence.bind( - metamaskNotificationsController, + notificationServicesController.checkAccountsPresence.bind( + notificationServicesController, ), createOnChainTriggers: - metamaskNotificationsController.createOnChainTriggers.bind( - metamaskNotificationsController, + notificationServicesController.createOnChainTriggers.bind( + notificationServicesController, ), deleteOnChainTriggersByAccount: - metamaskNotificationsController.deleteOnChainTriggersByAccount.bind( - metamaskNotificationsController, + notificationServicesController.deleteOnChainTriggersByAccount.bind( + notificationServicesController, ), updateOnChainTriggersByAccount: - metamaskNotificationsController.updateOnChainTriggersByAccount.bind( - metamaskNotificationsController, + notificationServicesController.updateOnChainTriggersByAccount.bind( + notificationServicesController, ), fetchAndUpdateMetamaskNotifications: - metamaskNotificationsController.fetchAndUpdateMetamaskNotifications.bind( - metamaskNotificationsController, + notificationServicesController.fetchAndUpdateMetamaskNotifications.bind( + notificationServicesController, ), markMetamaskNotificationsAsRead: - metamaskNotificationsController.markMetamaskNotificationsAsRead.bind( - metamaskNotificationsController, + notificationServicesController.markMetamaskNotificationsAsRead.bind( + notificationServicesController, ), setFeatureAnnouncementsEnabled: - metamaskNotificationsController.setFeatureAnnouncementsEnabled.bind( - metamaskNotificationsController, + notificationServicesController.setFeatureAnnouncementsEnabled.bind( + notificationServicesController, ), enablePushNotifications: - pushPlatformNotificationsController.enablePushNotifications.bind( - pushPlatformNotificationsController, + notificationServicesPushController.enablePushNotifications.bind( + notificationServicesPushController, ), disablePushNotifications: - pushPlatformNotificationsController.disablePushNotifications.bind( - pushPlatformNotificationsController, + notificationServicesPushController.disablePushNotifications.bind( + notificationServicesPushController, ), updateTriggerPushNotifications: - pushPlatformNotificationsController.updateTriggerPushNotifications.bind( - pushPlatformNotificationsController, + notificationServicesPushController.updateTriggerPushNotifications.bind( + notificationServicesPushController, ), enableMetamaskNotifications: - metamaskNotificationsController.enableMetamaskNotifications.bind( - metamaskNotificationsController, + notificationServicesController.enableMetamaskNotifications.bind( + notificationServicesController, ), disableMetamaskNotifications: - metamaskNotificationsController.disableMetamaskNotifications.bind( - metamaskNotificationsController, + notificationServicesController.disableNotificationServices.bind( + notificationServicesController, ), // E2E testing @@ -4102,7 +4158,9 @@ export default class MetamaskController extends EventEmitter { async _addAccountsWithBalance() { // Scan accounts until we find an empty one - const { chainId } = this.networkController.state.providerConfig; + const chainId = getCurrentChainId({ + metamask: this.networkController.state, + }); const ethQuery = new EthQuery(this.provider); const accounts = await this.keyringController.getAccounts(); let address = accounts[accounts.length - 1]; @@ -4363,7 +4421,9 @@ export default class MetamaskController extends EventEmitter { this.appStateController.setTrezorModel(model); } - keyring.network = this.networkController.state.providerConfig.type; + keyring.network = getProviderConfig({ + metamask: this.networkController.state, + }).type; return keyring; } @@ -4640,10 +4700,11 @@ export default class MetamaskController extends EventEmitter { /** * Stops exposing the specified chain ID to all third parties. - * Exposed chain IDs are stored in caveats of the permittedChains permission. This - * method uses `PermissionController.updatePermissionsByCaveat` to - * remove the specified chain ID from every permittedChains permission. If a - * permission only included this chain ID, the permission is revoked entirely. + * Exposed chain IDs are stored in caveats of the `endowment:permitted-chains` + * permission. This method uses `PermissionController.updatePermissionsByCaveat` + * to remove the specified chain ID from every `endowment:permitted-chains` + * permission. If a permission only included this chain ID, the permission is + * revoked entirely. * * @param {string} targetChainId - The chain ID to stop exposing * to third parties. @@ -4760,7 +4821,7 @@ export default class MetamaskController extends EventEmitter { transactionOptions, transactionParams, userOperationController: this.userOperationController, - chainId: this.networkController.state.providerConfig.chainId, + chainId: getCurrentChainId({ metamask: this.networkController.state }), ppomController: this.ppomController, securityAlertsEnabled: this.preferencesController.store.getState()?.securityAlertsEnabled, @@ -4784,11 +4845,11 @@ export default class MetamaskController extends EventEmitter { * by creating a new transaction. * * @param {number} originalTxId - the id of the txMeta that you want to - * attempt to cancel + * attempt to cancel * @param {import( * './controllers/transactions' * ).CustomGasSettings} [customGasSettings] - overrides to use for gas params - * instead of allowing this method to generate them + * instead of allowing this method to generate them * @param options * @returns {object} MetaMask state */ @@ -4807,11 +4868,11 @@ export default class MetamaskController extends EventEmitter { * by creating a new transaction. * * @param {number} originalTxId - the id of the txMeta that you want to - * attempt to speed up + * attempt to speed up * @param {import( * './controllers/transactions' * ).CustomGasSettings} [customGasSettings] - overrides to use for gas params - * instead of allowing this method to generate them + * instead of allowing this method to generate them * @param options * @returns {object} MetaMask state */ @@ -4938,7 +4999,7 @@ export default class MetamaskController extends EventEmitter { const { hostname } = new URL(sender.url); this.phishingController.maybeUpdateState(); // Check if new connection is blocked if phishing detection is on - const phishingTestResponse = this.phishingController.test(hostname); + const phishingTestResponse = this.phishingController.test(sender.url); if (phishingTestResponse?.result) { this.sendPhishingWarning(connectionStream, hostname); this.metaMetricsController.trackEvent({ @@ -5397,6 +5458,8 @@ export default class MetamaskController extends EventEmitter { engine.push(createTxVerificationMiddleware(this.networkController)); } + engine.push(createTracingMiddleware()); + engine.push( createPPOMMiddleware( this.ppomController, @@ -5575,7 +5638,9 @@ export default class MetamaskController extends EventEmitter { getChainPermissionsFeatureFlag: () => Boolean(process.env.CHAIN_PERMISSIONS), getCurrentRpcUrl: () => - this.networkController.state.providerConfig.rpcUrl, + getProviderConfig({ + metamask: this.networkController.state, + }).rpcUrl, // network configuration-related upsertNetworkConfiguration: this.networkController.upsertNetworkConfiguration.bind( @@ -6803,7 +6868,7 @@ export default class MetamaskController extends EventEmitter { ); } - _publishSmartTransactionHook(transactionMeta) { + _publishSmartTransactionHook(transactionMeta, signedTransactionInHex) { const state = this._getMetaMaskState(); const isSmartTransaction = getIsSmartTransaction(state); if (!isSmartTransaction) { @@ -6813,6 +6878,7 @@ export default class MetamaskController extends EventEmitter { const featureFlags = getFeatureFlagsByChainId(state); return submitSmartTransactionHook({ transactionMeta, + signedTransactionInHex, transactionController: this.txController, smartTransactionsController: this.smartTransactionsController, controllerMessenger: this.controllerMessenger, @@ -6830,7 +6896,9 @@ export default class MetamaskController extends EventEmitter { async #onPreferencesControllerStateChange(currentState, previousState) { const { currentLocale } = currentState; - const { chainId } = this.networkController.state.providerConfig; + const chainId = getCurrentChainId({ + metamask: this.networkController.state, + }); await updateCurrentLocale(currentLocale); diff --git a/app/scripts/metamask-controller.test.js b/app/scripts/metamask-controller.test.js index 9803f2f21c4f..9239c15bc081 100644 --- a/app/scripts/metamask-controller.test.js +++ b/app/scripts/metamask-controller.test.js @@ -19,7 +19,6 @@ import { BtcMethod, EthAccountType, } from '@metamask/keyring-api'; -import { NetworkType } from '@metamask/controller-utils'; import { ControllerMessenger } from '@metamask/base-controller'; import { LoggingController, LogType } from '@metamask/logging-controller'; import { TransactionController } from '@metamask/transaction-controller'; @@ -30,7 +29,6 @@ import { import ObjectMultiplex from '@metamask/object-multiplex'; import { TrezorKeyring } from '@metamask/eth-trezor-keyring'; import { LedgerKeyring } from '@metamask/eth-ledger-bridge-keyring'; -import { NETWORK_TYPES } from '../../shared/constants/network'; import { createTestProviderTools } from '../../test/stub/provider'; import { HardwareDeviceNames } from '../../shared/constants/hardware-wallets'; import { KeyringType } from '../../shared/constants/keyring'; @@ -40,6 +38,7 @@ import * as tokenUtils from '../../shared/lib/token-util'; import { flushPromises } from '../../test/lib/timer-helpers'; import { ETH_EOA_METHODS } from '../../shared/constants/eth-methods'; import { createMockInternalAccount } from '../../test/jest/mocks'; +import { mockNetworkState } from '../../test/stub/networks'; import { BalancesController as MultichainBalancesController, BALANCES_UPDATE_TIME as MULTICHAIN_BALANCES_UPDATE_TIME, @@ -245,49 +244,32 @@ const firstTimeState = { }, }, NetworkController: { - providerConfig: { - type: NETWORK_TYPES.RPC, - rpcUrl: ALT_MAINNET_RPC_URL, - chainId: MAINNET_CHAIN_ID, - ticker: ETH, - nickname: 'Alt Mainnet', - id: NETWORK_CONFIGURATION_ID_1, - }, - networkConfigurations: { - [NETWORK_CONFIGURATION_ID_1]: { + ...mockNetworkState( + { rpcUrl: ALT_MAINNET_RPC_URL, - type: NETWORK_TYPES.RPC, chainId: MAINNET_CHAIN_ID, ticker: ETH, nickname: 'Alt Mainnet', id: NETWORK_CONFIGURATION_ID_1, + blockExplorerUrl: undefined, }, - [NETWORK_CONFIGURATION_ID_2]: { + { rpcUrl: POLYGON_RPC_URL, - type: NETWORK_TYPES.RPC, chainId: POLYGON_CHAIN_ID, ticker: MATIC, nickname: 'Polygon', id: NETWORK_CONFIGURATION_ID_2, + blockExplorerUrl: undefined, }, - [NETWORK_CONFIGURATION_ID_3]: { + { rpcUrl: POLYGON_RPC_URL_2, - type: NETWORK_TYPES.RPC, chainId: POLYGON_CHAIN_ID, ticker: MATIC, nickname: 'Alt Polygon', - id: NETWORK_CONFIGURATION_ID_1, + id: NETWORK_CONFIGURATION_ID_3, + blockExplorerUrl: undefined, }, - }, - selectedNetworkClientId: NetworkType.mainnet, - networksMetadata: { - [NetworkType.mainnet]: { - EIPS: { - 1559: false, - }, - status: 'available', - }, - }, + ), }, NotificationController: { notifications: { @@ -341,10 +323,6 @@ describe('MetaMaskController', () => { blocklist: ['test.metamask-phishing.io'], name: ListNames.MetaMask, }, - phishfort_hotlist: { - blocklist: [], - name: ListNames.Phishfort, - }, }), ) .get(METAMASK_HOTLIST_DIFF_FILE) @@ -2097,7 +2075,6 @@ describe('MetaMaskController', () => { id: NETWORK_CONFIGURATION_ID_1, rpcUrl: ALT_MAINNET_RPC_URL, ticker: ETH, - type: NETWORK_TYPES.RPC, }); }); @@ -2108,7 +2085,6 @@ describe('MetaMaskController', () => { }), ).toStrictEqual({ rpcUrl: POLYGON_RPC_URL, - type: NETWORK_TYPES.RPC, chainId: POLYGON_CHAIN_ID, ticker: MATIC, nickname: 'Polygon', @@ -2127,7 +2103,6 @@ describe('MetaMaskController', () => { id: NETWORK_CONFIGURATION_ID_1, rpcUrl: ALT_MAINNET_RPC_URL, ticker: ETH, - type: NETWORK_TYPES.RPC, }); }); @@ -2146,7 +2121,6 @@ describe('MetaMaskController', () => { }), ).toStrictEqual({ rpcUrl: POLYGON_RPC_URL, - type: NETWORK_TYPES.RPC, chainId: POLYGON_CHAIN_ID, ticker: MATIC, nickname: 'Polygon', diff --git a/app/scripts/migrations/126.test.ts b/app/scripts/migrations/126.test.ts new file mode 100644 index 000000000000..bf05ce28a0d3 --- /dev/null +++ b/app/scripts/migrations/126.test.ts @@ -0,0 +1,50 @@ +import { migrate, version } from './126'; + +const oldVersion = 125; + +describe(`migration #${version}`, () => { + it('updates the version metadata', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: {}, + }; + + const newStorage = await migrate(oldStorage); + + expect(newStorage.meta).toStrictEqual({ version }); + }); + + it('Does nothing if `providerConfig` is not in the network controller state', async () => { + const oldState = { + NetworkController: { + selectedNetworkClientId: 'mainnet', + }, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: oldState, + }); + + expect(transformedState.data).toStrictEqual(oldState); + }); + + it('Removes providerConfig from the network controller state', async () => { + const oldState = { + NetworkController: { + selectedNetworkClientId: 'mainnet', + providerConfig: { + chainId: '0x1', + ticker: 'ETH', + } as object | undefined, + }, + }; + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: oldState, + }); + + delete oldState.NetworkController.providerConfig; + expect(transformedState.data).toStrictEqual(oldState); + }); +}); diff --git a/app/scripts/migrations/126.ts b/app/scripts/migrations/126.ts new file mode 100644 index 000000000000..87c099e7329c --- /dev/null +++ b/app/scripts/migrations/126.ts @@ -0,0 +1,39 @@ +import { hasProperty, isObject } from '@metamask/utils'; +import { cloneDeep } from 'lodash'; + +type VersionedData = { + meta: { version: number }; + data: Record; +}; + +export const version = 126; + +/** + * This migration removes `providerConfig` from the network controller state. + * + * @param originalVersionedData - Versioned MetaMask extension state, exactly what we persist to dist. + * @param originalVersionedData.meta - State metadata. + * @param originalVersionedData.meta.version - The current state version. + * @param originalVersionedData.data - The persisted MetaMask state, keyed by controller. + * @returns Updated versioned MetaMask extension state. + */ +export async function migrate( + originalVersionedData: VersionedData, +): Promise { + const versionedData = cloneDeep(originalVersionedData); + versionedData.meta.version = version; + transformState(versionedData.data); + return versionedData; +} + +function transformState( + state: Record, +): Record { + if ( + hasProperty(state, 'NetworkController') && + isObject(state.NetworkController) + ) { + delete state.NetworkController.providerConfig; + } + return state; +} diff --git a/app/scripts/migrations/index.js b/app/scripts/migrations/index.js index bb64ec957f75..e5cfb6218019 100644 --- a/app/scripts/migrations/index.js +++ b/app/scripts/migrations/index.js @@ -145,6 +145,7 @@ const migrations = [ require('./124'), require('./125'), require('./125.1'), + require('./126'), ]; export default migrations; diff --git a/app/scripts/skip-onboarding.js b/app/scripts/skip-onboarding.js index 17e56cdd9e1e..17ce107927ec 100644 --- a/app/scripts/skip-onboarding.js +++ b/app/scripts/skip-onboarding.js @@ -60,14 +60,6 @@ function generateAnnouncementControllerState() { function generateNetworkControllerState() { return { ...defaultFixture().data.NetworkController, - providerConfig: { - chainId: '0xaa36a7', - rpcPrefs: { - blockExplorerUrl: 'https://sepolia.etherscan.io', - }, - ticker: 'SepoliaETH', - type: 'sepolia', - }, networkConfigurations: { networkConfigurationId: { chainId: '0xaa36a7', diff --git a/app/scripts/snaps/preinstalled-snaps.ts b/app/scripts/snaps/preinstalled-snaps.ts index 6be0bb7f352d..f8bdeaf49a9c 100644 --- a/app/scripts/snaps/preinstalled-snaps.ts +++ b/app/scripts/snaps/preinstalled-snaps.ts @@ -1,6 +1,7 @@ import type { PreinstalledSnap } from '@metamask/snaps-controllers'; import MessageSigningSnap from '@metamask/message-signing-snap/dist/preinstalled-snap.json'; ///: BEGIN:ONLY_INCLUDE_IF(build-flask) +import AccountWatcherSnap from '@metamask/account-watcher/dist/preinstalled-snap.json'; import BitcoinWalletSnap from '@metamask/bitcoin-wallet-snap/dist/preinstalled-snap.json'; ///: END:ONLY_INCLUDE_IF @@ -8,6 +9,7 @@ import BitcoinWalletSnap from '@metamask/bitcoin-wallet-snap/dist/preinstalled-s const PREINSTALLED_SNAPS = Object.freeze([ MessageSigningSnap as PreinstalledSnap, ///: BEGIN:ONLY_INCLUDE_IF(build-flask) + AccountWatcherSnap as PreinstalledSnap, BitcoinWalletSnap as unknown as PreinstalledSnap, ///: END:ONLY_INCLUDE_IF ]); diff --git a/attribution.txt b/attribution.txt index 4932c2f809c5..364128f620f3 100644 --- a/attribution.txt +++ b/attribution.txt @@ -507,6 +507,33 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +****************************** + +asynckit +0.4.0 +The MIT License (MIT) + +Copyright (c) 2016 Alex Indigo + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + ****************************** async-mutex @@ -642,6 +669,19 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +****************************** + +axios +1.6.8 +# Copyright (c) 2014-present Matt Zabriskie & Collaborators + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + ****************************** b4a @@ -4436,6 +4476,31 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +****************************** + +combined-stream +1.0.8 +Copyright (c) 2011 Debuggable Limited + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + ****************************** commander @@ -4580,6 +4645,60 @@ ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +****************************** + +contentful +10.8.7 +The MIT License (MIT) + +Copyright (c) 2016 Contentful + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +contentful-resolve-response +1.8.1 +MIT License + +Copyright (c) 2018 Contentful + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + ****************************** @contentful/rich-text-html-renderer @@ -4634,6 +4753,33 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +****************************** + +contentful-sdk-core +8.1.2 +The MIT License (MIT) + +Copyright (c) 2016 Contentful + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + ****************************** convert-source-map @@ -5283,6 +5429,31 @@ The above copyright notice and this permission notice shall be included in all c THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +****************************** + +delayed-stream +1.0.0 +Copyright (c) 2011 Debuggable Limited + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + ****************************** delimit-stream @@ -11114,6 +11285,33 @@ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +****************************** + +fast-copy +2.1.7 +MIT License + +Copyright (c) 2018 Tony Quetano + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + ****************************** fast-deep-equal @@ -11755,6 +11953,30 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +****************************** + +follow-redirects +1.15.6 +Copyright 2014–present Olivier Lalonde , James Talmage , Ruben Verborgh + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + ****************************** for-each @@ -11804,6 +12026,31 @@ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +****************************** + +form-data +4.0.0 +Copyright (c) 2012 Felix Geisendörfer (felix@debuggable.com) and contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + ****************************** @fortawesome/fontawesome-free @@ -16916,6 +17163,87 @@ licenses; we recommend you read them, as their terms may differ from the terms above. +****************************** + +lodash.isplainobject +4.0.6 +Copyright jQuery Foundation and other contributors + +Based on Underscore.js, copyright Jeremy Ashkenas, +DocumentCloud and Investigative Reporters & Editors + +This software consists of voluntary contributions made by many +individuals. For exact contribution history, see the revision history +available at https://github.com/lodash/lodash + +The following license applies to all parts of this software except as +documented below: + +==== + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +==== + +Copyright and related rights for sample code are waived via CC0. Sample +code is defined as all source code displayed within the prose of the +documentation. + +CC0: http://creativecommons.org/publicdomain/zero/1.0/ + +==== + +Files located in the node_modules and vendor directories are externally +maintained libraries used by this software which have their own +licenses; we recommend you read them, as their terms may differ from the +terms above. + + +****************************** + +lodash.isstring +4.0.1 +Copyright 2012-2016 The Dojo Foundation +Based on Underscore.js, copyright 2009-2016 Jeremy Ashkenas, +DocumentCloud and Investigative Reporters & Editors + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + ****************************** loglevel @@ -20656,6 +20984,64 @@ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +****************************** + +mime-db +1.52.0 +(The MIT License) + +Copyright (c) 2014 Jonathan Ong +Copyright (c) 2015-2022 Douglas Christopher Wilson + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +mime-types +2.1.35 +(The MIT License) + +Copyright (c) 2014 Jonathan Ong +Copyright (c) 2015 Douglas Christopher Wilson + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + ****************************** mini-create-react-context @@ -22982,6 +23368,47 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +****************************** + +proxy-from-env +1.1.0 +The MIT License + +Copyright (C) 2016-2018 Rob Wu + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +p-throttle +4.1.1 +MIT License + +Copyright (c) Sindre Sorhus (https://sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + ****************************** punycode @@ -23102,6 +23529,41 @@ The above copyright notice and this permission notice shall be included in all c THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +****************************** + +qs +6.11.2 +BSD 3-Clause License + +Copyright (c) 2014, Nathan LaFreniere and other [contributors](https://github.com/ljharb/qs/graphs/contributors) +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + ****************************** queue-tick @@ -28480,6 +28942,13 @@ The above copyright notice and this permission notice shall be included in all c THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +****************************** + +type-fest +4.15.0 +license: (MIT OR CC0-1.0) +authors: Sindre Sorhus + ****************************** typeforce diff --git a/development/README.md b/development/README.md index b9247f66aa6f..33ab036975e4 100644 --- a/development/README.md +++ b/development/README.md @@ -55,7 +55,8 @@ You can inspect the requests in the `Network` tab of your browser's Developer To by filtering for `POST` requests to `/v1/batch`. The full url will be `http://localhost:9090/v1/batch` or `https://api.segment.io/v1/batch` respectively. -## Debugging Sentry +## Sentry +### Debugging Sentry 1. Set `SENTRY_DSN_DEV`, or `SENTRY_DSN` if using a production build, in `.metamaskrc` to a suitable Sentry URL. - The example value specified in `.metamaskrc.dist` uses the `test-metamask` project in the MetaMask account. @@ -72,10 +73,43 @@ or `https://api.segment.io/v1/batch` respectively. 6. Alternatively, call `window.stateHooks.throwTestError()` or `window.stateHooks.throwTestBackgroundError()` via the UI console. -## Source Maps +### Debugging the Publish Release Flow +#### Sentry UI Setup +1. Go to Sentry and Create an Organization, by clicking `Account Menu > Switch organization > Create a new organization` +2. Create a Javascript Project, by clicking `Projects > Create Project > Javascript` +3. Create a User Auth Token, by clicking `Account Menu > User auth tokens` +4. Select your newly created project and grant all permissions to your token +5. Copy your token to your clipboard + +[](sentry-auth-token.png) + +#### Sentry-Cli Setup +1. Go to your terminal, inside the `metamask-extension` project +2. Login to Sentry using the command line `yarn sentry-cli login --auth-token YOUR_TOKEN` +3. List your organizations and copy the id for the organization you want to see `yarn sentry-cli organizations list` +4. List your organization projects and copy the id for the you created `yarn sentry-cli projects list --org YOUR_ORG_ID` + +[](sentry-cli.png) + +#### Publish a Release to Sentry +1. Build your desired MetaMask project. Examples: + 1. `yarn dist` to create an MV3 build + 2. `yarn dist:mv2` to create an MV2 build + 3. (and so on) +2. Move the build to its corresponding folder. Ie: `mv dist dist-mv2` (skip this step, if you did the regular MV3 build) +3. Publish the release to Sentry: + 1. If it's an MV3 build `yarn sentry:publish --org YOUR_ORG_ID --project YOUR_PROJECT_ID` + 2. If it's an MV2 build `yarn sentry:publish --dist mv2 --org YOUR_ORG_ID --project YOUR_PROJECT_ID` +4. See build files and source maps are uploaded +5. Go to Sentry +6. Check the Source Maps have been uploaded correctly in Sentry: go to `Settings > Projects > Project Name > Source Maps` + +[](sentry-source-maps.png) + +Extra Note: if you already uploaded one version, you can change the `package.json` version and run again the publish step, to test the complete flow. +## Source Maps ### Debugging production builds using Source Maps - To unbundle the extensions compiled and minified JavaScript using Source Maps: - Open Chrome DevTools to inspect the `background.html` or `home.html` view diff --git a/development/verify-locale-strings.js b/development/verify-locale-strings.js index fd679815d52f..75f9ca52cd4c 100755 --- a/development/verify-locale-strings.js +++ b/development/verify-locale-strings.js @@ -192,7 +192,7 @@ async function verifyEnglishLocale() { 'app/scripts/constants/**/*.js', 'app/scripts/constants/**/*.ts', 'app/scripts/platforms/**/*.js', - 'app/scripts/controllers/push-platform-notifications/utils/get-notification-message.ts', + 'app/scripts/controllers/**/*.ts', ], { ignore: [...globsToStrictSearch, testGlob], diff --git a/docs/assets/sentry-auth-token.png b/docs/assets/sentry-auth-token.png new file mode 100644 index 000000000000..fb9a4de096c8 Binary files /dev/null and b/docs/assets/sentry-auth-token.png differ diff --git a/docs/assets/sentry-cli-release-process.gif b/docs/assets/sentry-cli-release-process.gif new file mode 100644 index 000000000000..2002d1902722 Binary files /dev/null and b/docs/assets/sentry-cli-release-process.gif differ diff --git a/docs/assets/sentry-source-maps.png b/docs/assets/sentry-source-maps.png new file mode 100644 index 000000000000..adde381cb76c Binary files /dev/null and b/docs/assets/sentry-source-maps.png differ diff --git a/jest.config.js b/jest.config.js index 4d7909a0996a..605848626405 100644 --- a/jest.config.js +++ b/jest.config.js @@ -7,11 +7,7 @@ module.exports = { '/test/unit-global/**/*.test.(js|ts|tsx)', ], coverageDirectory: './coverage', - coveragePathIgnorePatterns: [ - '/test/unit-global/protect-intrinsics.test.js', - '.stories.*', - '.snap', - ], + coveragePathIgnorePatterns: ['.stories.*', '.snap'], coverageReporters: process.env.CI ? ['json'] : ['html', 'json'], reporters: [ 'default', @@ -36,10 +32,7 @@ module.exports = { '/test/unit-global/**/*.test.(js|ts|tsx)', '/test/e2e/helpers.test.js', ], - testPathIgnorePatterns: [ - '/test/unit-global/protect-intrinsics.test.js', - '/development/webpack/', - ], + testPathIgnorePatterns: ['/development/webpack/'], testTimeout: 5500, // We have to specify the environment we are running in, which is jsdom. The // default is 'node'. This can be modified *per file* using a comment at the diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index 605c88c89ea1..8558b0b01b56 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -5,11 +5,6 @@ "regeneratorRuntime": "write" } }, - "@contentful/rich-text-html-renderer": { - "globals": { - "SuppressedError": true - } - }, "@ensdomains/content-hash": { "globals": { "console.warn": true @@ -180,8 +175,8 @@ }, "packages": { "@ethereumjs/tx>ethereum-cryptography>@noble/curves": true, - "@ethereumjs/tx>ethereum-cryptography>@noble/hashes": true, - "@ethereumjs/tx>ethereum-cryptography>@scure/bip32": true + "@ethereumjs/tx>ethereum-cryptography>@scure/bip32": true, + "@noble/hashes": true } }, "@ethereumjs/tx>ethereum-cryptography>@noble/curves": { @@ -189,20 +184,14 @@ "TextEncoder": true }, "packages": { - "@ethereumjs/tx>ethereum-cryptography>@noble/hashes": true - } - }, - "@ethereumjs/tx>ethereum-cryptography>@noble/hashes": { - "globals": { - "TextEncoder": true, - "crypto": true + "@noble/hashes": true } }, "@ethereumjs/tx>ethereum-cryptography>@scure/bip32": { "packages": { "@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/curves": true, - "@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/hashes": true, - "@metamask/utils>@scure/base": true + "@metamask/utils>@scure/base": true, + "@noble/hashes": true } }, "@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/curves": { @@ -210,13 +199,7 @@ "TextEncoder": true }, "packages": { - "@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/hashes": true - } - }, - "@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/hashes": { - "globals": { - "TextEncoder": true, - "crypto": true + "@noble/hashes": true } }, "@ethersproject/abi": { @@ -922,12 +905,12 @@ "@ethersproject/providers": true, "@metamask/abi-utils": true, "@metamask/assets-controllers>@metamask/base-controller": true, - "@metamask/assets-controllers>@metamask/controller-utils": true, "@metamask/assets-controllers>@metamask/polling-controller": true, "@metamask/assets-controllers>@metamask/utils": true, "@metamask/assets-controllers>cockatiel": true, "@metamask/assets-controllers>multiformats": true, "@metamask/contract-metadata": true, + "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/metamask-eth-abis": true, "@metamask/name-controller>async-mutex": true, @@ -946,24 +929,6 @@ "immer": true } }, - "@metamask/assets-controllers>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/assets-controllers>@metamask/utils": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, "@metamask/assets-controllers>@metamask/polling-controller": { "globals": { "clearTimeout": true, @@ -1045,15 +1010,30 @@ }, "packages": { "@ethereumjs/tx>@ethereumjs/util": true, + "@metamask/controller-utils>@metamask/utils": true, "@metamask/controller-utils>@spruceid/siwe-parser": true, "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/utils": true, "bn.js": true, "browserify>buffer": true, "eslint>fast-deep-equal": true, "eth-ens-namehash": true } }, + "@metamask/controller-utils>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/abi-utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true + } + }, "@metamask/controller-utils>@spruceid/siwe-parser": { "globals": { "console.error": true, @@ -1087,9 +1067,9 @@ "@metamask/ens-controller": { "packages": { "@ethersproject/providers": true, + "@metamask/controller-utils": true, "@metamask/ens-controller>@metamask/base-controller": true, - "@metamask/ens-controller>@metamask/controller-utils": true, - "@metamask/utils": true, + "@metamask/ens-controller>@metamask/utils": true, "punycode": true } }, @@ -1101,25 +1081,7 @@ "immer": true } }, - "@metamask/ens-controller>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ens-controller>@metamask/controller-utils>@metamask/utils": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, - "@metamask/ens-controller>@metamask/controller-utils>@metamask/utils": { + "@metamask/ens-controller>@metamask/utils": { "globals": { "TextDecoder": true, "TextEncoder": true @@ -1550,46 +1512,13 @@ "setInterval": true }, "packages": { + "@metamask/controller-utils": true, "@metamask/eth-query": true, - "@metamask/gas-fee-controller>@metamask/controller-utils": true, "@metamask/gas-fee-controller>@metamask/polling-controller": true, "bn.js": true, "uuid": true } }, - "@metamask/gas-fee-controller>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/gas-fee-controller>@metamask/controller-utils>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, - "@metamask/gas-fee-controller>@metamask/controller-utils>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/abi-utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/gas-fee-controller>@metamask/polling-controller": { "globals": { "clearTimeout": true, @@ -1861,8 +1790,8 @@ "fetch": true }, "packages": { + "@metamask/controller-utils": true, "@metamask/name-controller>@metamask/base-controller": true, - "@metamask/name-controller>@metamask/controller-utils": true, "@metamask/name-controller>async-mutex": true, "@metamask/utils": true } @@ -1875,39 +1804,6 @@ "immer": true } }, - "@metamask/name-controller>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/name-controller>@metamask/controller-utils>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, - "@metamask/name-controller>@metamask/controller-utils>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/abi-utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/name-controller>async-mutex": { "globals": { "clearTimeout": true, @@ -1925,17 +1821,17 @@ "setTimeout": true }, "packages": { + "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/eth-token-tracker>@metamask/eth-block-tracker": true, "@metamask/network-controller>@metamask/base-controller": true, - "@metamask/network-controller>@metamask/controller-utils": true, "@metamask/network-controller>@metamask/eth-json-rpc-infura": true, "@metamask/network-controller>@metamask/eth-json-rpc-middleware": true, "@metamask/network-controller>@metamask/eth-json-rpc-provider": true, "@metamask/network-controller>@metamask/swappable-obj-proxy": true, + "@metamask/network-controller>@metamask/utils": true, "@metamask/rpc-errors": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/utils": true, "browserify>assert": true, "uuid": true } @@ -1948,39 +1844,6 @@ "immer": true } }, - "@metamask/network-controller>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/network-controller>@metamask/controller-utils>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, - "@metamask/network-controller>@metamask/controller-utils>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/abi-utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/network-controller>@metamask/eth-json-rpc-infura": { "globals": { "setTimeout": true @@ -2017,16 +1880,31 @@ "@metamask/eth-json-rpc-middleware>safe-stable-stringify": true, "@metamask/eth-sig-util": true, "@metamask/network-controller>@metamask/eth-json-rpc-middleware>@metamask/json-rpc-engine": true, + "@metamask/network-controller>@metamask/eth-json-rpc-middleware>@metamask/utils": true, "@metamask/rpc-errors": true, - "@metamask/utils": true, "pify": true } }, "@metamask/network-controller>@metamask/eth-json-rpc-middleware>@metamask/json-rpc-engine": { "packages": { + "@metamask/network-controller>@metamask/eth-json-rpc-middleware>@metamask/utils": true, "@metamask/rpc-errors": true, - "@metamask/safe-event-emitter": true, - "@metamask/utils": true + "@metamask/safe-event-emitter": true + } + }, + "@metamask/network-controller>@metamask/eth-json-rpc-middleware>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true, + "superstruct": true } }, "@metamask/network-controller>@metamask/eth-json-rpc-provider": { @@ -2037,6 +1915,21 @@ "uuid": true } }, + "@metamask/network-controller>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/abi-utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true + } + }, "@metamask/notification-controller": { "packages": { "@metamask/base-controller": true, @@ -2049,80 +1942,194 @@ "crypto.getRandomValues": true } }, - "@metamask/object-multiplex": { + "@metamask/notification-services-controller": { "globals": { - "console.warn": true + "Intl.NumberFormat": true, + "addEventListener": true, + "fetch": true, + "registration": true, + "removeEventListener": true }, "packages": { - "@metamask/object-multiplex>once": true, - "readable-stream": true + "@metamask/controller-utils": true, + "@metamask/notification-services-controller>@contentful/rich-text-html-renderer": true, + "@metamask/notification-services-controller>@metamask/base-controller": true, + "@metamask/notification-services-controller>firebase": true, + "@metamask/profile-sync-controller": true, + "bignumber.js": true, + "loglevel": true, + "uuid": true } }, - "@metamask/object-multiplex>once": { + "@metamask/notification-services-controller>@contentful/rich-text-html-renderer": { + "globals": { + "SuppressedError": true + } + }, + "@metamask/notification-services-controller>@metamask/base-controller": { + "globals": { + "setTimeout": true + }, "packages": { - "@metamask/object-multiplex>once>wrappy": true + "immer": true } }, - "@metamask/obs-store": { + "@metamask/notification-services-controller>firebase": { "packages": { - "@metamask/safe-event-emitter": true, - "readable-stream": true + "@metamask/notification-services-controller>firebase>@firebase/app": true, + "@metamask/notification-services-controller>firebase>@firebase/messaging": true } }, - "@metamask/permission-controller": { + "@metamask/notification-services-controller>firebase>@firebase/app": { "globals": { - "console.error": true + "FinalizationRegistry": true, + "console.warn": true }, "packages": { - "@metamask/permission-controller>@metamask/base-controller": true, - "@metamask/permission-controller>@metamask/controller-utils": true, - "@metamask/permission-controller>nanoid": true, - "@metamask/rpc-errors": true, - "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true + "@metamask/notification-services-controller>firebase>@firebase/app>@firebase/component": true, + "@metamask/notification-services-controller>firebase>@firebase/app>@firebase/logger": true, + "@metamask/notification-services-controller>firebase>@firebase/app>idb": true, + "@metamask/notification-services-controller>firebase>@firebase/util": true } }, - "@metamask/permission-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, + "@metamask/notification-services-controller>firebase>@firebase/app>@firebase/component": { "packages": { - "immer": true + "@metamask/notification-services-controller>firebase>@firebase/util": true } }, - "@metamask/permission-controller>@metamask/controller-utils": { + "@metamask/notification-services-controller>firebase>@firebase/app>@firebase/logger": { "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true + "console": true }, "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/permission-controller>@metamask/controller-utils>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true + "@swc/helpers>tslib": true } }, - "@metamask/permission-controller>@metamask/controller-utils>@metamask/utils": { + "@metamask/notification-services-controller>firebase>@firebase/app>idb": { "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/abi-utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true + "DOMException": true, + "IDBCursor": true, + "IDBDatabase": true, + "IDBIndex": true, + "IDBObjectStore": true, + "IDBRequest": true, + "IDBTransaction": true, + "indexedDB.deleteDatabase": true, + "indexedDB.open": true + } + }, + "@metamask/notification-services-controller>firebase>@firebase/installations": { + "globals": { + "BroadcastChannel": true, + "Headers": true, + "btoa": true, + "console.error": true, + "crypto": true, + "fetch": true, + "msCrypto": true, + "navigator.onLine": true, + "setTimeout": true + }, + "packages": { + "@metamask/notification-services-controller>firebase>@firebase/app": true, + "@metamask/notification-services-controller>firebase>@firebase/app>@firebase/component": true, + "@metamask/notification-services-controller>firebase>@firebase/app>idb": true, + "@metamask/notification-services-controller>firebase>@firebase/util": true + } + }, + "@metamask/notification-services-controller>firebase>@firebase/messaging": { + "globals": { + "Headers": true, + "Notification.maxActions": true, + "Notification.permission": true, + "Notification.requestPermission": true, + "PushSubscription.prototype.hasOwnProperty": true, + "ServiceWorkerRegistration": true, + "URL": true, + "addEventListener": true, + "atob": true, + "btoa": true, + "clients.matchAll": true, + "clients.openWindow": true, + "console.warn": true, + "document": true, + "fetch": true, + "indexedDB": true, + "location.href": true, + "location.origin": true, + "navigator": true, + "origin.replace": true, + "registration.showNotification": true, + "setTimeout": true + }, + "packages": { + "@metamask/notification-services-controller>firebase>@firebase/app": true, + "@metamask/notification-services-controller>firebase>@firebase/app>@firebase/component": true, + "@metamask/notification-services-controller>firebase>@firebase/app>idb": true, + "@metamask/notification-services-controller>firebase>@firebase/installations": true, + "@metamask/notification-services-controller>firebase>@firebase/util": true, + "@swc/helpers>tslib": true + } + }, + "@metamask/notification-services-controller>firebase>@firebase/util": { + "globals": { + "atob": true, + "browser": true, + "btoa": true, + "chrome": true, + "console": true, + "document": true, + "indexedDB": true, + "navigator": true, + "process": true, + "self": true, + "setTimeout": true + }, + "packages": { + "process": true + } + }, + "@metamask/object-multiplex": { + "globals": { + "console.warn": true + }, + "packages": { + "@metamask/object-multiplex>once": true, + "readable-stream": true + } + }, + "@metamask/object-multiplex>once": { + "packages": { + "@metamask/object-multiplex>once>wrappy": true + } + }, + "@metamask/obs-store": { + "packages": { + "@metamask/safe-event-emitter": true, + "readable-stream": true + } + }, + "@metamask/permission-controller": { + "globals": { + "console.error": true + }, + "packages": { + "@metamask/controller-utils": true, + "@metamask/permission-controller>@metamask/base-controller": true, + "@metamask/permission-controller>nanoid": true, + "@metamask/rpc-errors": true, + "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, + "@metamask/utils": true, + "deep-freeze-strict": true, + "immer": true + } + }, + "@metamask/permission-controller>@metamask/base-controller": { + "globals": { + "setTimeout": true + }, + "packages": { + "immer": true } }, "@metamask/permission-controller>nanoid": { @@ -2138,18 +2145,25 @@ }, "@metamask/phishing-controller": { "globals": { + "TextEncoder": true, + "URL": true, "fetch": true }, "packages": { - "@metamask/base-controller": true, + "@ethereumjs/tx>ethereum-cryptography": true, "@metamask/controller-utils": true, - "@metamask/phishing-warning>eth-phishing-detect": true, + "@metamask/phishing-controller>@metamask/base-controller": true, + "@metamask/phishing-controller>fastest-levenshtein": true, + "@noble/hashes": true, "punycode": true } }, - "@metamask/phishing-warning>eth-phishing-detect": { + "@metamask/phishing-controller>@metamask/base-controller": { + "globals": { + "setTimeout": true + }, "packages": { - "eslint>optionator>fast-levenshtein": true + "immer": true } }, "@metamask/post-message-stream": { @@ -2416,27 +2430,57 @@ "console.info": true }, "packages": { - "@metamask/base-controller": true, "@metamask/controller-utils": true, "@metamask/logging-controller": true, - "@metamask/rpc-errors": true, + "@metamask/signature-controller>@metamask/base-controller": true, "@metamask/signature-controller>@metamask/message-manager": true, "lodash": true, "webpack>events": true } }, + "@metamask/signature-controller>@metamask/base-controller": { + "globals": { + "setTimeout": true + }, + "packages": { + "immer": true + } + }, "@metamask/signature-controller>@metamask/message-manager": { "packages": { - "@metamask/base-controller": true, "@metamask/controller-utils": true, "@metamask/eth-sig-util": true, "@metamask/message-manager>jsonschema": true, - "@metamask/utils": true, + "@metamask/signature-controller>@metamask/message-manager>@metamask/base-controller": true, + "@metamask/signature-controller>@metamask/utils": true, "browserify>buffer": true, "uuid": true, "webpack>events": true } }, + "@metamask/signature-controller>@metamask/message-manager>@metamask/base-controller": { + "globals": { + "setTimeout": true + }, + "packages": { + "immer": true + } + }, + "@metamask/signature-controller>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/abi-utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true + } + }, "@metamask/smart-transactions-controller": { "globals": { "URLSearchParams": true, @@ -2448,11 +2492,11 @@ }, "packages": { "@ethersproject/bytes": true, + "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/gas-fee-controller>@metamask/polling-controller": true, "@metamask/smart-transactions-controller>@ethereumjs/tx": true, "@metamask/smart-transactions-controller>@ethereumjs/util": true, - "@metamask/smart-transactions-controller>@metamask/controller-utils": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller": true, "@metamask/smart-transactions-controller>bignumber.js": true, "browserify>buffer": true, @@ -2509,52 +2553,6 @@ "immer": true } }, - "@metamask/smart-transactions-controller>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/smart-transactions-controller>@metamask/controller-utils>@ethereumjs/util": true, - "@metamask/smart-transactions-controller>@metamask/controller-utils>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, - "@metamask/smart-transactions-controller>@metamask/controller-utils>@ethereumjs/util": { - "globals": { - "console.warn": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/rlp": true, - "@ethereumjs/tx>@ethereumjs/util>micro-ftch": true, - "@ethereumjs/tx>ethereum-cryptography": true, - "browserify>buffer": true, - "browserify>insert-module-globals>is-buffer": true, - "webpack>events": true - } - }, - "@metamask/smart-transactions-controller>@metamask/controller-utils>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/abi-utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/smart-transactions-controller>@metamask/controllers>nanoid": { "globals": { "crypto.getRandomValues": true @@ -2572,6 +2570,7 @@ "@ethersproject/abi": true, "@ethersproject/contracts": true, "@ethersproject/providers": true, + "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/gas-fee-controller": true, "@metamask/metamask-eth-abis": true, @@ -2579,11 +2578,10 @@ "@metamask/network-controller": true, "@metamask/rpc-errors": true, "@metamask/smart-transactions-controller>@metamask/base-controller": true, - "@metamask/smart-transactions-controller>@metamask/controller-utils": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@ethereumjs/tx": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@ethereumjs/util": true, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/nonce-tracker": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry": true, - "@metamask/transaction-controller>@metamask/nonce-tracker": true, "@metamask/utils": true, "bn.js": true, "browserify>buffer": true, @@ -2616,6 +2614,22 @@ "webpack>events": true } }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/nonce-tracker": { + "packages": { + "@ethersproject/providers": true, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/nonce-tracker>async-mutex": true, + "browserify>assert": true + } + }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/nonce-tracker>async-mutex": { + "globals": { + "clearTimeout": true, + "setTimeout": true + }, + "packages": { + "@swc/helpers>tslib": true + } + }, "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry": { "packages": { "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-contract": true, @@ -2773,34 +2787,16 @@ "console.error": true }, "packages": { + "@metamask/controller-utils": true, "@metamask/rpc-errors": true, "@metamask/snaps-controllers>@metamask/base-controller": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/snaps-controllers>@metamask/phishing-controller>@metamask/controller-utils": true, "@metamask/snaps-controllers>@metamask/utils": true, "@metamask/snaps-controllers>nanoid": true, "deep-freeze-strict": true, "immer": true } }, - "@metamask/snaps-controllers>@metamask/phishing-controller>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/snaps-controllers>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, "@metamask/snaps-controllers>@metamask/utils": { "globals": { "TextDecoder": true, @@ -2921,34 +2917,16 @@ "console.error": true }, "packages": { + "@metamask/controller-utils": true, "@metamask/rpc-errors": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, "@metamask/snaps-execution-environments>@metamask/snaps-utils>@metamask/base-controller": true, - "@metamask/snaps-execution-environments>@metamask/snaps-utils>@metamask/permission-controller>@metamask/controller-utils": true, "@metamask/snaps-execution-environments>@metamask/utils": true, "@metamask/snaps-execution-environments>nanoid": true, "deep-freeze-strict": true, "immer": true } }, - "@metamask/snaps-execution-environments>@metamask/snaps-utils>@metamask/permission-controller>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/snaps-execution-environments>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, "@metamask/snaps-execution-environments>@metamask/utils": { "globals": { "TextDecoder": true, @@ -2986,10 +2964,10 @@ "console.error": true }, "packages": { + "@metamask/controller-utils": true, "@metamask/rpc-errors": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/controller-utils": true, "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": true, "@metamask/snaps-rpc-methods>@metamask/utils": true, "deep-freeze-strict": true, @@ -3004,24 +2982,6 @@ "immer": true } }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/snaps-rpc-methods>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": { "globals": { "crypto.getRandomValues": true @@ -3139,34 +3099,16 @@ "console.error": true }, "packages": { + "@metamask/controller-utils": true, "@metamask/rpc-errors": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, "@metamask/snaps-utils>@metamask/base-controller": true, - "@metamask/snaps-utils>@metamask/permission-controller>@metamask/controller-utils": true, "@metamask/snaps-utils>@metamask/permission-controller>nanoid": true, "@metamask/snaps-utils>@metamask/utils": true, "deep-freeze-strict": true, "immer": true } }, - "@metamask/snaps-utils>@metamask/permission-controller>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/snaps-utils>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, "@metamask/snaps-utils>@metamask/permission-controller>nanoid": { "globals": { "crypto.getRandomValues": true @@ -3314,6 +3256,7 @@ "@ethersproject/abi": true, "@ethersproject/contracts": true, "@ethersproject/providers": true, + "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/gas-fee-controller": true, "@metamask/metamask-eth-abis": true, @@ -3321,9 +3264,8 @@ "@metamask/network-controller": true, "@metamask/rpc-errors": true, "@metamask/transaction-controller>@metamask/base-controller": true, - "@metamask/transaction-controller>@metamask/controller-utils": true, "@metamask/transaction-controller>@metamask/nonce-tracker": true, - "@metamask/utils": true, + "@metamask/transaction-controller>@metamask/utils": true, "bn.js": true, "browserify>buffer": true, "eth-method-registry": true, @@ -3341,25 +3283,23 @@ "immer": true } }, - "@metamask/transaction-controller>@metamask/controller-utils": { + "@metamask/transaction-controller>@metamask/nonce-tracker": { + "packages": { + "@ethersproject/providers": true, + "@metamask/transaction-controller>@metamask/nonce-tracker>async-mutex": true, + "browserify>assert": true + } + }, + "@metamask/transaction-controller>@metamask/nonce-tracker>async-mutex": { "globals": { - "URL": true, - "console.error": true, - "fetch": true, + "clearTimeout": true, "setTimeout": true }, "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/transaction-controller>@metamask/controller-utils>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true + "@swc/helpers>tslib": true } }, - "@metamask/transaction-controller>@metamask/controller-utils>@metamask/utils": { + "@metamask/transaction-controller>@metamask/utils": { "globals": { "TextDecoder": true, "TextEncoder": true @@ -3374,34 +3314,18 @@ "semver": true } }, - "@metamask/transaction-controller>@metamask/nonce-tracker": { - "packages": { - "@ethersproject/providers": true, - "@metamask/transaction-controller>@metamask/nonce-tracker>async-mutex": true, - "browserify>assert": true - } - }, - "@metamask/transaction-controller>@metamask/nonce-tracker>async-mutex": { - "globals": { - "clearTimeout": true, - "setTimeout": true - }, - "packages": { - "@swc/helpers>tslib": true - } - }, "@metamask/user-operation-controller": { "globals": { "fetch": true }, "packages": { + "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/gas-fee-controller": true, "@metamask/gas-fee-controller>@metamask/polling-controller": true, "@metamask/rpc-errors": true, "@metamask/transaction-controller": true, "@metamask/user-operation-controller>@metamask/base-controller": true, - "@metamask/user-operation-controller>@metamask/controller-utils": true, "@metamask/utils": true, "bn.js": true, "lodash": true, @@ -3418,39 +3342,6 @@ "immer": true } }, - "@metamask/user-operation-controller>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/user-operation-controller>@metamask/controller-utils>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, - "@metamask/user-operation-controller>@metamask/controller-utils>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/abi-utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/utils": { "globals": { "TextDecoder": true, @@ -3826,9 +3717,41 @@ "@trezor/connect-web>@trezor/connect>@trezor/protobuf": { "packages": { "@swc/helpers>tslib": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs": true, "@trezor/connect-web>@trezor/connect>@trezor/schema-utils": true, - "browserify>buffer": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs": true + "browserify>buffer": true + } + }, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs": { + "globals": { + "process": true, + "setTimeout": true + }, + "packages": { + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/aspromise": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/base64": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/codegen": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/eventemitter": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/fetch": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/float": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/inquire": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/path": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/pool": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/utf8": true + } + }, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/codegen": { + "globals": { + "console.log": true + } + }, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/fetch": { + "globals": { + "XMLHttpRequest": true + }, + "packages": { + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/aspromise": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/inquire": true } }, "@trezor/connect-web>@trezor/connect>@trezor/schema-utils": { @@ -4376,16 +4299,6 @@ "koa>is-generator-function>has-tostringtag": true } }, - "eslint>optionator>fast-levenshtein": { - "globals": { - "Intl": true, - "Levenshtein": "write", - "console.log": true, - "define": true, - "importScripts": true, - "postMessage": true - } - }, "eth-ens-namehash": { "globals": { "name": "write" @@ -4776,153 +4689,6 @@ "setTimeout": true } }, - "firebase": { - "packages": { - "firebase>@firebase/app": true, - "firebase>@firebase/messaging": true - } - }, - "firebase>@firebase/app": { - "globals": { - "FinalizationRegistry": true, - "console.warn": true - }, - "packages": { - "firebase>@firebase/app>@firebase/component": true, - "firebase>@firebase/app>@firebase/logger": true, - "firebase>@firebase/app>idb": true, - "firebase>@firebase/util": true - } - }, - "firebase>@firebase/app>@firebase/component": { - "packages": { - "firebase>@firebase/util": true - } - }, - "firebase>@firebase/app>@firebase/logger": { - "globals": { - "console": true - }, - "packages": { - "@swc/helpers>tslib": true - } - }, - "firebase>@firebase/app>idb": { - "globals": { - "DOMException": true, - "IDBCursor": true, - "IDBDatabase": true, - "IDBIndex": true, - "IDBObjectStore": true, - "IDBRequest": true, - "IDBTransaction": true, - "indexedDB.deleteDatabase": true, - "indexedDB.open": true - } - }, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs": { - "globals": { - "process": true, - "setTimeout": true - }, - "packages": { - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/aspromise": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/base64": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/codegen": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/eventemitter": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/fetch": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/float": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/inquire": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/path": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/pool": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/utf8": true - } - }, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/codegen": { - "globals": { - "console.log": true - } - }, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/fetch": { - "globals": { - "XMLHttpRequest": true - }, - "packages": { - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/aspromise": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/inquire": true - } - }, - "firebase>@firebase/installations": { - "globals": { - "BroadcastChannel": true, - "Headers": true, - "btoa": true, - "console.error": true, - "crypto": true, - "fetch": true, - "msCrypto": true, - "navigator.onLine": true, - "setTimeout": true - }, - "packages": { - "firebase>@firebase/app": true, - "firebase>@firebase/app>@firebase/component": true, - "firebase>@firebase/app>idb": true, - "firebase>@firebase/util": true - } - }, - "firebase>@firebase/messaging": { - "globals": { - "Headers": true, - "Notification.maxActions": true, - "Notification.permission": true, - "Notification.requestPermission": true, - "PushSubscription.prototype.hasOwnProperty": true, - "ServiceWorkerRegistration": true, - "URL": true, - "addEventListener": true, - "atob": true, - "btoa": true, - "clients.matchAll": true, - "clients.openWindow": true, - "console.warn": true, - "document": true, - "fetch": true, - "indexedDB": true, - "location.href": true, - "location.origin": true, - "navigator": true, - "origin.replace": true, - "registration.showNotification": true, - "setTimeout": true - }, - "packages": { - "@swc/helpers>tslib": true, - "firebase>@firebase/app": true, - "firebase>@firebase/app>@firebase/component": true, - "firebase>@firebase/app>idb": true, - "firebase>@firebase/installations": true, - "firebase>@firebase/util": true - } - }, - "firebase>@firebase/util": { - "globals": { - "atob": true, - "browser": true, - "btoa": true, - "chrome": true, - "console": true, - "document": true, - "indexedDB": true, - "navigator": true, - "process": true, - "self": true, - "setTimeout": true - }, - "packages": { - "process": true - } - }, "fuse.js": { "globals": { "console": true, diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index 605c88c89ea1..8558b0b01b56 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -5,11 +5,6 @@ "regeneratorRuntime": "write" } }, - "@contentful/rich-text-html-renderer": { - "globals": { - "SuppressedError": true - } - }, "@ensdomains/content-hash": { "globals": { "console.warn": true @@ -180,8 +175,8 @@ }, "packages": { "@ethereumjs/tx>ethereum-cryptography>@noble/curves": true, - "@ethereumjs/tx>ethereum-cryptography>@noble/hashes": true, - "@ethereumjs/tx>ethereum-cryptography>@scure/bip32": true + "@ethereumjs/tx>ethereum-cryptography>@scure/bip32": true, + "@noble/hashes": true } }, "@ethereumjs/tx>ethereum-cryptography>@noble/curves": { @@ -189,20 +184,14 @@ "TextEncoder": true }, "packages": { - "@ethereumjs/tx>ethereum-cryptography>@noble/hashes": true - } - }, - "@ethereumjs/tx>ethereum-cryptography>@noble/hashes": { - "globals": { - "TextEncoder": true, - "crypto": true + "@noble/hashes": true } }, "@ethereumjs/tx>ethereum-cryptography>@scure/bip32": { "packages": { "@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/curves": true, - "@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/hashes": true, - "@metamask/utils>@scure/base": true + "@metamask/utils>@scure/base": true, + "@noble/hashes": true } }, "@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/curves": { @@ -210,13 +199,7 @@ "TextEncoder": true }, "packages": { - "@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/hashes": true - } - }, - "@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/hashes": { - "globals": { - "TextEncoder": true, - "crypto": true + "@noble/hashes": true } }, "@ethersproject/abi": { @@ -922,12 +905,12 @@ "@ethersproject/providers": true, "@metamask/abi-utils": true, "@metamask/assets-controllers>@metamask/base-controller": true, - "@metamask/assets-controllers>@metamask/controller-utils": true, "@metamask/assets-controllers>@metamask/polling-controller": true, "@metamask/assets-controllers>@metamask/utils": true, "@metamask/assets-controllers>cockatiel": true, "@metamask/assets-controllers>multiformats": true, "@metamask/contract-metadata": true, + "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/metamask-eth-abis": true, "@metamask/name-controller>async-mutex": true, @@ -946,24 +929,6 @@ "immer": true } }, - "@metamask/assets-controllers>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/assets-controllers>@metamask/utils": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, "@metamask/assets-controllers>@metamask/polling-controller": { "globals": { "clearTimeout": true, @@ -1045,15 +1010,30 @@ }, "packages": { "@ethereumjs/tx>@ethereumjs/util": true, + "@metamask/controller-utils>@metamask/utils": true, "@metamask/controller-utils>@spruceid/siwe-parser": true, "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/utils": true, "bn.js": true, "browserify>buffer": true, "eslint>fast-deep-equal": true, "eth-ens-namehash": true } }, + "@metamask/controller-utils>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/abi-utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true + } + }, "@metamask/controller-utils>@spruceid/siwe-parser": { "globals": { "console.error": true, @@ -1087,9 +1067,9 @@ "@metamask/ens-controller": { "packages": { "@ethersproject/providers": true, + "@metamask/controller-utils": true, "@metamask/ens-controller>@metamask/base-controller": true, - "@metamask/ens-controller>@metamask/controller-utils": true, - "@metamask/utils": true, + "@metamask/ens-controller>@metamask/utils": true, "punycode": true } }, @@ -1101,25 +1081,7 @@ "immer": true } }, - "@metamask/ens-controller>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ens-controller>@metamask/controller-utils>@metamask/utils": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, - "@metamask/ens-controller>@metamask/controller-utils>@metamask/utils": { + "@metamask/ens-controller>@metamask/utils": { "globals": { "TextDecoder": true, "TextEncoder": true @@ -1550,46 +1512,13 @@ "setInterval": true }, "packages": { + "@metamask/controller-utils": true, "@metamask/eth-query": true, - "@metamask/gas-fee-controller>@metamask/controller-utils": true, "@metamask/gas-fee-controller>@metamask/polling-controller": true, "bn.js": true, "uuid": true } }, - "@metamask/gas-fee-controller>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/gas-fee-controller>@metamask/controller-utils>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, - "@metamask/gas-fee-controller>@metamask/controller-utils>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/abi-utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/gas-fee-controller>@metamask/polling-controller": { "globals": { "clearTimeout": true, @@ -1861,8 +1790,8 @@ "fetch": true }, "packages": { + "@metamask/controller-utils": true, "@metamask/name-controller>@metamask/base-controller": true, - "@metamask/name-controller>@metamask/controller-utils": true, "@metamask/name-controller>async-mutex": true, "@metamask/utils": true } @@ -1875,39 +1804,6 @@ "immer": true } }, - "@metamask/name-controller>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/name-controller>@metamask/controller-utils>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, - "@metamask/name-controller>@metamask/controller-utils>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/abi-utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/name-controller>async-mutex": { "globals": { "clearTimeout": true, @@ -1925,17 +1821,17 @@ "setTimeout": true }, "packages": { + "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/eth-token-tracker>@metamask/eth-block-tracker": true, "@metamask/network-controller>@metamask/base-controller": true, - "@metamask/network-controller>@metamask/controller-utils": true, "@metamask/network-controller>@metamask/eth-json-rpc-infura": true, "@metamask/network-controller>@metamask/eth-json-rpc-middleware": true, "@metamask/network-controller>@metamask/eth-json-rpc-provider": true, "@metamask/network-controller>@metamask/swappable-obj-proxy": true, + "@metamask/network-controller>@metamask/utils": true, "@metamask/rpc-errors": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/utils": true, "browserify>assert": true, "uuid": true } @@ -1948,39 +1844,6 @@ "immer": true } }, - "@metamask/network-controller>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/network-controller>@metamask/controller-utils>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, - "@metamask/network-controller>@metamask/controller-utils>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/abi-utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/network-controller>@metamask/eth-json-rpc-infura": { "globals": { "setTimeout": true @@ -2017,16 +1880,31 @@ "@metamask/eth-json-rpc-middleware>safe-stable-stringify": true, "@metamask/eth-sig-util": true, "@metamask/network-controller>@metamask/eth-json-rpc-middleware>@metamask/json-rpc-engine": true, + "@metamask/network-controller>@metamask/eth-json-rpc-middleware>@metamask/utils": true, "@metamask/rpc-errors": true, - "@metamask/utils": true, "pify": true } }, "@metamask/network-controller>@metamask/eth-json-rpc-middleware>@metamask/json-rpc-engine": { "packages": { + "@metamask/network-controller>@metamask/eth-json-rpc-middleware>@metamask/utils": true, "@metamask/rpc-errors": true, - "@metamask/safe-event-emitter": true, - "@metamask/utils": true + "@metamask/safe-event-emitter": true + } + }, + "@metamask/network-controller>@metamask/eth-json-rpc-middleware>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true, + "superstruct": true } }, "@metamask/network-controller>@metamask/eth-json-rpc-provider": { @@ -2037,6 +1915,21 @@ "uuid": true } }, + "@metamask/network-controller>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/abi-utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true + } + }, "@metamask/notification-controller": { "packages": { "@metamask/base-controller": true, @@ -2049,80 +1942,194 @@ "crypto.getRandomValues": true } }, - "@metamask/object-multiplex": { + "@metamask/notification-services-controller": { "globals": { - "console.warn": true + "Intl.NumberFormat": true, + "addEventListener": true, + "fetch": true, + "registration": true, + "removeEventListener": true }, "packages": { - "@metamask/object-multiplex>once": true, - "readable-stream": true + "@metamask/controller-utils": true, + "@metamask/notification-services-controller>@contentful/rich-text-html-renderer": true, + "@metamask/notification-services-controller>@metamask/base-controller": true, + "@metamask/notification-services-controller>firebase": true, + "@metamask/profile-sync-controller": true, + "bignumber.js": true, + "loglevel": true, + "uuid": true } }, - "@metamask/object-multiplex>once": { + "@metamask/notification-services-controller>@contentful/rich-text-html-renderer": { + "globals": { + "SuppressedError": true + } + }, + "@metamask/notification-services-controller>@metamask/base-controller": { + "globals": { + "setTimeout": true + }, "packages": { - "@metamask/object-multiplex>once>wrappy": true + "immer": true } }, - "@metamask/obs-store": { + "@metamask/notification-services-controller>firebase": { "packages": { - "@metamask/safe-event-emitter": true, - "readable-stream": true + "@metamask/notification-services-controller>firebase>@firebase/app": true, + "@metamask/notification-services-controller>firebase>@firebase/messaging": true } }, - "@metamask/permission-controller": { + "@metamask/notification-services-controller>firebase>@firebase/app": { "globals": { - "console.error": true + "FinalizationRegistry": true, + "console.warn": true }, "packages": { - "@metamask/permission-controller>@metamask/base-controller": true, - "@metamask/permission-controller>@metamask/controller-utils": true, - "@metamask/permission-controller>nanoid": true, - "@metamask/rpc-errors": true, - "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true + "@metamask/notification-services-controller>firebase>@firebase/app>@firebase/component": true, + "@metamask/notification-services-controller>firebase>@firebase/app>@firebase/logger": true, + "@metamask/notification-services-controller>firebase>@firebase/app>idb": true, + "@metamask/notification-services-controller>firebase>@firebase/util": true } }, - "@metamask/permission-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, + "@metamask/notification-services-controller>firebase>@firebase/app>@firebase/component": { "packages": { - "immer": true + "@metamask/notification-services-controller>firebase>@firebase/util": true } }, - "@metamask/permission-controller>@metamask/controller-utils": { + "@metamask/notification-services-controller>firebase>@firebase/app>@firebase/logger": { "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true + "console": true }, "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/permission-controller>@metamask/controller-utils>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true + "@swc/helpers>tslib": true } }, - "@metamask/permission-controller>@metamask/controller-utils>@metamask/utils": { + "@metamask/notification-services-controller>firebase>@firebase/app>idb": { "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/abi-utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true + "DOMException": true, + "IDBCursor": true, + "IDBDatabase": true, + "IDBIndex": true, + "IDBObjectStore": true, + "IDBRequest": true, + "IDBTransaction": true, + "indexedDB.deleteDatabase": true, + "indexedDB.open": true + } + }, + "@metamask/notification-services-controller>firebase>@firebase/installations": { + "globals": { + "BroadcastChannel": true, + "Headers": true, + "btoa": true, + "console.error": true, + "crypto": true, + "fetch": true, + "msCrypto": true, + "navigator.onLine": true, + "setTimeout": true + }, + "packages": { + "@metamask/notification-services-controller>firebase>@firebase/app": true, + "@metamask/notification-services-controller>firebase>@firebase/app>@firebase/component": true, + "@metamask/notification-services-controller>firebase>@firebase/app>idb": true, + "@metamask/notification-services-controller>firebase>@firebase/util": true + } + }, + "@metamask/notification-services-controller>firebase>@firebase/messaging": { + "globals": { + "Headers": true, + "Notification.maxActions": true, + "Notification.permission": true, + "Notification.requestPermission": true, + "PushSubscription.prototype.hasOwnProperty": true, + "ServiceWorkerRegistration": true, + "URL": true, + "addEventListener": true, + "atob": true, + "btoa": true, + "clients.matchAll": true, + "clients.openWindow": true, + "console.warn": true, + "document": true, + "fetch": true, + "indexedDB": true, + "location.href": true, + "location.origin": true, + "navigator": true, + "origin.replace": true, + "registration.showNotification": true, + "setTimeout": true + }, + "packages": { + "@metamask/notification-services-controller>firebase>@firebase/app": true, + "@metamask/notification-services-controller>firebase>@firebase/app>@firebase/component": true, + "@metamask/notification-services-controller>firebase>@firebase/app>idb": true, + "@metamask/notification-services-controller>firebase>@firebase/installations": true, + "@metamask/notification-services-controller>firebase>@firebase/util": true, + "@swc/helpers>tslib": true + } + }, + "@metamask/notification-services-controller>firebase>@firebase/util": { + "globals": { + "atob": true, + "browser": true, + "btoa": true, + "chrome": true, + "console": true, + "document": true, + "indexedDB": true, + "navigator": true, + "process": true, + "self": true, + "setTimeout": true + }, + "packages": { + "process": true + } + }, + "@metamask/object-multiplex": { + "globals": { + "console.warn": true + }, + "packages": { + "@metamask/object-multiplex>once": true, + "readable-stream": true + } + }, + "@metamask/object-multiplex>once": { + "packages": { + "@metamask/object-multiplex>once>wrappy": true + } + }, + "@metamask/obs-store": { + "packages": { + "@metamask/safe-event-emitter": true, + "readable-stream": true + } + }, + "@metamask/permission-controller": { + "globals": { + "console.error": true + }, + "packages": { + "@metamask/controller-utils": true, + "@metamask/permission-controller>@metamask/base-controller": true, + "@metamask/permission-controller>nanoid": true, + "@metamask/rpc-errors": true, + "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, + "@metamask/utils": true, + "deep-freeze-strict": true, + "immer": true + } + }, + "@metamask/permission-controller>@metamask/base-controller": { + "globals": { + "setTimeout": true + }, + "packages": { + "immer": true } }, "@metamask/permission-controller>nanoid": { @@ -2138,18 +2145,25 @@ }, "@metamask/phishing-controller": { "globals": { + "TextEncoder": true, + "URL": true, "fetch": true }, "packages": { - "@metamask/base-controller": true, + "@ethereumjs/tx>ethereum-cryptography": true, "@metamask/controller-utils": true, - "@metamask/phishing-warning>eth-phishing-detect": true, + "@metamask/phishing-controller>@metamask/base-controller": true, + "@metamask/phishing-controller>fastest-levenshtein": true, + "@noble/hashes": true, "punycode": true } }, - "@metamask/phishing-warning>eth-phishing-detect": { + "@metamask/phishing-controller>@metamask/base-controller": { + "globals": { + "setTimeout": true + }, "packages": { - "eslint>optionator>fast-levenshtein": true + "immer": true } }, "@metamask/post-message-stream": { @@ -2416,27 +2430,57 @@ "console.info": true }, "packages": { - "@metamask/base-controller": true, "@metamask/controller-utils": true, "@metamask/logging-controller": true, - "@metamask/rpc-errors": true, + "@metamask/signature-controller>@metamask/base-controller": true, "@metamask/signature-controller>@metamask/message-manager": true, "lodash": true, "webpack>events": true } }, + "@metamask/signature-controller>@metamask/base-controller": { + "globals": { + "setTimeout": true + }, + "packages": { + "immer": true + } + }, "@metamask/signature-controller>@metamask/message-manager": { "packages": { - "@metamask/base-controller": true, "@metamask/controller-utils": true, "@metamask/eth-sig-util": true, "@metamask/message-manager>jsonschema": true, - "@metamask/utils": true, + "@metamask/signature-controller>@metamask/message-manager>@metamask/base-controller": true, + "@metamask/signature-controller>@metamask/utils": true, "browserify>buffer": true, "uuid": true, "webpack>events": true } }, + "@metamask/signature-controller>@metamask/message-manager>@metamask/base-controller": { + "globals": { + "setTimeout": true + }, + "packages": { + "immer": true + } + }, + "@metamask/signature-controller>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/abi-utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true + } + }, "@metamask/smart-transactions-controller": { "globals": { "URLSearchParams": true, @@ -2448,11 +2492,11 @@ }, "packages": { "@ethersproject/bytes": true, + "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/gas-fee-controller>@metamask/polling-controller": true, "@metamask/smart-transactions-controller>@ethereumjs/tx": true, "@metamask/smart-transactions-controller>@ethereumjs/util": true, - "@metamask/smart-transactions-controller>@metamask/controller-utils": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller": true, "@metamask/smart-transactions-controller>bignumber.js": true, "browserify>buffer": true, @@ -2509,52 +2553,6 @@ "immer": true } }, - "@metamask/smart-transactions-controller>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/smart-transactions-controller>@metamask/controller-utils>@ethereumjs/util": true, - "@metamask/smart-transactions-controller>@metamask/controller-utils>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, - "@metamask/smart-transactions-controller>@metamask/controller-utils>@ethereumjs/util": { - "globals": { - "console.warn": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/rlp": true, - "@ethereumjs/tx>@ethereumjs/util>micro-ftch": true, - "@ethereumjs/tx>ethereum-cryptography": true, - "browserify>buffer": true, - "browserify>insert-module-globals>is-buffer": true, - "webpack>events": true - } - }, - "@metamask/smart-transactions-controller>@metamask/controller-utils>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/abi-utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/smart-transactions-controller>@metamask/controllers>nanoid": { "globals": { "crypto.getRandomValues": true @@ -2572,6 +2570,7 @@ "@ethersproject/abi": true, "@ethersproject/contracts": true, "@ethersproject/providers": true, + "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/gas-fee-controller": true, "@metamask/metamask-eth-abis": true, @@ -2579,11 +2578,10 @@ "@metamask/network-controller": true, "@metamask/rpc-errors": true, "@metamask/smart-transactions-controller>@metamask/base-controller": true, - "@metamask/smart-transactions-controller>@metamask/controller-utils": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@ethereumjs/tx": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@ethereumjs/util": true, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/nonce-tracker": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry": true, - "@metamask/transaction-controller>@metamask/nonce-tracker": true, "@metamask/utils": true, "bn.js": true, "browserify>buffer": true, @@ -2616,6 +2614,22 @@ "webpack>events": true } }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/nonce-tracker": { + "packages": { + "@ethersproject/providers": true, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/nonce-tracker>async-mutex": true, + "browserify>assert": true + } + }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/nonce-tracker>async-mutex": { + "globals": { + "clearTimeout": true, + "setTimeout": true + }, + "packages": { + "@swc/helpers>tslib": true + } + }, "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry": { "packages": { "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-contract": true, @@ -2773,34 +2787,16 @@ "console.error": true }, "packages": { + "@metamask/controller-utils": true, "@metamask/rpc-errors": true, "@metamask/snaps-controllers>@metamask/base-controller": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/snaps-controllers>@metamask/phishing-controller>@metamask/controller-utils": true, "@metamask/snaps-controllers>@metamask/utils": true, "@metamask/snaps-controllers>nanoid": true, "deep-freeze-strict": true, "immer": true } }, - "@metamask/snaps-controllers>@metamask/phishing-controller>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/snaps-controllers>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, "@metamask/snaps-controllers>@metamask/utils": { "globals": { "TextDecoder": true, @@ -2921,34 +2917,16 @@ "console.error": true }, "packages": { + "@metamask/controller-utils": true, "@metamask/rpc-errors": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, "@metamask/snaps-execution-environments>@metamask/snaps-utils>@metamask/base-controller": true, - "@metamask/snaps-execution-environments>@metamask/snaps-utils>@metamask/permission-controller>@metamask/controller-utils": true, "@metamask/snaps-execution-environments>@metamask/utils": true, "@metamask/snaps-execution-environments>nanoid": true, "deep-freeze-strict": true, "immer": true } }, - "@metamask/snaps-execution-environments>@metamask/snaps-utils>@metamask/permission-controller>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/snaps-execution-environments>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, "@metamask/snaps-execution-environments>@metamask/utils": { "globals": { "TextDecoder": true, @@ -2986,10 +2964,10 @@ "console.error": true }, "packages": { + "@metamask/controller-utils": true, "@metamask/rpc-errors": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/controller-utils": true, "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": true, "@metamask/snaps-rpc-methods>@metamask/utils": true, "deep-freeze-strict": true, @@ -3004,24 +2982,6 @@ "immer": true } }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/snaps-rpc-methods>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": { "globals": { "crypto.getRandomValues": true @@ -3139,34 +3099,16 @@ "console.error": true }, "packages": { + "@metamask/controller-utils": true, "@metamask/rpc-errors": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, "@metamask/snaps-utils>@metamask/base-controller": true, - "@metamask/snaps-utils>@metamask/permission-controller>@metamask/controller-utils": true, "@metamask/snaps-utils>@metamask/permission-controller>nanoid": true, "@metamask/snaps-utils>@metamask/utils": true, "deep-freeze-strict": true, "immer": true } }, - "@metamask/snaps-utils>@metamask/permission-controller>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/snaps-utils>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, "@metamask/snaps-utils>@metamask/permission-controller>nanoid": { "globals": { "crypto.getRandomValues": true @@ -3314,6 +3256,7 @@ "@ethersproject/abi": true, "@ethersproject/contracts": true, "@ethersproject/providers": true, + "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/gas-fee-controller": true, "@metamask/metamask-eth-abis": true, @@ -3321,9 +3264,8 @@ "@metamask/network-controller": true, "@metamask/rpc-errors": true, "@metamask/transaction-controller>@metamask/base-controller": true, - "@metamask/transaction-controller>@metamask/controller-utils": true, "@metamask/transaction-controller>@metamask/nonce-tracker": true, - "@metamask/utils": true, + "@metamask/transaction-controller>@metamask/utils": true, "bn.js": true, "browserify>buffer": true, "eth-method-registry": true, @@ -3341,25 +3283,23 @@ "immer": true } }, - "@metamask/transaction-controller>@metamask/controller-utils": { + "@metamask/transaction-controller>@metamask/nonce-tracker": { + "packages": { + "@ethersproject/providers": true, + "@metamask/transaction-controller>@metamask/nonce-tracker>async-mutex": true, + "browserify>assert": true + } + }, + "@metamask/transaction-controller>@metamask/nonce-tracker>async-mutex": { "globals": { - "URL": true, - "console.error": true, - "fetch": true, + "clearTimeout": true, "setTimeout": true }, "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/transaction-controller>@metamask/controller-utils>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true + "@swc/helpers>tslib": true } }, - "@metamask/transaction-controller>@metamask/controller-utils>@metamask/utils": { + "@metamask/transaction-controller>@metamask/utils": { "globals": { "TextDecoder": true, "TextEncoder": true @@ -3374,34 +3314,18 @@ "semver": true } }, - "@metamask/transaction-controller>@metamask/nonce-tracker": { - "packages": { - "@ethersproject/providers": true, - "@metamask/transaction-controller>@metamask/nonce-tracker>async-mutex": true, - "browserify>assert": true - } - }, - "@metamask/transaction-controller>@metamask/nonce-tracker>async-mutex": { - "globals": { - "clearTimeout": true, - "setTimeout": true - }, - "packages": { - "@swc/helpers>tslib": true - } - }, "@metamask/user-operation-controller": { "globals": { "fetch": true }, "packages": { + "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/gas-fee-controller": true, "@metamask/gas-fee-controller>@metamask/polling-controller": true, "@metamask/rpc-errors": true, "@metamask/transaction-controller": true, "@metamask/user-operation-controller>@metamask/base-controller": true, - "@metamask/user-operation-controller>@metamask/controller-utils": true, "@metamask/utils": true, "bn.js": true, "lodash": true, @@ -3418,39 +3342,6 @@ "immer": true } }, - "@metamask/user-operation-controller>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/user-operation-controller>@metamask/controller-utils>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, - "@metamask/user-operation-controller>@metamask/controller-utils>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/abi-utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/utils": { "globals": { "TextDecoder": true, @@ -3826,9 +3717,41 @@ "@trezor/connect-web>@trezor/connect>@trezor/protobuf": { "packages": { "@swc/helpers>tslib": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs": true, "@trezor/connect-web>@trezor/connect>@trezor/schema-utils": true, - "browserify>buffer": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs": true + "browserify>buffer": true + } + }, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs": { + "globals": { + "process": true, + "setTimeout": true + }, + "packages": { + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/aspromise": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/base64": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/codegen": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/eventemitter": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/fetch": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/float": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/inquire": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/path": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/pool": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/utf8": true + } + }, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/codegen": { + "globals": { + "console.log": true + } + }, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/fetch": { + "globals": { + "XMLHttpRequest": true + }, + "packages": { + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/aspromise": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/inquire": true } }, "@trezor/connect-web>@trezor/connect>@trezor/schema-utils": { @@ -4376,16 +4299,6 @@ "koa>is-generator-function>has-tostringtag": true } }, - "eslint>optionator>fast-levenshtein": { - "globals": { - "Intl": true, - "Levenshtein": "write", - "console.log": true, - "define": true, - "importScripts": true, - "postMessage": true - } - }, "eth-ens-namehash": { "globals": { "name": "write" @@ -4776,153 +4689,6 @@ "setTimeout": true } }, - "firebase": { - "packages": { - "firebase>@firebase/app": true, - "firebase>@firebase/messaging": true - } - }, - "firebase>@firebase/app": { - "globals": { - "FinalizationRegistry": true, - "console.warn": true - }, - "packages": { - "firebase>@firebase/app>@firebase/component": true, - "firebase>@firebase/app>@firebase/logger": true, - "firebase>@firebase/app>idb": true, - "firebase>@firebase/util": true - } - }, - "firebase>@firebase/app>@firebase/component": { - "packages": { - "firebase>@firebase/util": true - } - }, - "firebase>@firebase/app>@firebase/logger": { - "globals": { - "console": true - }, - "packages": { - "@swc/helpers>tslib": true - } - }, - "firebase>@firebase/app>idb": { - "globals": { - "DOMException": true, - "IDBCursor": true, - "IDBDatabase": true, - "IDBIndex": true, - "IDBObjectStore": true, - "IDBRequest": true, - "IDBTransaction": true, - "indexedDB.deleteDatabase": true, - "indexedDB.open": true - } - }, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs": { - "globals": { - "process": true, - "setTimeout": true - }, - "packages": { - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/aspromise": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/base64": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/codegen": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/eventemitter": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/fetch": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/float": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/inquire": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/path": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/pool": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/utf8": true - } - }, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/codegen": { - "globals": { - "console.log": true - } - }, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/fetch": { - "globals": { - "XMLHttpRequest": true - }, - "packages": { - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/aspromise": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/inquire": true - } - }, - "firebase>@firebase/installations": { - "globals": { - "BroadcastChannel": true, - "Headers": true, - "btoa": true, - "console.error": true, - "crypto": true, - "fetch": true, - "msCrypto": true, - "navigator.onLine": true, - "setTimeout": true - }, - "packages": { - "firebase>@firebase/app": true, - "firebase>@firebase/app>@firebase/component": true, - "firebase>@firebase/app>idb": true, - "firebase>@firebase/util": true - } - }, - "firebase>@firebase/messaging": { - "globals": { - "Headers": true, - "Notification.maxActions": true, - "Notification.permission": true, - "Notification.requestPermission": true, - "PushSubscription.prototype.hasOwnProperty": true, - "ServiceWorkerRegistration": true, - "URL": true, - "addEventListener": true, - "atob": true, - "btoa": true, - "clients.matchAll": true, - "clients.openWindow": true, - "console.warn": true, - "document": true, - "fetch": true, - "indexedDB": true, - "location.href": true, - "location.origin": true, - "navigator": true, - "origin.replace": true, - "registration.showNotification": true, - "setTimeout": true - }, - "packages": { - "@swc/helpers>tslib": true, - "firebase>@firebase/app": true, - "firebase>@firebase/app>@firebase/component": true, - "firebase>@firebase/app>idb": true, - "firebase>@firebase/installations": true, - "firebase>@firebase/util": true - } - }, - "firebase>@firebase/util": { - "globals": { - "atob": true, - "browser": true, - "btoa": true, - "chrome": true, - "console": true, - "document": true, - "indexedDB": true, - "navigator": true, - "process": true, - "self": true, - "setTimeout": true - }, - "packages": { - "process": true - } - }, "fuse.js": { "globals": { "console": true, diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index 605c88c89ea1..8558b0b01b56 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -5,11 +5,6 @@ "regeneratorRuntime": "write" } }, - "@contentful/rich-text-html-renderer": { - "globals": { - "SuppressedError": true - } - }, "@ensdomains/content-hash": { "globals": { "console.warn": true @@ -180,8 +175,8 @@ }, "packages": { "@ethereumjs/tx>ethereum-cryptography>@noble/curves": true, - "@ethereumjs/tx>ethereum-cryptography>@noble/hashes": true, - "@ethereumjs/tx>ethereum-cryptography>@scure/bip32": true + "@ethereumjs/tx>ethereum-cryptography>@scure/bip32": true, + "@noble/hashes": true } }, "@ethereumjs/tx>ethereum-cryptography>@noble/curves": { @@ -189,20 +184,14 @@ "TextEncoder": true }, "packages": { - "@ethereumjs/tx>ethereum-cryptography>@noble/hashes": true - } - }, - "@ethereumjs/tx>ethereum-cryptography>@noble/hashes": { - "globals": { - "TextEncoder": true, - "crypto": true + "@noble/hashes": true } }, "@ethereumjs/tx>ethereum-cryptography>@scure/bip32": { "packages": { "@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/curves": true, - "@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/hashes": true, - "@metamask/utils>@scure/base": true + "@metamask/utils>@scure/base": true, + "@noble/hashes": true } }, "@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/curves": { @@ -210,13 +199,7 @@ "TextEncoder": true }, "packages": { - "@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/hashes": true - } - }, - "@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/hashes": { - "globals": { - "TextEncoder": true, - "crypto": true + "@noble/hashes": true } }, "@ethersproject/abi": { @@ -922,12 +905,12 @@ "@ethersproject/providers": true, "@metamask/abi-utils": true, "@metamask/assets-controllers>@metamask/base-controller": true, - "@metamask/assets-controllers>@metamask/controller-utils": true, "@metamask/assets-controllers>@metamask/polling-controller": true, "@metamask/assets-controllers>@metamask/utils": true, "@metamask/assets-controllers>cockatiel": true, "@metamask/assets-controllers>multiformats": true, "@metamask/contract-metadata": true, + "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/metamask-eth-abis": true, "@metamask/name-controller>async-mutex": true, @@ -946,24 +929,6 @@ "immer": true } }, - "@metamask/assets-controllers>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/assets-controllers>@metamask/utils": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, "@metamask/assets-controllers>@metamask/polling-controller": { "globals": { "clearTimeout": true, @@ -1045,15 +1010,30 @@ }, "packages": { "@ethereumjs/tx>@ethereumjs/util": true, + "@metamask/controller-utils>@metamask/utils": true, "@metamask/controller-utils>@spruceid/siwe-parser": true, "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/utils": true, "bn.js": true, "browserify>buffer": true, "eslint>fast-deep-equal": true, "eth-ens-namehash": true } }, + "@metamask/controller-utils>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/abi-utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true + } + }, "@metamask/controller-utils>@spruceid/siwe-parser": { "globals": { "console.error": true, @@ -1087,9 +1067,9 @@ "@metamask/ens-controller": { "packages": { "@ethersproject/providers": true, + "@metamask/controller-utils": true, "@metamask/ens-controller>@metamask/base-controller": true, - "@metamask/ens-controller>@metamask/controller-utils": true, - "@metamask/utils": true, + "@metamask/ens-controller>@metamask/utils": true, "punycode": true } }, @@ -1101,25 +1081,7 @@ "immer": true } }, - "@metamask/ens-controller>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ens-controller>@metamask/controller-utils>@metamask/utils": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, - "@metamask/ens-controller>@metamask/controller-utils>@metamask/utils": { + "@metamask/ens-controller>@metamask/utils": { "globals": { "TextDecoder": true, "TextEncoder": true @@ -1550,46 +1512,13 @@ "setInterval": true }, "packages": { + "@metamask/controller-utils": true, "@metamask/eth-query": true, - "@metamask/gas-fee-controller>@metamask/controller-utils": true, "@metamask/gas-fee-controller>@metamask/polling-controller": true, "bn.js": true, "uuid": true } }, - "@metamask/gas-fee-controller>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/gas-fee-controller>@metamask/controller-utils>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, - "@metamask/gas-fee-controller>@metamask/controller-utils>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/abi-utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/gas-fee-controller>@metamask/polling-controller": { "globals": { "clearTimeout": true, @@ -1861,8 +1790,8 @@ "fetch": true }, "packages": { + "@metamask/controller-utils": true, "@metamask/name-controller>@metamask/base-controller": true, - "@metamask/name-controller>@metamask/controller-utils": true, "@metamask/name-controller>async-mutex": true, "@metamask/utils": true } @@ -1875,39 +1804,6 @@ "immer": true } }, - "@metamask/name-controller>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/name-controller>@metamask/controller-utils>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, - "@metamask/name-controller>@metamask/controller-utils>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/abi-utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/name-controller>async-mutex": { "globals": { "clearTimeout": true, @@ -1925,17 +1821,17 @@ "setTimeout": true }, "packages": { + "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/eth-token-tracker>@metamask/eth-block-tracker": true, "@metamask/network-controller>@metamask/base-controller": true, - "@metamask/network-controller>@metamask/controller-utils": true, "@metamask/network-controller>@metamask/eth-json-rpc-infura": true, "@metamask/network-controller>@metamask/eth-json-rpc-middleware": true, "@metamask/network-controller>@metamask/eth-json-rpc-provider": true, "@metamask/network-controller>@metamask/swappable-obj-proxy": true, + "@metamask/network-controller>@metamask/utils": true, "@metamask/rpc-errors": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/utils": true, "browserify>assert": true, "uuid": true } @@ -1948,39 +1844,6 @@ "immer": true } }, - "@metamask/network-controller>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/network-controller>@metamask/controller-utils>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, - "@metamask/network-controller>@metamask/controller-utils>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/abi-utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/network-controller>@metamask/eth-json-rpc-infura": { "globals": { "setTimeout": true @@ -2017,16 +1880,31 @@ "@metamask/eth-json-rpc-middleware>safe-stable-stringify": true, "@metamask/eth-sig-util": true, "@metamask/network-controller>@metamask/eth-json-rpc-middleware>@metamask/json-rpc-engine": true, + "@metamask/network-controller>@metamask/eth-json-rpc-middleware>@metamask/utils": true, "@metamask/rpc-errors": true, - "@metamask/utils": true, "pify": true } }, "@metamask/network-controller>@metamask/eth-json-rpc-middleware>@metamask/json-rpc-engine": { "packages": { + "@metamask/network-controller>@metamask/eth-json-rpc-middleware>@metamask/utils": true, "@metamask/rpc-errors": true, - "@metamask/safe-event-emitter": true, - "@metamask/utils": true + "@metamask/safe-event-emitter": true + } + }, + "@metamask/network-controller>@metamask/eth-json-rpc-middleware>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true, + "superstruct": true } }, "@metamask/network-controller>@metamask/eth-json-rpc-provider": { @@ -2037,6 +1915,21 @@ "uuid": true } }, + "@metamask/network-controller>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/abi-utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true + } + }, "@metamask/notification-controller": { "packages": { "@metamask/base-controller": true, @@ -2049,80 +1942,194 @@ "crypto.getRandomValues": true } }, - "@metamask/object-multiplex": { + "@metamask/notification-services-controller": { "globals": { - "console.warn": true + "Intl.NumberFormat": true, + "addEventListener": true, + "fetch": true, + "registration": true, + "removeEventListener": true }, "packages": { - "@metamask/object-multiplex>once": true, - "readable-stream": true + "@metamask/controller-utils": true, + "@metamask/notification-services-controller>@contentful/rich-text-html-renderer": true, + "@metamask/notification-services-controller>@metamask/base-controller": true, + "@metamask/notification-services-controller>firebase": true, + "@metamask/profile-sync-controller": true, + "bignumber.js": true, + "loglevel": true, + "uuid": true } }, - "@metamask/object-multiplex>once": { + "@metamask/notification-services-controller>@contentful/rich-text-html-renderer": { + "globals": { + "SuppressedError": true + } + }, + "@metamask/notification-services-controller>@metamask/base-controller": { + "globals": { + "setTimeout": true + }, "packages": { - "@metamask/object-multiplex>once>wrappy": true + "immer": true } }, - "@metamask/obs-store": { + "@metamask/notification-services-controller>firebase": { "packages": { - "@metamask/safe-event-emitter": true, - "readable-stream": true + "@metamask/notification-services-controller>firebase>@firebase/app": true, + "@metamask/notification-services-controller>firebase>@firebase/messaging": true } }, - "@metamask/permission-controller": { + "@metamask/notification-services-controller>firebase>@firebase/app": { "globals": { - "console.error": true + "FinalizationRegistry": true, + "console.warn": true }, "packages": { - "@metamask/permission-controller>@metamask/base-controller": true, - "@metamask/permission-controller>@metamask/controller-utils": true, - "@metamask/permission-controller>nanoid": true, - "@metamask/rpc-errors": true, - "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true + "@metamask/notification-services-controller>firebase>@firebase/app>@firebase/component": true, + "@metamask/notification-services-controller>firebase>@firebase/app>@firebase/logger": true, + "@metamask/notification-services-controller>firebase>@firebase/app>idb": true, + "@metamask/notification-services-controller>firebase>@firebase/util": true } }, - "@metamask/permission-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, + "@metamask/notification-services-controller>firebase>@firebase/app>@firebase/component": { "packages": { - "immer": true + "@metamask/notification-services-controller>firebase>@firebase/util": true } }, - "@metamask/permission-controller>@metamask/controller-utils": { + "@metamask/notification-services-controller>firebase>@firebase/app>@firebase/logger": { "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true + "console": true }, "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/permission-controller>@metamask/controller-utils>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true + "@swc/helpers>tslib": true } }, - "@metamask/permission-controller>@metamask/controller-utils>@metamask/utils": { + "@metamask/notification-services-controller>firebase>@firebase/app>idb": { "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/abi-utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true + "DOMException": true, + "IDBCursor": true, + "IDBDatabase": true, + "IDBIndex": true, + "IDBObjectStore": true, + "IDBRequest": true, + "IDBTransaction": true, + "indexedDB.deleteDatabase": true, + "indexedDB.open": true + } + }, + "@metamask/notification-services-controller>firebase>@firebase/installations": { + "globals": { + "BroadcastChannel": true, + "Headers": true, + "btoa": true, + "console.error": true, + "crypto": true, + "fetch": true, + "msCrypto": true, + "navigator.onLine": true, + "setTimeout": true + }, + "packages": { + "@metamask/notification-services-controller>firebase>@firebase/app": true, + "@metamask/notification-services-controller>firebase>@firebase/app>@firebase/component": true, + "@metamask/notification-services-controller>firebase>@firebase/app>idb": true, + "@metamask/notification-services-controller>firebase>@firebase/util": true + } + }, + "@metamask/notification-services-controller>firebase>@firebase/messaging": { + "globals": { + "Headers": true, + "Notification.maxActions": true, + "Notification.permission": true, + "Notification.requestPermission": true, + "PushSubscription.prototype.hasOwnProperty": true, + "ServiceWorkerRegistration": true, + "URL": true, + "addEventListener": true, + "atob": true, + "btoa": true, + "clients.matchAll": true, + "clients.openWindow": true, + "console.warn": true, + "document": true, + "fetch": true, + "indexedDB": true, + "location.href": true, + "location.origin": true, + "navigator": true, + "origin.replace": true, + "registration.showNotification": true, + "setTimeout": true + }, + "packages": { + "@metamask/notification-services-controller>firebase>@firebase/app": true, + "@metamask/notification-services-controller>firebase>@firebase/app>@firebase/component": true, + "@metamask/notification-services-controller>firebase>@firebase/app>idb": true, + "@metamask/notification-services-controller>firebase>@firebase/installations": true, + "@metamask/notification-services-controller>firebase>@firebase/util": true, + "@swc/helpers>tslib": true + } + }, + "@metamask/notification-services-controller>firebase>@firebase/util": { + "globals": { + "atob": true, + "browser": true, + "btoa": true, + "chrome": true, + "console": true, + "document": true, + "indexedDB": true, + "navigator": true, + "process": true, + "self": true, + "setTimeout": true + }, + "packages": { + "process": true + } + }, + "@metamask/object-multiplex": { + "globals": { + "console.warn": true + }, + "packages": { + "@metamask/object-multiplex>once": true, + "readable-stream": true + } + }, + "@metamask/object-multiplex>once": { + "packages": { + "@metamask/object-multiplex>once>wrappy": true + } + }, + "@metamask/obs-store": { + "packages": { + "@metamask/safe-event-emitter": true, + "readable-stream": true + } + }, + "@metamask/permission-controller": { + "globals": { + "console.error": true + }, + "packages": { + "@metamask/controller-utils": true, + "@metamask/permission-controller>@metamask/base-controller": true, + "@metamask/permission-controller>nanoid": true, + "@metamask/rpc-errors": true, + "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, + "@metamask/utils": true, + "deep-freeze-strict": true, + "immer": true + } + }, + "@metamask/permission-controller>@metamask/base-controller": { + "globals": { + "setTimeout": true + }, + "packages": { + "immer": true } }, "@metamask/permission-controller>nanoid": { @@ -2138,18 +2145,25 @@ }, "@metamask/phishing-controller": { "globals": { + "TextEncoder": true, + "URL": true, "fetch": true }, "packages": { - "@metamask/base-controller": true, + "@ethereumjs/tx>ethereum-cryptography": true, "@metamask/controller-utils": true, - "@metamask/phishing-warning>eth-phishing-detect": true, + "@metamask/phishing-controller>@metamask/base-controller": true, + "@metamask/phishing-controller>fastest-levenshtein": true, + "@noble/hashes": true, "punycode": true } }, - "@metamask/phishing-warning>eth-phishing-detect": { + "@metamask/phishing-controller>@metamask/base-controller": { + "globals": { + "setTimeout": true + }, "packages": { - "eslint>optionator>fast-levenshtein": true + "immer": true } }, "@metamask/post-message-stream": { @@ -2416,27 +2430,57 @@ "console.info": true }, "packages": { - "@metamask/base-controller": true, "@metamask/controller-utils": true, "@metamask/logging-controller": true, - "@metamask/rpc-errors": true, + "@metamask/signature-controller>@metamask/base-controller": true, "@metamask/signature-controller>@metamask/message-manager": true, "lodash": true, "webpack>events": true } }, + "@metamask/signature-controller>@metamask/base-controller": { + "globals": { + "setTimeout": true + }, + "packages": { + "immer": true + } + }, "@metamask/signature-controller>@metamask/message-manager": { "packages": { - "@metamask/base-controller": true, "@metamask/controller-utils": true, "@metamask/eth-sig-util": true, "@metamask/message-manager>jsonschema": true, - "@metamask/utils": true, + "@metamask/signature-controller>@metamask/message-manager>@metamask/base-controller": true, + "@metamask/signature-controller>@metamask/utils": true, "browserify>buffer": true, "uuid": true, "webpack>events": true } }, + "@metamask/signature-controller>@metamask/message-manager>@metamask/base-controller": { + "globals": { + "setTimeout": true + }, + "packages": { + "immer": true + } + }, + "@metamask/signature-controller>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/abi-utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true + } + }, "@metamask/smart-transactions-controller": { "globals": { "URLSearchParams": true, @@ -2448,11 +2492,11 @@ }, "packages": { "@ethersproject/bytes": true, + "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/gas-fee-controller>@metamask/polling-controller": true, "@metamask/smart-transactions-controller>@ethereumjs/tx": true, "@metamask/smart-transactions-controller>@ethereumjs/util": true, - "@metamask/smart-transactions-controller>@metamask/controller-utils": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller": true, "@metamask/smart-transactions-controller>bignumber.js": true, "browserify>buffer": true, @@ -2509,52 +2553,6 @@ "immer": true } }, - "@metamask/smart-transactions-controller>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/smart-transactions-controller>@metamask/controller-utils>@ethereumjs/util": true, - "@metamask/smart-transactions-controller>@metamask/controller-utils>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, - "@metamask/smart-transactions-controller>@metamask/controller-utils>@ethereumjs/util": { - "globals": { - "console.warn": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/rlp": true, - "@ethereumjs/tx>@ethereumjs/util>micro-ftch": true, - "@ethereumjs/tx>ethereum-cryptography": true, - "browserify>buffer": true, - "browserify>insert-module-globals>is-buffer": true, - "webpack>events": true - } - }, - "@metamask/smart-transactions-controller>@metamask/controller-utils>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/abi-utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/smart-transactions-controller>@metamask/controllers>nanoid": { "globals": { "crypto.getRandomValues": true @@ -2572,6 +2570,7 @@ "@ethersproject/abi": true, "@ethersproject/contracts": true, "@ethersproject/providers": true, + "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/gas-fee-controller": true, "@metamask/metamask-eth-abis": true, @@ -2579,11 +2578,10 @@ "@metamask/network-controller": true, "@metamask/rpc-errors": true, "@metamask/smart-transactions-controller>@metamask/base-controller": true, - "@metamask/smart-transactions-controller>@metamask/controller-utils": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@ethereumjs/tx": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@ethereumjs/util": true, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/nonce-tracker": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry": true, - "@metamask/transaction-controller>@metamask/nonce-tracker": true, "@metamask/utils": true, "bn.js": true, "browserify>buffer": true, @@ -2616,6 +2614,22 @@ "webpack>events": true } }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/nonce-tracker": { + "packages": { + "@ethersproject/providers": true, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/nonce-tracker>async-mutex": true, + "browserify>assert": true + } + }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/nonce-tracker>async-mutex": { + "globals": { + "clearTimeout": true, + "setTimeout": true + }, + "packages": { + "@swc/helpers>tslib": true + } + }, "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry": { "packages": { "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-contract": true, @@ -2773,34 +2787,16 @@ "console.error": true }, "packages": { + "@metamask/controller-utils": true, "@metamask/rpc-errors": true, "@metamask/snaps-controllers>@metamask/base-controller": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/snaps-controllers>@metamask/phishing-controller>@metamask/controller-utils": true, "@metamask/snaps-controllers>@metamask/utils": true, "@metamask/snaps-controllers>nanoid": true, "deep-freeze-strict": true, "immer": true } }, - "@metamask/snaps-controllers>@metamask/phishing-controller>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/snaps-controllers>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, "@metamask/snaps-controllers>@metamask/utils": { "globals": { "TextDecoder": true, @@ -2921,34 +2917,16 @@ "console.error": true }, "packages": { + "@metamask/controller-utils": true, "@metamask/rpc-errors": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, "@metamask/snaps-execution-environments>@metamask/snaps-utils>@metamask/base-controller": true, - "@metamask/snaps-execution-environments>@metamask/snaps-utils>@metamask/permission-controller>@metamask/controller-utils": true, "@metamask/snaps-execution-environments>@metamask/utils": true, "@metamask/snaps-execution-environments>nanoid": true, "deep-freeze-strict": true, "immer": true } }, - "@metamask/snaps-execution-environments>@metamask/snaps-utils>@metamask/permission-controller>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/snaps-execution-environments>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, "@metamask/snaps-execution-environments>@metamask/utils": { "globals": { "TextDecoder": true, @@ -2986,10 +2964,10 @@ "console.error": true }, "packages": { + "@metamask/controller-utils": true, "@metamask/rpc-errors": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/controller-utils": true, "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": true, "@metamask/snaps-rpc-methods>@metamask/utils": true, "deep-freeze-strict": true, @@ -3004,24 +2982,6 @@ "immer": true } }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/snaps-rpc-methods>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": { "globals": { "crypto.getRandomValues": true @@ -3139,34 +3099,16 @@ "console.error": true }, "packages": { + "@metamask/controller-utils": true, "@metamask/rpc-errors": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, "@metamask/snaps-utils>@metamask/base-controller": true, - "@metamask/snaps-utils>@metamask/permission-controller>@metamask/controller-utils": true, "@metamask/snaps-utils>@metamask/permission-controller>nanoid": true, "@metamask/snaps-utils>@metamask/utils": true, "deep-freeze-strict": true, "immer": true } }, - "@metamask/snaps-utils>@metamask/permission-controller>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/snaps-utils>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, "@metamask/snaps-utils>@metamask/permission-controller>nanoid": { "globals": { "crypto.getRandomValues": true @@ -3314,6 +3256,7 @@ "@ethersproject/abi": true, "@ethersproject/contracts": true, "@ethersproject/providers": true, + "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/gas-fee-controller": true, "@metamask/metamask-eth-abis": true, @@ -3321,9 +3264,8 @@ "@metamask/network-controller": true, "@metamask/rpc-errors": true, "@metamask/transaction-controller>@metamask/base-controller": true, - "@metamask/transaction-controller>@metamask/controller-utils": true, "@metamask/transaction-controller>@metamask/nonce-tracker": true, - "@metamask/utils": true, + "@metamask/transaction-controller>@metamask/utils": true, "bn.js": true, "browserify>buffer": true, "eth-method-registry": true, @@ -3341,25 +3283,23 @@ "immer": true } }, - "@metamask/transaction-controller>@metamask/controller-utils": { + "@metamask/transaction-controller>@metamask/nonce-tracker": { + "packages": { + "@ethersproject/providers": true, + "@metamask/transaction-controller>@metamask/nonce-tracker>async-mutex": true, + "browserify>assert": true + } + }, + "@metamask/transaction-controller>@metamask/nonce-tracker>async-mutex": { "globals": { - "URL": true, - "console.error": true, - "fetch": true, + "clearTimeout": true, "setTimeout": true }, "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/transaction-controller>@metamask/controller-utils>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true + "@swc/helpers>tslib": true } }, - "@metamask/transaction-controller>@metamask/controller-utils>@metamask/utils": { + "@metamask/transaction-controller>@metamask/utils": { "globals": { "TextDecoder": true, "TextEncoder": true @@ -3374,34 +3314,18 @@ "semver": true } }, - "@metamask/transaction-controller>@metamask/nonce-tracker": { - "packages": { - "@ethersproject/providers": true, - "@metamask/transaction-controller>@metamask/nonce-tracker>async-mutex": true, - "browserify>assert": true - } - }, - "@metamask/transaction-controller>@metamask/nonce-tracker>async-mutex": { - "globals": { - "clearTimeout": true, - "setTimeout": true - }, - "packages": { - "@swc/helpers>tslib": true - } - }, "@metamask/user-operation-controller": { "globals": { "fetch": true }, "packages": { + "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/gas-fee-controller": true, "@metamask/gas-fee-controller>@metamask/polling-controller": true, "@metamask/rpc-errors": true, "@metamask/transaction-controller": true, "@metamask/user-operation-controller>@metamask/base-controller": true, - "@metamask/user-operation-controller>@metamask/controller-utils": true, "@metamask/utils": true, "bn.js": true, "lodash": true, @@ -3418,39 +3342,6 @@ "immer": true } }, - "@metamask/user-operation-controller>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/user-operation-controller>@metamask/controller-utils>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, - "@metamask/user-operation-controller>@metamask/controller-utils>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/abi-utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/utils": { "globals": { "TextDecoder": true, @@ -3826,9 +3717,41 @@ "@trezor/connect-web>@trezor/connect>@trezor/protobuf": { "packages": { "@swc/helpers>tslib": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs": true, "@trezor/connect-web>@trezor/connect>@trezor/schema-utils": true, - "browserify>buffer": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs": true + "browserify>buffer": true + } + }, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs": { + "globals": { + "process": true, + "setTimeout": true + }, + "packages": { + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/aspromise": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/base64": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/codegen": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/eventemitter": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/fetch": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/float": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/inquire": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/path": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/pool": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/utf8": true + } + }, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/codegen": { + "globals": { + "console.log": true + } + }, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/fetch": { + "globals": { + "XMLHttpRequest": true + }, + "packages": { + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/aspromise": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/inquire": true } }, "@trezor/connect-web>@trezor/connect>@trezor/schema-utils": { @@ -4376,16 +4299,6 @@ "koa>is-generator-function>has-tostringtag": true } }, - "eslint>optionator>fast-levenshtein": { - "globals": { - "Intl": true, - "Levenshtein": "write", - "console.log": true, - "define": true, - "importScripts": true, - "postMessage": true - } - }, "eth-ens-namehash": { "globals": { "name": "write" @@ -4776,153 +4689,6 @@ "setTimeout": true } }, - "firebase": { - "packages": { - "firebase>@firebase/app": true, - "firebase>@firebase/messaging": true - } - }, - "firebase>@firebase/app": { - "globals": { - "FinalizationRegistry": true, - "console.warn": true - }, - "packages": { - "firebase>@firebase/app>@firebase/component": true, - "firebase>@firebase/app>@firebase/logger": true, - "firebase>@firebase/app>idb": true, - "firebase>@firebase/util": true - } - }, - "firebase>@firebase/app>@firebase/component": { - "packages": { - "firebase>@firebase/util": true - } - }, - "firebase>@firebase/app>@firebase/logger": { - "globals": { - "console": true - }, - "packages": { - "@swc/helpers>tslib": true - } - }, - "firebase>@firebase/app>idb": { - "globals": { - "DOMException": true, - "IDBCursor": true, - "IDBDatabase": true, - "IDBIndex": true, - "IDBObjectStore": true, - "IDBRequest": true, - "IDBTransaction": true, - "indexedDB.deleteDatabase": true, - "indexedDB.open": true - } - }, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs": { - "globals": { - "process": true, - "setTimeout": true - }, - "packages": { - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/aspromise": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/base64": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/codegen": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/eventemitter": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/fetch": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/float": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/inquire": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/path": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/pool": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/utf8": true - } - }, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/codegen": { - "globals": { - "console.log": true - } - }, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/fetch": { - "globals": { - "XMLHttpRequest": true - }, - "packages": { - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/aspromise": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/inquire": true - } - }, - "firebase>@firebase/installations": { - "globals": { - "BroadcastChannel": true, - "Headers": true, - "btoa": true, - "console.error": true, - "crypto": true, - "fetch": true, - "msCrypto": true, - "navigator.onLine": true, - "setTimeout": true - }, - "packages": { - "firebase>@firebase/app": true, - "firebase>@firebase/app>@firebase/component": true, - "firebase>@firebase/app>idb": true, - "firebase>@firebase/util": true - } - }, - "firebase>@firebase/messaging": { - "globals": { - "Headers": true, - "Notification.maxActions": true, - "Notification.permission": true, - "Notification.requestPermission": true, - "PushSubscription.prototype.hasOwnProperty": true, - "ServiceWorkerRegistration": true, - "URL": true, - "addEventListener": true, - "atob": true, - "btoa": true, - "clients.matchAll": true, - "clients.openWindow": true, - "console.warn": true, - "document": true, - "fetch": true, - "indexedDB": true, - "location.href": true, - "location.origin": true, - "navigator": true, - "origin.replace": true, - "registration.showNotification": true, - "setTimeout": true - }, - "packages": { - "@swc/helpers>tslib": true, - "firebase>@firebase/app": true, - "firebase>@firebase/app>@firebase/component": true, - "firebase>@firebase/app>idb": true, - "firebase>@firebase/installations": true, - "firebase>@firebase/util": true - } - }, - "firebase>@firebase/util": { - "globals": { - "atob": true, - "browser": true, - "btoa": true, - "chrome": true, - "console": true, - "document": true, - "indexedDB": true, - "navigator": true, - "process": true, - "self": true, - "setTimeout": true - }, - "packages": { - "process": true - } - }, "fuse.js": { "globals": { "console": true, diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json index 96ba8b467983..2021f5491abf 100644 --- a/lavamoat/browserify/mmi/policy.json +++ b/lavamoat/browserify/mmi/policy.json @@ -5,11 +5,6 @@ "regeneratorRuntime": "write" } }, - "@contentful/rich-text-html-renderer": { - "globals": { - "SuppressedError": true - } - }, "@ensdomains/content-hash": { "globals": { "console.warn": true @@ -180,8 +175,8 @@ }, "packages": { "@ethereumjs/tx>ethereum-cryptography>@noble/curves": true, - "@ethereumjs/tx>ethereum-cryptography>@noble/hashes": true, - "@ethereumjs/tx>ethereum-cryptography>@scure/bip32": true + "@ethereumjs/tx>ethereum-cryptography>@scure/bip32": true, + "@noble/hashes": true } }, "@ethereumjs/tx>ethereum-cryptography>@noble/curves": { @@ -189,20 +184,14 @@ "TextEncoder": true }, "packages": { - "@ethereumjs/tx>ethereum-cryptography>@noble/hashes": true - } - }, - "@ethereumjs/tx>ethereum-cryptography>@noble/hashes": { - "globals": { - "TextEncoder": true, - "crypto": true + "@noble/hashes": true } }, "@ethereumjs/tx>ethereum-cryptography>@scure/bip32": { "packages": { "@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/curves": true, - "@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/hashes": true, - "@metamask/utils>@scure/base": true + "@metamask/utils>@scure/base": true, + "@noble/hashes": true } }, "@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/curves": { @@ -210,13 +199,7 @@ "TextEncoder": true }, "packages": { - "@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/hashes": true - } - }, - "@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/hashes": { - "globals": { - "TextEncoder": true, - "crypto": true + "@noble/hashes": true } }, "@ethersproject/abi": { @@ -1014,12 +997,12 @@ "@ethersproject/providers": true, "@metamask/abi-utils": true, "@metamask/assets-controllers>@metamask/base-controller": true, - "@metamask/assets-controllers>@metamask/controller-utils": true, "@metamask/assets-controllers>@metamask/polling-controller": true, "@metamask/assets-controllers>@metamask/utils": true, "@metamask/assets-controllers>cockatiel": true, "@metamask/assets-controllers>multiformats": true, "@metamask/contract-metadata": true, + "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/metamask-eth-abis": true, "@metamask/name-controller>async-mutex": true, @@ -1038,24 +1021,6 @@ "immer": true } }, - "@metamask/assets-controllers>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/assets-controllers>@metamask/utils": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, "@metamask/assets-controllers>@metamask/polling-controller": { "globals": { "clearTimeout": true, @@ -1137,15 +1102,30 @@ }, "packages": { "@ethereumjs/tx>@ethereumjs/util": true, + "@metamask/controller-utils>@metamask/utils": true, "@metamask/controller-utils>@spruceid/siwe-parser": true, "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/utils": true, "bn.js": true, "browserify>buffer": true, "eslint>fast-deep-equal": true, "eth-ens-namehash": true } }, + "@metamask/controller-utils>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/abi-utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true + } + }, "@metamask/controller-utils>@spruceid/siwe-parser": { "globals": { "console.error": true, @@ -1179,9 +1159,9 @@ "@metamask/ens-controller": { "packages": { "@ethersproject/providers": true, + "@metamask/controller-utils": true, "@metamask/ens-controller>@metamask/base-controller": true, - "@metamask/ens-controller>@metamask/controller-utils": true, - "@metamask/utils": true, + "@metamask/ens-controller>@metamask/utils": true, "punycode": true } }, @@ -1193,25 +1173,7 @@ "immer": true } }, - "@metamask/ens-controller>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ens-controller>@metamask/controller-utils>@metamask/utils": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, - "@metamask/ens-controller>@metamask/controller-utils>@metamask/utils": { + "@metamask/ens-controller>@metamask/utils": { "globals": { "TextDecoder": true, "TextEncoder": true @@ -1642,46 +1604,13 @@ "setInterval": true }, "packages": { + "@metamask/controller-utils": true, "@metamask/eth-query": true, - "@metamask/gas-fee-controller>@metamask/controller-utils": true, "@metamask/gas-fee-controller>@metamask/polling-controller": true, "bn.js": true, "uuid": true } }, - "@metamask/gas-fee-controller>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/gas-fee-controller>@metamask/controller-utils>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, - "@metamask/gas-fee-controller>@metamask/controller-utils>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/abi-utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/gas-fee-controller>@metamask/polling-controller": { "globals": { "clearTimeout": true, @@ -1953,8 +1882,8 @@ "fetch": true }, "packages": { + "@metamask/controller-utils": true, "@metamask/name-controller>@metamask/base-controller": true, - "@metamask/name-controller>@metamask/controller-utils": true, "@metamask/name-controller>async-mutex": true, "@metamask/utils": true } @@ -1967,39 +1896,6 @@ "immer": true } }, - "@metamask/name-controller>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/name-controller>@metamask/controller-utils>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, - "@metamask/name-controller>@metamask/controller-utils>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/abi-utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/name-controller>async-mutex": { "globals": { "clearTimeout": true, @@ -2017,17 +1913,17 @@ "setTimeout": true }, "packages": { + "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/eth-token-tracker>@metamask/eth-block-tracker": true, "@metamask/network-controller>@metamask/base-controller": true, - "@metamask/network-controller>@metamask/controller-utils": true, "@metamask/network-controller>@metamask/eth-json-rpc-infura": true, "@metamask/network-controller>@metamask/eth-json-rpc-middleware": true, "@metamask/network-controller>@metamask/eth-json-rpc-provider": true, "@metamask/network-controller>@metamask/swappable-obj-proxy": true, + "@metamask/network-controller>@metamask/utils": true, "@metamask/rpc-errors": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/utils": true, "browserify>assert": true, "uuid": true } @@ -2040,39 +1936,6 @@ "immer": true } }, - "@metamask/network-controller>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/network-controller>@metamask/controller-utils>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, - "@metamask/network-controller>@metamask/controller-utils>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/abi-utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/network-controller>@metamask/eth-json-rpc-infura": { "globals": { "setTimeout": true @@ -2109,16 +1972,31 @@ "@metamask/eth-json-rpc-middleware>safe-stable-stringify": true, "@metamask/eth-sig-util": true, "@metamask/network-controller>@metamask/eth-json-rpc-middleware>@metamask/json-rpc-engine": true, + "@metamask/network-controller>@metamask/eth-json-rpc-middleware>@metamask/utils": true, "@metamask/rpc-errors": true, - "@metamask/utils": true, "pify": true } }, "@metamask/network-controller>@metamask/eth-json-rpc-middleware>@metamask/json-rpc-engine": { "packages": { + "@metamask/network-controller>@metamask/eth-json-rpc-middleware>@metamask/utils": true, "@metamask/rpc-errors": true, - "@metamask/safe-event-emitter": true, - "@metamask/utils": true + "@metamask/safe-event-emitter": true + } + }, + "@metamask/network-controller>@metamask/eth-json-rpc-middleware>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true, + "superstruct": true } }, "@metamask/network-controller>@metamask/eth-json-rpc-provider": { @@ -2129,6 +2007,21 @@ "uuid": true } }, + "@metamask/network-controller>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/abi-utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true + } + }, "@metamask/notification-controller": { "packages": { "@metamask/base-controller": true, @@ -2141,80 +2034,194 @@ "crypto.getRandomValues": true } }, - "@metamask/object-multiplex": { + "@metamask/notification-services-controller": { "globals": { - "console.warn": true + "Intl.NumberFormat": true, + "addEventListener": true, + "fetch": true, + "registration": true, + "removeEventListener": true }, "packages": { - "@metamask/object-multiplex>once": true, - "readable-stream": true + "@metamask/controller-utils": true, + "@metamask/notification-services-controller>@contentful/rich-text-html-renderer": true, + "@metamask/notification-services-controller>@metamask/base-controller": true, + "@metamask/notification-services-controller>firebase": true, + "@metamask/profile-sync-controller": true, + "bignumber.js": true, + "loglevel": true, + "uuid": true } }, - "@metamask/object-multiplex>once": { + "@metamask/notification-services-controller>@contentful/rich-text-html-renderer": { + "globals": { + "SuppressedError": true + } + }, + "@metamask/notification-services-controller>@metamask/base-controller": { + "globals": { + "setTimeout": true + }, "packages": { - "@metamask/object-multiplex>once>wrappy": true + "immer": true } }, - "@metamask/obs-store": { + "@metamask/notification-services-controller>firebase": { "packages": { - "@metamask/safe-event-emitter": true, - "readable-stream": true + "@metamask/notification-services-controller>firebase>@firebase/app": true, + "@metamask/notification-services-controller>firebase>@firebase/messaging": true } }, - "@metamask/permission-controller": { + "@metamask/notification-services-controller>firebase>@firebase/app": { "globals": { - "console.error": true + "FinalizationRegistry": true, + "console.warn": true }, "packages": { - "@metamask/permission-controller>@metamask/base-controller": true, - "@metamask/permission-controller>@metamask/controller-utils": true, - "@metamask/permission-controller>nanoid": true, - "@metamask/rpc-errors": true, - "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true + "@metamask/notification-services-controller>firebase>@firebase/app>@firebase/component": true, + "@metamask/notification-services-controller>firebase>@firebase/app>@firebase/logger": true, + "@metamask/notification-services-controller>firebase>@firebase/app>idb": true, + "@metamask/notification-services-controller>firebase>@firebase/util": true } }, - "@metamask/permission-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, + "@metamask/notification-services-controller>firebase>@firebase/app>@firebase/component": { "packages": { - "immer": true + "@metamask/notification-services-controller>firebase>@firebase/util": true } }, - "@metamask/permission-controller>@metamask/controller-utils": { + "@metamask/notification-services-controller>firebase>@firebase/app>@firebase/logger": { "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true + "console": true }, "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/permission-controller>@metamask/controller-utils>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true + "@swc/helpers>tslib": true } }, - "@metamask/permission-controller>@metamask/controller-utils>@metamask/utils": { + "@metamask/notification-services-controller>firebase>@firebase/app>idb": { "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/abi-utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true + "DOMException": true, + "IDBCursor": true, + "IDBDatabase": true, + "IDBIndex": true, + "IDBObjectStore": true, + "IDBRequest": true, + "IDBTransaction": true, + "indexedDB.deleteDatabase": true, + "indexedDB.open": true + } + }, + "@metamask/notification-services-controller>firebase>@firebase/installations": { + "globals": { + "BroadcastChannel": true, + "Headers": true, + "btoa": true, + "console.error": true, + "crypto": true, + "fetch": true, + "msCrypto": true, + "navigator.onLine": true, + "setTimeout": true + }, + "packages": { + "@metamask/notification-services-controller>firebase>@firebase/app": true, + "@metamask/notification-services-controller>firebase>@firebase/app>@firebase/component": true, + "@metamask/notification-services-controller>firebase>@firebase/app>idb": true, + "@metamask/notification-services-controller>firebase>@firebase/util": true + } + }, + "@metamask/notification-services-controller>firebase>@firebase/messaging": { + "globals": { + "Headers": true, + "Notification.maxActions": true, + "Notification.permission": true, + "Notification.requestPermission": true, + "PushSubscription.prototype.hasOwnProperty": true, + "ServiceWorkerRegistration": true, + "URL": true, + "addEventListener": true, + "atob": true, + "btoa": true, + "clients.matchAll": true, + "clients.openWindow": true, + "console.warn": true, + "document": true, + "fetch": true, + "indexedDB": true, + "location.href": true, + "location.origin": true, + "navigator": true, + "origin.replace": true, + "registration.showNotification": true, + "setTimeout": true + }, + "packages": { + "@metamask/notification-services-controller>firebase>@firebase/app": true, + "@metamask/notification-services-controller>firebase>@firebase/app>@firebase/component": true, + "@metamask/notification-services-controller>firebase>@firebase/app>idb": true, + "@metamask/notification-services-controller>firebase>@firebase/installations": true, + "@metamask/notification-services-controller>firebase>@firebase/util": true, + "@swc/helpers>tslib": true + } + }, + "@metamask/notification-services-controller>firebase>@firebase/util": { + "globals": { + "atob": true, + "browser": true, + "btoa": true, + "chrome": true, + "console": true, + "document": true, + "indexedDB": true, + "navigator": true, + "process": true, + "self": true, + "setTimeout": true + }, + "packages": { + "process": true + } + }, + "@metamask/object-multiplex": { + "globals": { + "console.warn": true + }, + "packages": { + "@metamask/object-multiplex>once": true, + "readable-stream": true + } + }, + "@metamask/object-multiplex>once": { + "packages": { + "@metamask/object-multiplex>once>wrappy": true + } + }, + "@metamask/obs-store": { + "packages": { + "@metamask/safe-event-emitter": true, + "readable-stream": true + } + }, + "@metamask/permission-controller": { + "globals": { + "console.error": true + }, + "packages": { + "@metamask/controller-utils": true, + "@metamask/permission-controller>@metamask/base-controller": true, + "@metamask/permission-controller>nanoid": true, + "@metamask/rpc-errors": true, + "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, + "@metamask/utils": true, + "deep-freeze-strict": true, + "immer": true + } + }, + "@metamask/permission-controller>@metamask/base-controller": { + "globals": { + "setTimeout": true + }, + "packages": { + "immer": true } }, "@metamask/permission-controller>nanoid": { @@ -2230,18 +2237,25 @@ }, "@metamask/phishing-controller": { "globals": { + "TextEncoder": true, + "URL": true, "fetch": true }, "packages": { - "@metamask/base-controller": true, + "@ethereumjs/tx>ethereum-cryptography": true, "@metamask/controller-utils": true, - "@metamask/phishing-warning>eth-phishing-detect": true, + "@metamask/phishing-controller>@metamask/base-controller": true, + "@metamask/phishing-controller>fastest-levenshtein": true, + "@noble/hashes": true, "punycode": true } }, - "@metamask/phishing-warning>eth-phishing-detect": { + "@metamask/phishing-controller>@metamask/base-controller": { + "globals": { + "setTimeout": true + }, "packages": { - "eslint>optionator>fast-levenshtein": true + "immer": true } }, "@metamask/post-message-stream": { @@ -2508,27 +2522,57 @@ "console.info": true }, "packages": { - "@metamask/base-controller": true, "@metamask/controller-utils": true, "@metamask/logging-controller": true, - "@metamask/rpc-errors": true, + "@metamask/signature-controller>@metamask/base-controller": true, "@metamask/signature-controller>@metamask/message-manager": true, "lodash": true, "webpack>events": true } }, + "@metamask/signature-controller>@metamask/base-controller": { + "globals": { + "setTimeout": true + }, + "packages": { + "immer": true + } + }, "@metamask/signature-controller>@metamask/message-manager": { "packages": { - "@metamask/base-controller": true, "@metamask/controller-utils": true, "@metamask/eth-sig-util": true, "@metamask/message-manager>jsonschema": true, - "@metamask/utils": true, + "@metamask/signature-controller>@metamask/message-manager>@metamask/base-controller": true, + "@metamask/signature-controller>@metamask/utils": true, "browserify>buffer": true, "uuid": true, "webpack>events": true } }, + "@metamask/signature-controller>@metamask/message-manager>@metamask/base-controller": { + "globals": { + "setTimeout": true + }, + "packages": { + "immer": true + } + }, + "@metamask/signature-controller>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/abi-utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true + } + }, "@metamask/smart-transactions-controller": { "globals": { "URLSearchParams": true, @@ -2540,11 +2584,11 @@ }, "packages": { "@ethersproject/bytes": true, + "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/gas-fee-controller>@metamask/polling-controller": true, "@metamask/smart-transactions-controller>@ethereumjs/tx": true, "@metamask/smart-transactions-controller>@ethereumjs/util": true, - "@metamask/smart-transactions-controller>@metamask/controller-utils": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller": true, "@metamask/smart-transactions-controller>bignumber.js": true, "browserify>buffer": true, @@ -2601,52 +2645,6 @@ "immer": true } }, - "@metamask/smart-transactions-controller>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/smart-transactions-controller>@metamask/controller-utils>@ethereumjs/util": true, - "@metamask/smart-transactions-controller>@metamask/controller-utils>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, - "@metamask/smart-transactions-controller>@metamask/controller-utils>@ethereumjs/util": { - "globals": { - "console.warn": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/rlp": true, - "@ethereumjs/tx>@ethereumjs/util>micro-ftch": true, - "@ethereumjs/tx>ethereum-cryptography": true, - "browserify>buffer": true, - "browserify>insert-module-globals>is-buffer": true, - "webpack>events": true - } - }, - "@metamask/smart-transactions-controller>@metamask/controller-utils>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/abi-utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/smart-transactions-controller>@metamask/controllers>nanoid": { "globals": { "crypto.getRandomValues": true @@ -2664,6 +2662,7 @@ "@ethersproject/abi": true, "@ethersproject/contracts": true, "@ethersproject/providers": true, + "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/gas-fee-controller": true, "@metamask/metamask-eth-abis": true, @@ -2671,11 +2670,10 @@ "@metamask/network-controller": true, "@metamask/rpc-errors": true, "@metamask/smart-transactions-controller>@metamask/base-controller": true, - "@metamask/smart-transactions-controller>@metamask/controller-utils": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@ethereumjs/tx": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@ethereumjs/util": true, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/nonce-tracker": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry": true, - "@metamask/transaction-controller>@metamask/nonce-tracker": true, "@metamask/utils": true, "bn.js": true, "browserify>buffer": true, @@ -2708,6 +2706,22 @@ "webpack>events": true } }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/nonce-tracker": { + "packages": { + "@ethersproject/providers": true, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/nonce-tracker>async-mutex": true, + "browserify>assert": true + } + }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/nonce-tracker>async-mutex": { + "globals": { + "clearTimeout": true, + "setTimeout": true + }, + "packages": { + "@swc/helpers>tslib": true + } + }, "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry": { "packages": { "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-contract": true, @@ -2865,34 +2879,16 @@ "console.error": true }, "packages": { + "@metamask/controller-utils": true, "@metamask/rpc-errors": true, "@metamask/snaps-controllers>@metamask/base-controller": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/snaps-controllers>@metamask/phishing-controller>@metamask/controller-utils": true, "@metamask/snaps-controllers>@metamask/utils": true, "@metamask/snaps-controllers>nanoid": true, "deep-freeze-strict": true, "immer": true } }, - "@metamask/snaps-controllers>@metamask/phishing-controller>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/snaps-controllers>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, "@metamask/snaps-controllers>@metamask/utils": { "globals": { "TextDecoder": true, @@ -3013,34 +3009,16 @@ "console.error": true }, "packages": { + "@metamask/controller-utils": true, "@metamask/rpc-errors": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, "@metamask/snaps-execution-environments>@metamask/snaps-utils>@metamask/base-controller": true, - "@metamask/snaps-execution-environments>@metamask/snaps-utils>@metamask/permission-controller>@metamask/controller-utils": true, "@metamask/snaps-execution-environments>@metamask/utils": true, "@metamask/snaps-execution-environments>nanoid": true, "deep-freeze-strict": true, "immer": true } }, - "@metamask/snaps-execution-environments>@metamask/snaps-utils>@metamask/permission-controller>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/snaps-execution-environments>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, "@metamask/snaps-execution-environments>@metamask/utils": { "globals": { "TextDecoder": true, @@ -3078,10 +3056,10 @@ "console.error": true }, "packages": { + "@metamask/controller-utils": true, "@metamask/rpc-errors": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/controller-utils": true, "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": true, "@metamask/snaps-rpc-methods>@metamask/utils": true, "deep-freeze-strict": true, @@ -3096,24 +3074,6 @@ "immer": true } }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/snaps-rpc-methods>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": { "globals": { "crypto.getRandomValues": true @@ -3231,34 +3191,16 @@ "console.error": true }, "packages": { + "@metamask/controller-utils": true, "@metamask/rpc-errors": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, "@metamask/snaps-utils>@metamask/base-controller": true, - "@metamask/snaps-utils>@metamask/permission-controller>@metamask/controller-utils": true, "@metamask/snaps-utils>@metamask/permission-controller>nanoid": true, "@metamask/snaps-utils>@metamask/utils": true, "deep-freeze-strict": true, "immer": true } }, - "@metamask/snaps-utils>@metamask/permission-controller>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/snaps-utils>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, "@metamask/snaps-utils>@metamask/permission-controller>nanoid": { "globals": { "crypto.getRandomValues": true @@ -3406,6 +3348,7 @@ "@ethersproject/abi": true, "@ethersproject/contracts": true, "@ethersproject/providers": true, + "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/gas-fee-controller": true, "@metamask/metamask-eth-abis": true, @@ -3413,9 +3356,8 @@ "@metamask/network-controller": true, "@metamask/rpc-errors": true, "@metamask/transaction-controller>@metamask/base-controller": true, - "@metamask/transaction-controller>@metamask/controller-utils": true, "@metamask/transaction-controller>@metamask/nonce-tracker": true, - "@metamask/utils": true, + "@metamask/transaction-controller>@metamask/utils": true, "bn.js": true, "browserify>buffer": true, "eth-method-registry": true, @@ -3433,25 +3375,23 @@ "immer": true } }, - "@metamask/transaction-controller>@metamask/controller-utils": { + "@metamask/transaction-controller>@metamask/nonce-tracker": { + "packages": { + "@ethersproject/providers": true, + "@metamask/transaction-controller>@metamask/nonce-tracker>async-mutex": true, + "browserify>assert": true + } + }, + "@metamask/transaction-controller>@metamask/nonce-tracker>async-mutex": { "globals": { - "URL": true, - "console.error": true, - "fetch": true, + "clearTimeout": true, "setTimeout": true }, "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/transaction-controller>@metamask/controller-utils>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true + "@swc/helpers>tslib": true } }, - "@metamask/transaction-controller>@metamask/controller-utils>@metamask/utils": { + "@metamask/transaction-controller>@metamask/utils": { "globals": { "TextDecoder": true, "TextEncoder": true @@ -3466,34 +3406,18 @@ "semver": true } }, - "@metamask/transaction-controller>@metamask/nonce-tracker": { - "packages": { - "@ethersproject/providers": true, - "@metamask/transaction-controller>@metamask/nonce-tracker>async-mutex": true, - "browserify>assert": true - } - }, - "@metamask/transaction-controller>@metamask/nonce-tracker>async-mutex": { - "globals": { - "clearTimeout": true, - "setTimeout": true - }, - "packages": { - "@swc/helpers>tslib": true - } - }, "@metamask/user-operation-controller": { "globals": { "fetch": true }, "packages": { + "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/gas-fee-controller": true, "@metamask/gas-fee-controller>@metamask/polling-controller": true, "@metamask/rpc-errors": true, "@metamask/transaction-controller": true, "@metamask/user-operation-controller>@metamask/base-controller": true, - "@metamask/user-operation-controller>@metamask/controller-utils": true, "@metamask/utils": true, "bn.js": true, "lodash": true, @@ -3510,39 +3434,6 @@ "immer": true } }, - "@metamask/user-operation-controller>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/user-operation-controller>@metamask/controller-utils>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, - "@metamask/user-operation-controller>@metamask/controller-utils>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/abi-utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/utils": { "globals": { "TextDecoder": true, @@ -3918,9 +3809,41 @@ "@trezor/connect-web>@trezor/connect>@trezor/protobuf": { "packages": { "@swc/helpers>tslib": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs": true, "@trezor/connect-web>@trezor/connect>@trezor/schema-utils": true, - "browserify>buffer": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs": true + "browserify>buffer": true + } + }, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs": { + "globals": { + "process": true, + "setTimeout": true + }, + "packages": { + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/aspromise": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/base64": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/codegen": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/eventemitter": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/fetch": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/float": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/inquire": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/path": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/pool": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/utf8": true + } + }, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/codegen": { + "globals": { + "console.log": true + } + }, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/fetch": { + "globals": { + "XMLHttpRequest": true + }, + "packages": { + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/aspromise": true, + "@trezor/connect-web>@trezor/connect>@trezor/protobuf>protobufjs>@protobufjs/inquire": true } }, "@trezor/connect-web>@trezor/connect>@trezor/schema-utils": { @@ -4468,16 +4391,6 @@ "koa>is-generator-function>has-tostringtag": true } }, - "eslint>optionator>fast-levenshtein": { - "globals": { - "Intl": true, - "Levenshtein": "write", - "console.log": true, - "define": true, - "importScripts": true, - "postMessage": true - } - }, "eth-ens-namehash": { "globals": { "name": "write" @@ -4868,153 +4781,6 @@ "setTimeout": true } }, - "firebase": { - "packages": { - "firebase>@firebase/app": true, - "firebase>@firebase/messaging": true - } - }, - "firebase>@firebase/app": { - "globals": { - "FinalizationRegistry": true, - "console.warn": true - }, - "packages": { - "firebase>@firebase/app>@firebase/component": true, - "firebase>@firebase/app>@firebase/logger": true, - "firebase>@firebase/app>idb": true, - "firebase>@firebase/util": true - } - }, - "firebase>@firebase/app>@firebase/component": { - "packages": { - "firebase>@firebase/util": true - } - }, - "firebase>@firebase/app>@firebase/logger": { - "globals": { - "console": true - }, - "packages": { - "@swc/helpers>tslib": true - } - }, - "firebase>@firebase/app>idb": { - "globals": { - "DOMException": true, - "IDBCursor": true, - "IDBDatabase": true, - "IDBIndex": true, - "IDBObjectStore": true, - "IDBRequest": true, - "IDBTransaction": true, - "indexedDB.deleteDatabase": true, - "indexedDB.open": true - } - }, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs": { - "globals": { - "process": true, - "setTimeout": true - }, - "packages": { - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/aspromise": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/base64": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/codegen": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/eventemitter": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/fetch": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/float": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/inquire": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/path": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/pool": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/utf8": true - } - }, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/codegen": { - "globals": { - "console.log": true - } - }, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/fetch": { - "globals": { - "XMLHttpRequest": true - }, - "packages": { - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/aspromise": true, - "firebase>@firebase/firestore>@grpc/proto-loader>protobufjs>@protobufjs/inquire": true - } - }, - "firebase>@firebase/installations": { - "globals": { - "BroadcastChannel": true, - "Headers": true, - "btoa": true, - "console.error": true, - "crypto": true, - "fetch": true, - "msCrypto": true, - "navigator.onLine": true, - "setTimeout": true - }, - "packages": { - "firebase>@firebase/app": true, - "firebase>@firebase/app>@firebase/component": true, - "firebase>@firebase/app>idb": true, - "firebase>@firebase/util": true - } - }, - "firebase>@firebase/messaging": { - "globals": { - "Headers": true, - "Notification.maxActions": true, - "Notification.permission": true, - "Notification.requestPermission": true, - "PushSubscription.prototype.hasOwnProperty": true, - "ServiceWorkerRegistration": true, - "URL": true, - "addEventListener": true, - "atob": true, - "btoa": true, - "clients.matchAll": true, - "clients.openWindow": true, - "console.warn": true, - "document": true, - "fetch": true, - "indexedDB": true, - "location.href": true, - "location.origin": true, - "navigator": true, - "origin.replace": true, - "registration.showNotification": true, - "setTimeout": true - }, - "packages": { - "@swc/helpers>tslib": true, - "firebase>@firebase/app": true, - "firebase>@firebase/app>@firebase/component": true, - "firebase>@firebase/app>idb": true, - "firebase>@firebase/installations": true, - "firebase>@firebase/util": true - } - }, - "firebase>@firebase/util": { - "globals": { - "atob": true, - "browser": true, - "btoa": true, - "chrome": true, - "console": true, - "document": true, - "indexedDB": true, - "navigator": true, - "process": true, - "self": true, - "setTimeout": true - }, - "packages": { - "process": true - } - }, "fuse.js": { "globals": { "console": true, diff --git a/package.json b/package.json index 51894461c76e..d8fece95d0c0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "metamask-crx", - "version": "12.3.1", + "version": "12.4.0", "private": true, "repository": { "type": "git", @@ -61,6 +61,7 @@ "test:e2e:mmi:visual": "./test/e2e/playwright/mmi/scripts/run-visual-test.sh check", "test:e2e:mmi:visual:update": "./test/e2e/playwright/mmi/scripts/run-visual-test.sh update", "test:e2e:swap": "yarn playwright test --project=swap", + "test:e2e:global": "yarn playwright test --project=global", "test:e2e:pw:report": "yarn playwright show-report public/playwright/playwright-reports/html", "test:e2e:chrome:rpc": "SELENIUM_BROWSER=chrome node test/e2e/run-all.js --rpc", "test:e2e:chrome:multi-provider": "MULTIPROVIDER=true SELENIUM_BROWSER=chrome node test/e2e/run-all.js --multi-provider", @@ -117,7 +118,6 @@ "generate-beta-commit": "node ./development/generate-beta-commit.js", "validate-branch-name": "validate-branch-name", "add-release-label-to-pr-and-linked-issues": "ts-node ./.github/scripts/add-release-label-to-pr-and-linked-issues.ts", - "add-team-label": "ts-node ./.github/scripts/add-team-label.ts", "check-pr-has-required-labels": "ts-node ./.github/scripts/check-pr-has-required-labels.ts", "close-release-bug-report-issue": "ts-node ./.github/scripts/close-release-bug-report-issue.ts", "check-template-and-add-labels": "ts-node ./.github/scripts/check-template-and-add-labels.ts", @@ -227,7 +227,7 @@ "semver@7.3.8": "^7.5.4", "@trezor/schema-utils@npm:1.0.2": "patch:@trezor/schema-utils@npm%3A1.0.2#~/.yarn/patches/@trezor-schema-utils-npm-1.0.2-7dd48689b2.patch", "lavamoat-core@npm:^15.1.1": "patch:lavamoat-core@npm%3A15.1.1#~/.yarn/patches/lavamoat-core-npm-15.1.1-51fbe39988.patch", - "@metamask/snaps-sdk": "^6.2.1", + "@metamask/snaps-sdk": "6.3.0", "@swc/types@0.1.5": "^0.1.6", "@babel/runtime@npm:^7.7.6": "patch:@babel/runtime@npm%3A7.24.0#~/.yarn/patches/@babel-runtime-npm-7.24.0-7eb1dd11a2.patch", "@babel/runtime@npm:^7.9.2": "patch:@babel/runtime@npm%3A7.24.0#~/.yarn/patches/@babel-runtime-npm-7.24.0-7eb1dd11a2.patch", @@ -256,22 +256,20 @@ "sucrase@npm:3.34.0": "^3.35.0", "@expo/config/glob": "^10.3.10", "@expo/config-plugins/glob": "^10.3.10", - "@metamask/network-controller": "patch:@metamask/network-controller@npm%3A19.0.0#~/.yarn/patches/@metamask-network-controller-npm-19.0.0-a5e0d1fe14.patch", + "@metamask/network-controller": "patch:@metamask/network-controller@npm%3A20.2.0#~/.yarn/patches/@metamask-network-controller-npm-20.2.0-98b1a5ae59.patch", "@solana/web3.js/rpc-websockets": "^8.0.1", - "@metamask/network-controller@npm:^19.0.0": "patch:@metamask/network-controller@npm%3A19.0.0#~/.yarn/patches/@metamask-network-controller-npm-19.0.0-a5e0d1fe14.patch", + "@metamask/network-controller@npm:^19.0.0": "patch:@metamask/network-controller@npm%3A20.2.0#~/.yarn/patches/@metamask-network-controller-npm-20.2.0-98b1a5ae59.patch", "@metamask/gas-fee-controller@npm:^15.1.1": "patch:@metamask/gas-fee-controller@npm%3A15.1.2#~/.yarn/patches/@metamask-gas-fee-controller-npm-15.1.2-db4d2976aa.patch", - "@metamask/snaps-controllers@npm:^8.1.1": "patch:@metamask/snaps-controllers@npm%3A9.4.0#~/.yarn/patches/@metamask-snaps-controllers-npm-9.4.0-7c3abbbea6.patch", - "@metamask/snaps-controllers@npm:^9.4.0": "patch:@metamask/snaps-controllers@npm%3A9.4.0#~/.yarn/patches/@metamask-snaps-controllers-npm-9.4.0-7c3abbbea6.patch", + "@metamask/snaps-controllers@npm:^8.1.1": "patch:@metamask/snaps-controllers@npm%3A9.5.0#~/.yarn/patches/@metamask-snaps-controllers-npm-9.5.0-8b21e3c072.patch", + "@metamask/snaps-controllers@npm:^9.5.0": "patch:@metamask/snaps-controllers@npm%3A9.5.0#~/.yarn/patches/@metamask-snaps-controllers-npm-9.5.0-8b21e3c072.patch", "@metamask/nonce-tracker@npm:^5.0.0": "patch:@metamask/nonce-tracker@npm%3A5.0.0#~/.yarn/patches/@metamask-nonce-tracker-npm-5.0.0-d81478218e.patch", "@metamask/keyring-controller@npm:^17.1.0": "patch:@metamask/keyring-controller@npm%3A17.1.1#~/.yarn/patches/@metamask-keyring-controller-npm-17.1.1-098cb41930.patch", "@trezor/connect-web@npm:^9.1.11": "patch:@trezor/connect-web@npm%3A9.3.0#~/.yarn/patches/@trezor-connect-web-npm-9.3.0-040ab10d9a.patch", - "@metamask/transaction-controller@npm:^34.0.0": "patch:@metamask/transaction-controller@npm%3A34.0.0#~/.yarn/patches/@metamask-transaction-controller-npm-34.0.0-8bdfa87aaf.patch", "path-to-regexp": "1.9.0" }, "dependencies": { "@babel/runtime": "patch:@babel/runtime@npm%3A7.24.0#~/.yarn/patches/@babel-runtime-npm-7.24.0-7eb1dd11a2.patch", "@blockaid/ppom_release": "^1.5.3", - "@contentful/rich-text-html-renderer": "^16.3.5", "@ensdomains/content-hash": "^2.5.7", "@ethereumjs/tx": "^4.1.1", "@ethersproject/abi": "^5.6.4", @@ -298,6 +296,7 @@ "@metamask-institutional/transaction-update": "^0.2.5", "@metamask-institutional/types": "^1.1.0", "@metamask/abi-utils": "^2.0.2", + "@metamask/account-watcher": "^4.1.0", "@metamask/accounts-controller": "^18.1.0", "@metamask/address-book-controller": "^4.0.1", "@metamask/announcement-controller": "^6.1.0", @@ -307,9 +306,9 @@ "@metamask/bitcoin-wallet-snap": "^0.5.0", "@metamask/browser-passworder": "^4.3.0", "@metamask/contract-metadata": "^2.5.0", - "@metamask/controller-utils": "^10.0.0", + "@metamask/controller-utils": "^11.1.0", "@metamask/design-tokens": "^4.0.0", - "@metamask/ens-controller": "^12.0.0", + "@metamask/ens-controller": "^13.0.0", "@metamask/eth-json-rpc-filters": "^7.0.0", "@metamask/eth-json-rpc-middleware": "patch:@metamask/eth-json-rpc-middleware@npm%3A14.0.1#~/.yarn/patches/@metamask-eth-json-rpc-middleware-npm-14.0.1-b6c2ccbe8c.patch", "@metamask/eth-ledger-bridge-keyring": "patch:@metamask/eth-ledger-bridge-keyring@npm%3A2.0.1#~/.yarn/patches/@metamask-eth-ledger-bridge-keyring-npm-2.0.1-7a5d815b2d.patch", @@ -332,16 +331,17 @@ "@metamask/message-signing-snap": "^0.3.3", "@metamask/metamask-eth-abis": "^3.1.1", "@metamask/name-controller": "^8.0.0", - "@metamask/network-controller": "patch:@metamask/network-controller@npm%3A19.0.0#~/.yarn/patches/@metamask-network-controller-npm-19.0.0-a5e0d1fe14.patch", + "@metamask/network-controller": "patch:@metamask/network-controller@npm%3A20.2.0#~/.yarn/patches/@metamask-network-controller-npm-20.2.0-98b1a5ae59.patch", "@metamask/notification-controller": "^5.0.1", + "@metamask/notification-services-controller": "^0.2.1", "@metamask/object-multiplex": "^2.0.0", "@metamask/obs-store": "^9.0.0", "@metamask/permission-controller": "^10.0.0", "@metamask/permission-log-controller": "^2.0.1", - "@metamask/phishing-controller": "^9.0.3", + "@metamask/phishing-controller": "^12.0.1", "@metamask/post-message-stream": "^8.0.0", - "@metamask/ppom-validator": "^0.32.0", - "@metamask/profile-sync-controller": "patch:@metamask/profile-sync-controller@npm%3A0.2.0#~/.yarn/patches/@metamask-profile-sync-controller-npm-0.2.0-4d922a9e03.patch", + "@metamask/ppom-validator": "patch:@metamask/ppom-validator@npm%3A0.32.0#~/.yarn/patches/@metamask-ppom-validator-npm-0.32.0-f677deea54.patch", + "@metamask/profile-sync-controller": "^0.2.1", "@metamask/providers": "^14.0.2", "@metamask/queued-request-controller": "^2.0.0", "@metamask/rate-limit-controller": "^5.0.1", @@ -349,14 +349,14 @@ "@metamask/safe-event-emitter": "^3.1.1", "@metamask/scure-bip39": "^2.0.3", "@metamask/selected-network-controller": "^15.0.2", - "@metamask/signature-controller": "^17.0.0", - "@metamask/smart-transactions-controller": "^11.0.0", - "@metamask/snaps-controllers": "^9.4.0", + "@metamask/signature-controller": "^18.1.0", + "@metamask/smart-transactions-controller": "12.0.1", + "@metamask/snaps-controllers": "^9.5.0", "@metamask/snaps-execution-environments": "^6.6.2", "@metamask/snaps-rpc-methods": "^11.0.0", - "@metamask/snaps-sdk": "^6.2.1", + "@metamask/snaps-sdk": "6.3.0", "@metamask/snaps-utils": "^8.0.1", - "@metamask/transaction-controller": "patch:@metamask/transaction-controller@npm%3A34.0.0#~/.yarn/patches/@metamask-transaction-controller-npm-34.0.0-8bdfa87aaf.patch", + "@metamask/transaction-controller": "^37.0.0", "@metamask/user-operation-controller": "^13.0.0", "@metamask/utils": "^8.2.1", "@ngraveio/bc-ur": "^1.1.12", @@ -392,7 +392,6 @@ "ethereumjs-util": "^7.0.10", "extension-port-stream": "^3.0.0", "fast-json-patch": "^3.1.1", - "firebase": "^10.11.0", "fuse.js": "^3.2.0", "he": "^1.2.0", "human-standard-token-abi": "^2.0.0", @@ -554,7 +553,6 @@ "chalk": "^4.1.2", "chokidar": "^3.6.0", "concurrently": "^8.2.2", - "contentful": "^10.8.7", "copy-webpack-plugin": "^12.0.2", "core-js-pure": "^3.38.0", "cross-spawn": "^7.0.3", @@ -601,7 +599,7 @@ "gulp-watch": "^5.0.1", "gulp-zip": "^5.1.0", "history": "^5.0.0", - "html-bundler-webpack-plugin": "^3.6.5", + "html-bundler-webpack-plugin": "^3.17.3", "https-browserify": "^1.0.0", "husky": "^8.0.3", "ini": "^3.0.0", @@ -627,7 +625,7 @@ "postcss-loader": "^8.1.1", "postcss-rtlcss": "^4.0.9", "prettier": "^2.7.1", - "prettier-eslint": "^16.3.0", + "prettier-eslint": "^15.0.1", "prettier-plugin-sort-json": "^1.0.0", "process": "^0.11.10", "pumpify": "^2.0.1", diff --git a/playwright.config.ts b/playwright.config.ts index dd7dfd987ba1..6632e1983e79 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -72,6 +72,15 @@ const config: PlaywrightTestConfig = { headless: true, }, }, + // Global: universal, common, shared, and non feature related tests + { + name: 'global', + testMatch: '/global/specs/**.spec.ts', + use: { + ...devices['Desktop Chrome'], + headless: true, + }, + }, ], /* Folder for test artifacts such as screenshots, videos, traces, etc. */ diff --git a/privacy-snapshot.json b/privacy-snapshot.json index d9fa1781f6bc..3b4ded67481c 100644 --- a/privacy-snapshot.json +++ b/privacy-snapshot.json @@ -57,5 +57,6 @@ "authentication.api.cx.metamask.io", "oidc.api.cx.metamask.io", "price.api.cx.metamask.io", - "token.api.cx.metamask.io" + "token.api.cx.metamask.io", + "client-side-detection.api.cx.metamask.io" ] diff --git a/shared/constants/metametrics.ts b/shared/constants/metametrics.ts index 0ae891fee8e7..b6799a1ad936 100644 --- a/shared/constants/metametrics.ts +++ b/shared/constants/metametrics.ts @@ -570,7 +570,6 @@ export enum MetaMetricsEventName { OnboardingWalletCreationComplete = 'Wallet Created', OnboardingWalletSetupComplete = 'Application Opened', OnboardingWalletAdvancedSettings = 'Settings Updated', - OnboardingWalletAdvancedSettingsTurnOnProfileSyncing = 'Turn On Profile Syncing', OnboardingWalletImportAttempted = 'Wallet Import Attempted', OnboardingWalletVideoPlay = 'SRP Intro Video Played', OnboardingTwitterClick = 'External Link Clicked', @@ -628,6 +627,9 @@ export enum MetaMetricsEventName { WalletSetupCanceled = 'Wallet Setup Canceled', WalletSetupFailed = 'Wallet Setup Failed', WalletCreated = 'Wallet Created', + // BEGIN:ONLY_INCLUDE_IF(build-flask) + WatchEthereumAccountsToggled = 'Watch Ethereum Accounts Toggled', + // END:ONLY_INCLUDE_IF ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) DeeplinkClicked = 'Deeplink Clicked', ConnectCustodialAccountClicked = 'Connect Custodial Account Clicked', @@ -820,6 +822,8 @@ export enum MetaMetricsTransactionEventSource { } export enum MetaMetricsEventLocation { + AlertFrictionModal = 'alert_friction_modal', + Confirmation = 'confirmation', SignatureConfirmation = 'signature_confirmation', TokenDetails = 'token_details', TokenDetection = 'token_detection', diff --git a/shared/constants/multichain/networks.ts b/shared/constants/multichain/networks.ts index 366a161a27a2..5217394a5415 100644 --- a/shared/constants/multichain/networks.ts +++ b/shared/constants/multichain/networks.ts @@ -1,9 +1,13 @@ -import { ProviderConfig } from '@metamask/network-controller'; import { CaipChainId } from '@metamask/utils'; import { isBtcMainnetAddress, isBtcTestnetAddress } from '../../lib/multichain'; -export type ProviderConfigWithImageUrl = Omit & { - rpcPrefs?: { imageUrl?: string }; +export type ProviderConfigWithImageUrl = { + rpcUrl?: string; + type: string; + ticker: string; + nickname?: string; + rpcPrefs?: { blockExplorerUrl?: string; imageUrl?: string }; + id?: string; }; export type MultichainProviderConfig = ProviderConfigWithImageUrl & { diff --git a/shared/constants/network.ts b/shared/constants/network.ts index 620d9d16f02d..de8429d8ba6d 100644 --- a/shared/constants/network.ts +++ b/shared/constants/network.ts @@ -1,7 +1,6 @@ import { capitalize, pick } from 'lodash'; /** - * A type representing any valid value for 'type' for setProviderType and other - * methods that add or manipulate networks in MetaMask state. + * A type representing built-in network types, used as an identifier. */ export type NetworkType = (typeof NETWORK_TYPES)[keyof typeof NETWORK_TYPES]; @@ -144,6 +143,8 @@ export const CHAIN_IDS = { SEI: '0x531', BERACHAIN: '0x138d5', METACHAIN_ONE: '0x1b6e6', + NEAR: '0x18d', + NEAR_TESTNET: '0x18e', } as const; export const CHAINLIST_CHAIN_IDS_MAP = { @@ -174,6 +175,7 @@ export const CHAINLIST_CHAIN_IDS_MAP = { FLARE_MAINNET: '0xe', FUSE_GOLD_MAINNET: '0x7a', HAQQ_NETWORK: '0x2be3', + IOTEX_MAINNET: '0x1251', KCC_MAINNET: '0x141', KLAYTN_MAINNET_CYPRESS: '0x2019', KROMA_MAINNET: '0xff', @@ -361,6 +363,7 @@ const CHAINLIST_CURRENCY_SYMBOLS_MAP = { OASYS_MAINNET: 'OAS', HUOBI_ECO_CHAIN_MAINNET: 'HT', ACALA_NETWORK: 'ACA', + IOTEX_MAINNET: 'IOTX', } as const; export const CHAINLIST_CURRENCY_SYMBOLS_MAP_NETWORK_COLLISION = { @@ -405,6 +408,8 @@ export const EVMOS_IMAGE_URL = './images/evmos.svg'; export const FLARE_MAINNET_IMAGE_URL = './images/flare-mainnet.svg'; export const FUSE_GOLD_MAINNET_IMAGE_URL = './images/fuse-mainnet.jpg'; export const HAQQ_NETWORK_IMAGE_URL = './images/haqq.svg'; +export const IOTEX_MAINNET_IMAGE_URL = './images/iotex.svg'; +export const IOTEX_TOKEN_IMAGE_URL = './images/iotex-token.svg'; export const KCC_MAINNET_IMAGE_URL = './images/kcc-mainnet.svg'; export const KLAYTN_MAINNET_IMAGE_URL = './images/klaytn.svg'; export const KROMA_MAINNET_IMAGE_URL = './images/kroma.svg'; @@ -413,6 +418,8 @@ export const MANTA_PACIFIC_MAINNET_IMAGE_URL = './images/manta.svg'; export const MANTLE_MAINNET_IMAGE_URL = './images/mantle.svg'; export const MOONBEAM_IMAGE_URL = './images/moonbeam.svg'; export const MOONRIVER_IMAGE_URL = './images/moonriver.svg'; +export const MOONBEAM_TOKEN_IMAGE_URL = './images/moonbeam-token.svg'; +export const MOONRIVER_TOKEN_IMAGE_URL = './images/moonriver-token.svg'; export const NEAR_AURORA_MAINNET_IMAGE_URL = './images/near-aurora.svg'; export const NEBULA_MAINNET_IMAGE_URL = './images/nebula.svg'; export const OASYS_MAINNET_IMAGE_URL = './images/oasys.svg'; @@ -435,6 +442,7 @@ export const SCROLL_IMAGE_URL = './images/scroll.svg'; export const NUMBERS_MAINNET_IMAGE_URL = './images/numbers-mainnet.svg'; export const NUMBERS_TOKEN_IMAGE_URL = './images/numbers-token.png'; export const SEI_IMAGE_URL = './images/sei.svg'; +export const NEAR_IMAGE_URL = './images/near.svg'; export const INFURA_PROVIDER_TYPES = [ NETWORK_TYPES.MAINNET, @@ -490,10 +498,12 @@ export const BUILT_IN_NETWORKS = { [NETWORK_TYPES.MAINNET]: { chainId: CHAIN_IDS.MAINNET, blockExplorerUrl: `https://etherscan.io`, + ticker: CURRENCY_SYMBOLS.ETH, }, [NETWORK_TYPES.LINEA_MAINNET]: { chainId: CHAIN_IDS.LINEA_MAINNET, blockExplorerUrl: 'https://lineascan.build', + ticker: CURRENCY_SYMBOLS.ETH, }, [NETWORK_TYPES.LOCALHOST]: { chainId: CHAIN_IDS.LOCALHOST, @@ -657,6 +667,8 @@ export const CHAIN_ID_TO_CURRENCY_SYMBOL_MAP = { CHAINLIST_CURRENCY_SYMBOLS_MAP.HUOBI_ECO_CHAIN_MAINNET, [CHAINLIST_CHAIN_IDS_MAP.ACALA_NETWORK]: CHAINLIST_CURRENCY_SYMBOLS_MAP.ACALA_NETWORK, + [CHAINLIST_CHAIN_IDS_MAP.IOTEX_MAINNET]: + CHAINLIST_CURRENCY_SYMBOLS_MAP.IOTEX_MAINNET, } as const; /** @@ -711,6 +723,8 @@ export const CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP = { [CHAIN_IDS.PALM]: PALM_TOKEN_IMAGE_URL, [CHAIN_IDS.CELO]: CELO_TOKEN_IMAGE_URL, [CHAIN_IDS.GNOSIS]: GNOSIS_TOKEN_IMAGE_URL, + [CHAIN_IDS.NEAR]: NEAR_IMAGE_URL, + [CHAIN_IDS.NEAR_TESTNET]: NEAR_IMAGE_URL, [CHAINLIST_CHAIN_IDS_MAP.ACALA_NETWORK]: ACALA_TOKEN_IMAGE_URL, [CHAINLIST_CHAIN_IDS_MAP.ARBITRUM_NOVA]: ARBITRUM_NOVA_IMAGE_URL, [CHAINLIST_CHAIN_IDS_MAP.ASTAR]: ASTAR_IMAGE_URL, @@ -731,6 +745,7 @@ export const CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP = { [CHAINLIST_CHAIN_IDS_MAP.EVMOS]: EVMOS_IMAGE_URL, [CHAINLIST_CHAIN_IDS_MAP.FLARE_MAINNET]: FLARE_MAINNET_IMAGE_URL, [CHAINLIST_CHAIN_IDS_MAP.FUSE_GOLD_MAINNET]: FUSE_GOLD_MAINNET_IMAGE_URL, + [CHAINLIST_CHAIN_IDS_MAP.IOTEX_MAINNET]: IOTEX_MAINNET_IMAGE_URL, [CHAINLIST_CHAIN_IDS_MAP.HAQQ_NETWORK]: HAQQ_NETWORK_IMAGE_URL, [CHAINLIST_CHAIN_IDS_MAP.KCC_MAINNET]: KCC_MAINNET_IMAGE_URL, [CHAINLIST_CHAIN_IDS_MAP.KLAYTN_MAINNET_CYPRESS]: KLAYTN_MAINNET_IMAGE_URL, @@ -788,6 +803,11 @@ export const CHAIN_ID_TOKEN_IMAGE_MAP = { [CHAIN_IDS.SCROLL_SEPOLIA]: SCROLL_IMAGE_URL, [CHAIN_IDS.NUMBERS]: NUMBERS_TOKEN_IMAGE_URL, [CHAIN_IDS.SEI]: SEI_IMAGE_URL, + [CHAIN_IDS.NEAR]: NEAR_IMAGE_URL, + [CHAIN_IDS.NEAR_TESTNET]: NEAR_IMAGE_URL, + [CHAIN_IDS.MOONRIVER]: MOONRIVER_TOKEN_IMAGE_URL, + [CHAIN_IDS.MOONBEAM]: MOONBEAM_TOKEN_IMAGE_URL, + [CHAINLIST_CHAIN_IDS_MAP.IOTEX_MAINNET]: IOTEX_TOKEN_IMAGE_URL, } as const; export const INFURA_BLOCKED_KEY = 'countryBlocked'; diff --git a/shared/constants/permissions.test.js b/shared/constants/permissions.test.js index b63014359bb7..7a1fb0a57500 100644 --- a/shared/constants/permissions.test.js +++ b/shared/constants/permissions.test.js @@ -10,16 +10,13 @@ import { describe('EndowmentPermissions', () => { it('has the expected permission keys', () => { - // Since some permissions are fenced out, this causes problems with the - // test, so we re-add them here. expect(Object.keys(EndowmentPermissions).sort()).toStrictEqual( - [ - 'endowment:name-lookup', - ...Object.keys(endowmentPermissionBuilders).filter( + Object.keys(endowmentPermissionBuilders) + .filter( (targetName) => !Object.keys(ExcludedSnapEndowments).includes(targetName), - ), - ].sort(), + ) + .sort(), ); }); }); diff --git a/shared/constants/snaps/permissions.ts b/shared/constants/snaps/permissions.ts index aca6f5ef8023..837beea4d9ff 100644 --- a/shared/constants/snaps/permissions.ts +++ b/shared/constants/snaps/permissions.ts @@ -8,9 +8,7 @@ export const EndowmentPermissions = Object.freeze({ 'endowment:lifecycle-hooks': 'endowment:lifecycle-hooks', 'endowment:page-home': 'endowment:page-home', 'endowment:signature-insight': 'endowment:signature-insight', - ///: BEGIN:ONLY_INCLUDE_IF(build-flask) 'endowment:name-lookup': 'endowment:name-lookup', - ///: END:ONLY_INCLUDE_IF ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps) 'endowment:keyring': 'endowment:keyring', ///: END:ONLY_INCLUDE_IF @@ -22,11 +20,6 @@ export const ExcludedSnapPermissions = Object.freeze({ 'eth_accounts is disabled. For more information please see https://github.com/MetaMask/snaps/issues/990.', }); -export const ExcludedSnapEndowments = Object.freeze({ - ///: BEGIN:ONLY_INCLUDE_IF(build-main) - 'endowment:name-lookup': - 'This endowment is experimental and therefore not available.', - ///: END:ONLY_INCLUDE_IF -}); +export const ExcludedSnapEndowments = Object.freeze({}); export const DynamicSnapPermissions = Object.freeze(['eth_accounts']); diff --git a/shared/modules/object.utils.js b/shared/modules/object.utils.js index 111db13c4a92..3da08956cb02 100644 --- a/shared/modules/object.utils.js +++ b/shared/modules/object.utils.js @@ -35,7 +35,8 @@ export function maskObject(object, mask) { } else if (maskKey && typeof maskKey === 'object') { state[key] = maskObject(object[key], maskKey); } else if (maskKey === undefined || maskKey === false) { - state[key] = typeof object[key]; + // As typeof null (misleadingly) returns “object,” it would be more readable to display “null” instead of “object.” + state[key] = object[key] === null ? null : typeof object[key]; } else { throw new Error(`Unsupported mask entry: ${maskKey}`); } diff --git a/shared/modules/selectors/index.test.ts b/shared/modules/selectors/index.test.ts index 9cd5d1049148..f1dc4fee5ec2 100644 --- a/shared/modules/selectors/index.test.ts +++ b/shared/modules/selectors/index.test.ts @@ -1,5 +1,6 @@ import { createSwapsMockStore } from '../../../test/jest'; -import { CHAIN_IDS, CURRENCY_SYMBOLS } from '../../constants/network'; +import { CHAIN_IDS } from '../../constants/network'; +import { mockNetworkState } from '../../../test/stub/networks'; import { getSmartTransactionsOptInStatus, getCurrentChainSupportsSmartTransactions, @@ -35,9 +36,6 @@ describe('Selectors', () => { balance: '0x15f6f0b9d4f8d000', }, }, - providerConfig: { - chainId: CHAIN_IDS.MAINNET, - }, swapsState: { swapsFeatureFlags: { ethereum: { @@ -58,13 +56,11 @@ describe('Selectors', () => { smartTransactionsState: { liveness: true, }, - networkConfigurations: { - 'network-configuration-id-1': { - chainId: CHAIN_IDS.MAINNET, - ticker: CURRENCY_SYMBOLS.ETH, - rpcUrl: 'https://mainnet.infura.io/v3/', - }, - }, + ...mockNetworkState({ + id: 'network-configuration-id-1', + chainId: CHAIN_IDS.MAINNET, + rpcUrl: 'https://mainnet.infura.io/v3/', + }), }, }; }; @@ -90,10 +86,7 @@ describe('Selectors', () => { ...state, metamask: { ...state.metamask, - providerConfig: { - ...state.metamask.providerConfig, - chainId: CHAIN_IDS.POLYGON, - }, + ...mockNetworkState({ chainId: CHAIN_IDS.POLYGON }), }, }; const result = getCurrentChainSupportsSmartTransactions(newState); @@ -150,10 +143,7 @@ describe('Selectors', () => { ...state, metamask: { ...state.metamask, - providerConfig: { - ...state.metamask.providerConfig, - chainId: CHAIN_IDS.POLYGON, - }, + ...mockNetworkState({ chainId: CHAIN_IDS.POLYGON }), }, }; expect(getSmartTransactionsEnabled(newState)).toBe(false); @@ -165,10 +155,7 @@ describe('Selectors', () => { ...state, metamask: { ...state.metamask, - providerConfig: { - ...state.metamask.providerConfig, - chainId: CHAIN_IDS.BSC, - }, + ...mockNetworkState({ chainId: CHAIN_IDS.BSC }), }, }; expect(getSmartTransactionsEnabled(newState)).toBe(false); @@ -180,10 +167,7 @@ describe('Selectors', () => { ...state, metamask: { ...state.metamask, - providerConfig: { - ...state.metamask.providerConfig, - chainId: CHAIN_IDS.LINEA_MAINNET, - }, + ...mockNetworkState({ chainId: CHAIN_IDS.LINEA_MAINNET }), }, }; expect(getSmartTransactionsEnabled(newState)).toBe(false); @@ -277,10 +261,7 @@ describe('Selectors', () => { ...state.metamask.preferences, smartTransactionsOptInStatus: null, }, - providerConfig: { - ...state.metamask.providerConfig, - chainId: CHAIN_IDS.POLYGON, - }, + ...mockNetworkState({ chainId: CHAIN_IDS.POLYGON }), }, }; expect(getIsSmartTransactionsOptInModalAvailable(newState)).toBe(false); @@ -296,13 +277,10 @@ describe('Selectors', () => { ...state.metamask.preferences, smartTransactionsOptInStatus: null, }, - networkConfigurations: { - 'network-configuration-id-1': { - chainId: CHAIN_IDS.MAINNET, - ticker: CURRENCY_SYMBOLS.ETH, - rpcUrl: 'https://mainnet.quiknode.pro/', - }, - }, + ...mockNetworkState({ + chainId: CHAIN_IDS.MAINNET, + rpcUrl: 'https://mainnet.quiknode.pro/', + }), }, }; expect(getIsSmartTransactionsOptInModalAvailable(newState)).toBe(false); diff --git a/shared/modules/selectors/smart-transactions.ts b/shared/modules/selectors/smart-transactions.ts index c510e817dbdb..92fe42171ddc 100644 --- a/shared/modules/selectors/smart-transactions.ts +++ b/shared/modules/selectors/smart-transactions.ts @@ -29,9 +29,6 @@ type SmartTransactionsMetaMaskState = { }; }; }; - providerConfig: { - chainId: Hex; - }; swapsState: { swapsFeatureFlags: { ethereum: { @@ -52,7 +49,8 @@ type SmartTransactionsMetaMaskState = { smartTransactionsState: { liveness: boolean; }; - networkConfigurations: { + selectedNetworkClientId: string; + networkConfigurations?: { [key: string]: { chainId: Hex; rpcUrl: string; diff --git a/test/data/confirmations/contract-interaction.ts b/test/data/confirmations/contract-interaction.ts index 1851d592e58e..a35ebcc74a3b 100644 --- a/test/data/confirmations/contract-interaction.ts +++ b/test/data/confirmations/contract-interaction.ts @@ -1,9 +1,14 @@ import { + SimulationData, + TransactionMeta, TransactionStatus, TransactionType, } from '@metamask/transaction-controller'; import { Hex } from '@metamask/utils'; -import { Confirmation } from '../../../ui/pages/confirmations/types/confirm'; +import { + Confirmation, + SignatureRequestType, +} from '../../../ui/pages/confirmations/types/confirm'; export const PAYMASTER_AND_DATA = '0x9d6ac51b972544251fcc0f2902e633e3f9bd3f2900000000000000000000000000000000000000000000000000000000666bfd410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003498a76eb88b702e5e52b00fbc16a36baf89ebe3e0dd23170949cffc0a623011383cced660ff67930308c22e5aa746a2d586629ddbd87046a146225bf80e9d6f1b'; @@ -16,140 +21,151 @@ export const DEPOSIT_METHOD_DATA = '0xd0e30db0'; export const genUnapprovedContractInteractionConfirmation = ({ address = CONTRACT_INTERACTION_SENDER_ADDRESS, txData = DEPOSIT_METHOD_DATA, + simulationData, }: { address?: Hex; txData?: Hex; -} = {}): Confirmation => ({ - actionId: String(400855682), - chainId: '0xaa36a7', - dappSuggestedGasFees: { - gas: '0xab77', - }, - defaultGasEstimates: { - estimateType: 'medium', - gas: '0xab77', - maxFeePerGas: '0xaa350353', - maxPriorityFeePerGas: '0x59682f00', - }, - gasFeeEstimatesLoaded: true, - history: [ - { - actionId: String(400855682), - chainId: '0xaa36a7', - dappSuggestedGasFees: { - gas: '0xab77', - }, - defaultGasEstimates: { - estimateType: 'medium', - gas: '0xab77', - maxFeePerGas: '0xaa350353', - maxPriorityFeePerGas: '0x59682f00', - }, - id: '1d7c08c0-fe54-11ee-9243-91b1e533746a', - origin: 'https://metamask.github.io', - securityAlertResponse: { - reason: 'loading', - result_type: 'validation_in_progress', - }, - sendFlowHistory: [], - status: TransactionStatus.unapproved, - time: 1713534772044, - txParams: { - data: txData, - from: address, - gas: '0xab77', - maxFeePerGas: '0xaa350353', - maxPriorityFeePerGas: '0x59682f00', - to: '0x88aa6343307ec9a652ccddda3646e62b2f1a5125', - value: '0x3782dace9d900000', - }, - type: TransactionType.contractInteraction, - userEditedGasLimit: false, - userFeeLevel: 'medium', - verifiedOnBlockchain: false, + simulationData?: SimulationData; +} = {}): Confirmation => { + const confirmation: Confirmation = { + actionId: String(400855682), + chainId: '0xaa36a7', + dappSuggestedGasFees: { + gas: '0xab77', }, - [ + defaultGasEstimates: { + estimateType: 'medium', + gas: '0xab77', + maxFeePerGas: '0xaa350353', + maxPriorityFeePerGas: '0x59682f00', + }, + gasFeeEstimatesLoaded: true, + history: [ { - note: 'TransactionController#updateSimulationData - Update simulation data', - op: 'add', - path: '/simulationData', - timestamp: 1713534772417, - value: { - nativeBalanceChange: { - difference: '0x3782dace9d900000', - isDecrease: true, - newBalance: '0xcc0ea4fb7ffa87d', - previousBalance: '0x4443c51e558fa87d', - }, - tokenBalanceChanges: [], + actionId: String(400855682), + chainId: '0xaa36a7', + dappSuggestedGasFees: { + gas: '0xab77', }, + defaultGasEstimates: { + estimateType: 'medium', + gas: '0xab77', + maxFeePerGas: '0xaa350353', + maxPriorityFeePerGas: '0x59682f00', + }, + id: '1d7c08c0-fe54-11ee-9243-91b1e533746a', + origin: 'https://metamask.github.io', + securityAlertResponse: { + reason: 'loading', + result_type: 'validation_in_progress', + }, + sendFlowHistory: [], + status: TransactionStatus.unapproved, + time: 1713534772044, + txParams: { + data: txData, + from: address, + gas: '0xab77', + maxFeePerGas: '0xaa350353', + maxPriorityFeePerGas: '0x59682f00', + to: '0x88aa6343307ec9a652ccddda3646e62b2f1a5125', + value: '0x3782dace9d900000', + }, + type: TransactionType.contractInteraction, + userEditedGasLimit: false, + userFeeLevel: 'medium', + verifiedOnBlockchain: false, }, - { - op: 'add', - path: '/gasFeeEstimatesLoaded', - value: true, - }, + [ + { + note: 'TransactionController#updateSimulationData - Update simulation data', + op: 'add', + path: '/simulationData', + timestamp: 1713534772417, + value: { + nativeBalanceChange: { + difference: '0x3782dace9d900000', + isDecrease: true, + newBalance: '0xcc0ea4fb7ffa87d', + previousBalance: '0x4443c51e558fa87d', + }, + tokenBalanceChanges: [], + }, + }, + { + op: 'add', + path: '/gasFeeEstimatesLoaded', + value: true, + }, + ], + [ + { + note: 'TransactionController:updatesecurityAlertResponse - securityAlertResponse updated', + op: 'replace', + path: '/securityAlertResponse/result_type', + timestamp: 1713534773213, + value: 'Benign', + }, + { + op: 'replace', + path: '/securityAlertResponse/reason', + value: '', + }, + { + op: 'add', + path: '/securityAlertResponse/description', + value: '', + }, + { + op: 'add', + path: '/securityAlertResponse/features', + value: [], + }, + { + op: 'add', + path: '/securityAlertResponse/block', + value: 5732063, + }, + ], ], - [ - { - note: 'TransactionController:updatesecurityAlertResponse - securityAlertResponse updated', - op: 'replace', - path: '/securityAlertResponse/result_type', - timestamp: 1713534773213, - value: 'Benign', - }, - { - op: 'replace', - path: '/securityAlertResponse/reason', - value: '', - }, - { - op: 'add', - path: '/securityAlertResponse/description', - value: '', - }, - { - op: 'add', - path: '/securityAlertResponse/features', - value: [], - }, - { - op: 'add', - path: '/securityAlertResponse/block', - value: 5732063, + id: '1d7c08c0-fe54-11ee-9243-91b1e533746a', + origin: 'https://metamask.github.io', + securityAlertResponse: { + features: [], + reason: '', + result_type: 'Benign', + }, + sendFlowHistory: [], + simulationData: { + nativeBalanceChange: { + difference: '0x3782dace9d900000', + isDecrease: true, + newBalance: '0xcc0ea4fb7ffa87d', + previousBalance: '0x4443c51e558fa87d', }, - ], - ], - id: '1d7c08c0-fe54-11ee-9243-91b1e533746a', - origin: 'https://metamask.github.io', - securityAlertResponse: { - features: [], - reason: '', - result_type: 'Benign', - }, - sendFlowHistory: [], - simulationData: { - nativeBalanceChange: { - difference: '0x3782dace9d900000', - isDecrease: true, - newBalance: '0xcc0ea4fb7ffa87d', - previousBalance: '0x4443c51e558fa87d', + tokenBalanceChanges: [], + }, + status: TransactionStatus.unapproved, + time: 1713534772044, + txParams: { + data: txData, + from: address, + gas: '0xab77', + maxFeePerGas: '0xaa350353', + maxPriorityFeePerGas: '0x59682f00', + to: '0x88aa6343307ec9a652ccddda3646e62b2f1a5125', + value: '0x3782dace9d900000', }, - tokenBalanceChanges: [], - }, - status: TransactionStatus.unapproved, - time: 1713534772044, - txParams: { - data: txData, - from: address, - gas: '0xab77', - maxFeePerGas: '0xaa350353', - maxPriorityFeePerGas: '0x59682f00', - to: '0x88aa6343307ec9a652ccddda3646e62b2f1a5125', - value: '0x3782dace9d900000', - }, - type: TransactionType.contractInteraction, - userEditedGasLimit: false, - userFeeLevel: 'medium', - verifiedOnBlockchain: false, -}); + type: TransactionType.contractInteraction, + userEditedGasLimit: false, + userFeeLevel: 'medium', + verifiedOnBlockchain: false, + } as SignatureRequestType; + + // Overwrite simulation data if provided + if (simulationData) { + (confirmation as TransactionMeta).simulationData = simulationData; + } + + return confirmation; +}; diff --git a/test/data/confirmations/helper.ts b/test/data/confirmations/helper.ts new file mode 100644 index 000000000000..e2d0a79f95ae --- /dev/null +++ b/test/data/confirmations/helper.ts @@ -0,0 +1,95 @@ +import { ApprovalType } from '@metamask/controller-utils'; +import { + TransactionStatus, + TransactionType, +} from '@metamask/transaction-controller'; + +import mockState from '../mock-state.json'; + +type RootState = { metamask: Record } & Record< + string, + unknown +>; + +export const getExampleMockSignatureConfirmState = ( + args: RootState = { metamask: {} }, +) => ({ + ...mockState, + ...args, + metamask: { + ...mockState.metamask, + preferences: { + redesignedTransactionsEnabled: true, + redesignedConfirmationsEnabled: true, + isRedesignedConfirmationsDeveloperEnabled: true, + }, + pendingApprovals: { + '123': { + id: '123', + type: ApprovalType.EthSignTypedData, + }, + }, + unapprovedTypedMessages: { + '123': { + id: '123', + chainId: + mockState.metamask.networkConfigurations.testNetworkConfigurationId + .chainId, + type: TransactionType.signTypedData, + status: TransactionStatus.unapproved, + txParams: { from: Object.keys(mockState.metamask.identities)[0] }, + msgParams: { + signatureMethod: 'eth_signTypedData_v4', + }, + }, + }, + ...args.metamask, + }, +}); + +export const getExampleMockContractInteractionConfirmState = ( + args: RootState = { metamask: {} }, +) => ({ + ...mockState, + ...args, + metamask: { + ...mockState.metamask, + preferences: { + redesignedTransactionsEnabled: true, + redesignedConfirmationsEnabled: true, + isRedesignedConfirmationsDeveloperEnabled: true, + }, + pendingApprovals: { + '123': { + id: '123', + type: ApprovalType.Transaction, + }, + }, + transactions: [ + { + id: '123', + type: TransactionType.contractInteraction, + chainId: + mockState.metamask.networkConfigurations.testNetworkConfigurationId + .chainId, + status: TransactionStatus.unapproved, + txParams: { from: Object.keys(mockState.metamask.identities)[0] }, + }, + ], + ...args.metamask, + }, +}); + +export const getMockConfirmState = (args: RootState = { metamask: {} }) => ({ + ...mockState, + ...args, + metamask: { + ...mockState.metamask, + preferences: { + redesignedTransactionsEnabled: true, + redesignedConfirmationsEnabled: true, + isRedesignedConfirmationsDeveloperEnabled: true, + }, + ...args.metamask, + }, +}); diff --git a/test/data/mock-send-state.json b/test/data/mock-send-state.json index 300d4428520b..09b9cad15757 100644 --- a/test/data/mock-send-state.json +++ b/test/data/mock-send-state.json @@ -144,17 +144,17 @@ "0x539": true }, "showTestnetMessageInDropdown": true, - "networkConfigurations": {}, + "networkConfigurations": { + "goerli": { + "id": "goerli", + "chainId": "0x5" + } + }, "alertEnabledness": { "unconnectedAccount": true }, "featureFlags": {}, "network": "5", - "providerConfig": { - "type": "rpc", - "chainId": "0x5", - "ticker": "ETH" - }, "internalAccounts": { "accounts": { "cf8dace4-9439-4bd4-b3a8-88c821c8fcb3": { diff --git a/test/data/mock-state.json b/test/data/mock-state.json index 2cc738853fa1..93af077c77b1 100644 --- a/test/data/mock-state.json +++ b/test/data/mock-state.json @@ -381,13 +381,6 @@ "unconnectedAccount": true }, "featureFlags": {}, - "providerConfig": { - "type": "rpc", - "nickname": "goerli", - "chainId": "0x5", - "ticker": "ETH", - "id": "chain5" - }, "networkConfigurations": { "testNetworkConfigurationId": { "rpcUrl": "https://testrpc.com", @@ -396,12 +389,12 @@ "type": "rpc", "id": "testNetworkConfigurationId" }, - "chain5": { + "goerli": { "type": "rpc", "chainId": "0x5", "ticker": "ETH", "nickname": "Chain 5", - "id": "chain5" + "id": "goerli" } }, "internalAccounts": { @@ -1873,6 +1866,7 @@ } ], "addSnapAccountEnabled": false, + "watchEthereumAccountEnabled": false, "bitcoinSupportEnabled": false, "bitcoinTestnetSupportEnabled": false, "pendingApprovals": { diff --git a/test/e2e/default-fixture.js b/test/e2e/default-fixture.js index d07ca3ab3b58..27db906cd754 100644 --- a/test/e2e/default-fixture.js +++ b/test/e2e/default-fixture.js @@ -1,4 +1,4 @@ -const { NetworkStatus } = require('@metamask/network-controller'); +const { mockNetworkState } = require('../stub/networks'); const { CHAIN_IDS } = require('../../shared/constants/network'); const { FirstTimeFlowType } = require('../../shared/constants/onboarding'); @@ -16,10 +16,10 @@ function defaultFixture(inputChainId = CHAIN_IDS.LOCALHOST) { UserStorageController: { isProfileSyncingEnabled: true, }, - MetamaskNotificationsController: { + NotificationServicesController: { subscriptionAccountsSeen: [], isFeatureAnnouncementsEnabled: false, - isMetamaskNotificationsEnabled: false, + isNotificationServicesEnabled: false, isMetamaskNotificationsFeatureSeen: false, metamaskNotificationsList: [], metamaskNotificationsReadList: [], @@ -120,6 +120,8 @@ function defaultFixture(inputChainId = CHAIN_IDS.LOCALHOST) { bridgeState: { bridgeFeatureFlags: { extensionSupport: false, + srcNetworkAllowlist: ['0x1', '0xa', '0xe708'], + destNetworkAllowlist: ['0x1', '0xa', '0xe708'], }, }, }, @@ -151,32 +153,15 @@ function defaultFixture(inputChainId = CHAIN_IDS.LOCALHOST) { traits: {}, }, NetworkController: { - selectedNetworkClientId: 'networkConfigurationId', - networksMetadata: { - networkConfigurationId: { - EIPS: {}, - status: NetworkStatus.Available, - }, - }, - providerConfig: { + ...mockNetworkState({ + id: 'networkConfigurationId', chainId: inputChainId, nickname: 'Localhost 8545', - rpcPrefs: {}, rpcUrl: 'http://localhost:8545', ticker: 'ETH', - type: 'rpc', - id: 'networkConfigurationId', - }, - networkConfigurations: { - networkConfigurationId: { - chainId: inputChainId, - nickname: 'Localhost 8545', - rpcPrefs: {}, - rpcUrl: 'http://localhost:8545', - ticker: 'ETH', - networkConfigurationId: 'networkConfigurationId', - }, - }, + blockExplorerUrl: undefined, + }), + providerConfig: { id: 'networkConfigurationId' }, }, OnboardingController: { completedOnboarding: true, diff --git a/test/e2e/fixture-builder.js b/test/e2e/fixture-builder.js index 76eb86e12ad1..d4172d65fe50 100644 --- a/test/e2e/fixture-builder.js +++ b/test/e2e/fixture-builder.js @@ -4,9 +4,9 @@ const { } = require('@metamask/snaps-utils'); const { merge } = require('lodash'); const { toHex } = require('@metamask/controller-utils'); -const { NetworkStatus } = require('@metamask/network-controller'); +const { mockNetworkState } = require('../stub/networks'); -const { CHAIN_IDS, NETWORK_TYPES } = require('../../shared/constants/network'); +const { CHAIN_IDS } = require('../../shared/constants/network'); const { SMART_CONTRACTS } = require('./seeder/smart-contracts'); const { DAPP_URL, DAPP_ONE_URL } = require('./helpers'); const { DEFAULT_FIXTURE_ACCOUNT, ERC_4337_ACCOUNT } = require('./constants'); @@ -40,32 +40,15 @@ function onboardingFixture() { }, }, NetworkController: { - selectedNetworkClientId: 'networkConfigurationId', - networksMetadata: { - networkConfigurationId: { - EIPS: {}, - status: NetworkStatus.Available, - }, - }, - providerConfig: { - ticker: 'ETH', - type: 'rpc', - rpcUrl: 'http://localhost:8545', + ...mockNetworkState({ + id: 'networkConfigurationId', chainId: CHAIN_IDS.LOCALHOST, nickname: 'Localhost 8545', - id: 'networkConfigurationId', - }, - networkConfigurations: { - networkConfigurationId: { - chainId: CHAIN_IDS.LOCALHOST, - nickname: 'Localhost 8545', - rpcPrefs: {}, - rpcUrl: 'http://localhost:8545', - ticker: 'ETH', - networkConfigurationId: 'networkConfigurationId', - type: 'rpc', - }, - }, + rpcUrl: 'http://localhost:8545', + ticker: 'ETH', + blockExplorerUrl: undefined, + }), + providerConfig: { id: 'networkConfigurationId' }, }, PreferencesController: { advancedGasFee: null, @@ -224,19 +207,14 @@ class FixtureBuilder { withNetworkController(data) { merge(this.fixture.data.NetworkController, data); + this.fixture.data.NetworkController.providerConfig = { + id: this.fixture.data.NetworkController.selectedNetworkClientId, + }; return this; } withNetworkControllerOnMainnet() { - merge(this.fixture.data.NetworkController, { - providerConfig: { - chainId: CHAIN_IDS.MAINNET, - nickname: '', - rpcUrl: '', - type: NETWORK_TYPES.MAINNET, - }, - }); - return this; + return this.withNetworkController({ selectedNetworkClientId: 'mainnet' }); } withNetworkControllerOnOptimism() { @@ -256,43 +234,37 @@ class FixtureBuilder { } withNetworkControllerDoubleGanache() { - return this.withNetworkController({ - networkConfigurations: { - networkConfigurationId: { - chainId: CHAIN_IDS.LOCALHOST, - nickname: 'Localhost 8545', - rpcPrefs: {}, - rpcUrl: 'http://localhost:8545', - ticker: 'ETH', - networkConfigurationId: 'networkConfigurationId', - id: 'networkConfigurationId', - }, - '76e9cd59-d8e2-47e7-b369-9c205ccb602c': { - id: '76e9cd59-d8e2-47e7-b369-9c205ccb602c', - rpcUrl: 'http://localhost:8546', - chainId: '0x53a', - ticker: 'ETH', - nickname: 'Localhost 8546', - rpcPrefs: {}, - }, + const ganacheNetworks = mockNetworkState( + { + chainId: CHAIN_IDS.LOCALHOST, + nickname: 'Localhost 8545', + rpcUrl: 'http://localhost:8545', + ticker: 'ETH', }, - }); + { + id: '76e9cd59-d8e2-47e7-b369-9c205ccb602c', + rpcUrl: 'http://localhost:8546', + chainId: '0x53a', + ticker: 'ETH', + nickname: 'Localhost 8546', + }, + ); + delete ganacheNetworks.selectedNetworkClientId; + return this.withNetworkController(ganacheNetworks); } withNetworkControllerTripleGanache() { this.withNetworkControllerDoubleGanache(); - merge(this.fixture.data.NetworkController, { - networkConfigurations: { - '243ad4c2-10a6-4621-9536-e3a67f4dd4c9': { - id: '243ad4c2-10a6-4621-9536-e3a67f4dd4c9', - rpcUrl: 'http://localhost:7777', - chainId: '0x3e8', - ticker: 'ETH', - nickname: 'Localhost 7777', - rpcPrefs: {}, - }, - }, - }); + merge( + this.fixture.data.NetworkController, + mockNetworkState({ + rpcUrl: 'http://localhost:7777', + chainId: '0x3e8', + ticker: 'ETH', + nickname: 'Localhost 7777', + blockExplorerUrl: undefined, + }), + ); return this; } @@ -382,6 +354,19 @@ class FixtureBuilder { return this; } + withBridgeControllerDefaultState() { + this.fixture.data.BridgeController = { + bridgeState: { + bridgeFeatureFlags: { + destNetworkAllowlist: [], + extensionSupport: false, + srcNetworkAllowlist: [], + }, + }, + }; + return this; + } + withPermissionControllerConnectedToTestDapp(restrictReturnedAccounts = true) { return this.withPermissionController({ subjects: { @@ -779,7 +764,7 @@ class FixtureBuilder { return this; } - withTokensControllerERC20() { + withTokensControllerERC20({ chainId = 1337 } = {}) { merge(this.fixture.data.TokensController, { tokens: [ { @@ -795,7 +780,7 @@ class FixtureBuilder { ignoredTokens: [], detectedTokens: [], allTokens: { - [toHex(1337)]: { + [toHex(chainId)]: { '0x5cfe73b6021e818b776b421b1c4db2474086a7e1': [ { address: `__FIXTURE_SUBSTITUTION__CONTRACT${SMART_CONTRACTS.HST}`, diff --git a/test/e2e/flask/btc/btc-account-overview.spec.ts b/test/e2e/flask/btc/btc-account-overview.spec.ts index 070a87c5bd6e..f45ec4acf17c 100644 --- a/test/e2e/flask/btc/btc-account-overview.spec.ts +++ b/test/e2e/flask/btc/btc-account-overview.spec.ts @@ -36,7 +36,7 @@ describe('BTC Account - Overview', function (this: Suite) { assert.equal(await buySellButton.isEnabled(), true); const portfolioButton = await driver.waitForSelector( - '[data-testid="coin-overview-portfolio"]', + '[data-testid="coin-overview-receive"]', ); assert.equal(await portfolioButton.isEnabled(), true); }, diff --git a/test/e2e/flask/create-watch-account.spec.ts b/test/e2e/flask/create-watch-account.spec.ts new file mode 100644 index 000000000000..75be806a221c --- /dev/null +++ b/test/e2e/flask/create-watch-account.spec.ts @@ -0,0 +1,461 @@ +import { strict as assert } from 'assert'; +import { Suite } from 'mocha'; +import messages from '../../../app/_locales/en/messages.json'; +import FixtureBuilder from '../fixture-builder'; +import { defaultGanacheOptions, unlockWallet, withFixtures } from '../helpers'; +import { Driver } from '../webdriver/driver'; + +const ACCOUNT_1 = '0x5CfE73b6021E818B776b421B1c4Db2474086a7e1'; +const EOA_ADDRESS = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'; +const SHORTENED_EOA_ADDRESS = '0xd8dA6...96045'; +const DEFAULT_WATCHED_ACCOUNT_NAME = 'Watched Account 1'; + +/** + * Start the flow to create a watch account by clicking the account menu and selecting the option to add a watch account. + * + * @param driver - The WebDriver instance used to control the browser. + * @param unlockWalletFirst - Whether to unlock the wallet before starting the flow. + */ +async function startCreateWatchAccountFlow( + driver: Driver, + unlockWalletFirst: boolean = true, +): Promise { + if (unlockWalletFirst) { + await unlockWallet(driver); + } + + await driver.clickElement('[data-testid="account-menu-icon"]'); + await driver.clickElement( + '[data-testid="multichain-account-menu-popover-action-button"]', + ); + await driver.clickElement( + '[data-testid="multichain-account-menu-popover-add-watch-only-account"]', + ); +} + +/** + * Watches an EOA address. + * + * @param driver - The WebDriver instance used to control the browser. + * @param unlockWalletFirst - Whether to unlock the wallet before watching the address. + * @param address - The EOA address to watch. + */ +async function watchEoaAddress( + driver: Driver, + unlockWalletFirst: boolean = true, + address: string = EOA_ADDRESS, +): Promise { + await startCreateWatchAccountFlow(driver, unlockWalletFirst); + await driver.fill( + '[placeholder="Enter a public address or ENS name"]', + address, + ); + await driver.clickElement({ text: 'Watch account', tag: 'button' }); + await driver.clickElement('[data-testid="submit-add-account-with-name"]'); +} + +/** + * Removes the selected account. + * + * @param driver - The WebDriver instance used to control the browser. + */ +async function removeSelectedAccount(driver: Driver): Promise { + await driver.clickElement('[data-testid="account-menu-icon"]'); + await driver.clickElement( + '.multichain-account-list-item--selected [data-testid="account-list-item-menu-button"]', + ); + await driver.clickElement('[data-testid="account-list-menu-remove"]'); + await driver.clickElement({ text: 'Remove', tag: 'button' }); +} + +describe('Account-watcher snap', function (this: Suite) { + describe('Adding watched accounts', function () { + it('adds watch account with valid EOA address', async function () { + await withFixtures( + { + fixtures: new FixtureBuilder() + .withPreferencesControllerAndFeatureFlag({ + watchEthereumAccountEnabled: true, + }) + .withNetworkControllerOnMainnet() + .build(), + ganacheOptions: defaultGanacheOptions, + title: this.test?.fullTitle(), + }, + async ({ driver }: { driver: Driver }) => { + // watch an EOA address + await watchEoaAddress(driver); + + // new account should be displayed in the account list + await driver.findElement({ + css: '[data-testid="account-menu-icon"]', + text: DEFAULT_WATCHED_ACCOUNT_NAME, + }); + await driver.findElement({ + css: '.mm-text--ellipsis', + text: SHORTENED_EOA_ADDRESS, + }); + }, + ); + }); + + it("disables 'Send' 'Swap' and 'Bridge' buttons for watch accounts", async function () { + await withFixtures( + { + fixtures: new FixtureBuilder() + .withPreferencesControllerAndFeatureFlag({ + watchEthereumAccountEnabled: true, + }) + .withNetworkControllerOnMainnet() + .build(), + ganacheOptions: defaultGanacheOptions, + title: this.test?.fullTitle(), + }, + async ({ driver }: { driver: Driver }) => { + // watch an EOA address + await watchEoaAddress(driver); + + // 'Send' button should be disabled + await driver.findElement( + '[data-testid="eth-overview-send"][disabled]', + ); + await driver.findElement( + '[data-testid="eth-overview-send"].icon-button--disabled', + ); + + // 'Swap' button should be disabled + await driver.findElement( + '[data-testid="token-overview-button-swap"][disabled]', + ); + await driver.findElement( + '[data-testid="token-overview-button-swap"].icon-button--disabled', + ); + + // 'Bridge' button should be disabled + await driver.findElement( + '[data-testid="eth-overview-bridge"][disabled]', + ); + await driver.findElement( + '[data-testid="eth-overview-bridge"].icon-button--disabled', + ); + + // check tooltips for disabled buttons + await driver.findElement( + '.icon-button--disabled [data-tooltipped][data-original-title="Not supported with this account."]', + ); + }, + ); + }); + }); + + describe('Invalid input handling', function () { + const invalidInputTests = [ + { + input: 'd8dA6BF26964aF9D7eEd9e03E53415D37aA96045', + message: 'Invalid input', + description: 'address missing 0x prefix', + }, + { + input: '0x123ABC', + message: 'Invalid address', + description: 'invalid address', + }, + { + input: 'invalid.eth', + message: 'Invalid ENS name', + description: 'invalid ENS name', + }, + { + input: ACCOUNT_1, + message: `Unknown snap error: Account address '${ACCOUNT_1}' already exists`, + description: 'existing address', + }, + ]; + + invalidInputTests.forEach(({ input, message, description }) => { + it(`handles invalid input: ${description}`, async function () { + await withFixtures( + { + fixtures: new FixtureBuilder() + .withPreferencesControllerAndFeatureFlag({ + watchEthereumAccountEnabled: true, + }) + .withNetworkControllerOnMainnet() + .build(), + ganacheOptions: defaultGanacheOptions, + title: this.test?.fullTitle(), + }, + async ({ driver }: { driver: Driver }) => { + await startCreateWatchAccountFlow(driver); + + await driver.fill( + '[placeholder="Enter a public address or ENS name"]', + input, + ); + await driver.clickElement({ text: 'Watch account', tag: 'button' }); + + // error message should be displayed by the snap + await driver.findElement({ + css: '.snap-ui-renderer__text', + text: message, + }); + }, + ); + }); + }); + }); + + describe('Account management', function () { + it('does not allow user to import private key of watched address', async function () { + const PRIVATE_KEY_TWO = + '0xf444f52ea41e3a39586d7069cb8e8233e9f6b9dea9cbb700cce69ae860661cc8'; + const ACCOUNT_2 = '0x09781764c08de8ca82e156bbf156a3ca217c7950'; + + await withFixtures( + { + fixtures: new FixtureBuilder() + .withPreferencesControllerAndFeatureFlag({ + watchEthereumAccountEnabled: true, + }) + .withNetworkControllerOnMainnet() + .build(), + ganacheOptions: defaultGanacheOptions, + title: this.test?.fullTitle(), + }, + async ({ driver }: { driver: Driver }) => { + // watch an EOA address for ACCOUNT_2 + await watchEoaAddress(driver, true, ACCOUNT_2); + + // try to import private key of watched ACCOUNT_2 address + await driver.clickElement('[data-testid="account-menu-icon"]'); + await driver.clickElement( + '[data-testid="multichain-account-menu-popover-action-button"]', + ); + await driver.clickElement({ text: 'Import account', tag: 'button' }); + await driver.findClickableElement('#private-key-box'); + await driver.fill('#private-key-box', PRIVATE_KEY_TWO); + await driver.clickElement( + '[data-testid="import-account-confirm-button"]', + ); + + // error message should be displayed + await driver.findElement({ + css: '.mm-box--color-error-default', + text: 'KeyringController - The account you are trying to import is a duplicate', + }); + }, + ); + }); + + it("does not display 'Show private key' button for watch accounts", async function () { + await withFixtures( + { + fixtures: new FixtureBuilder() + .withPreferencesControllerAndFeatureFlag({ + watchEthereumAccountEnabled: true, + }) + .withNetworkControllerOnMainnet() + .build(), + ganacheOptions: defaultGanacheOptions, + title: this.test?.fullTitle(), + }, + async ({ driver }: { driver: Driver }) => { + // watch an EOA address + await watchEoaAddress(driver); + + // click to view account details + await driver.clickElement( + '[data-testid="account-options-menu-button"]', + ); + await driver.clickElement( + '[data-testid="account-list-menu-details"]', + ); + // 'Show private key' button should not be displayed + await driver.assertElementNotPresent({ + css: 'button', + text: 'Show private key', + }); + }, + ); + }); + + it('removes a watched account', async function () { + await withFixtures( + { + fixtures: new FixtureBuilder() + .withPreferencesControllerAndFeatureFlag({ + watchEthereumAccountEnabled: true, + }) + .withNetworkControllerOnMainnet() + .build(), + ganacheOptions: defaultGanacheOptions, + title: this.test?.fullTitle(), + }, + async ({ driver }: { driver: Driver }) => { + // watch an EOA address + await watchEoaAddress(driver); + + // remove the selected watched account + await removeSelectedAccount(driver); + + // account should be removed from the account list + await driver.assertElementNotPresent({ + css: '[data-testid="account-menu-icon"]', + text: DEFAULT_WATCHED_ACCOUNT_NAME, + }); + await driver.assertElementNotPresent({ + css: '.mm-text--ellipsis', + text: SHORTENED_EOA_ADDRESS, + }); + }, + ); + }); + + it('can remove and recreate a watched account', async function () { + await withFixtures( + { + fixtures: new FixtureBuilder() + .withPreferencesControllerAndFeatureFlag({ + watchEthereumAccountEnabled: true, + }) + .withNetworkControllerOnMainnet() + .build(), + ganacheOptions: defaultGanacheOptions, + title: this.test?.fullTitle(), + }, + async ({ driver }: { driver: Driver }) => { + // watch an EOA address + await watchEoaAddress(driver); + + // remove the selected watched account + await removeSelectedAccount(driver); + + // account should be removed from the account list + await driver.assertElementNotPresent({ + css: '[data-testid="account-menu-icon"]', + text: DEFAULT_WATCHED_ACCOUNT_NAME, + }); + await driver.assertElementNotPresent({ + css: '.mm-text--ellipsis', + text: SHORTENED_EOA_ADDRESS, + }); + + // watch the same EOA address again + await watchEoaAddress(driver, false); + + // same account should be displayed in the account list + await driver.findElement({ + css: '[data-testid="account-menu-icon"]', + text: DEFAULT_WATCHED_ACCOUNT_NAME, + }); + await driver.findElement({ + css: '.mm-text--ellipsis', + text: SHORTENED_EOA_ADDRESS, + }); + }, + ); + }); + }); + + describe('Experimental toggle', function () { + const navigateToExperimentalSettings = async (driver: Driver) => { + await driver.clickElement('[data-testid="account-options-menu-button"]'); + await driver.clickElement({ text: 'Settings', tag: 'div' }); + await driver.clickElement({ text: 'Experimental', tag: 'div' }); + await driver.waitForSelector({ + text: messages.watchEthereumAccountsToggle.message, + tag: 'span', + }); + }; + + const getToggleState = async (driver: Driver): Promise => { + const toggleInput = await driver.findElement( + '[data-testid="watch-account-toggle"]', + ); + return toggleInput.isSelected(); + }; + + const toggleWatchAccountOptionAndCloseSettings = async (driver: Driver) => { + await driver.clickElement('[data-testid="watch-account-toggle-div"]'); + await driver.clickElement('button[aria-label="Close"]'); + }; + + const verifyWatchAccountOptionAndCloseMenu = async ( + driver: Driver, + shouldBePresent: boolean, + ) => { + await driver.clickElement('[data-testid="account-menu-icon"]'); + await driver.clickElement( + '[data-testid="multichain-account-menu-popover-action-button"]', + ); + if (shouldBePresent) { + await driver.waitForSelector({ + text: messages.addEthereumWatchOnlyAccount.message, + tag: 'button', + }); + } else { + await driver.assertElementNotPresent({ + text: messages.addEthereumWatchOnlyAccount.message, + tag: 'button', + }); + } + await driver.clickElement('button[aria-label="Close"]'); + }; + + it("will show the 'Watch an Ethereum account (Beta)' option when setting is enabled", async function () { + await withFixtures( + { + fixtures: new FixtureBuilder().build(), + ganacheOptions: defaultGanacheOptions, + title: this.test?.fullTitle(), + }, + async ({ driver }) => { + await unlockWallet(driver); + await navigateToExperimentalSettings(driver); + + // verify toggle is off by default + assert.equal( + await getToggleState(driver), + false, + 'Toggle should be off by default', + ); + + // enable the toggle + await toggleWatchAccountOptionAndCloseSettings(driver); + + // verify the 'Watch and Ethereum account (Beta)' option is available + await verifyWatchAccountOptionAndCloseMenu(driver, true); + }, + ); + }); + + it('enables and then disables the toggle and the option to add a watch-only account behaves as expected', async function () { + await withFixtures( + { + fixtures: new FixtureBuilder().build(), + ganacheOptions: defaultGanacheOptions, + title: this.test?.fullTitle(), + }, + async ({ driver }) => { + await unlockWallet(driver); + await navigateToExperimentalSettings(driver); + + // enable the toggle + await toggleWatchAccountOptionAndCloseSettings(driver); + + // verify the 'Watch and Ethereum account (Beta)' option is available + await verifyWatchAccountOptionAndCloseMenu(driver, true); + + // navigate back to experimental settings + await navigateToExperimentalSettings(driver); + + // disable the toggle + await toggleWatchAccountOptionAndCloseSettings(driver); + + // verify the 'Watch and Ethereum account (Beta)' option is not available + await verifyWatchAccountOptionAndCloseMenu(driver, false); + }, + ); + }); + }); +}); diff --git a/test/e2e/helpers.js b/test/e2e/helpers.js index 167ac13a0e18..3dfec7a8bdb1 100644 --- a/test/e2e/helpers.js +++ b/test/e2e/helpers.js @@ -40,6 +40,22 @@ const convertToHexValue = (val) => `0x${new BigNumber(val, 10).toString(16)}`; const convertETHToHexGwei = (eth) => convertToHexValue(eth * 10 ** 18); +/** + * @typedef {object} Fixtures + * @property {import('./webdriver/driver').Driver} driver - The driver number. + * @property {GanacheContractAddressRegistry | undefined} contractRegistry - The contract registry. + * @property {Ganache | undefined} ganacheServer - The Ganache server. + * @property {Ganache | undefined} secondaryGanacheServer - The secondary Ganache server. + * @property {mockttp.MockedEndpoint[]} mockedEndpoint - The mocked endpoint. + * @property {Bundler} bundlerServer - The bundler server. + * @property {mockttp.Mockttp} mockServer - The mock server. + */ + +/** + * + * @param {object} options + * @param {(fixtures: Fixtures) => Promise} testSuite + */ async function withFixtures(options, testSuite) { const { dapp, @@ -180,9 +196,9 @@ async function withFixtures(options, testSuite) { if (typeof originalProperty === 'function') { return (...args) => { console.log( - `[driver] Called '${prop}' with arguments ${JSON.stringify( + `${new Date().toISOString()} [driver] Called '${prop}' with arguments ${JSON.stringify( args, - ).slice(0, 200)}`, // limit the length of the log entry to 200 characters + ).slice(0, 224)}`, // limit the length of the log entry to 224 characters ); return originalProperty.bind(target)(...args); }; @@ -734,6 +750,10 @@ const switchToOrOpenDapp = async ( } }; +/** + * + * @param {import('./webdriver/driver').Driver} driver + */ const connectToDapp = async (driver) => { await openDapp(driver); // Connect to dapp @@ -742,12 +762,12 @@ const connectToDapp = async (driver) => { tag: 'button', }); - await switchToNotificationWindow(driver); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); await driver.clickElement({ text: 'Next', tag: 'button', }); - await driver.clickElement({ + await driver.clickElementAndWaitForWindowToClose({ text: 'Confirm', tag: 'button', }); @@ -1066,7 +1086,7 @@ async function switchToNotificationWindow(driver) { * @param {WebDriver} driver * @param {import('mockttp').MockedEndpoint[]} mockedEndpoints * @param {boolean} hasRequest - * @returns {import('mockttp/dist/pluggable-admin').MockttpClientResponse[]} + * @returns {Promise} */ async function getEventPayloads(driver, mockedEndpoints, hasRequest = true) { await driver.wait( diff --git a/test/e2e/mock-e2e.js b/test/e2e/mock-e2e.js index 3e3719eed432..c71cc00c7a32 100644 --- a/test/e2e/mock-e2e.js +++ b/test/e2e/mock-e2e.js @@ -1,11 +1,17 @@ const fs = require('fs'); -const { BRIDGE_API_BASE_URL } = require('../../shared/constants/bridge'); +const { + BRIDGE_DEV_API_BASE_URL, + BRIDGE_PROD_API_BASE_URL, +} = require('../../shared/constants/bridge'); const { GAS_API_BASE_URL, SWAPS_API_V2_BASE_URL, TOKEN_API_BASE_URL, } = require('../../shared/constants/swaps'); +const { + DEFAULT_FEATURE_FLAGS_RESPONSE: BRIDGE_DEFAULT_FEATURE_FLAGS_RESPONSE, +} = require('./tests/bridge/constants'); const CDN_CONFIG_PATH = 'test/e2e/mock-cdn/cdn-config.txt'; const CDN_STALE_DIFF_PATH = 'test/e2e/mock-cdn/cdn-stale-diff.txt'; @@ -71,7 +77,7 @@ const browserAPIRequestDomains = * Setup E2E network mocks. * * @param {Mockttp} server - The mock server used for network mocks. - * @param {(server: Mockttp) => MockedEndpoint} testSpecificMock - A function for setting up test-specific network mocks + * @param {(server: Mockttp) => Promise} testSpecificMock - A function for setting up test-specific network mocks * @param {object} options - Network mock options. * @param {string} options.chainId - The chain ID used by the default configured network. * @param {string} options.ethConversionInUsd - The USD conversion rate for ETH. @@ -294,16 +300,18 @@ async function setupMocking( }; }); - await server - .forGet(`${BRIDGE_API_BASE_URL}/getAllFeatureFlags`) - .thenCallback(() => { - return { - statusCode: 200, - json: { - 'extension-support': false, - }, - }; - }); + [ + `${BRIDGE_DEV_API_BASE_URL}/getAllFeatureFlags`, + `${BRIDGE_PROD_API_BASE_URL}/getAllFeatureFlags`, + ].forEach( + async (url) => + await server.forGet(url).thenCallback(() => { + return { + statusCode: 200, + json: BRIDGE_DEFAULT_FEATURE_FLAGS_RESPONSE, + }; + }), + ); await server .forGet(`https://token.api.cx.metamask.io/tokens/${chainId}`) diff --git a/test/e2e/page-objects/flows/transaction.ts b/test/e2e/page-objects/flows/transaction.ts new file mode 100644 index 000000000000..e2fdbd652034 --- /dev/null +++ b/test/e2e/page-objects/flows/transaction.ts @@ -0,0 +1,40 @@ +import { TransactionParams } from '@metamask/transaction-controller'; +import { DEFAULT_FIXTURE_ACCOUNT } from '../../constants'; +import { Driver } from '../../webdriver/driver'; +import HomePage from '../pages/homepage'; +import SendTokenPage from '../pages/send/send-token-page'; +import TestDapp from '../pages/test-dapp'; + +export const createInternalTransaction = async (driver: Driver) => { + // Firefox has incorrect balance if send flow started too quickly. + await driver.delay(1000); + + const homePage = new HomePage(driver); + await homePage.startSendFlow(); + + const sendToPage = new SendTokenPage(driver); + await sendToPage.check_pageIsLoaded(); + await sendToPage.fillRecipient('0x2f318C334780961FB129D2a6c30D0763d9a5C970'); + await sendToPage.fillAmount('1'); + await sendToPage.goToNextScreen(); +}; + +export const createDappTransaction = async ( + driver: Driver, + override?: Partial, +) => { + const testDapp = new TestDapp(driver); + + await testDapp.request('eth_sendTransaction', [ + { + data: '0x', + from: DEFAULT_FIXTURE_ACCOUNT, + maxFeePerGas: '0x0', + maxPriorityFeePerGas: '0x0', + to: '0x2f318C334780961FB129D2a6c30D0763d9a5C970', + value: '0x38d7ea4c68000', + type: '0x2', + ...override, + }, + ]); +}; diff --git a/test/e2e/page-objects/pages/confirmations/legacy/navigation.ts b/test/e2e/page-objects/pages/confirmations/legacy/navigation.ts new file mode 100644 index 000000000000..ab04f85a4a2e --- /dev/null +++ b/test/e2e/page-objects/pages/confirmations/legacy/navigation.ts @@ -0,0 +1,57 @@ +import { Driver } from '../../../../webdriver/driver'; + +class ConfirmationNavigation { + private driver: Driver; + + private nextPageButton: string; + + private previousPageButton: string; + + private firstPageButton: string; + + private lastPageButton: string; + + private navigationTitle: string; + + constructor(driver: Driver) { + this.driver = driver; + this.nextPageButton = '[data-testid="next-page"]'; + this.previousPageButton = '[data-testid="previous-page"]'; + this.firstPageButton = '[data-testid="first-page"]'; + this.lastPageButton = '[data-testid="last-page"]'; + this.navigationTitle = '.confirm-page-container-navigation'; + } + + async clickNextPage(): Promise { + await this.driver.clickElement(this.nextPageButton); + } + + async clickPreviousPage(): Promise { + await this.driver.clickElement(this.previousPageButton); + } + + async clickFirstPage(): Promise { + await this.driver.clickElement(this.firstPageButton); + } + + async clickLastPage(): Promise { + await this.driver.clickElement(this.lastPageButton); + } + + async check_pageNumbers( + currentPage: number, + totalPages: number, + ): Promise { + try { + await this.driver.findElement({ + css: this.navigationTitle, + text: `${currentPage} of ${totalPages}`, + }); + } catch (e) { + console.log('Timeout while waiting for navigation page numbers', e); + throw e; + } + } +} + +export default ConfirmationNavigation; diff --git a/test/e2e/page-objects/pages/test-dapp.ts b/test/e2e/page-objects/pages/test-dapp.ts new file mode 100644 index 000000000000..8c2fe513ca10 --- /dev/null +++ b/test/e2e/page-objects/pages/test-dapp.ts @@ -0,0 +1,37 @@ +import { Driver } from '../../webdriver/driver'; + +const DAPP_HOST_ADDRESS = '127.0.0.1:8080'; +const DAPP_URL = `http://${DAPP_HOST_ADDRESS}`; + +class TestDapp { + private driver: Driver; + + constructor(driver: Driver) { + this.driver = driver; + } + + async open({ + contractAddress, + url = DAPP_URL, + }: { + contractAddress?: string; + url?: string; + }) { + const dappUrl = contractAddress + ? `${url}/?contract=${contractAddress}` + : url; + + return await this.driver.openNewPage(dappUrl); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + async request(method: string, params: any[]) { + await this.open({ + url: `${DAPP_URL}/request?method=${method}¶ms=${JSON.stringify( + params, + )}`, + }); + } +} + +export default TestDapp; diff --git a/test/e2e/playwright/global/specs/protect-intrinsics.spec.ts b/test/e2e/playwright/global/specs/protect-intrinsics.spec.ts new file mode 100644 index 000000000000..7f67cd587f2b --- /dev/null +++ b/test/e2e/playwright/global/specs/protect-intrinsics.spec.ts @@ -0,0 +1,17 @@ +import { test, expect } from '@playwright/test'; +// @ts-expect-error lint fails otherwise +import 'ses'; +import '../../../../../app/scripts/lockdown-run'; +import '../../../../../app/scripts/lockdown-more'; +import { + getGlobalProperties, + testIntrinsic, +} from '../../../../helpers/protect-intrinsics-helpers'; + +test.describe('non-modifiable intrinsics', () => { + getGlobalProperties().forEach((propertyName) => { + test(`intrinsic globalThis["${propertyName}"]`, () => { + expect(() => testIntrinsic(propertyName)).not.toThrow(); + }); + }); +}); diff --git a/test/e2e/playwright/mmi/pageObjects/mmi-auth0-page.ts b/test/e2e/playwright/mmi/pageObjects/mmi-auth0-page.ts index 761fb8684de7..c8c304412936 100644 --- a/test/e2e/playwright/mmi/pageObjects/mmi-auth0-page.ts +++ b/test/e2e/playwright/mmi/pageObjects/mmi-auth0-page.ts @@ -45,6 +45,7 @@ export class Auth0Page { .locator('#password') .fill(process.env.MMI_E2E_E2E_AUTH0_PASSWORD as string); await this.page.getByRole('button', { name: /continue/iu }).click(); + await this.page.getByRole('button', { name: /E2E Organization/iu }).click(); await expect(this.page).toHaveURL(portfolio); } } diff --git a/test/e2e/playwright/mmi/specs/extension.visual.spec.ts b/test/e2e/playwright/mmi/specs/extension.visual.spec.ts index 8a9aa0c6fff3..c9f6717b26ec 100644 --- a/test/e2e/playwright/mmi/specs/extension.visual.spec.ts +++ b/test/e2e/playwright/mmi/specs/extension.visual.spec.ts @@ -105,6 +105,8 @@ test.describe('MMI extension', () => { 'Custody Account R', 'Custody Account S', 'Custody Account T', + 'TR', + 'TR2', ]; // Getting extension id of MMI diff --git a/test/e2e/playwright/mmi/specs/navigation.spec.ts b/test/e2e/playwright/mmi/specs/navigation.spec.ts index 06c29f422971..7aca00aba1c4 100644 --- a/test/e2e/playwright/mmi/specs/navigation.spec.ts +++ b/test/e2e/playwright/mmi/specs/navigation.spec.ts @@ -7,11 +7,10 @@ import { getPageAndCloseRepeated, } from '../helpers/utils'; import { MMIMainMenuPage } from '../pageObjects/mmi-mainMenu-page'; -import { Auth0Page } from '../pageObjects/mmi-auth0-page'; import { MMIMainPage } from '../pageObjects/mmi-main-page'; -const portfolio = `${process.env.MMI_E2E_MMI_DASHBOARD_URL}/portfolio`; -const stake = `${process.env.MMI_E2E_MMI_DASHBOARD_URL}/stake`; +const portfolio = `${process.env.MMI_E2E_MMI_DASHBOARD_URL}`; +const stake = `${process.env.MMI_E2E_MMI_DASHBOARD_URL}`; const support = 'https://mmi-support.metamask.io/hc/en-us'; const supportContactUs = 'https://mmi-support.metamask.io/hc/en-us/requests/new'; @@ -51,12 +50,6 @@ test.describe('MMI Navigation', () => { // await mainMenuPage.showIncomingTransactionsOff() await mainMenuPage.closeSettings(); - // This is removed to improve test performance - // Signin auth0 - const auth0 = new Auth0Page(await context.newPage()); - await auth0.signIn(); - await auth0.page.close(); - // // Close pages not used to remove data from logs await closePages(context, ['metamask-institutional.io']); const mainPage = new MMIMainPage( diff --git a/test/e2e/run-all.js b/test/e2e/run-all.js index d52a37e9afe6..6150f5c71aa9 100644 --- a/test/e2e/run-all.js +++ b/test/e2e/run-all.js @@ -9,7 +9,7 @@ const { loadBuildTypesConfig } = require('../../development/lib/build-type'); const { filterE2eChangedFiles } = require('./changedFilesUtil'); // These tests should only be run on Flask for now. -const FLASK_ONLY_TESTS = ['test-snap-namelookup.spec.js']; +const FLASK_ONLY_TESTS = []; const getTestPathsForTestDir = async (testDir) => { const testFilenames = await fs.promises.readdir(testDir, { diff --git a/test/e2e/snaps/enums.js b/test/e2e/snaps/enums.js index 981984634a37..a7a69e13a13c 100644 --- a/test/e2e/snaps/enums.js +++ b/test/e2e/snaps/enums.js @@ -1,3 +1,3 @@ module.exports = { - TEST_SNAPS_WEBSITE_URL: 'https://metamask.github.io/snaps/test-snaps/2.12.0/', + TEST_SNAPS_WEBSITE_URL: 'https://metamask.github.io/snaps/test-snaps/2.13.0/', }; diff --git a/test/e2e/snaps/test-snap-dialog.spec.js b/test/e2e/snaps/test-snap-dialog.spec.js index 5f2700e5fd40..3b82781321da 100644 --- a/test/e2e/snaps/test-snap-dialog.spec.js +++ b/test/e2e/snaps/test-snap-dialog.spec.js @@ -2,14 +2,13 @@ const { defaultGanacheOptions, withFixtures, unlockWallet, - switchToNotificationWindow, WINDOW_TITLES, } = require('../helpers'); const FixtureBuilder = require('../fixture-builder'); const { TEST_SNAPS_WEBSITE_URL } = require('./enums'); describe('Test Snap Dialog', function () { - it('test all three snap_dialog types', async function () { + it('test all four snap_dialog types', async function () { await withFixtures( { fixtures: new FixtureBuilder().build(), @@ -34,7 +33,7 @@ describe('Test Snap Dialog', function () { await driver.clickElement('#connectdialogs'); // switch to metamask extension and click connect - await switchToNotificationWindow(driver); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); await driver.clickElement({ text: 'Connect', tag: 'button', @@ -49,7 +48,7 @@ describe('Test Snap Dialog', function () { await driver.waitForSelector({ text: 'OK' }); - await driver.clickElement({ + await driver.clickElementAndWaitForWindowToClose({ text: 'OK', tag: 'button', }); @@ -63,11 +62,12 @@ describe('Test Snap Dialog', function () { text: 'Reconnect to Dialogs Snap', }); + // test 1 - alert dialog // click on alert dialog await driver.clickElement('#sendAlertButton'); // switch to dialog popup - await switchToNotificationWindow(driver); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); // check dialog contents const result = await driver.findElement('.snap-ui-renderer__panel'); @@ -78,7 +78,7 @@ describe('Test Snap Dialog', function () { }); // click ok button - await driver.clickElement({ + await driver.clickElementAndWaitForWindowToClose({ text: 'OK', tag: 'button', }); @@ -92,14 +92,15 @@ describe('Test Snap Dialog', function () { text: 'null', }); + // test 2 - confirmation dialog // click conf button await driver.clickElement('#sendConfirmationButton'); // switch to dialog popup - await switchToNotificationWindow(driver); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); // click reject - await driver.clickElement({ + await driver.clickElementAndWaitForWindowToClose({ text: 'Reject', tag: 'button', }); @@ -117,10 +118,10 @@ describe('Test Snap Dialog', function () { await driver.clickElement('#sendConfirmationButton'); // switch to dialog popup - await switchToNotificationWindow(driver); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); // click accept - await driver.clickElement({ + await driver.clickElementAndWaitForWindowToClose({ text: 'Approve', tag: 'button', }); @@ -134,14 +135,15 @@ describe('Test Snap Dialog', function () { text: 'true', }); + // test 3 - prompt dialog // click prompt button await driver.clickElement('#sendPromptButton'); // switch to dialog popup - await switchToNotificationWindow(driver); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); // click cancel button - await driver.clickElement({ + await driver.clickElementAndWaitForWindowToClose({ text: 'Cancel', tag: 'button', }); @@ -159,13 +161,13 @@ describe('Test Snap Dialog', function () { await driver.clickElement('#sendPromptButton'); // switch to dialog popup - await switchToNotificationWindow(driver); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); // fill '2323' in form field await driver.pasteIntoField('.mm-input', '2323'); // click submit button - await driver.clickElement({ + await driver.clickElementAndWaitForWindowToClose({ text: 'Submit', tag: 'button', }); @@ -178,6 +180,52 @@ describe('Test Snap Dialog', function () { css: '#dialogResult', text: '"2323"', }); + + // test 4 - custom dialog + // click custom button + await driver.clickElement('#sendCustomButton'); + + // switch to dialog popup + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); + + // click cancel button + await driver.clickElementAndWaitForWindowToClose({ + text: 'Cancel', + tag: 'span', + }); + + // switch back to test snaps tab + await driver.switchToWindowWithTitle(WINDOW_TITLES.TestSnaps); + + // check result is equal to 'null' + await driver.waitForSelector({ + css: '#dialogResult', + text: 'null', + }); + + // click prompt button + await driver.clickElement('#sendCustomButton'); + + // switch to dialog popup + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); + + // fill '2323' in form field + await driver.pasteIntoField('#custom-input', '2323'); + + // click confirm button + await driver.clickElementAndWaitForWindowToClose({ + text: 'Confirm', + tag: 'span', + }); + + // switch back to test snaps tab + await driver.switchToWindowWithTitle(WINDOW_TITLES.TestSnaps); + + // check result is equal to '2323' + await driver.waitForSelector({ + css: '#dialogResult', + text: '"2323"', + }); }, ); }); diff --git a/test/e2e/snaps/test-snap-interactive-ui.spec.js b/test/e2e/snaps/test-snap-interactive-ui.spec.js index ec4d47fee817..edbea8567872 100644 --- a/test/e2e/snaps/test-snap-interactive-ui.spec.js +++ b/test/e2e/snaps/test-snap-interactive-ui.spec.js @@ -35,7 +35,11 @@ describe('Test Snap Interactive UI', function () { tag: 'button', }); - await driver.clickElementSafe('[data-testid="snap-install-scroll"]'); + // We need a bigger timeout as the Connect action takes some time + await driver.clickElementSafe( + '[data-testid="snap-install-scroll"]', + 3000, + ); await driver.waitForSelector({ text: 'Confirm' }); diff --git a/test/e2e/snaps/test-snap-jsx.spec.js b/test/e2e/snaps/test-snap-jsx.spec.js index 6f3b5d025365..b5f651125232 100644 --- a/test/e2e/snaps/test-snap-jsx.spec.js +++ b/test/e2e/snaps/test-snap-jsx.spec.js @@ -2,7 +2,6 @@ const { defaultGanacheOptions, withFixtures, unlockWallet, - switchToNotificationWindow, WINDOW_TITLES, } = require('../helpers'); const FixtureBuilder = require('../fixture-builder'); @@ -35,8 +34,8 @@ describe('Test Snap JSX', function () { await driver.waitForSelector('#connectjsx'); await driver.clickElement('#connectjsx'); - // switch to metamask extension and click connect - await switchToNotificationWindow(driver, 2); + // switch to dialog window and click connect + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); await driver.waitForSelector({ text: 'Connect', tag: 'button', @@ -57,7 +56,7 @@ describe('Test Snap JSX', function () { await driver.waitForSelector({ text: 'OK' }); - await driver.clickElement({ + await driver.clickElementAndWaitForWindowToClose({ text: 'OK', tag: 'button', }); @@ -75,12 +74,12 @@ describe('Test Snap JSX', function () { await driver.clickElement('#displayJsx'); // switch to dialog window - await switchToNotificationWindow(driver, 2); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); // check for count zero await driver.waitForSelector({ text: '0', - tag: 'b', + tag: 'p', }); // click increment twice @@ -89,7 +88,7 @@ describe('Test Snap JSX', function () { // wait for count to be 1 await driver.waitForSelector({ text: '1', - tag: 'b', + tag: 'p', }); }, ); diff --git a/test/e2e/snaps/test-snap-namelookup.spec.js b/test/e2e/snaps/test-snap-namelookup.spec.js index 4e58231256dd..94af575deb9c 100644 --- a/test/e2e/snaps/test-snap-namelookup.spec.js +++ b/test/e2e/snaps/test-snap-namelookup.spec.js @@ -50,7 +50,7 @@ describe('Test Snap Name Lookup', function () { await driver.waitForSelector({ text: 'OK' }); - await driver.clickElement({ + await driver.clickElementAndWaitForWindowToClose({ text: 'OK', tag: 'button', }); @@ -71,12 +71,18 @@ describe('Test Snap Name Lookup', function () { tag: 'p', }); + // ensure we are on Mainnet + await driver.waitForSelector('[data-testid="staking-entrypoint-0x1"]'); + // click send await driver.clickElement('[data-testid="eth-overview-send"]'); // wait for input field and enter name to lookup await driver.waitForSelector('[data-testid="ens-input"]'); - await driver.fill('[data-testid="ens-input"]', 'metamask.domain'); + await driver.pasteIntoField( + '[data-testid="ens-input"]', + 'metamask.domain', + ); // verify name output from snap await driver.waitForSelector({ diff --git a/test/e2e/snaps/test-snap-siginsights.spec.js b/test/e2e/snaps/test-snap-siginsights.spec.js index af21897789f8..8e90f8336b2e 100644 --- a/test/e2e/snaps/test-snap-siginsights.spec.js +++ b/test/e2e/snaps/test-snap-siginsights.spec.js @@ -4,7 +4,6 @@ const { defaultGanacheOptions, openDapp, unlockWallet, - switchToNotificationWindow, tempToggleSettingRedesignedConfirmations, WINDOW_TITLES, } = require('../helpers'); @@ -12,7 +11,7 @@ const FixtureBuilder = require('../fixture-builder'); const { TEST_SNAPS_WEBSITE_URL } = require('./enums'); describe('Test Snap Signature Insights', function () { - it('tests Signature Insights functionality', async function () { + it('tests Signature Insights functionality (New)', async function () { await withFixtures( { dapp: true, @@ -25,7 +24,6 @@ describe('Test Snap Signature Insights', function () { }, async ({ driver }) => { await unlockWallet(driver); - await tempToggleSettingRedesignedConfirmations(driver); // navigate to test snaps page and connect await driver.openNewPage(TEST_SNAPS_WEBSITE_URL); @@ -40,7 +38,7 @@ describe('Test Snap Signature Insights', function () { await driver.clickElement('#connectsignature-insights'); // switch to metamask extension and click connect - await switchToNotificationWindow(driver); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); await driver.clickElement({ text: 'Connect', tag: 'button', @@ -55,7 +53,7 @@ describe('Test Snap Signature Insights', function () { await driver.waitForSelector({ text: 'OK' }); - await driver.clickElement({ + await driver.clickElementAndWaitForWindowToClose({ text: 'OK', tag: 'button', }); @@ -66,7 +64,273 @@ describe('Test Snap Signature Insights', function () { // open the test-dapp page await openDapp(driver); - // poll windowHandles and switch to test-dapp + // TEST ONE: personal sign + // find and scroll to personal sign and click sign + const personalSignButton1 = await driver.findElement('#personalSign'); + await driver.scrollToElement(personalSignButton1); + await driver.clickElement('#personalSign'); + + // switch back to MetaMask window and switch to tx insights pane + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); + + // wait for information from signature request screen + await driver.waitForSelector({ + text: 'Example `personal_sign` message', + tag: 'p', + }); + + // click down arrow + await driver.waitForSelector({ + text: 'Signature Insights Example Snap', + tag: 'span', + }); + await driver.clickElement({ + text: 'Signature Insights Example Snap', + tag: 'span', + }); + + // look for returned signature insights data + await driver.waitForSelector({ + text: '0x4578616d706c652060706572736f6e616c5f7369676e60206d657373616765', + tag: 'p', + }); + + // click sign button + await driver.clickElementAndWaitForWindowToClose( + '[data-testid="confirm-footer-button"]', + ); + + // switch back to test-dapp window + await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); + + // check result of test + await driver.waitForSelector({ + text: '0xa10b6707dd79e2f1f91ba243ab7abe15a46f58b052ad9cec170c5366ef5667c447a87eba2c0a9d4c9fbfa0a23e9db1fb55865d0568c32bd7cc681b8d0860e7af1b', + tag: 'span', + }); + + // TEST TWO: sign typed data + // find and scroll to sign typed data and click sign + const signTypedButton1 = await driver.findElement('#signTypedData'); + await driver.scrollToElement(signTypedButton1); + await driver.clickElement('#signTypedData'); + + // switch back to MetaMask window and switch to tx insights pane + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); + + // wait for information from signature request screen + await driver.waitForSelector({ + text: 'Hi, Alice!', + tag: 'p', + }); + + // click down arrow + // await driver.waitForSelector('[aria-label="Scroll down"]'); + await driver.clickElementSafe('[aria-label="Scroll down"]'); + + // required: delay for scroll to render + await driver.delay(500); + + // click down arrow + await driver.waitForSelector({ + text: 'Signature Insights Example Snap', + tag: 'span', + }); + await driver.clickElement({ + text: 'Signature Insights Example Snap', + tag: 'span', + }); + + // look for returned signature insights data + await driver.waitForSelector({ + text: '1', + tag: 'p', + }); + + // click sign button + await driver.clickElementAndWaitForWindowToClose( + '[data-testid="confirm-footer-button"]', + ); + + // switch back to test-dapp window + await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); + + // check result of test + await driver.waitForSelector({ + text: '0x32791e3c41d40dd5bbfb42e66cf80ca354b0869ae503ad61cd19ba68e11d4f0d2e42a5835b0bfd633596b6a7834ef7d36033633a2479dacfdb96bda360d51f451b', + tag: 'span', + }); + + // TEST THREE: sign typed data v3 + // find and scroll to sign typed data v3 and click sign + const signTypedV3Button1 = await driver.findElement('#signTypedDataV3'); + await driver.scrollToElement(signTypedV3Button1); + await driver.clickElement('#signTypedDataV3'); + + // switch back to MetaMask window and switch to tx insights pane + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); + + // click down arrow + await driver.clickElementSafe('[aria-label="Scroll down"]'); + + // required: delay for scroll to render + await driver.delay(500); + + // wait for information from signature request screen + await driver.waitForSelector({ + text: 'Hello, Bob!', + tag: 'p', + }); + + // click down arrow + await driver.clickElementSafe('[aria-label="Scroll down"]'); + + // required: delay for scroll to render + await driver.delay(500); + + // click signature insights + await driver.waitForSelector({ + text: 'Signature Insights Example Snap', + tag: 'span', + }); + await driver.clickElement({ + text: 'Signature Insights Example Snap', + tag: 'span', + }); + + // look for returned signature insights data + await driver.waitForSelector({ + text: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC has been identified as a malicious verifying contract.', + tag: 'p', + }); + + // click sign button + await driver.clickElementAndWaitForWindowToClose( + '[data-testid="confirm-footer-button"]', + ); + + // switch back to test-dapp window + await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); + + // check result of test + await driver.waitForSelector({ + text: '0x0a22f7796a2a70c8dc918e7e6eb8452c8f2999d1a1eb5ad714473d36270a40d6724472e5609948c778a07216bd082b60b6f6853d6354c731fd8ccdd3a2f4af261b', + tag: 'span', + }); + + // TEST FOUR: sign typed data v4 + // find and scroll to sign typed data v4 and click sign + const signTypedV4Button1 = await driver.findElement('#signTypedDataV4'); + await driver.scrollToElement(signTypedV4Button1); + await driver.clickElement('#signTypedDataV4'); + + // switch back to MetaMask window and switch to tx insights pane + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); + + // click down arrow + await driver.clickElementSafe('[aria-label="Scroll down"]'); + + // required: delay for scroll to render + await driver.delay(500); + + // wait for information from signature request screen + await driver.waitForSelector({ + text: 'Hello, Bob!', + tag: 'p', + }); + + // click down arrow + await driver.clickElementSafe('[aria-label="Scroll down"]'); + + // required: delay for scroll to render + await driver.delay(500); + + // click signature insights + await driver.waitForSelector({ + text: 'Signature Insights Example Snap', + tag: 'span', + }); + await driver.clickElement({ + text: 'Signature Insights Example Snap', + tag: 'span', + }); + + // look for returned signature insights data + await driver.waitForSelector({ + text: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC has been identified as a malicious verifying contract.', + tag: 'p', + }); + + // click sign button + await driver.clickElementAndWaitForWindowToClose( + '[data-testid="confirm-footer-button"]', + ); + + // switch back to test-dapp window + await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); + + // check results of test + await driver.waitForSelector({ + text: '0xcd2f9c55840f5e1bcf61812e93c1932485b524ca673b36355482a4fbdf52f692684f92b4f4ab6f6c8572dacce46bd107da154be1c06939b855ecce57a1616ba71b', + tag: 'span', + }); + }, + ); + }); + + it('tests Signature Insights functionality (Legacy)', async function () { + await withFixtures( + { + dapp: true, + fixtures: new FixtureBuilder() + .withPermissionControllerConnectedToTestDapp() + .build(), + ganacheOptions: defaultGanacheOptions, + failOnConsoleError: false, + title: this.test.fullTitle(), + }, + async ({ driver }) => { + await unlockWallet(driver); + await tempToggleSettingRedesignedConfirmations(driver); + + // navigate to test snaps page and connect + await driver.openNewPage(TEST_SNAPS_WEBSITE_URL); + await driver.delay(1000); + + // find and scroll to the transaction-insights test and connect + const snapButton1 = await driver.findElement( + '#connectsignature-insights', + ); + await driver.scrollToElement(snapButton1); + await driver.delay(1000); + await driver.clickElement('#connectsignature-insights'); + + // switch to metamask extension and click connect + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); + await driver.clickElement({ + text: 'Connect', + tag: 'button', + }); + + await driver.waitForSelector({ text: 'Confirm' }); + + await driver.clickElement({ + text: 'Confirm', + tag: 'button', + }); + + await driver.waitForSelector({ text: 'OK' }); + + await driver.clickElementAndWaitForWindowToClose({ + text: 'OK', + tag: 'button', + }); + + // switch to test-snaps page + await driver.switchToWindowWithTitle(WINDOW_TITLES.TestSnaps); + + // open the test-dapp page + await openDapp(driver); // TEST ONE: personal sign // find and scroll to personal sign and click sign @@ -75,7 +339,7 @@ describe('Test Snap Signature Insights', function () { await driver.clickElement('#personalSign'); // switch back to MetaMask window and switch to tx insights pane - await switchToNotificationWindow(driver, 4); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); // wait for and click sign await clickSignOnSignatureConfirmation({ @@ -93,7 +357,9 @@ describe('Test Snap Signature Insights', function () { await driver.clickElement('.mm-checkbox__input-wrapper'); // click sign button - await driver.clickElement('[data-testid="snapInsightsButtonConfirm"]'); + await driver.clickElementAndWaitForWindowToClose( + '[data-testid="snapInsightsButtonConfirm"]', + ); // switch back to test-dapp window await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); @@ -111,7 +377,7 @@ describe('Test Snap Signature Insights', function () { await driver.clickElement('#signTypedData'); // switch back to MetaMask window and switch to tx insights pane - await switchToNotificationWindow(driver, 4); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); // wait for and click sign await clickSignOnSignatureConfirmation({ @@ -129,7 +395,9 @@ describe('Test Snap Signature Insights', function () { await driver.clickElement('.mm-checkbox__input-wrapper'); // click sign button - await driver.clickElement('[data-testid="snapInsightsButtonConfirm"]'); + await driver.clickElementAndWaitForWindowToClose( + '[data-testid="snapInsightsButtonConfirm"]', + ); // switch back to test-dapp window await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); @@ -147,7 +415,7 @@ describe('Test Snap Signature Insights', function () { await driver.clickElement('#signTypedDataV3'); // switch back to MetaMask window and switch to tx insights pane - await switchToNotificationWindow(driver, 4); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); // click down arrow await driver.waitForSelector('.fa-arrow-down'); @@ -169,7 +437,9 @@ describe('Test Snap Signature Insights', function () { await driver.clickElement('.mm-checkbox__input-wrapper'); // click sign button - await driver.clickElement('[data-testid="snapInsightsButtonConfirm"]'); + await driver.clickElementAndWaitForWindowToClose( + '[data-testid="snapInsightsButtonConfirm"]', + ); // switch back to test-dapp window await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); @@ -187,7 +457,7 @@ describe('Test Snap Signature Insights', function () { await driver.clickElement('#signTypedDataV4'); // switch back to MetaMask window and switch to tx insights pane - await switchToNotificationWindow(driver, 4); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); // click down arrow await driver.waitForSelector('.fa-arrow-down'); @@ -209,7 +479,9 @@ describe('Test Snap Signature Insights', function () { await driver.clickElement('.mm-checkbox__input-wrapper'); // click sign button - await driver.clickElement('[data-testid="snapInsightsButtonConfirm"]'); + await driver.clickElementAndWaitForWindowToClose( + '[data-testid="snapInsightsButtonConfirm"]', + ); // switch back to test-dapp window await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); diff --git a/test/e2e/snaps/test-snap-ui-imgs.spec.js b/test/e2e/snaps/test-snap-ui-imgs.spec.js index aac75d719aad..4d8f17ad852a 100644 --- a/test/e2e/snaps/test-snap-ui-imgs.spec.js +++ b/test/e2e/snaps/test-snap-ui-imgs.spec.js @@ -52,7 +52,7 @@ describe('Test Snap Images', function () { // deal with OK button await driver.waitForSelector({ text: 'OK' }); - await driver.clickElement({ + await driver.clickElementAndWaitForWindowToClose({ text: 'OK', tag: 'button', }); @@ -70,13 +70,15 @@ describe('Test Snap Images', function () { await driver.clickElement('#showSVGImage'); // switch to notification window - await switchToNotificationWindow(driver, 2); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); // check snaps ui image using waitForSelector await driver.waitForSelector('[data-testid="snaps-ui-image"]'); // click ok to close window - await driver.clickElement('[data-testid="confirmation-submit-button"]'); + await driver.clickElementAndWaitForWindowToClose( + '[data-testid="confirmation-submit-button"]', + ); // switch back to test-snaps window await driver.switchToWindowWithTitle(WINDOW_TITLES.TestSnaps); @@ -85,7 +87,7 @@ describe('Test Snap Images', function () { await driver.clickElement('#showPNGImage'); // switch to notification window - await switchToNotificationWindow(driver, 2); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); // check snaps ui image using waitForSelector await driver.waitForSelector('[data-testid="snaps-ui-image"]'); diff --git a/test/e2e/tests/account/import-flow.spec.js b/test/e2e/tests/account/import-flow.spec.js index cd2c14d9e73d..3c05131090cc 100644 --- a/test/e2e/tests/account/import-flow.spec.js +++ b/test/e2e/tests/account/import-flow.spec.js @@ -61,7 +61,7 @@ describe('Import flow @no-mmi', function () { await driver.clickElement( '[data-testid="account-list-item-menu-button"]', ); - await driver.clickElement('[data-testid="account-list-menu-details"'); + await driver.clickElement('[data-testid="account-list-menu-details"]'); await driver.findVisibleElement('.qr-code__wrapper'); // shows a QR code for the account @@ -187,11 +187,21 @@ describe('Import flow @no-mmi', function () { ); await driver.clickElement('[data-testid="account-list-menu-details"'); await driver.findVisibleElement('.qr-code__wrapper'); - // shows the correct account address - await driver.findElement({ - css: '.qr-code [data-testid="address-copy-button-text"]', - text: testAddress, - }); + + // Extract address segments from the DOM + const outerSegment = await driver.findElement( + '.qr-code__address-segments', + ); + + // Get the text content of each segment + const displayedAddress = await outerSegment.getText(); + + // Assert that the displayed address matches the testAddress + assert.strictEqual( + displayedAddress.toLowerCase(), + testAddress.toLowerCase(), + 'The displayed address does not match the test address', + ); }, ); }); diff --git a/test/e2e/tests/account/incremental-security.spec.js b/test/e2e/tests/account/incremental-security.spec.js index bdbde3b26101..f16fb5350b91 100644 --- a/test/e2e/tests/account/incremental-security.spec.js +++ b/test/e2e/tests/account/incremental-security.spec.js @@ -73,11 +73,10 @@ describe('Incremental Security', function () { ); await driver.clickElement('[data-testid="account-list-menu-details"'); - // gets the current accounts address - const address = await driver.findElement( - '.qr-code .multichain-address-copy-button', + const outerSegment = await driver.findElement( + '.qr-code__address-segments', ); - const publicAddress = await address.getText(); + const publicAddress = await outerSegment.getText(); // wait for account modal to be visible await driver.findVisibleElement( diff --git a/test/e2e/tests/account/metamask-responsive-ui.spec.js b/test/e2e/tests/account/metamask-responsive-ui.spec.js index b13390288b16..d9e9aa2f965c 100644 --- a/test/e2e/tests/account/metamask-responsive-ui.spec.js +++ b/test/e2e/tests/account/metamask-responsive-ui.spec.js @@ -11,7 +11,7 @@ const FixtureBuilder = require('../../fixture-builder'); describe('MetaMask Responsive UI', function () { it('Creating a new wallet @no-mmi', async function () { - const driverOptions = { openDevToolsForTabs: true }; + const driverOptions = { responsive: true }; await withFixtures( { @@ -81,7 +81,7 @@ describe('MetaMask Responsive UI', function () { }); it('Importing existing wallet from lock page', async function () { - const driverOptions = { openDevToolsForTabs: true }; + const driverOptions = { responsive: true }; await withFixtures( { @@ -115,7 +115,7 @@ describe('MetaMask Responsive UI', function () { }); it('Send Transaction from responsive window', async function () { - const driverOptions = { openDevToolsForTabs: true }; + const driverOptions = { responsive: true }; await withFixtures( { fixtures: new FixtureBuilder().build(), diff --git a/test/e2e/tests/api-usage/account-tracker-api-usage.spec.ts b/test/e2e/tests/api-usage/account-tracker-api-usage.spec.ts index 9c534c1a768a..f0cd40cb7373 100644 --- a/test/e2e/tests/api-usage/account-tracker-api-usage.spec.ts +++ b/test/e2e/tests/api-usage/account-tracker-api-usage.spec.ts @@ -1,14 +1,13 @@ import { strict as assert } from 'assert'; -import { MockedEndpoint } from 'mockttp'; import { JsonRpcRequest } from '@metamask/utils'; +import { MockedEndpoint } from 'mockttp'; +import FixtureBuilder from '../../fixture-builder'; import { - withFixtures, defaultGanacheOptions, unlockWallet, veryLargeDelayMs, + withFixtures, } from '../../helpers'; -import FixtureBuilder from '../../fixture-builder'; -import { Driver } from '../../webdriver/driver'; import { Mockttp } from '../../mock-e2e'; async function mockInfura(mockServer: Mockttp): Promise { @@ -106,13 +105,7 @@ describe('Account Tracker API Usage', function () { title: this.test?.fullTitle(), testSpecificMock: mockInfura, }, - async ({ - driver, - mockedEndpoint, - }: { - driver: Driver; - mockedEndpoint: MockedEndpoint[]; - }) => { + async ({ driver, mockedEndpoint }) => { await driver.delay(veryLargeDelayMs); let allInfuraJsonRpcRequests = await getAllInfuraJsonRpcRequests( mockedEndpoint, @@ -164,13 +157,7 @@ describe('Account Tracker API Usage', function () { title: this.test?.fullTitle(), testSpecificMock: mockInfura, }, - async ({ - driver, - mockedEndpoint, - }: { - driver: Driver; - mockedEndpoint: MockedEndpoint[]; - }) => { + async ({ driver, mockedEndpoint }) => { await unlockWallet(driver); await driver.delay(veryLargeDelayMs); const initialInfuraJsonRpcRequests = await getAllInfuraJsonRpcRequests( diff --git a/test/e2e/tests/bridge/bridge-button-opens-portfolio.spec.ts b/test/e2e/tests/bridge/bridge-button-opens-portfolio.spec.ts index 3c3045326b8a..fc79bcaa3e4c 100644 --- a/test/e2e/tests/bridge/bridge-button-opens-portfolio.spec.ts +++ b/test/e2e/tests/bridge/bridge-button-opens-portfolio.spec.ts @@ -1,21 +1,12 @@ import { Suite } from 'mocha'; -import { withFixtures, logInWithBalanceValidation } from '../../helpers'; -import { Ganache } from '../../seeder/ganache'; -import { Driver } from '../../webdriver/driver'; -import GanacheContractAddressRegistry from '../../seeder/ganache-contract-address-registry'; +import { logInWithBalanceValidation, withFixtures } from '../../helpers'; import { BridgePage, getBridgeFixtures } from './bridge-test-utils'; describe('Click bridge button @no-mmi', function (this: Suite) { it('loads portfolio tab from wallet overview when flag is turned off', async function () { await withFixtures( getBridgeFixtures(this.test?.fullTitle()), - async ({ - driver, - ganacheServer, - }: { - driver: Driver; - ganacheServer: Ganache; - }) => { + async ({ driver, ganacheServer }) => { const bridgePage = new BridgePage(driver); await logInWithBalanceValidation(driver, ganacheServer); await bridgePage.navigateToBridgePage(); @@ -26,29 +17,20 @@ describe('Click bridge button @no-mmi', function (this: Suite) { it('loads portfolio tab from asset overview when flag is turned off', async function () { await withFixtures( - // withErc20 param is false, as we test it manually below - getBridgeFixtures(this.test?.fullTitle(), undefined, false), - async ({ - driver, - ganacheServer, - contractRegistry, - }: { - driver: Driver; - ganacheServer: Ganache; - contractRegistry: GanacheContractAddressRegistry; - }) => { + getBridgeFixtures(this.test?.fullTitle(), undefined, true), + async ({ driver, ganacheServer }) => { const bridgePage = new BridgePage(driver); await logInWithBalanceValidation(driver, ganacheServer); // ETH - await bridgePage.navigateToAssetPage(contractRegistry, 'ETH', false); + await bridgePage.navigateToAssetPage('ETH'); await bridgePage.navigateToBridgePage('coin-overview'); await bridgePage.verifyPortfolioTab(2); await bridgePage.reloadHome(); // TST - await bridgePage.navigateToAssetPage(contractRegistry, 'TST'); + await bridgePage.navigateToAssetPage('TST'); await bridgePage.navigateToBridgePage('token-overview'); await bridgePage.verifyPortfolioTab(3); }, diff --git a/test/e2e/tests/bridge/bridge-button-routing.spec.ts b/test/e2e/tests/bridge/bridge-button-routing.spec.ts index 099db43d660e..a3ecb70a2967 100644 --- a/test/e2e/tests/bridge/bridge-button-routing.spec.ts +++ b/test/e2e/tests/bridge/bridge-button-routing.spec.ts @@ -1,20 +1,12 @@ import { Suite } from 'mocha'; -import { withFixtures, logInWithBalanceValidation } from '../../helpers'; -import { Ganache } from '../../seeder/ganache'; -import { Driver } from '../../webdriver/driver'; +import { logInWithBalanceValidation, withFixtures } from '../../helpers'; import { BridgePage, getBridgeFixtures } from './bridge-test-utils'; describe('Click bridge button @no-mmi', function (this: Suite) { it('loads placeholder swap route from wallet overview when flag is turned on', async function () { await withFixtures( getBridgeFixtures(this.test?.fullTitle(), { 'extension-support': true }), - async ({ - driver, - ganacheServer, - }: { - driver: Driver; - ganacheServer: Ganache; - }) => { + async ({ driver, ganacheServer }) => { const bridgePage = new BridgePage(driver); await logInWithBalanceValidation(driver, ganacheServer); await bridgePage.navigateToBridgePage(); diff --git a/test/e2e/tests/bridge/bridge-test-utils.ts b/test/e2e/tests/bridge/bridge-test-utils.ts index c6566dab3981..596c7623208d 100644 --- a/test/e2e/tests/bridge/bridge-test-utils.ts +++ b/test/e2e/tests/bridge/bridge-test-utils.ts @@ -2,7 +2,7 @@ import { strict as assert } from 'assert'; import { Mockttp } from 'mockttp'; import { Browser } from 'selenium-webdriver'; import FixtureBuilder from '../../fixture-builder'; -import { clickNestedButton, generateGanacheOptions } from '../../helpers'; +import { generateGanacheOptions } from '../../helpers'; import { BRIDGE_CLIENT_ID, BRIDGE_DEV_API_BASE_URL, @@ -10,14 +10,12 @@ import { } from '../../../../shared/constants/bridge'; import { SMART_CONTRACTS } from '../../seeder/smart-contracts'; import { CHAIN_IDS } from '../../../../shared/constants/network'; -import GanacheContractAddressRegistry from '../../seeder/ganache-contract-address-registry'; import { Driver } from '../../webdriver/driver'; import { FeatureFlagResponse } from '../../../../ui/pages/bridge/bridge.util'; import { isManifestV3 } from '../../../../shared/modules/mv3.utils'; import { DEFAULT_FEATURE_FLAGS_RESPONSE, ETH_CONVERSION_RATE_USD, - LOCATOR, MOCK_CURRENCY_RATES, } from './constants'; @@ -59,43 +57,12 @@ export class BridgePage { ); }; - navigateToAssetPage = async ( - contractRegistry: GanacheContractAddressRegistry, - symbol: string, - shouldImportToken = true, - ) => { - if (shouldImportToken) { - // Import token - const contractAddress = await contractRegistry.getContractAddress( - SMART_CONTRACTS.HST, - ); - await clickNestedButton(this.driver, 'Import tokens'); - await clickNestedButton(this.driver, 'Custom token'); - await this.driver.fill( - LOCATOR.MM_IMPORT_TOKENS_MODAL('custom-address'), - contractAddress, - ); - await this.driver.fill( - LOCATOR.MM_IMPORT_TOKENS_MODAL('custom-symbol'), - symbol, - ); - await this.driver.waitForSelector( - LOCATOR.MM_IMPORT_TOKENS_MODAL('custom-decimals'), - ); - await clickNestedButton(this.driver, 'Next'); - await this.driver.clickElement( - LOCATOR.MM_IMPORT_TOKENS_MODAL('import-button'), - ); - await this.driver.assertElementNotPresent( - '[data-testid="import-tokens-modal-import-button"]', - ); - await this.driver.clickElement({ text: symbol }); - } else { - await this.driver.clickElement({ - css: '[data-testid="multichain-token-list-button"]', - text: symbol, - }); - } + navigateToAssetPage = async (symbol: string) => { + await this.driver.clickElement({ + css: '[data-testid="multichain-token-list-button"]', + text: symbol, + }); + await this.driver.delay(2000); assert.ok((await this.driver.getCurrentUrl()).includes('asset')); }; @@ -165,10 +132,11 @@ export const getBridgeFixtures = ( inputChainId: CHAIN_IDS.MAINNET, }) .withNetworkControllerOnMainnet() - .withCurrencyController(MOCK_CURRENCY_RATES); + .withCurrencyController(MOCK_CURRENCY_RATES) + .withBridgeControllerDefaultState(); if (withErc20) { - fixtureBuilder.withTokensControllerERC20(); + fixtureBuilder.withTokensControllerERC20({ chainId: 1 }); } return { diff --git a/test/e2e/tests/bridge/constants.ts b/test/e2e/tests/bridge/constants.ts index f5d90afcea0e..e79a1dfe4553 100644 --- a/test/e2e/tests/bridge/constants.ts +++ b/test/e2e/tests/bridge/constants.ts @@ -2,6 +2,8 @@ import { FeatureFlagResponse } from '../../../../ui/pages/bridge/bridge.util'; export const DEFAULT_FEATURE_FLAGS_RESPONSE: FeatureFlagResponse = { 'extension-support': false, + 'src-network-allowlist': [1, 42161, 59144], + 'dest-network-allowlist': [1, 42161, 59144], }; export const LOCATOR = { diff --git a/test/e2e/tests/confirmations/alerts/insufficient-funds.spec.ts b/test/e2e/tests/confirmations/alerts/insufficient-funds.spec.ts index e600ed920c9b..9c74714c82e6 100644 --- a/test/e2e/tests/confirmations/alerts/insufficient-funds.spec.ts +++ b/test/e2e/tests/confirmations/alerts/insufficient-funds.spec.ts @@ -74,6 +74,7 @@ async function verifyAlertForInsufficientBalance(driver: Driver) { alertDescriptionText, 'You do not have enough ETH in your account to pay for transaction fees.', ); + await driver.clickElement('[data-testid="alert-modal-close-button"]'); } async function mintNft(driver: Driver) { diff --git a/test/e2e/tests/confirmations/navigation.spec.ts b/test/e2e/tests/confirmations/navigation.spec.ts index b5fa59a2f134..f6347981ce15 100644 --- a/test/e2e/tests/confirmations/navigation.spec.ts +++ b/test/e2e/tests/confirmations/navigation.spec.ts @@ -151,24 +151,22 @@ async function verifySignedTypeV4Confirmation(driver: Driver) { } async function queueSignatures(driver: Driver) { + // There is a race condition which changes the order in which signatures are displayed (#25251) + // We fix it deterministically by waiting for an element in the screen for each signature await driver.clickElement('#signTypedData'); await driver.waitUntilXWindowHandles(3); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); + await driver.findElement({ text: 'Hi, Alice!' }); await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); - await driver.delay(2000); // Delay needed due to a race condition - // To be fixed in https://github.com/MetaMask/metamask-extension/issues/25251 - await driver.clickElement('#signTypedDataV3'); await driver.waitUntilXWindowHandles(3); await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - await driver.delay(2000); + await driver.findElement({ text: 'Reject all' }); - await driver.waitUntilXWindowHandles(3); await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); - await driver.clickElement('#signTypedDataV4'); await driver.waitUntilXWindowHandles(3); await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - await driver.delay(2000); } async function queueSignaturesAndTransactions(driver: Driver) { diff --git a/test/e2e/tests/confirmations/signatures/malicious-signatures.spec.ts b/test/e2e/tests/confirmations/signatures/malicious-signatures.spec.ts new file mode 100644 index 000000000000..a9a6e7c57b91 --- /dev/null +++ b/test/e2e/tests/confirmations/signatures/malicious-signatures.spec.ts @@ -0,0 +1,137 @@ +import { strict as assert } from 'assert'; +import { Suite } from 'mocha'; +import { MockedEndpoint } from 'mockttp'; +import { WINDOW_TITLES } from '../../../helpers'; +import { Driver } from '../../../webdriver/driver'; +import { + scrollAndConfirmAndAssertConfirm, + withRedesignConfirmationFixtures, +} from '../helpers'; +import { TestSuiteArguments } from '../transactions/shared'; +import { + assertSignatureRejectedMetrics, + openDappAndTriggerSignature, + SignatureType, +} from './signature-helpers'; + +describe('Malicious Confirmation Signature - Bad Domain @no-mmi', function (this: Suite) { + it('displays alert for domain binding and confirms', async function () { + await withRedesignConfirmationFixtures( + this.test?.fullTitle(), + async ({ driver }: TestSuiteArguments) => { + await openDappAndTriggerSignature(driver, SignatureType.SIWE_BadDomain); + + await verifyAlertIsDisplayed(driver); + + await acknowledgeAlert(driver); + + await scrollAndConfirmAndAssertConfirm(driver); + + await confirmFromAlertModal(driver); + + await assertVerifiedMessage( + driver, + '0x24e559452c37827008633f9ae50c68cdb28e33f547f795af687839b520b022e4093c38bf1dfebda875ded715f2754d458ed62a19248e5a9bd2205bd1cb66f9b51b', + ); + }, + ); + }); + + it('initiates and rejects from confirmation screen', async function () { + await withRedesignConfirmationFixtures( + this.test?.fullTitle(), + async ({ + driver, + mockedEndpoint: mockedEndpoints, + }: TestSuiteArguments) => { + await openDappAndTriggerSignature(driver, SignatureType.SIWE_BadDomain); + + await driver.clickElement( + '[data-testid="confirm-footer-cancel-button"]', + ); + + await driver.waitUntilXWindowHandles(2); + await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); + + const rejectionResult = await driver.waitForSelector({ + css: '#siweResult', + text: 'Error: User rejected the request.', + }); + assert.ok(rejectionResult); + await assertSignatureRejectedMetrics({ + driver, + mockedEndpoints: mockedEndpoints as MockedEndpoint[], + signatureType: 'personal_sign', + uiCustomizations: [ + 'redesigned_confirmation', + 'sign_in_with_ethereum', + ], + location: 'confirmation', + }); + }, + ); + }); + + it('initiates and rejects from alert friction modal', async function () { + await withRedesignConfirmationFixtures( + this.test?.fullTitle(), + async ({ + driver, + mockedEndpoint: mockedEndpoints, + }: TestSuiteArguments) => { + await openDappAndTriggerSignature(driver, SignatureType.SIWE_BadDomain); + + await scrollAndConfirmAndAssertConfirm(driver); + + await driver.clickElement( + '[data-testid="confirm-alert-modal-cancel-button"]', + ); + await driver.waitUntilXWindowHandles(2); + await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); + + const rejectionResult = await driver.waitForSelector({ + css: '#siweResult', + text: 'Error: User rejected the request.', + }); + assert.ok(rejectionResult); + await assertSignatureRejectedMetrics({ + driver, + mockedEndpoints: mockedEndpoints as MockedEndpoint[], + signatureType: 'personal_sign', + uiCustomizations: [ + 'redesigned_confirmation', + 'sign_in_with_ethereum', + ], + location: 'alert_friction_modal', + }); + }, + ); + }); +}); + +async function confirmFromAlertModal(driver: Driver) { + await driver.clickElement('[data-testid="alert-modal-acknowledge-checkbox"]'); + await driver.clickElement( + '[data-testid="confirm-alert-modal-submit-button"]', + ); +} + +async function acknowledgeAlert(driver: Driver) { + await driver.clickElement('[data-testid="alert-modal-acknowledge-checkbox"]'); + await driver.clickElement('[data-testid="alert-modal-button"]'); +} + +async function verifyAlertIsDisplayed(driver: Driver) { + await driver.clickElementSafe('.confirm-scroll-to-bottom__button'); + const alert = await driver.findElement('[data-testid="inline-alert"]'); + assert.equal(await alert.getText(), 'Alert'); + await driver.clickElement('[data-testid="inline-alert"]'); +} + +async function assertVerifiedMessage(driver: Driver, message: string) { + await driver.waitUntilXWindowHandles(2); + await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); + + const verifySigUtil = await driver.findElement('#siweResult'); + assert.equal(await verifySigUtil.getText(), message); +} diff --git a/test/e2e/tests/confirmations/signatures/permit.spec.ts b/test/e2e/tests/confirmations/signatures/permit.spec.ts index 6dadf732c246..54b622d62198 100644 --- a/test/e2e/tests/confirmations/signatures/permit.spec.ts +++ b/test/e2e/tests/confirmations/signatures/permit.spec.ts @@ -5,7 +5,6 @@ import { DAPP_HOST_ADDRESS, WINDOW_TITLES, openDapp, - switchToNotificationWindow, unlockWallet, } from '../../../helpers'; import { Ganache } from '../../../seeder/ganache'; @@ -19,9 +18,12 @@ import { assertAccountDetailsMetrics, assertHeaderInfoBalance, assertPastedAddress, - assertSignatureMetrics, + assertSignatureConfirmedMetrics, + assertSignatureRejectedMetrics, clickHeaderInfoBtn, copyAddressAndPasteWalletAddress, + openDappAndTriggerSignature, + SignatureType, } from './signature-helpers'; describe('Confirmation Signature - Permit @no-mmi', function (this: Suite) { @@ -36,10 +38,7 @@ describe('Confirmation Signature - Permit @no-mmi', function (this: Suite) { const addresses = await (ganacheServer as Ganache).getAccounts(); const publicAddress = addresses?.[0] as string; - await unlockWallet(driver); - await openDapp(driver); - await driver.clickElement('#signPermit'); - await switchToNotificationWindow(driver); + await openDappAndTriggerSignature(driver, SignatureType.Permit); await clickHeaderInfoBtn(driver); await assertHeaderInfoBalance(driver); @@ -51,19 +50,19 @@ describe('Confirmation Signature - Permit @no-mmi', function (this: Suite) { await copyAddressAndPasteWalletAddress(driver); await assertPastedAddress(driver); - await switchToNotificationWindow(driver); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); await assertInfoValues(driver); await scrollAndConfirmAndAssertConfirm(driver); await driver.delay(1000); - await assertSignatureMetrics( + await assertSignatureConfirmedMetrics({ driver, - mockedEndpoints as MockedEndpoint[], - 'eth_signTypedData_v4', - 'Permit', - ['redesigned_confirmation', 'permit'], - ); + mockedEndpoints: mockedEndpoints as MockedEndpoint[], + signatureType: 'eth_signTypedData_v4', + primaryType: 'Permit', + uiCustomizations: ['redesigned_confirmation', 'permit'], + }); await assertVerifiedResults(driver, publicAddress); }, @@ -80,14 +79,12 @@ describe('Confirmation Signature - Permit @no-mmi', function (this: Suite) { await unlockWallet(driver); await openDapp(driver); await driver.clickElement('#signPermit'); - await switchToNotificationWindow(driver); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - await driver.clickElement( + await driver.clickElementAndWaitForWindowToClose( '[data-testid="confirm-footer-cancel-button"]', ); - await driver.delay(1000); - await driver.waitUntilXWindowHandles(2); await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); const rejectionResult = await driver.findElement('#signPermitResult'); @@ -96,13 +93,14 @@ describe('Confirmation Signature - Permit @no-mmi', function (this: Suite) { 'Error: User rejected the request.', ); - await assertSignatureMetrics( + await assertSignatureRejectedMetrics({ driver, - mockedEndpoints as MockedEndpoint[], - 'eth_signTypedData_v4', - 'Permit', - ['redesigned_confirmation', 'permit'], - ); + mockedEndpoints: mockedEndpoints as MockedEndpoint[], + signatureType: 'eth_signTypedData_v4', + primaryType: 'Permit', + uiCustomizations: ['redesigned_confirmation', 'permit'], + location: 'confirmation', + }); }, ); }); diff --git a/test/e2e/tests/confirmations/signatures/personal-sign.spec.ts b/test/e2e/tests/confirmations/signatures/personal-sign.spec.ts index fed1e097d76c..a6a1f43af675 100644 --- a/test/e2e/tests/confirmations/signatures/personal-sign.spec.ts +++ b/test/e2e/tests/confirmations/signatures/personal-sign.spec.ts @@ -1,13 +1,7 @@ import { strict as assert } from 'assert'; import { Suite } from 'mocha'; import { MockedEndpoint } from 'mockttp'; -import { - DAPP_HOST_ADDRESS, - WINDOW_TITLES, - openDapp, - switchToNotificationWindow, - unlockWallet, -} from '../../../helpers'; +import { DAPP_HOST_ADDRESS, WINDOW_TITLES } from '../../../helpers'; import { Ganache } from '../../../seeder/ganache'; import { Driver } from '../../../webdriver/driver'; import { withRedesignConfirmationFixtures } from '../helpers'; @@ -16,9 +10,12 @@ import { assertAccountDetailsMetrics, assertHeaderInfoBalance, assertPastedAddress, - assertSignatureMetrics, + assertSignatureConfirmedMetrics, + assertSignatureRejectedMetrics, clickHeaderInfoBtn, copyAddressAndPasteWalletAddress, + openDappAndTriggerSignature, + SignatureType, } from './signature-helpers'; describe('Confirmation Signature - Personal Sign @no-mmi', function (this: Suite) { @@ -33,10 +30,7 @@ describe('Confirmation Signature - Personal Sign @no-mmi', function (this: Suite const addresses = await (ganacheServer as Ganache).getAccounts(); const publicAddress = addresses?.[0] as string; - await unlockWallet(driver); - await openDapp(driver); - await driver.clickElement('#personalSign'); - await switchToNotificationWindow(driver); + await openDappAndTriggerSignature(driver, SignatureType.PersonalSign); await clickHeaderInfoBtn(driver); await assertHeaderInfoBalance(driver); @@ -48,17 +42,17 @@ describe('Confirmation Signature - Personal Sign @no-mmi', function (this: Suite mockedEndpoints as MockedEndpoint[], 'personal_sign', ); - await switchToNotificationWindow(driver); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); await assertInfoValues(driver); await driver.clickElement('[data-testid="confirm-footer-button"]'); await assertVerifiedPersonalMessage(driver, publicAddress); - await assertSignatureMetrics( + await assertSignatureConfirmedMetrics({ driver, - mockedEndpoints as MockedEndpoint[], - 'personal_sign', - ); + mockedEndpoints: mockedEndpoints as MockedEndpoint[], + signatureType: 'personal_sign', + }); }, ); }); @@ -70,10 +64,7 @@ describe('Confirmation Signature - Personal Sign @no-mmi', function (this: Suite driver, mockedEndpoint: mockedEndpoints, }: TestSuiteArguments) => { - await unlockWallet(driver); - await openDapp(driver); - await driver.clickElement('#personalSign'); - await switchToNotificationWindow(driver); + await openDappAndTriggerSignature(driver, SignatureType.PersonalSign); await driver.clickElement( '[data-testid="confirm-footer-cancel-button"]', @@ -87,11 +78,12 @@ describe('Confirmation Signature - Personal Sign @no-mmi', function (this: Suite text: 'Error: User rejected the request.', }); assert.ok(rejectionResult); - await assertSignatureMetrics( + await assertSignatureRejectedMetrics({ driver, - mockedEndpoints as MockedEndpoint[], - 'personal_sign', - ); + mockedEndpoints: mockedEndpoints as MockedEndpoint[], + signatureType: 'personal_sign', + location: 'confirmation', + }); }, ); }); diff --git a/test/e2e/tests/confirmations/signatures/sign-typed-data-v3.spec.ts b/test/e2e/tests/confirmations/signatures/sign-typed-data-v3.spec.ts index 097acfbbb2d4..2512bdbeeccf 100644 --- a/test/e2e/tests/confirmations/signatures/sign-typed-data-v3.spec.ts +++ b/test/e2e/tests/confirmations/signatures/sign-typed-data-v3.spec.ts @@ -1,13 +1,7 @@ import { strict as assert } from 'assert'; import { Suite } from 'mocha'; import { MockedEndpoint } from 'mockttp'; -import { - DAPP_HOST_ADDRESS, - WINDOW_TITLES, - openDapp, - switchToNotificationWindow, - unlockWallet, -} from '../../../helpers'; +import { DAPP_HOST_ADDRESS, WINDOW_TITLES } from '../../../helpers'; import { Ganache } from '../../../seeder/ganache'; import { Driver } from '../../../webdriver/driver'; import { @@ -19,9 +13,12 @@ import { assertAccountDetailsMetrics, assertHeaderInfoBalance, assertPastedAddress, - assertSignatureMetrics, + assertSignatureConfirmedMetrics, + assertSignatureRejectedMetrics, clickHeaderInfoBtn, copyAddressAndPasteWalletAddress, + openDappAndTriggerSignature, + SignatureType, } from './signature-helpers'; describe('Confirmation Signature - Sign Typed Data V3 @no-mmi', function (this: Suite) { @@ -36,10 +33,10 @@ describe('Confirmation Signature - Sign Typed Data V3 @no-mmi', function (this: const addresses = await (ganacheServer as Ganache).getAccounts(); const publicAddress = addresses?.[0] as string; - await unlockWallet(driver); - await openDapp(driver); - await driver.clickElement('#signTypedDataV3'); - await switchToNotificationWindow(driver); + await openDappAndTriggerSignature( + driver, + SignatureType.SignTypedDataV3, + ); await clickHeaderInfoBtn(driver); await assertHeaderInfoBalance(driver); @@ -51,16 +48,16 @@ describe('Confirmation Signature - Sign Typed Data V3 @no-mmi', function (this: mockedEndpoints as MockedEndpoint[], 'eth_signTypedData_v3', ); - await switchToNotificationWindow(driver); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); await assertInfoValues(driver); await scrollAndConfirmAndAssertConfirm(driver); await driver.delay(1000); - await assertSignatureMetrics( + await assertSignatureConfirmedMetrics({ driver, - mockedEndpoints as MockedEndpoint[], - 'eth_signTypedData_v3', - ); + mockedEndpoints: mockedEndpoints as MockedEndpoint[], + signatureType: 'eth_signTypedData_v3', + }); await assertVerifiedResults(driver, publicAddress); }, ); @@ -73,21 +70,22 @@ describe('Confirmation Signature - Sign Typed Data V3 @no-mmi', function (this: driver, mockedEndpoint: mockedEndpoints, }: TestSuiteArguments) => { - await unlockWallet(driver); - await openDapp(driver); - await driver.clickElement('#signTypedDataV3'); - await switchToNotificationWindow(driver); + await openDappAndTriggerSignature( + driver, + SignatureType.SignTypedDataV3, + ); await driver.clickElement( '[data-testid="confirm-footer-cancel-button"]', ); await driver.delay(1000); - await assertSignatureMetrics( + await assertSignatureRejectedMetrics({ driver, - mockedEndpoints as MockedEndpoint[], - 'eth_signTypedData_v3', - ); + mockedEndpoints: mockedEndpoints as MockedEndpoint[], + signatureType: 'eth_signTypedData_v3', + location: 'confirmation', + }); await driver.waitUntilXWindowHandles(2); await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); diff --git a/test/e2e/tests/confirmations/signatures/sign-typed-data-v4.spec.ts b/test/e2e/tests/confirmations/signatures/sign-typed-data-v4.spec.ts index d539e12afbdb..24ab428581a5 100644 --- a/test/e2e/tests/confirmations/signatures/sign-typed-data-v4.spec.ts +++ b/test/e2e/tests/confirmations/signatures/sign-typed-data-v4.spec.ts @@ -1,13 +1,7 @@ import { strict as assert } from 'assert'; import { Suite } from 'mocha'; import { MockedEndpoint } from 'mockttp'; -import { - DAPP_HOST_ADDRESS, - WINDOW_TITLES, - openDapp, - switchToNotificationWindow, - unlockWallet, -} from '../../../helpers'; +import { DAPP_HOST_ADDRESS, WINDOW_TITLES } from '../../../helpers'; import { Ganache } from '../../../seeder/ganache'; import { Driver } from '../../../webdriver/driver'; import { @@ -19,9 +13,12 @@ import { assertAccountDetailsMetrics, assertHeaderInfoBalance, assertPastedAddress, - assertSignatureMetrics, + assertSignatureConfirmedMetrics, + assertSignatureRejectedMetrics, clickHeaderInfoBtn, copyAddressAndPasteWalletAddress, + openDappAndTriggerSignature, + SignatureType, } from './signature-helpers'; describe('Confirmation Signature - Sign Typed Data V4 @no-mmi', function (this: Suite) { @@ -36,10 +33,10 @@ describe('Confirmation Signature - Sign Typed Data V4 @no-mmi', function (this: const addresses = await (ganacheServer as Ganache).getAccounts(); const publicAddress = addresses?.[0] as string; - await unlockWallet(driver); - await openDapp(driver); - await driver.clickElement('#signTypedDataV4'); - await switchToNotificationWindow(driver); + await openDappAndTriggerSignature( + driver, + SignatureType.SignTypedDataV4, + ); await clickHeaderInfoBtn(driver); await assertHeaderInfoBalance(driver); @@ -56,12 +53,13 @@ describe('Confirmation Signature - Sign Typed Data V4 @no-mmi', function (this: await scrollAndConfirmAndAssertConfirm(driver); await driver.delay(1000); - await assertSignatureMetrics( + await assertSignatureConfirmedMetrics({ driver, - mockedEndpoints as MockedEndpoint[], - 'eth_signTypedData_v4', - 'Mail', - ); + mockedEndpoints: mockedEndpoints as MockedEndpoint[], + signatureType: 'eth_signTypedData_v4', + primaryType: 'Mail', + }); + await assertVerifiedResults(driver, publicAddress); }, ); @@ -74,22 +72,23 @@ describe('Confirmation Signature - Sign Typed Data V4 @no-mmi', function (this: driver, mockedEndpoint: mockedEndpoints, }: TestSuiteArguments) => { - await unlockWallet(driver); - await openDapp(driver); - await driver.clickElement('#signTypedDataV4'); - await switchToNotificationWindow(driver); + await openDappAndTriggerSignature( + driver, + SignatureType.SignTypedDataV4, + ); await driver.clickElement( '[data-testid="confirm-footer-cancel-button"]', ); await driver.delay(1000); - await assertSignatureMetrics( + await assertSignatureRejectedMetrics({ driver, - mockedEndpoints as MockedEndpoint[], - 'eth_signTypedData_v4', - 'Mail', - ); + mockedEndpoints: mockedEndpoints as MockedEndpoint[], + signatureType: 'eth_signTypedData_v4', + primaryType: 'Mail', + location: 'confirmation', + }); await driver.waitUntilXWindowHandles(2); await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); diff --git a/test/e2e/tests/confirmations/signatures/sign-typed-data.spec.ts b/test/e2e/tests/confirmations/signatures/sign-typed-data.spec.ts index 2baae49c6690..ca7c2d503977 100644 --- a/test/e2e/tests/confirmations/signatures/sign-typed-data.spec.ts +++ b/test/e2e/tests/confirmations/signatures/sign-typed-data.spec.ts @@ -1,13 +1,7 @@ import { strict as assert } from 'assert'; import { Suite } from 'mocha'; import { MockedEndpoint } from 'mockttp'; -import { - DAPP_HOST_ADDRESS, - WINDOW_TITLES, - openDapp, - switchToNotificationWindow, - unlockWallet, -} from '../../../helpers'; +import { DAPP_HOST_ADDRESS, WINDOW_TITLES } from '../../../helpers'; import { Ganache } from '../../../seeder/ganache'; import { Driver } from '../../../webdriver/driver'; import { withRedesignConfirmationFixtures } from '../helpers'; @@ -16,9 +10,12 @@ import { assertAccountDetailsMetrics, assertHeaderInfoBalance, assertPastedAddress, - assertSignatureMetrics, + assertSignatureConfirmedMetrics, + assertSignatureRejectedMetrics, clickHeaderInfoBtn, copyAddressAndPasteWalletAddress, + openDappAndTriggerSignature, + SignatureType, } from './signature-helpers'; describe('Confirmation Signature - Sign Typed Data @no-mmi', function (this: Suite) { @@ -33,10 +30,7 @@ describe('Confirmation Signature - Sign Typed Data @no-mmi', function (this: Sui const addresses = await (ganacheServer as Ganache).getAccounts(); const publicAddress = addresses?.[0] as string; - await unlockWallet(driver); - await openDapp(driver); - await driver.clickElement('#signTypedData'); - await switchToNotificationWindow(driver); + await openDappAndTriggerSignature(driver, SignatureType.SignTypedData); await clickHeaderInfoBtn(driver); await assertHeaderInfoBalance(driver); @@ -54,11 +48,11 @@ describe('Confirmation Signature - Sign Typed Data @no-mmi', function (this: Sui await driver.clickElement('[data-testid="confirm-footer-button"]'); await driver.delay(1000); - await assertSignatureMetrics( + await assertSignatureConfirmedMetrics({ driver, - mockedEndpoints as MockedEndpoint[], - 'eth_signTypedData', - ); + mockedEndpoints: mockedEndpoints as MockedEndpoint[], + signatureType: 'eth_signTypedData', + }); await assertVerifiedResults(driver, publicAddress); }, @@ -72,21 +66,19 @@ describe('Confirmation Signature - Sign Typed Data @no-mmi', function (this: Sui driver, mockedEndpoint: mockedEndpoints, }: TestSuiteArguments) => { - await unlockWallet(driver); - await openDapp(driver); - await driver.clickElement('#signTypedData'); - await switchToNotificationWindow(driver); + await openDappAndTriggerSignature(driver, SignatureType.SignTypedData); await driver.clickElement( '[data-testid="confirm-footer-cancel-button"]', ); await driver.delay(1000); - await assertSignatureMetrics( + await assertSignatureRejectedMetrics({ driver, - mockedEndpoints as MockedEndpoint[], - 'eth_signTypedData', - ); + mockedEndpoints: mockedEndpoints as MockedEndpoint[], + signatureType: 'eth_signTypedData', + location: 'confirmation', + }); await driver.waitUntilXWindowHandles(2); await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); diff --git a/test/e2e/tests/confirmations/signatures/signature-helpers.ts b/test/e2e/tests/confirmations/signatures/signature-helpers.ts index c2f1cb02fd9b..041d1bfbf23a 100644 --- a/test/e2e/tests/confirmations/signatures/signature-helpers.ts +++ b/test/e2e/tests/confirmations/signatures/signature-helpers.ts @@ -1,24 +1,55 @@ import { strict as assert } from 'assert'; import { MockedEndpoint } from 'mockttp'; -import { WINDOW_TITLES, getEventPayloads } from '../../../helpers'; +import { + WINDOW_TITLES, + getEventPayloads, + openDapp, + unlockWallet, +} from '../../../helpers'; import { Driver } from '../../../webdriver/driver'; export const WALLET_ADDRESS = '0x5CfE73b6021E818B776b421B1c4Db2474086a7e1'; export const WALLET_ETH_BALANCE = '25'; +export enum SignatureType { + PersonalSign = '#personalSign', + Permit = '#signPermit', + SignTypedDataV3 = '#signTypedDataV3', + SignTypedDataV4 = '#signTypedDataV4', + SignTypedData = '#signTypedData', + SIWE = '#siwe', + SIWE_BadDomain = '#siweBadDomain', +} -export async function assertSignatureMetrics( - driver: Driver, - mockedEndpoints: MockedEndpoint[], - type: string, - primaryType: string = '', - uiCustomizations = ['redesigned_confirmation'], -) { - const events = await getEventPayloads(driver, mockedEndpoints); +type AssertSignatureMetricsOptions = { + driver: Driver; + mockedEndpoints: MockedEndpoint[]; + signatureType: string; + primaryType?: string; + uiCustomizations?: string[]; + location?: string; +}; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const signatureEventProperty: any = { +type SignatureEventProperty = { + account_type: 'MetaMask'; + category: 'inpage_provider'; + chain_id: '0x539'; + environment_type: 'background'; + locale: 'en'; + security_alert_response: 'NotApplicable'; + signature_type: string; + eip712_primary_type?: string; + ui_customizations?: string[]; + location?: string; +}; + +function getSignatureEventProperty( + signatureType: string, + primaryType: string, + uiCustomizations: string[], +): SignatureEventProperty { + const signatureEventProperty: SignatureEventProperty = { account_type: 'MetaMask', - signature_type: type, + signature_type: signatureType, category: 'inpage_provider', chain_id: '0x539', environment_type: 'background', @@ -31,6 +62,15 @@ export async function assertSignatureMetrics( signatureEventProperty.eip712_primary_type = primaryType; } + return signatureEventProperty; +} + +function assertSignatureRequestedMetrics( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + events: any[], + signatureEventProperty: SignatureEventProperty, +) { + assert.equal(events[0].event, 'Signature Requested'); assert.deepStrictEqual( events[0].properties, { @@ -39,10 +79,55 @@ export async function assertSignatureMetrics( }, 'Signature request event details do not match', ); +} + +export async function assertSignatureConfirmedMetrics({ + driver, + mockedEndpoints, + signatureType, + primaryType = '', + uiCustomizations = ['redesigned_confirmation'], +}: AssertSignatureMetricsOptions) { + const events = await getEventPayloads(driver, mockedEndpoints); + const signatureEventProperty = getSignatureEventProperty( + signatureType, + primaryType, + uiCustomizations, + ); + + assertSignatureRequestedMetrics(events, signatureEventProperty); + assert.equal(events[1].event, 'Signature Approved'); assert.deepStrictEqual( events[1].properties, signatureEventProperty, - 'Signature Accepted/Rejected event properties do not match', + 'Signature Accepted event properties do not match', + ); +} + +export async function assertSignatureRejectedMetrics({ + driver, + mockedEndpoints, + signatureType, + primaryType = '', + uiCustomizations = ['redesigned_confirmation'], + location, +}: AssertSignatureMetricsOptions) { + const events = await getEventPayloads(driver, mockedEndpoints); + const signatureEventProperty = getSignatureEventProperty( + signatureType, + primaryType, + uiCustomizations, + ); + + assertSignatureRequestedMetrics(events, signatureEventProperty); + assert.equal(events[1].event, 'Signature Rejected'); + assert.deepStrictEqual( + events[1].properties, + { + ...signatureEventProperty, + location, + }, + 'Signature Rejected event properties do not match', ); } @@ -99,3 +184,13 @@ export async function assertPastedAddress(driver: Driver) { const formFieldEl = await driver.findElement('#eip747ContractAddress'); assert.equal(await formFieldEl.getAttribute('value'), WALLET_ADDRESS); } + +export async function openDappAndTriggerSignature( + driver: Driver, + type: string, +) { + await unlockWallet(driver); + await openDapp(driver); + await driver.clickElement(type); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); +} diff --git a/test/e2e/tests/confirmations/signatures/siwe.spec.ts b/test/e2e/tests/confirmations/signatures/siwe.spec.ts index fcb7cc71eddd..ebf4f0a3f4b2 100644 --- a/test/e2e/tests/confirmations/signatures/siwe.spec.ts +++ b/test/e2e/tests/confirmations/signatures/siwe.spec.ts @@ -1,13 +1,7 @@ import { strict as assert } from 'assert'; import { Suite } from 'mocha'; import { MockedEndpoint } from 'mockttp'; -import { - DAPP_HOST_ADDRESS, - WINDOW_TITLES, - openDapp, - switchToNotificationWindow, - unlockWallet, -} from '../../../helpers'; +import { DAPP_HOST_ADDRESS, WINDOW_TITLES } from '../../../helpers'; import { Driver } from '../../../webdriver/driver'; import { scrollAndConfirmAndAssertConfirm, @@ -18,9 +12,12 @@ import { assertAccountDetailsMetrics, assertHeaderInfoBalance, assertPastedAddress, - assertSignatureMetrics, + assertSignatureConfirmedMetrics, + assertSignatureRejectedMetrics, clickHeaderInfoBtn, copyAddressAndPasteWalletAddress, + openDappAndTriggerSignature, + SignatureType, } from './signature-helpers'; describe('Confirmation Signature - SIWE @no-mmi', function (this: Suite) { @@ -31,10 +28,7 @@ describe('Confirmation Signature - SIWE @no-mmi', function (this: Suite) { driver, mockedEndpoint: mockedEndpoints, }: TestSuiteArguments) => { - await unlockWallet(driver); - await openDapp(driver); - await driver.clickElement('#siwe'); - await switchToNotificationWindow(driver); + await openDappAndTriggerSignature(driver, SignatureType.SIWE); await clickHeaderInfoBtn(driver); await assertHeaderInfoBalance(driver); @@ -46,7 +40,7 @@ describe('Confirmation Signature - SIWE @no-mmi', function (this: Suite) { mockedEndpoints as MockedEndpoint[], 'personal_sign', ); - await switchToNotificationWindow(driver); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); await assertInfoValues(driver); await scrollAndConfirmAndAssertConfirm(driver); await driver.delay(1000); @@ -55,13 +49,15 @@ describe('Confirmation Signature - SIWE @no-mmi', function (this: Suite) { driver, '0xef8674a92d62a1876624547bdccaef6c67014ae821de18fa910fbff56577a65830f68848585b33d1f4b9ea1c3da1c1b11553b6aabe8446717daf7cd1e38a68271c', ); - await assertSignatureMetrics( + await assertSignatureConfirmedMetrics({ driver, - mockedEndpoints as MockedEndpoint[], - 'personal_sign', - '', - ['redesigned_confirmation', 'sign_in_with_ethereum'], - ); + mockedEndpoints: mockedEndpoints as MockedEndpoint[], + signatureType: 'personal_sign', + uiCustomizations: [ + 'redesigned_confirmation', + 'sign_in_with_ethereum', + ], + }); }, ); }); @@ -73,10 +69,7 @@ describe('Confirmation Signature - SIWE @no-mmi', function (this: Suite) { driver, mockedEndpoint: mockedEndpoints, }: TestSuiteArguments) => { - await unlockWallet(driver); - await openDapp(driver); - await driver.clickElement('#siwe'); - await switchToNotificationWindow(driver); + await openDappAndTriggerSignature(driver, SignatureType.SIWE); await driver.clickElement( '[data-testid="confirm-footer-cancel-button"]', @@ -90,48 +83,16 @@ describe('Confirmation Signature - SIWE @no-mmi', function (this: Suite) { await rejectionResult.getText(), 'Error: User rejected the request.', ); - await assertSignatureMetrics( - driver, - mockedEndpoints as MockedEndpoint[], - 'personal_sign', - '', - ['redesigned_confirmation', 'sign_in_with_ethereum'], - ); - }, - ); - }); - - it('displays alert for domain binding and confirms', async function () { - await withRedesignConfirmationFixtures( - this.test?.fullTitle(), - async ({ driver }: TestSuiteArguments) => { - await unlockWallet(driver); - await openDapp(driver); - await driver.clickElement('#siweBadDomain'); - await switchToNotificationWindow(driver); - - const alert = await driver.findElement('[data-testid="inline-alert"]'); - assert.equal(await alert.getText(), 'Alert'); - await driver.clickElement('[data-testid="inline-alert"]'); - - await driver.clickElement( - '[data-testid="alert-modal-acknowledge-checkbox"]', - ); - await driver.clickElement('[data-testid="alert-modal-button"]'); - - await scrollAndConfirmAndAssertConfirm(driver); - - await driver.clickElement( - '[data-testid="alert-modal-acknowledge-checkbox"]', - ); - await driver.clickElement( - '[data-testid="confirm-alert-modal-submit-button"]', - ); - - await assertVerifiedSiweMessage( + await assertSignatureRejectedMetrics({ driver, - '0x24e559452c37827008633f9ae50c68cdb28e33f547f795af687839b520b022e4093c38bf1dfebda875ded715f2754d458ed62a19248e5a9bd2205bd1cb66f9b51b', - ); + mockedEndpoints: mockedEndpoints as MockedEndpoint[], + signatureType: 'personal_sign', + uiCustomizations: [ + 'redesigned_confirmation', + 'sign_in_with_ethereum', + ], + location: 'confirmation', + }); }, ); }); diff --git a/test/e2e/tests/confirmations/transactions/contract-deployment-redesign.spec.ts b/test/e2e/tests/confirmations/transactions/contract-deployment-redesign.spec.ts new file mode 100644 index 000000000000..7f937f4be6fc --- /dev/null +++ b/test/e2e/tests/confirmations/transactions/contract-deployment-redesign.spec.ts @@ -0,0 +1,86 @@ +/* eslint-disable @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires */ +import { openDapp, unlockWallet } from '../../../helpers'; +import { + confirmDepositTransaction, + confirmRedesignedContractDeploymentTransaction, + createContractDeploymentTransaction, + createDepositTransaction, + TestSuiteArguments, +} from './shared'; + +const { + defaultGanacheOptions, + defaultGanacheOptionsForType2Transactions, + withFixtures, +} = require('../../../helpers'); +const FixtureBuilder = require('../../../fixture-builder'); + +describe('Confirmation Redesign Contract Deployment Component', function () { + describe('Create a deploy transaction @no-mmi', function () { + it(`Sends a contract interaction type 0 transaction (Legacy)`, async function () { + await withFixtures( + { + dapp: true, + fixtures: new FixtureBuilder() + .withPermissionControllerConnectedToTestDapp() + .withPreferencesController({ + preferences: { + redesignedConfirmationsEnabled: true, + isRedesignedConfirmationsDeveloperEnabled: true, + }, + }) + .build(), + ganacheOptions: defaultGanacheOptions, + title: this.test?.fullTitle(), + }, + async ({ driver }: TestSuiteArguments) => { + await unlockWallet(driver); + + await openDapp(driver); + + await createContractDeploymentTransaction(driver); + + await confirmRedesignedContractDeploymentTransaction(driver); + + // confirm deposits can be made to the deployed contract + await createDepositTransaction(driver); + + await confirmDepositTransaction(driver); + }, + ); + }); + + it(`Sends a contract interaction type 2 transaction (EIP1559)`, async function () { + await withFixtures( + { + dapp: true, + fixtures: new FixtureBuilder() + .withPermissionControllerConnectedToTestDapp() + .withPreferencesController({ + preferences: { + redesignedConfirmationsEnabled: true, + isRedesignedConfirmationsDeveloperEnabled: true, + }, + }) + .build(), + ganacheOptions: defaultGanacheOptionsForType2Transactions, + title: this.test?.fullTitle(), + }, + async ({ driver }: TestSuiteArguments) => { + await unlockWallet(driver); + + await openDapp(driver); + + await createContractDeploymentTransaction(driver); + + await confirmRedesignedContractDeploymentTransaction(driver); + + // confirm deposits can be made to the deployed contract + await createDepositTransaction(driver); + + await confirmDepositTransaction(driver); + }, + ); + }); + }); +}); diff --git a/test/e2e/tests/confirmations/transactions/contract-interaction-redesign.spec.ts b/test/e2e/tests/confirmations/transactions/contract-interaction-redesign.spec.ts index fc6ca9e2ff4e..2a815a025d18 100644 --- a/test/e2e/tests/confirmations/transactions/contract-interaction-redesign.spec.ts +++ b/test/e2e/tests/confirmations/transactions/contract-interaction-redesign.spec.ts @@ -1,9 +1,10 @@ /* eslint-disable @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires */ import { Mockttp } from 'mockttp'; -import { createDappTransaction, unlockWallet } from '../../../helpers'; +import { openDapp, unlockWallet } from '../../../helpers'; +import { createDappTransaction } from '../../../page-objects/flows/transaction'; +import GanacheContractAddressRegistry from '../../../seeder/ganache-contract-address-registry'; import { Driver } from '../../../webdriver/driver'; import { MockedEndpoint } from '../../../mock-e2e'; -import { DEFAULT_FIXTURE_ACCOUNT } from '../../../constants'; import { assertAdvancedGasDetails, confirmDepositTransaction, @@ -106,10 +107,16 @@ describe('Confirmation Redesign Contract Interaction Component', function () { title: this.test?.fullTitle(), testSpecificMock: mockOptimismOracle, }, - async ({ driver }: TestSuiteArguments) => { + async ({ driver, contractRegistry }: TestSuiteArguments) => { await unlockWallet(driver); await createLayer2Transaction(driver); + const contractAddress = await ( + contractRegistry as GanacheContractAddressRegistry + ).getContractAddress(smartContract); + + await openDapp(driver, contractAddress); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); await toggleAdvancedDetails(driver); @@ -281,11 +288,7 @@ describe('Confirmation Redesign Contract Interaction Component', function () { async function createLayer2Transaction(driver: Driver) { await createDappTransaction(driver, { data: '0x1234', - from: DEFAULT_FIXTURE_ACCOUNT, to: '0x581c3C1A2A4EBDE2A0Df29B5cf4c116E42945947', - gas: '0x31f10', - maxFeePerGas: '0x3b014b3', - maxPriorityFeePerGas: '0x3b014b3', }); } diff --git a/test/e2e/tests/confirmations/transactions/erc721-approve-redesign.spec.ts b/test/e2e/tests/confirmations/transactions/erc721-approve-redesign.spec.ts new file mode 100644 index 000000000000..b1ed24a9c171 --- /dev/null +++ b/test/e2e/tests/confirmations/transactions/erc721-approve-redesign.spec.ts @@ -0,0 +1,153 @@ +/* eslint-disable @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires */ +import { veryLargeDelayMs, WINDOW_TITLES } from '../../../helpers'; +import { Driver } from '../../../webdriver/driver'; +import { scrollAndConfirmAndAssertConfirm } from '../helpers'; +import { + openDAppWithContract, + TestSuiteArguments, + toggleAdvancedDetails, +} from './shared'; + +const { + defaultGanacheOptions, + defaultGanacheOptionsForType2Transactions, + withFixtures, +} = require('../../../helpers'); +const FixtureBuilder = require('../../../fixture-builder'); +const { SMART_CONTRACTS } = require('../../../seeder/smart-contracts'); + +describe('Confirmation Redesign ERC721 Approve Component', function () { + const smartContract = SMART_CONTRACTS.NFTS; + + describe('Submit an Approve transaction @no-mmi', function () { + it('Sends a type 0 transaction (Legacy)', async function () { + await withFixtures( + { + dapp: true, + fixtures: new FixtureBuilder() + .withPermissionControllerConnectedToTestDapp() + .withPreferencesController({ + preferences: { + redesignedConfirmationsEnabled: true, + isRedesignedConfirmationsDeveloperEnabled: true, + }, + }) + .build(), + ganacheOptions: defaultGanacheOptions, + smartContract, + title: this.test?.fullTitle(), + }, + async ({ driver, contractRegistry }: TestSuiteArguments) => { + await openDAppWithContract(driver, contractRegistry, smartContract); + + await createMintTransaction(driver); + await confirmMintTransaction(driver); + + await createApproveTransaction(driver); + + await assertApproveDetails(driver); + await confirmApproveTransaction(driver); + }, + ); + }); + + it('Sends a type 2 transaction (EIP1559)', async function () { + await withFixtures( + { + dapp: true, + fixtures: new FixtureBuilder() + .withPermissionControllerConnectedToTestDapp() + .withPreferencesController({ + preferences: { + redesignedConfirmationsEnabled: true, + isRedesignedConfirmationsDeveloperEnabled: true, + }, + }) + .build(), + ganacheOptions: defaultGanacheOptionsForType2Transactions, + smartContract, + title: this.test?.fullTitle(), + }, + async ({ driver, contractRegistry }: TestSuiteArguments) => { + await openDAppWithContract(driver, contractRegistry, smartContract); + + await createMintTransaction(driver); + await confirmMintTransaction(driver); + + await createApproveTransaction(driver); + await assertApproveDetails(driver); + await confirmApproveTransaction(driver); + }, + ); + }); + }); +}); + +async function createMintTransaction(driver: Driver) { + await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); + await driver.clickElement('#mintButton'); +} + +export async function confirmMintTransaction(driver: Driver) { + await driver.waitUntilXWindowHandles(3); + + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); + + await driver.waitForSelector({ + css: 'h2', + text: 'Transaction request', + }); + + await scrollAndConfirmAndAssertConfirm(driver); +} + +async function createApproveTransaction(driver: Driver) { + await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); + await driver.clickElement('#approveButton'); +} + +async function assertApproveDetails(driver: Driver) { + await driver.delay(veryLargeDelayMs); + await driver.waitUntilXWindowHandles(3); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); + + await driver.waitForSelector({ + css: 'h2', + text: 'Allowance request', + }); + + await driver.waitForSelector({ + css: 'p', + text: 'This site wants permission to withdraw your NFTs', + }); + + await toggleAdvancedDetails(driver); + + await driver.waitForSelector({ + css: 'p', + text: 'Request from', + }); + + await driver.waitForSelector({ + css: 'p', + text: 'Interacting with', + }); + + await driver.waitForSelector({ + css: 'p', + text: 'Method', + }); +} + +async function confirmApproveTransaction(driver: Driver) { + await scrollAndConfirmAndAssertConfirm(driver); + + await driver.delay(veryLargeDelayMs); + await driver.waitUntilXWindowHandles(2); + await driver.switchToWindowWithTitle(WINDOW_TITLES.ExtensionInFullScreenView); + + await driver.clickElement({ text: 'Activity', tag: 'button' }); + await driver.waitForSelector( + '.transaction-list__completed-transactions .activity-list-item:nth-of-type(1)', + ); +} diff --git a/test/e2e/tests/confirmations/transactions/metrics.spec.ts b/test/e2e/tests/confirmations/transactions/metrics.spec.ts index f362196a50f6..aed8c0ba7f20 100644 --- a/test/e2e/tests/confirmations/transactions/metrics.spec.ts +++ b/test/e2e/tests/confirmations/transactions/metrics.spec.ts @@ -73,38 +73,62 @@ describe('Metrics @no-mmi', function () { events[0].event, AnonymousTransactionMetaMetricsEvent.added, ); - assert.equal(events[0].properties.ui_customizations, null); - assert.equal(events[0].properties.transaction_advanced_view, null); + assert.equal( + events[0].properties.ui_customizations[0], + 'redesigned_confirmation', + ); + assert.equal(events[0].properties.transaction_advanced_view, undefined); assert.equal(events[1].event, TransactionMetaMetricsEvent.added); - assert.equal(events[1].properties.ui_customizations, null); - assert.equal(events[1].properties.transaction_advanced_view, null); + assert.equal( + events[1].properties.ui_customizations[0], + 'redesigned_confirmation', + ); + assert.equal(events[1].properties.transaction_advanced_view, undefined); assert.equal( events[2].event, AnonymousTransactionMetaMetricsEvent.submitted, ); - assert.equal(events[2].properties.ui_customizations, null); - assert.equal(events[2].properties.transaction_advanced_view, null); + assert.equal( + events[2].properties.ui_customizations[0], + 'redesigned_confirmation', + ); + assert.equal(events[2].properties.transaction_advanced_view, undefined); assert.equal(events[3].event, TransactionMetaMetricsEvent.submitted); - assert.equal(events[3].properties.ui_customizations, null); - assert.equal(events[3].properties.transaction_advanced_view, null); + assert.equal( + events[3].properties.ui_customizations[0], + 'redesigned_confirmation', + ); + assert.equal(events[3].properties.transaction_advanced_view, undefined); assert.equal( events[4].event, AnonymousTransactionMetaMetricsEvent.approved, ); - assert.equal(events[4].properties.ui_customizations, null); - assert.equal(events[4].properties.transaction_advanced_view, null); + assert.equal( + events[4].properties.ui_customizations[0], + 'redesigned_confirmation', + ); + assert.equal(events[4].properties.transaction_advanced_view, undefined); assert.equal(events[5].event, TransactionMetaMetricsEvent.approved); - assert.equal(events[5].properties.ui_customizations, null); - assert.equal(events[5].properties.transaction_advanced_view, null); + assert.equal( + events[5].properties.ui_customizations[0], + 'redesigned_confirmation', + ); + assert.equal(events[5].properties.transaction_advanced_view, undefined); assert.equal( events[6].event, AnonymousTransactionMetaMetricsEvent.finalized, ); - assert.equal(events[6].properties.ui_customizations, null); - assert.equal(events[6].properties.transaction_advanced_view, null); + assert.equal( + events[6].properties.ui_customizations[0], + 'redesigned_confirmation', + ); + assert.equal(events[6].properties.transaction_advanced_view, undefined); assert.equal(events[7].event, TransactionMetaMetricsEvent.finalized); - assert.equal(events[7].properties.ui_customizations, null); - assert.equal(events[7].properties.transaction_advanced_view, null); + assert.equal( + events[7].properties.ui_customizations[0], + 'redesigned_confirmation', + ); + assert.equal(events[7].properties.transaction_advanced_view, undefined); // deposit tx (contract interaction) -- ui_customizations is set assert.equal( diff --git a/test/e2e/tests/confirmations/transactions/shared.ts b/test/e2e/tests/confirmations/transactions/shared.ts index fe85a2e3f851..163b41f99aff 100644 --- a/test/e2e/tests/confirmations/transactions/shared.ts +++ b/test/e2e/tests/confirmations/transactions/shared.ts @@ -43,11 +43,39 @@ export async function confirmContractDeploymentTransaction(driver: Driver) { await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); await driver.waitForSelector({ - css: '.confirm-page-container-summary__action__name', - text: 'Contract deployment', + css: 'h2', + text: 'Deploy a contract', + }); + + await scrollAndConfirmAndAssertConfirm(driver); + + await driver.delay(2000); + await driver.waitUntilXWindowHandles(2); + + await driver.switchToWindowWithTitle(WINDOW_TITLES.ExtensionInFullScreenView); + await driver.clickElement({ text: 'Activity', tag: 'button' }); + await driver.waitForSelector( + '.transaction-list__completed-transactions .activity-list-item:nth-of-type(1)', + ); +} + +export async function confirmRedesignedContractDeploymentTransaction( + driver: Driver, +) { + await driver.waitUntilXWindowHandles(3); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); + + await driver.waitForSelector({ + css: 'h2', + text: 'Deploy a contract', }); - await driver.clickElement({ text: 'Confirm', tag: 'button' }); + await driver.waitForSelector({ + css: 'p', + text: 'This site wants you to deploy a contract', + }); + + await scrollAndConfirmAndAssertConfirm(driver); await driver.delay(2000); await driver.waitUntilXWindowHandles(2); @@ -80,6 +108,7 @@ export async function confirmDepositTransaction(driver: Driver) { text: 'Nonce', }); + await driver.delay(veryLargeDelayMs); await scrollAndConfirmAndAssertConfirm(driver); } diff --git a/test/e2e/tests/dapp-interactions/block-explorer.spec.js b/test/e2e/tests/dapp-interactions/block-explorer.spec.js index 50c8d09efc6c..bde8eb0cf485 100644 --- a/test/e2e/tests/dapp-interactions/block-explorer.spec.js +++ b/test/e2e/tests/dapp-interactions/block-explorer.spec.js @@ -1,4 +1,6 @@ const { strict: assert } = require('assert'); +const { mockNetworkState } = require('../../../stub/networks'); + const { defaultGanacheOptions, withFixtures, @@ -12,21 +14,15 @@ describe('Block Explorer', function () { await withFixtures( { fixtures: new FixtureBuilder() - .withNetworkController({ - providerConfig: { - rpcPrefs: { blockExplorerUrl: 'https://etherscan.io/' }, - }, - networkConfigurations: { - networkConfigurationId: { - chainId: '0x539', - nickname: 'Localhost 8545', - rpcUrl: 'http://localhost:8545', - ticker: 'ETH', - rpcPrefs: { blockExplorerUrl: 'https://etherscan.io/' }, - }, - }, - selectedNetworkClientId: 'networkConfigurationId', - }) + .withNetworkController( + mockNetworkState({ + chainId: '0x539', + nickname: 'Localhost 8545', + rpcUrl: 'http://localhost:8545', + ticker: 'ETH', + blockExplorerUrl: 'https://etherscan.io/', + }), + ) .build(), ganacheOptions: defaultGanacheOptions, title: this.test.fullTitle(), @@ -64,21 +60,15 @@ describe('Block Explorer', function () { { dapp: true, fixtures: new FixtureBuilder() - .withNetworkController({ - providerConfig: { - rpcPrefs: { blockExplorerUrl: 'https://etherscan.io/' }, - }, - networkConfigurations: { - networkConfigurationId: { - chainId: '0x539', - nickname: 'Localhost 8545', - rpcUrl: 'http://localhost:8545', - ticker: 'ETH', - rpcPrefs: { blockExplorerUrl: 'https://etherscan.io/' }, - }, - }, - selectedNetworkClientId: 'networkConfigurationId', - }) + .withNetworkController( + mockNetworkState({ + chainId: '0x539', + nickname: 'Localhost 8545', + rpcUrl: 'http://localhost:8545', + ticker: 'ETH', + blockExplorerUrl: 'https://etherscan.io/', + }), + ) .withTokensControllerERC20() .build(), ganacheOptions: defaultGanacheOptions, @@ -124,21 +114,16 @@ describe('Block Explorer', function () { await withFixtures( { fixtures: new FixtureBuilder() - .withNetworkController({ - providerConfig: { - rpcPrefs: { blockExplorerUrl: 'https://etherscan.io/' }, - }, - networkConfigurations: { - networkConfigurationId: { - chainId: '0x539', - nickname: 'Localhost 8545', - rpcUrl: 'http://localhost:8545', - ticker: 'ETH', - rpcPrefs: { blockExplorerUrl: 'https://etherscan.io/' }, - }, - }, - selectedNetworkClientId: 'networkConfigurationId', - }) + .withNetworkController( + mockNetworkState({ + id: 'localhost-client-id', + chainId: '0x539', + nickname: 'Localhost 8545', + rpcUrl: 'http://localhost:8545', + ticker: 'ETH', + blockExplorerUrl: 'https://etherscan.io', + }), + ) .withTransactionControllerCompletedTransaction() .build(), ganacheOptions: defaultGanacheOptions, diff --git a/test/e2e/tests/metrics/errors.spec.js b/test/e2e/tests/metrics/errors.spec.js index 36d9a46daead..464dbedc51fd 100644 --- a/test/e2e/tests/metrics/errors.spec.js +++ b/test/e2e/tests/metrics/errors.spec.js @@ -310,12 +310,11 @@ describe('Sentry errors', function () { async ({ driver, mockedEndpoint }) => { // we don't wait for the controllers to be loaded await driver.navigate(PAGES.HOME, { waitForControllers: false }); - // Wait for Sentry request await driver.wait(async () => { const isPending = await mockedEndpoint.isPending(); return isPending === false; - }, 3000); + }, 8000); const [mockedRequest] = await mockedEndpoint.getSeenRequests(); const mockTextBody = (await mockedRequest.body.getText()).split('\n'); @@ -359,7 +358,7 @@ describe('Sentry errors', function () { await driver.wait(async () => { const isPending = await mockedEndpoint.isPending(); return isPending === false; - }, 3000); + }, 8000); const [mockedRequest] = await mockedEndpoint.getSeenRequests(); const mockTextBody = (await mockedRequest.body.getText()).split('\n'); @@ -469,7 +468,7 @@ describe('Sentry errors', function () { await driver.wait(async () => { const isPending = await mockedEndpoint.isPending(); return isPending === false; - }, 3000); + }, 8000); const [mockedRequest] = await mockedEndpoint.getSeenRequests(); const mockTextBody = (await mockedRequest.body.getText()).split('\n'); const mockJsonBody = JSON.parse(mockTextBody[2]); @@ -511,7 +510,7 @@ describe('Sentry errors', function () { await driver.wait(async () => { const isPending = await mockedEndpoint.isPending(); return isPending === false; - }, 3000); + }, 8000); const [mockedRequest] = await mockedEndpoint.getSeenRequests(); const mockTextBody = (await mockedRequest.body.getText()).split('\n'); const mockJsonBody = JSON.parse(mockTextBody[2]); @@ -667,6 +666,7 @@ describe('Sentry errors', function () { async ({ driver, ganacheServer, mockedEndpoint }) => { await logInWithBalanceValidation(driver, ganacheServer); + await driver.delay(2000); // Trigger error await driver.executeScript( 'window.stateHooks.throwTestBackgroundError()', @@ -766,6 +766,8 @@ describe('Sentry errors', function () { async ({ driver, ganacheServer, mockedEndpoint }) => { await logInWithBalanceValidation(driver, ganacheServer); + await driver.delay(2000); + // Trigger error await driver.executeScript('window.stateHooks.throwTestError()'); diff --git a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json index 7bb029e18cad..c8b74e873e7e 100644 --- a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json +++ b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json @@ -37,7 +37,7 @@ "showNetworkBanner": true, "showAccountBanner": true, "trezorModel": null, - "onboardingDate": "object", + "onboardingDate": null, "newPrivacyPolicyToastClickedOrClosed": "boolean", "newPrivacyPolicyToastShownDate": "number", "hadAdvancedGasFeesSetPriorToMigration92_3": false, @@ -48,7 +48,7 @@ "snapsInstallPrivacyWarningShown": true, "surveyLinkLastClickedOrClosed": "object", "signatureSecurityAlertResponses": "object", - "switchedNetworkDetails": "object", + "switchedNetworkDetails": null, "switchedNetworkNeverShowMessage": "boolean", "currentExtensionPopupId": "number" }, @@ -59,7 +59,21 @@ }, "AuthenticationController": { "isSignedIn": "boolean" }, "BridgeController": { - "bridgeState": { "bridgeFeatureFlags": { "extensionSupport": "boolean" } } + "bridgeState": { + "bridgeFeatureFlags": { + "extensionSupport": "boolean", + "srcNetworkAllowlist": { + "0": "string", + "1": "string", + "2": "string" + }, + "destNetworkAllowlist": { + "0": "string", + "1": "string", + "2": "string" + } + } + } }, "CronjobController": { "jobs": "object" }, "CurrencyController": { @@ -110,18 +124,6 @@ "fragments": "object", "segmentApiCalls": "object" }, - "MetamaskNotificationsController": { - "subscriptionAccountsSeen": "object", - "isMetamaskNotificationsFeatureSeen": "boolean", - "isMetamaskNotificationsEnabled": "boolean", - "isFeatureAnnouncementsEnabled": "boolean", - "metamaskNotificationsList": "object", - "metamaskNotificationsReadList": "object", - "isUpdatingMetamaskNotifications": "boolean", - "isFetchingMetamaskNotifications": "boolean", - "isUpdatingMetamaskNotificationsAccount": "object", - "isCheckingAccountsPresence": "boolean" - }, "MultichainBalancesController": { "balances": "object" }, "MultichainRatesController": { "fiatCurrency": "usd", @@ -131,15 +133,6 @@ "NameController": { "names": "object", "nameSources": "object" }, "NetworkController": { "selectedNetworkClientId": "string", - "providerConfig": { - "chainId": "0x539", - "nickname": "Localhost 8545", - "rpcPrefs": "object", - "rpcUrl": "string", - "ticker": "ETH", - "type": "rpc", - "id": "networkConfigurationId" - }, "networksMetadata": { "networkConfigurationId": { "EIPS": { "1559": false }, @@ -157,6 +150,19 @@ "ignoredNfts": "object" }, "NotificationController": { "notifications": "object" }, + "NotificationServicesController": { + "subscriptionAccountsSeen": "object", + "isMetamaskNotificationsFeatureSeen": "boolean", + "isNotificationServicesEnabled": "boolean", + "isFeatureAnnouncementsEnabled": "boolean", + "metamaskNotificationsList": "object", + "metamaskNotificationsReadList": "object", + "isUpdatingMetamaskNotifications": "boolean", + "isFetchingMetamaskNotifications": "boolean", + "isUpdatingMetamaskNotificationsAccount": "object", + "isCheckingAccountsPresence": "boolean" + }, + "NotificationServicesPushController": { "fcmToken": "string" }, "OnboardingController": { "seedPhraseBackedUp": true, "firstTimeFlowType": "import", @@ -183,6 +189,7 @@ "useRequestQueue": true, "openSeaEnabled": false, "securityAlertsEnabled": "boolean", + "watchEthereumAccountEnabled": "boolean", "bitcoinSupportEnabled": "boolean", "bitcoinTestnetSupportEnabled": "boolean", "addSnapAccountEnabled": "boolean", @@ -202,9 +209,9 @@ "smartTransactionsOptInStatus": false, "useNativeCurrencyAsPrimaryCurrency": true, "petnamesEnabled": true, + "isRedesignedConfirmationsDeveloperEnabled": "boolean", "redesignedConfirmationsEnabled": true, - "redesignedTransactionsEnabled": "boolean", - "isRedesignedConfirmationsDeveloperEnabled": "boolean" + "redesignedTransactionsEnabled": "boolean" }, "ipfsGateway": "string", "isIpfsGatewayEnabled": "boolean", @@ -219,7 +226,6 @@ "useExternalServices": "boolean", "selectedAddress": "string" }, - "PushPlatformNotificationsController": { "fcmToken": "string" }, "QueuedRequestController": { "queuedRequestCount": 0 }, "SelectedNetworkController": { "domains": "object" }, "SignatureController": { @@ -240,10 +246,11 @@ "snapStates": "object", "unencryptedSnapStates": "object" }, - "SnapInterfaceController": "object", + "SnapInsightsController": { "insights": "object" }, + "SnapInterfaceController": { "interfaces": "object" }, "SnapsRegistry": { - "database": "object", - "lastUpdated": "object", + "database": null, + "lastUpdated": null, "databaseUnavailable": "boolean" }, "SubjectMetadataController": { "subjectMetadata": "object" }, @@ -252,9 +259,9 @@ "quotes": "object", "quotesPollingLimitEnabled": false, "fetchParams": null, - "tokens": "object", - "tradeTxId": "object", - "approveTxId": "object", + "tokens": null, + "tradeTxId": null, + "approveTxId": null, "quotesLastFetched": null, "customMaxGas": "", "customGasPrice": null, @@ -264,7 +271,7 @@ "selectedAggId": null, "customApproveTxData": "string", "errorKey": "", - "topAggId": "object", + "topAggId": null, "routeState": "", "swapsFeatureIsLive": true, "saveFetchedQuotes": false, diff --git a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json index fad5d690e9de..b3546bc54cbe 100644 --- a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json +++ b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json @@ -34,9 +34,9 @@ "smartTransactionsOptInStatus": false, "useNativeCurrencyAsPrimaryCurrency": true, "petnamesEnabled": true, + "isRedesignedConfirmationsDeveloperEnabled": "boolean", "redesignedConfirmationsEnabled": true, - "redesignedTransactionsEnabled": "boolean", - "isRedesignedConfirmationsDeveloperEnabled": "boolean" + "redesignedTransactionsEnabled": "boolean" }, "firstTimeFlowType": "import", "completedOnboarding": true, @@ -52,15 +52,6 @@ "usdConversionRate": 1700 } }, - "providerConfig": { - "chainId": "0x539", - "nickname": "Localhost 8545", - "rpcPrefs": "object", - "rpcUrl": "string", - "ticker": "ETH", - "type": "rpc", - "id": "networkConfigurationId" - }, "connectedStatusPopoverHasBeenShown": true, "defaultHomeActiveTabName": null, "browserEnvironment": { "os": "string", "browser": "string" }, @@ -77,7 +68,7 @@ "showNetworkBanner": true, "showAccountBanner": true, "trezorModel": null, - "onboardingDate": "object", + "onboardingDate": null, "newPrivacyPolicyToastClickedOrClosed": "boolean", "newPrivacyPolicyToastShownDate": "number", "hadAdvancedGasFeesSetPriorToMigration92_3": false, @@ -88,7 +79,7 @@ "snapsInstallPrivacyWarningShown": true, "surveyLinkLastClickedOrClosed": "object", "signatureSecurityAlertResponses": "object", - "switchedNetworkDetails": "object", + "switchedNetworkDetails": null, "switchedNetworkNeverShowMessage": "boolean", "currentExtensionPopupId": "number", "currentAppVersion": "string", @@ -115,6 +106,7 @@ "useRequestQueue": true, "openSeaEnabled": false, "securityAlertsEnabled": "boolean", + "watchEthereumAccountEnabled": "boolean", "bitcoinSupportEnabled": "boolean", "bitcoinTestnetSupportEnabled": "boolean", "addSnapAccountEnabled": "boolean", @@ -189,11 +181,12 @@ "snapStates": "object", "unencryptedSnapStates": "object", "jobs": "object", - "database": "object", - "lastUpdated": "object", + "database": null, + "lastUpdated": null, "databaseUnavailable": "boolean", "notifications": "object", "interfaces": "object", + "insights": "object", "names": "object", "nameSources": "object", "userOperations": "object", @@ -202,7 +195,7 @@ "isProfileSyncingUpdateLoading": "boolean", "subscriptionAccountsSeen": "object", "isMetamaskNotificationsFeatureSeen": "boolean", - "isMetamaskNotificationsEnabled": "boolean", + "isNotificationServicesEnabled": "boolean", "isFeatureAnnouncementsEnabled": "boolean", "metamaskNotificationsList": "object", "metamaskNotificationsReadList": "object", @@ -227,9 +220,9 @@ "quotes": "object", "quotesPollingLimitEnabled": false, "fetchParams": null, - "tokens": "object", - "tradeTxId": "object", - "approveTxId": "object", + "tokens": null, + "tradeTxId": null, + "approveTxId": null, "quotesLastFetched": null, "customMaxGas": "", "customGasPrice": null, @@ -239,7 +232,7 @@ "selectedAggId": null, "customApproveTxData": "string", "errorKey": "", - "topAggId": "object", + "topAggId": null, "routeState": "", "swapsFeatureIsLive": true, "saveFetchedQuotes": false, @@ -251,7 +244,21 @@ "swapsStxMaxFeeMultiplier": 2, "swapsFeatureFlags": {} }, - "bridgeState": { "bridgeFeatureFlags": { "extensionSupport": "boolean" } }, + "bridgeState": { + "bridgeFeatureFlags": { + "extensionSupport": "boolean", + "srcNetworkAllowlist": { + "0": "string", + "1": "string", + "2": "string" + }, + "destNetworkAllowlist": { + "0": "string", + "1": "string", + "2": "string" + } + } + }, "ensEntries": "object", "ensResolutionsByAddress": "object", "pendingApprovals": "object", diff --git a/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-background-state.json b/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-background-state.json index a0029379e0bf..c30e331583cd 100644 --- a/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-background-state.json +++ b/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-background-state.json @@ -2,10 +2,10 @@ "data": { "AuthenticationController": { "isSignedIn": "boolean" }, "UserStorageController": { "isProfileSyncingEnabled": true }, - "MetamaskNotificationsController": { + "NotificationServicesController": { "subscriptionAccountsSeen": "object", "isFeatureAnnouncementsEnabled": "boolean", - "isMetamaskNotificationsEnabled": "boolean", + "isNotificationServicesEnabled": "boolean", "isMetamaskNotificationsFeatureSeen": "boolean", "metamaskNotificationsList": "object", "metamaskNotificationsReadList": "object" @@ -79,15 +79,7 @@ "networksMetadata": { "networkConfigurationId": { "EIPS": {}, "status": "available" } }, - "providerConfig": { - "chainId": "0x539", - "nickname": "Localhost 8545", - "rpcPrefs": "object", - "rpcUrl": "string", - "ticker": "ETH", - "type": "rpc", - "id": "networkConfigurationId" - }, + "providerConfig": "object", "networkConfigurations": "object" }, "OnboardingController": { @@ -144,7 +136,17 @@ "BridgeController": { "bridgeState": { "bridgeFeatureFlags": { - "extensionSupport": "boolean" + "extensionSupport": "boolean", + "srcNetworkAllowlist": { + "0": "string", + "1": "string", + "2": "string" + }, + "destNetworkAllowlist": { + "0": "string", + "1": "string", + "2": "string" + } } } }, diff --git a/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-ui-state.json b/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-ui-state.json index 24cf95223295..16204cb379fc 100644 --- a/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-ui-state.json +++ b/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-ui-state.json @@ -2,10 +2,10 @@ "data": { "AuthenticationController": { "isSignedIn": "boolean" }, "UserStorageController": { "isProfileSyncingEnabled": true }, - "MetamaskNotificationsController": { + "NotificationServicesController": { "subscriptionAccountsSeen": "object", "isFeatureAnnouncementsEnabled": "boolean", - "isMetamaskNotificationsEnabled": "boolean", + "isNotificationServicesEnabled": "boolean", "isMetamaskNotificationsFeatureSeen": "boolean", "metamaskNotificationsList": "object", "metamaskNotificationsReadList": "object" @@ -79,15 +79,7 @@ "networksMetadata": { "networkConfigurationId": { "EIPS": {}, "status": "available" } }, - "providerConfig": { - "chainId": "0x539", - "nickname": "Localhost 8545", - "rpcPrefs": "object", - "rpcUrl": "string", - "ticker": "ETH", - "type": "rpc", - "id": "networkConfigurationId" - }, + "providerConfig": "object", "networkConfigurations": "object" }, "OnboardingController": { @@ -153,7 +145,17 @@ "BridgeController": { "bridgeState": { "bridgeFeatureFlags": { - "extensionSupport": "boolean" + "extensionSupport": "boolean", + "srcNetworkAllowlist": { + "0": "string", + "1": "string", + "2": "string" + }, + "destNetworkAllowlist": { + "0": "string", + "1": "string", + "2": "string" + } } } }, diff --git a/test/e2e/tests/network/add-custom-network.spec.js b/test/e2e/tests/network/add-custom-network.spec.js index 5a67a7d140fb..df87c915a742 100644 --- a/test/e2e/tests/network/add-custom-network.spec.js +++ b/test/e2e/tests/network/add-custom-network.spec.js @@ -1,5 +1,6 @@ const { strict: assert } = require('assert'); const { toHex } = require('@metamask/controller-utils'); +const { mockNetworkState } = require('../../../stub/networks'); const FixtureBuilder = require('../../fixture-builder'); const { defaultGanacheOptions, @@ -586,15 +587,13 @@ describe('Custom network', function () { { fixtures: new FixtureBuilder() .withNetworkController({ - networkConfigurations: { - networkConfigurationId: { - rpcUrl: networkURL, - chainId: chainID, - nickname: networkNAME, - ticker: currencySYMBOL, - rpcPrefs: {}, - }, - }, + ...mockNetworkState({ + rpcUrl: networkURL, + chainId: chainID, + nickname: networkNAME, + ticker: currencySYMBOL, + }), + selectedNetworkClientId: 'mainnet', }) .build(), ganacheOptions: defaultGanacheOptions, @@ -752,8 +751,7 @@ describe('Custom network', function () { assert.equal(suggestedTicker, false); assert.equal(tickerWarning, false); - driver.clickElement(selectors.tickerButton); - driver.clickElement(selectors.saveButton); + await driver.clickElement(selectors.saveButton); // Validate the network was added const networkAdded = await driver.isElementPresent( @@ -867,8 +865,8 @@ describe('Custom network', function () { assert.equal(suggestedTicker, true); assert.equal(tickerWarning, true); - driver.clickElement(selectors.tickerButton); - driver.clickElement(selectors.saveButton); + await driver.clickElement(selectors.tickerButton); + await driver.clickElement(selectors.saveButton); // Validate the network was added const networkAdded = await driver.isElementPresent( @@ -1014,7 +1012,7 @@ async function toggleOffSafeChainsListValidation(driver) { 'Safe chains list validation toggle is ON', ); - driver.delay(regularDelayMs); + await driver.delay(regularDelayMs); // return to the home screen const appHeaderSelector = '[data-testid="app-header-logo"]'; diff --git a/test/e2e/tests/network/custom-rpc-history.spec.js b/test/e2e/tests/network/custom-rpc-history.spec.js index b2a45a968f49..b8bda1a2f14f 100644 --- a/test/e2e/tests/network/custom-rpc-history.spec.js +++ b/test/e2e/tests/network/custom-rpc-history.spec.js @@ -1,4 +1,6 @@ const { strict: assert } = require('assert'); +const { mockNetworkState } = require('../../../stub/networks'); + const { defaultGanacheOptions, generateGanacheOptions, @@ -182,29 +184,26 @@ describe('Custom RPC history', function () { }); it('finds all recent RPCs in history', async function () { + const networkState = mockNetworkState( + { + rpcUrl: 'http://127.0.0.1:8545/1', + chainId: '0x539', + ticker: 'ETH', + nickname: 'http://127.0.0.1:8545/1', + }, + { + rpcUrl: 'http://127.0.0.1:8545/2', + chainId: '0x539', + ticker: 'ETH', + nickname: 'http://127.0.0.1:8545/2', + }, + ); + delete networkState.selectedNetworkClientId; + await withFixtures( { fixtures: new FixtureBuilder() - .withNetworkController({ - networkConfigurations: { - networkConfigurationIdOne: { - rpcUrl: 'http://127.0.0.1:8545/1', - chainId: '0x539', - ticker: 'ETH', - nickname: 'http://127.0.0.1:8545/1', - rpcPrefs: {}, - type: 'rpc', - }, - networkConfigurationIdTwo: { - rpcUrl: 'http://127.0.0.1:8545/2', - chainId: '0x539', - ticker: 'ETH', - nickname: 'http://127.0.0.1:8545/2', - rpcPrefs: {}, - type: 'rpc', - }, - }, - }) + .withNetworkController(networkState) .build(), ganacheOptions: defaultGanacheOptions, title: this.test.fullTitle(), @@ -234,27 +233,26 @@ describe('Custom RPC history', function () { }); it('deletes a custom RPC', async function () { + const networkState = mockNetworkState( + { + rpcUrl: 'http://127.0.0.1:8545/1', + chainId: '0x539', + ticker: 'ETH', + nickname: 'http://127.0.0.1:8545/1', + }, + { + rpcUrl: 'http://127.0.0.1:8545/2', + chainId: '0x539', + ticker: 'ETH', + nickname: 'http://127.0.0.1:8545/2', + }, + ); + delete networkState.selectedNetworkClientId; + await withFixtures( { fixtures: new FixtureBuilder() - .withNetworkController({ - networkConfigurations: { - networkConfigurationIdOne: { - rpcUrl: 'http://127.0.0.1:8545/1', - chainId: '0x539', - ticker: 'ETH', - nickname: 'http://127.0.0.1:8545/1', - rpcPrefs: {}, - }, - networkConfigurationIdTwo: { - rpcUrl: 'http://127.0.0.1:8545/2', - chainId: '0x539', - ticker: 'ETH', - nickname: 'http://127.0.0.1:8545/2', - rpcPrefs: {}, - }, - }, - }) + .withNetworkController(networkState) .build(), ganacheOptions: defaultGanacheOptions, title: this.test.fullTitle(), diff --git a/test/e2e/tests/notifications/mocks.ts b/test/e2e/tests/notifications/mocks.ts index 16fd122532f3..7a2c3fdfef92 100644 --- a/test/e2e/tests/notifications/mocks.ts +++ b/test/e2e/tests/notifications/mocks.ts @@ -4,21 +4,14 @@ import { UserStorageController, } from '@metamask/profile-sync-controller'; import { - getMockFeatureAnnouncementResponse, - getMockBatchCreateTriggersResponse, - getMockBatchDeleteTriggersResponse, - getMockListNotificationsResponse, - getMockMarkNotificationsAsReadResponse, -} from '../../../../app/scripts/controllers/metamask-notifications/mocks/mockResponses'; -import { - getMockRetrievePushNotificationLinksResponse, - getMockUpdatePushNotificationLinksResponse, - getMockCreateFCMRegistrationTokenResponse, - getMockDeleteFCMRegistrationTokenResponse, -} from '../../../../app/scripts/controllers/push-platform-notifications/mocks/mockResponse'; + NotificationServicesController, + NotificationsServicesPushController, +} from '@metamask/notification-services-controller'; const AuthMocks = AuthenticationController.Mocks; const StorageMocks = UserStorageController.Mocks; +const NotificationMocks = NotificationServicesController.Mocks; +const PushMocks = NotificationsServicesPushController.Mocks; type MockResponse = { url: string | RegExp; @@ -42,17 +35,20 @@ export function mockNotificationServices(server: Mockttp) { mockAPICall(server, StorageMocks.getMockUserStoragePutResponse()); // Notifications - mockAPICall(server, getMockFeatureAnnouncementResponse()); - mockAPICall(server, getMockBatchCreateTriggersResponse()); - mockAPICall(server, getMockBatchDeleteTriggersResponse()); - mockAPICall(server, getMockListNotificationsResponse()); - mockAPICall(server, getMockMarkNotificationsAsReadResponse()); + mockAPICall(server, NotificationMocks.getMockFeatureAnnouncementResponse()); + mockAPICall(server, NotificationMocks.getMockBatchCreateTriggersResponse()); + mockAPICall(server, NotificationMocks.getMockBatchDeleteTriggersResponse()); + mockAPICall(server, NotificationMocks.getMockListNotificationsResponse()); + mockAPICall( + server, + NotificationMocks.getMockMarkNotificationsAsReadResponse(), + ); // Push Notifications - mockAPICall(server, getMockRetrievePushNotificationLinksResponse()); - mockAPICall(server, getMockUpdatePushNotificationLinksResponse()); - mockAPICall(server, getMockCreateFCMRegistrationTokenResponse()); - mockAPICall(server, getMockDeleteFCMRegistrationTokenResponse()); + mockAPICall(server, PushMocks.getMockRetrievePushNotificationLinksResponse()); + mockAPICall(server, PushMocks.getMockUpdatePushNotificationLinksResponse()); + mockAPICall(server, PushMocks.getMockCreateFCMRegistrationTokenResponse()); + mockAPICall(server, PushMocks.getMockDeleteFCMRegistrationTokenResponse()); } function mockAPICall(server: Mockttp, response: MockResponse) { diff --git a/test/e2e/tests/phishing-controller/helpers.js b/test/e2e/tests/phishing-controller/helpers.js index a00d5ddb398f..bc0703af760d 100644 --- a/test/e2e/tests/phishing-controller/helpers.js +++ b/test/e2e/tests/phishing-controller/helpers.js @@ -1,6 +1,7 @@ const { METAMASK_STALELIST_URL, METAMASK_HOTLIST_DIFF_URL, + C2_DOMAIN_BLOCKLIST_URL, ListNames, } = require('@metamask/phishing-controller'); @@ -10,16 +11,15 @@ const { * @enum {BlockProvider} * @readonly * @property {string} MetaMask - The name of the MetaMask block provider. - * @property {string} PhishFort - The name of the PhishFort block provider. */ const BlockProvider = { MetaMask: 'metamask', - PhishFort: 'phishfort', }; module.exports = { METAMASK_HOTLIST_DIFF_URL, METAMASK_STALELIST_URL, + C2_DOMAIN_BLOCKLIST_URL, BlockProvider, ListNames, }; diff --git a/test/e2e/tests/phishing-controller/mocks.js b/test/e2e/tests/phishing-controller/mocks.js index f15e4b8487e2..fe11118c6fd2 100644 --- a/test/e2e/tests/phishing-controller/mocks.js +++ b/test/e2e/tests/phishing-controller/mocks.js @@ -1,6 +1,7 @@ const { METAMASK_STALELIST_URL, METAMASK_HOTLIST_DIFF_URL, + C2_DOMAIN_BLOCKLIST_URL, ListNames, BlockProvider, } = require('./helpers'); @@ -8,6 +9,11 @@ const { // last updated must not be 0 const lastUpdated = 1; const defaultHotlist = { data: [] }; +const defaultC2DomainBlocklist = { + recentlyAdded: [], + recentlyRemoved: [], + lastFetchedAt: '2024-08-27T15:30:45Z', +}; const defaultStalelist = { version: 2, tolerance: 2, @@ -16,12 +22,9 @@ const defaultStalelist = { fuzzylist: [], allowlist: [], blocklist: [], + c2DomainBlocklist: [], name: ListNames.MetaMask, }, - phishfort_hotlist: { - blocklist: [], - name: ListNames.Phishfort, - }, }; const emptyHtmlPage = (blockProvider) => ` @@ -39,12 +42,13 @@ const emptyHtmlPage = (blockProvider) => ` * Setup fetch mocks for the phishing detection feature. * * The mock configuration will show that "127.0.0.1" is blocked. The dynamic lookup on the warning - * page can be customized, so that we can test both the MetaMask and PhishFort block cases. + * page can be customized, so that we can test the MetaMask block cases. * * @param {import('mockttp').Mockttp} mockServer - The mock server. * @param {object} mockPhishingConfigResponseConfig - The response for the dynamic phishing * @param {number} mockPhishingConfigResponseConfig.statusCode - The status code for the response. * @param {string[]} mockPhishingConfigResponseConfig.blocklist - The blocklist for the response. + * @param {string[]} mockPhishingConfigResponseConfig.c2DomainBlocklist - The c2DomainBlocklist for the response. * @param {BlockProvider} mockPhishingConfigResponseConfig.blockProvider - The name of the provider who blocked the page. * configuration lookup performed by the warning page. */ @@ -53,6 +57,9 @@ async function setupPhishingDetectionMocks( { statusCode = 200, blocklist = ['127.0.0.1'], + c2DomainBlocklist = [ + 'a379a6f6eeafb9a55e378c118034e2751e682fab9f2d30ab13d2125586ce1947', + ], blockProvider = BlockProvider.MetaMask, }, ) { @@ -69,6 +76,7 @@ async function setupPhishingDetectionMocks( [blockProviderConfig]: { ...defaultStalelist[blockProviderConfig], blocklist, + c2DomainBlocklist, }, }, }, @@ -88,16 +96,17 @@ async function setupPhishingDetectionMocks( }); await mockServer - .forGet('https://github.com/MetaMask/eth-phishing-detect/issues/new') + .forGet(C2_DOMAIN_BLOCKLIST_URL) + .withQuery({ timestamp: '2024-08-27T15:30:45Z' }) .thenCallback(() => { return { statusCode: 200, - body: emptyHtmlPage(blockProvider), + json: defaultC2DomainBlocklist, }; }); await mockServer - .forGet('https://github.com/phishfort/phishfort-lists/issues/new') + .forGet('https://github.com/MetaMask/eth-phishing-detect/issues/new') .thenCallback(() => { return { statusCode: 200, @@ -155,14 +164,10 @@ async function mockEmptyStalelistAndHotlist(mockServer) { * @returns {string} The name of the phishing config in the response. */ function resolveProviderConfigName(providerName) { - switch (providerName.toLowerCase()) { - case BlockProvider.MetaMask: - return 'eth_phishing_detect_config'; - case BlockProvider.PhishFort: - return 'phishfort_hotlist'; - default: - throw new Error('Provider name must either be metamask or phishfort'); + if (providerName.toLowerCase() === BlockProvider.MetaMask) { + return 'eth_phishing_detect_config'; } + throw new Error(`Unknown provider: ${providerName}`); } module.exports = { diff --git a/test/e2e/tests/phishing-controller/phishing-detection.spec.js b/test/e2e/tests/phishing-controller/phishing-detection.spec.js index 494e6e7aafba..a2bff0e44b9d 100644 --- a/test/e2e/tests/phishing-controller/phishing-detection.spec.js +++ b/test/e2e/tests/phishing-controller/phishing-detection.spec.js @@ -253,39 +253,6 @@ describe('Phishing Detection', function () { ); }); - it('should navigate the user to PhishFort to dispute a Phishfort Block', async function () { - await withFixtures( - { - fixtures: new FixtureBuilder().build(), - ganacheOptions: defaultGanacheOptions, - title: this.test.fullTitle(), - testSpecificMock: async (mockServer) => { - return setupPhishingDetectionMocks(mockServer, { - blockProvider: BlockProvider.PhishFort, - blocklist: ['127.0.0.1'], - }); - }, - dapp: true, - }, - async ({ driver }) => { - await unlockWallet(driver); - await driver.openNewPage('http://127.0.0.1:8080'); - - await driver.switchToWindowWithTitle('MetaMask Phishing Detection'); - await driver.clickElement({ text: 'report a detection problem.' }); - - // wait for page to load before checking URL. - await driver.findElement({ - text: `Empty page by ${BlockProvider.PhishFort}`, - }); - assert.equal( - await driver.getCurrentUrl(), - `https://github.com/phishfort/phishfort-lists/issues/new?title=[Legitimate%20Site%20Blocked]%20127.0.0.1&body=http%3A%2F%2F127.0.0.1%2F`, - ); - }, - ); - }); - it('should open a new extension expanded view when clicking back to safety button', async function () { await withFixtures( { diff --git a/test/e2e/tests/portfolio/portfolio-site.spec.js b/test/e2e/tests/portfolio/portfolio-site.spec.js index 99c515ac36be..ff4c7c363a71 100644 --- a/test/e2e/tests/portfolio/portfolio-site.spec.js +++ b/test/e2e/tests/portfolio/portfolio-site.spec.js @@ -36,7 +36,7 @@ describe('Portfolio site', function () { await unlockWallet(driver); // Click Portfolio site - await driver.clickElement('[data-testid="eth-overview-portfolio"]'); + await driver.clickElement('[data-testid="portfolio-link"]'); await driver.waitUntilXWindowHandles(2); const windowHandles = await driver.getAllWindowHandles(); await driver.switchToWindowWithTitle('E2E Test Page', windowHandles); diff --git a/test/e2e/tests/request-queuing/ui.spec.js b/test/e2e/tests/request-queuing/ui.spec.js index ae84d7125d02..5da270427ca5 100644 --- a/test/e2e/tests/request-queuing/ui.spec.js +++ b/test/e2e/tests/request-queuing/ui.spec.js @@ -1,5 +1,8 @@ const { strict: assert } = require('assert'); const { Browser, until } = require('selenium-webdriver'); +const { + BUILT_IN_INFURA_NETWORKS, +} = require('../../../../shared/constants/network'); const FixtureBuilder = require('../../fixture-builder'); const { withFixtures, @@ -10,7 +13,6 @@ const { regularDelayMs, WINDOW_TITLES, defaultGanacheOptions, - switchToNotificationWindow, tempToggleSettingRedesignedConfirmations, veryLargeDelayMs, DAPP_TWO_URL, @@ -24,12 +26,7 @@ const { PAGES } = require('../../webdriver/driver'); // validate two dapps instead of 3 const IS_FIREFOX = process.env.SELENIUM_BROWSER === Browser.FIREFOX; -async function openDappAndSwitchChain( - driver, - dappUrl, - chainId, - notificationWindowIndex = 3, -) { +async function openDappAndSwitchChain(driver, dappUrl, chainId) { // Open the dapp await openDapp(driver, undefined, dappUrl); @@ -38,14 +35,14 @@ async function openDappAndSwitchChain( await driver.clickElement('#connectButton'); await driver.delay(regularDelayMs); - await switchToNotificationWindow(driver, notificationWindowIndex); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); await driver.clickElement({ text: 'Next', tag: 'button', css: '[data-testid="page-container-footer-next"]', }); - await driver.clickElement({ + await driver.clickElementAndWaitForWindowToClose({ text: 'Confirm', tag: 'button', css: '[data-testid="page-container-footer-next"]', @@ -67,12 +64,14 @@ async function openDappAndSwitchChain( ); await driver.delay(veryLargeDelayMs); - await switchToNotificationWindow(driver, notificationWindowIndex); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); await driver.findClickableElement( '[data-testid="confirmation-submit-button"]', ); - await driver.clickElement('[data-testid="confirmation-submit-button"]'); + await driver.clickElementAndWaitForWindowToClose( + '[data-testid="confirmation-submit-button"]', + ); // Switch back to the dapp await driver.switchToWindowWithUrl(dappUrl); @@ -89,13 +88,9 @@ async function selectDappClickPersonalSign(driver, dappUrl) { await driver.clickElement('#personalSign'); } -async function switchToNotificationPopoverValidateDetails( - driver, - expectedDetails, -) { +async function switchToDialogPopoverValidateDetails(driver, expectedDetails) { // Switches to the MetaMask Dialog window for confirmation - const windowHandles = await driver.getAllWindowHandles(); - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog, windowHandles); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); await driver.findElement({ css: '[data-testid="network-display"], [data-testid="signature-request-network-display"]', @@ -112,12 +107,22 @@ async function switchToNotificationPopoverValidateDetails( window.stateHooks?.getCleanAppState?.(), ); - const { chainId } = notificationWindowState.metamask.providerConfig; + const { + metamask: { selectedNetworkClientId, networkConfigurations }, + } = notificationWindowState; + + const { chainId } = + BUILT_IN_INFURA_NETWORKS[selectedNetworkClientId] ?? + networkConfigurations[selectedNetworkClientId]; + assert.equal(chainId, expectedDetails.chainId); } async function rejectTransaction(driver) { - await driver.clickElement({ tag: 'button', text: 'Reject' }); + await driver.clickElementAndWaitForWindowToClose({ + tag: 'button', + text: 'Reject', + }); } async function confirmTransaction(driver) { @@ -201,7 +206,7 @@ describe('Request-queue UI changes', function () { await openDappAndSwitchChain(driver, DAPP_URL); // Open the second dapp and switch chains - await openDappAndSwitchChain(driver, DAPP_ONE_URL, '0x53a', 4); + await openDappAndSwitchChain(driver, DAPP_ONE_URL, '0x53a'); // Go to wallet fullscreen, ensure that the global network changed to Ethereum Mainnet await driver.switchToWindowWithTitle( @@ -214,7 +219,7 @@ describe('Request-queue UI changes', function () { // Go to the first dapp, ensure it uses localhost await selectDappClickSend(driver, DAPP_URL); - await switchToNotificationPopoverValidateDetails(driver, { + await switchToDialogPopoverValidateDetails(driver, { chainId: '0x539', networkText: 'Localhost 8545', originText: DAPP_URL, @@ -223,7 +228,7 @@ describe('Request-queue UI changes', function () { // Go to the second dapp, ensure it uses Ethereum Mainnet await selectDappClickSend(driver, DAPP_ONE_URL); - await switchToNotificationPopoverValidateDetails(driver, { + await switchToDialogPopoverValidateDetails(driver, { chainId: '0x53a', networkText: 'Localhost 8546', originText: DAPP_ONE_URL, @@ -274,11 +279,11 @@ describe('Request-queue UI changes', function () { await openDappAndSwitchChain(driver, DAPP_URL); // Open the second dapp and switch chains - await openDappAndSwitchChain(driver, DAPP_ONE_URL, '0x53a', 4); + await openDappAndSwitchChain(driver, DAPP_ONE_URL, '0x53a'); if (!IS_FIREFOX) { // Open the third dapp and switch chains - await openDappAndSwitchChain(driver, DAPP_TWO_URL, '0x3e8', 5); + await openDappAndSwitchChain(driver, DAPP_TWO_URL, '0x3e8'); } // Trigger a send confirmation on the first dapp, do not confirm or reject @@ -293,7 +298,7 @@ describe('Request-queue UI changes', function () { } // Switch to the Notification window, ensure first transaction still showing - await switchToNotificationPopoverValidateDetails(driver, { + await switchToDialogPopoverValidateDetails(driver, { chainId: '0x539', networkText: 'Localhost 8545', originText: DAPP_URL, @@ -304,7 +309,7 @@ describe('Request-queue UI changes', function () { await driver.delay(veryLargeDelayMs); // Switch to the new Notification window, ensure second transaction showing - await switchToNotificationPopoverValidateDetails(driver, { + await switchToDialogPopoverValidateDetails(driver, { chainId: '0x53a', networkText: 'Localhost 8546', originText: DAPP_ONE_URL, @@ -316,7 +321,7 @@ describe('Request-queue UI changes', function () { if (!IS_FIREFOX) { // Switch to the new Notification window, ensure third transaction showing - await switchToNotificationPopoverValidateDetails(driver, { + await switchToDialogPopoverValidateDetails(driver, { chainId: '0x3e8', networkText: 'Localhost 7777', originText: DAPP_TWO_URL, @@ -392,7 +397,7 @@ describe('Request-queue UI changes', function () { await openDappAndSwitchChain(driver, DAPP_URL); // Open the second dapp and switch chains - await openDappAndSwitchChain(driver, DAPP_ONE_URL, '0x1', 4); + await openDappAndSwitchChain(driver, DAPP_ONE_URL, '0x1'); // Go to wallet fullscreen, ensure that the global network changed to Ethereum Mainnet await driver.switchToWindowWithTitle( @@ -426,7 +431,7 @@ describe('Request-queue UI changes', function () { // The current globally selected network, Ethereum Mainnet, should be used await selectDappClickSend(driver, DAPP_URL); await driver.delay(veryLargeDelayMs); - await switchToNotificationPopoverValidateDetails(driver, { + await switchToDialogPopoverValidateDetails(driver, { chainId: '0x1', networkText: 'Ethereum Mainnet', originText: DAPP_URL, @@ -514,7 +519,7 @@ describe('Request-queue UI changes', function () { await openDappAndSwitchChain(driver, DAPP_URL); // Open tab 2, switch to Ethereum Mainnet - await openDappAndSwitchChain(driver, DAPP_ONE_URL, '0x1', 4); + await openDappAndSwitchChain(driver, DAPP_ONE_URL, '0x1'); // Open the popup with shimmed activeTabOrigin await openPopupWithActiveTabOrigin(driver, DAPP_URL); @@ -567,7 +572,7 @@ describe('Request-queue UI changes', function () { await openDappAndSwitchChain(driver, DAPP_URL); // Open tab 2, switch to Ethereum Mainnet - await openDappAndSwitchChain(driver, DAPP_ONE_URL, '0x1', 4); + await openDappAndSwitchChain(driver, DAPP_ONE_URL, '0x1'); await driver.waitForSelector({ css: '.error-message-text', text: 'You are on the Ethereum Mainnet.', @@ -645,7 +650,7 @@ describe('Request-queue UI changes', function () { await openDappAndSwitchChain(driver, DAPP_URL); // Open the second dapp and switch chains - await openDappAndSwitchChain(driver, DAPP_ONE_URL, '0x1', 4); + await openDappAndSwitchChain(driver, DAPP_ONE_URL, '0x1'); // Go to wallet fullscreen, ensure that the global network changed to Ethereum Mainnet await driver.switchToWindowWithTitle( @@ -667,7 +672,7 @@ describe('Request-queue UI changes', function () { // popup to take a few seconds to open in MV3 (issue #25690) await driver.waitUntilXWindowHandles(4, 1000, 15000); - await switchToNotificationPopoverValidateDetails(driver, { + await switchToDialogPopoverValidateDetails(driver, { chainId: '0x539', networkText: 'Localhost 8545', originText: DAPP_URL, @@ -715,7 +720,7 @@ describe('Request-queue UI changes', function () { await openDappAndSwitchChain(driver, DAPP_URL); // Open the second dapp and switch chains - await openDappAndSwitchChain(driver, DAPP_ONE_URL, '0x1', 4); + await openDappAndSwitchChain(driver, DAPP_ONE_URL, '0x1'); // Go to wallet fullscreen, ensure that the global network changed to Ethereum Mainnet await driver.switchToWindowWithTitle( @@ -737,7 +742,7 @@ describe('Request-queue UI changes', function () { // popup to take a few seconds to open in MV3 (issue #25690) await driver.waitUntilXWindowHandles(4, 1000, 15000); - await switchToNotificationPopoverValidateDetails(driver, { + await switchToDialogPopoverValidateDetails(driver, { chainId: '0x539', networkText: 'Localhost 8545', originText: DAPP_URL, diff --git a/test/e2e/tests/simulation-details/simulation-details.spec.ts b/test/e2e/tests/simulation-details/simulation-details.spec.ts index e74ae74505a6..ea0f7b9f2632 100644 --- a/test/e2e/tests/simulation-details/simulation-details.spec.ts +++ b/test/e2e/tests/simulation-details/simulation-details.spec.ts @@ -1,46 +1,47 @@ -import { Mockttp, MockttpServer } from 'mockttp'; import { hexToNumber } from '@metamask/utils'; +import { Mockttp, MockttpServer } from 'mockttp'; +import { CHAIN_IDS } from '../../../../shared/constants/network'; import FixtureBuilder from '../../fixture-builder'; import { + createDappTransaction, + Fixtures, unlockWallet, + WINDOW_TITLES, withFixtures, - createDappTransaction, - switchToNotificationWindow, } from '../../helpers'; import { Driver } from '../../webdriver/driver'; -import { CHAIN_IDS } from '../../../../shared/constants/network'; -import { - SEND_ETH_REQUEST_MOCK, - SEND_ETH_TRANSACTION_MOCK, -} from './mock-request-send-eth'; -import { - BUY_ERC20_TRANSACTION, - BUY_ERC20_REQUEST_1_MOCK, - BUY_ERC20_REQUEST_2_MOCK, -} from './mock-request-buy-erc20'; -import { MockRequestResponse } from './types'; -import { - INSUFFICIENT_GAS_REQUEST_MOCK, - INSUFFICIENT_GAS_TRANSACTION_MOCK, -} from './mock-request-error-insuffient-gas'; import { BUY_ERC1155_REQUEST_1_MOCK, BUY_ERC1155_REQUEST_2_MOCK, BUY_ERC1155_TRANSACTION_MOCK, } from './mock-request-buy-erc1155'; +import { + BUY_ERC20_REQUEST_1_MOCK, + BUY_ERC20_REQUEST_2_MOCK, + BUY_ERC20_TRANSACTION, +} from './mock-request-buy-erc20'; import { BUY_ERC721_REQUEST_1_MOCK, BUY_ERC721_REQUEST_2_MOCK, BUY_ERC721_TRANSACTION_MOCK, } from './mock-request-buy-erc721'; import { - NO_CHANGES_REQUEST_MOCK, - NO_CHANGES_TRANSACTION_MOCK, -} from './mock-request-no-changes'; + INSUFFICIENT_GAS_REQUEST_MOCK, + INSUFFICIENT_GAS_TRANSACTION_MOCK, +} from './mock-request-error-insuffient-gas'; import { MALFORMED_TRANSACTION_MOCK, MALFORMED_TRANSACTION_REQUEST_MOCK, } from './mock-request-error-malformed-transaction'; +import { + NO_CHANGES_REQUEST_MOCK, + NO_CHANGES_TRANSACTION_MOCK, +} from './mock-request-no-changes'; +import { + SEND_ETH_REQUEST_MOCK, + SEND_ETH_TRANSACTION_MOCK, +} from './mock-request-send-eth'; +import { MockRequestResponse } from './types'; const TX_SENTINEL_URL = 'https://tx-sentinel-ethereum-mainnet.api.cx.metamask.io/'; @@ -51,11 +52,6 @@ const mockNetworkRequest = async (mockServer: Mockttp) => { }); }; -type TestArgs = { - driver: Driver; - mockServer: MockttpServer; -}; - async function withFixturesForSimulationDetails( { title, @@ -66,7 +62,7 @@ async function withFixturesForSimulationDetails( inputChainId?: string; mockRequests: (mockServer: MockttpServer) => Promise; }, - test: (args: TestArgs) => Promise, + test: (args: Pick) => Promise, ) { const testSpecificMock = async (mockServer: MockttpServer) => { await mockNetworkRequest(mockServer); @@ -85,7 +81,7 @@ async function withFixturesForSimulationDetails( chainId: hexToNumber(inputChainId), }, }, - async ({ driver, mockServer }: TestArgs) => { + async ({ driver, mockServer }) => { await unlockWallet(driver); await test({ driver, mockServer }); }, @@ -138,7 +134,7 @@ describe('Simulation Details', () => { async ({ driver }) => { await createDappTransaction(driver, SEND_ETH_TRANSACTION_MOCK); - await switchToNotificationWindow(driver); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); await expectBalanceChange(driver, true, 0, '- 0.001', 'ETH'); }, ); @@ -151,10 +147,10 @@ describe('Simulation Details', () => { }; await withFixturesForSimulationDetails( { title: this.test?.fullTitle(), mockRequests }, - async ({ driver }: TestArgs) => { + async ({ driver }) => { await createDappTransaction(driver, BUY_ERC20_TRANSACTION); - await switchToNotificationWindow(driver); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); await expectBalanceChange(driver, true, 0, '- 0.002', 'ETH'); await expectBalanceChange(driver, false, 0, '+ 6.756', 'DAI'); }, @@ -168,10 +164,10 @@ describe('Simulation Details', () => { }; await withFixturesForSimulationDetails( { title: this.test?.fullTitle(), mockRequests }, - async ({ driver }: TestArgs) => { + async ({ driver }) => { await createDappTransaction(driver, BUY_ERC721_TRANSACTION_MOCK); - await switchToNotificationWindow(driver); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); await expectBalanceChange(driver, true, 0, '- 0.014', 'ETH'); await expectBalanceChange( driver, @@ -191,10 +187,10 @@ describe('Simulation Details', () => { }; await withFixturesForSimulationDetails( { title: this.test?.fullTitle(), mockRequests }, - async ({ driver }: TestArgs) => { + async ({ driver }) => { await createDappTransaction(driver, BUY_ERC1155_TRANSACTION_MOCK); - await switchToNotificationWindow(driver); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); await expectBalanceChange(driver, true, 0, '- 0.00045', 'ETH'); await expectBalanceChange( driver, @@ -216,7 +212,7 @@ describe('Simulation Details', () => { async ({ driver }) => { await createDappTransaction(driver, NO_CHANGES_TRANSACTION_MOCK); - await switchToNotificationWindow(driver); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); await driver.findElement({ css: '[data-testid="simulation-details-layout"]', text: 'No changes predicted for your wallet', @@ -234,7 +230,7 @@ describe('Simulation Details', () => { async ({ driver }) => { await createDappTransaction(driver, INSUFFICIENT_GAS_TRANSACTION_MOCK); - await switchToNotificationWindow(driver); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); await driver.findElement({ css: '[data-testid="simulation-details-layout"]', text: 'This transaction is likely to fail', @@ -256,7 +252,7 @@ describe('Simulation Details', () => { async ({ driver }) => { await createDappTransaction(driver, SEND_ETH_TRANSACTION_MOCK); - await switchToNotificationWindow(driver); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); await driver.assertElementNotPresent( '[data-testid="simulation-details-layout"]', { waitAtLeastGuard: 1000 }, @@ -277,7 +273,7 @@ describe('Simulation Details', () => { async ({ driver }) => { await createDappTransaction(driver, MALFORMED_TRANSACTION_MOCK); - await switchToNotificationWindow(driver); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); await driver.findElement({ css: '[data-testid="simulation-details-layout"]', text: 'There was an error loading your estimation', diff --git a/test/e2e/tests/swap-send/swap-send-erc20.spec.ts b/test/e2e/tests/swap-send/swap-send-erc20.spec.ts index 8fbfe1e45dcb..21e6c49a0c61 100644 --- a/test/e2e/tests/swap-send/swap-send-erc20.spec.ts +++ b/test/e2e/tests/swap-send/swap-send-erc20.spec.ts @@ -4,8 +4,6 @@ import { openActionMenuAndStartSendFlow, logInWithBalanceValidation, } from '../../helpers'; -import { Driver } from '../../webdriver/driver'; -import type { Ganache } from '../../seeder/ganache'; import { NATIVE_TOKEN_SYMBOL, SwapSendPage, @@ -24,13 +22,7 @@ describe('Swap-Send ERC20 @no-mmi', function () { SWAP_SEND_QUOTES_RESPONSE_TST_ETH, '?sourceAmount=100000&sourceToken=0x581c3C1A2A4EBDE2A0Df29B5cf4c116E42945947&destinationToken=0x0000000000000000000000000000000000000000&sender=0x5cfe73b6021e818b776b421b1c4db2474086a7e1&recipient=0xc427D562164062a23a5cFf596A4a3208e72Acd28&slippage=2', ), - async ({ - driver, - ganacheServer, - }: { - driver: Driver; - ganacheServer: Ganache; - }) => { + async ({ driver, ganacheServer }) => { const swapSendPage = new SwapSendPage(driver); await logInWithBalanceValidation(driver, ganacheServer); diff --git a/test/e2e/tests/swap-send/swap-send-eth.spec.ts b/test/e2e/tests/swap-send/swap-send-eth.spec.ts index a0398094ac4e..f9457bdca8d1 100644 --- a/test/e2e/tests/swap-send/swap-send-eth.spec.ts +++ b/test/e2e/tests/swap-send/swap-send-eth.spec.ts @@ -1,10 +1,9 @@ import { Suite } from 'mocha'; import { - withFixtures, - openActionMenuAndStartSendFlow, logInWithBalanceValidation, + openActionMenuAndStartSendFlow, + withFixtures, } from '../../helpers'; -import type { Ganache } from '../../seeder/ganache'; import { NATIVE_TOKEN_SYMBOL, SwapSendPage, @@ -18,15 +17,7 @@ describe('Swap-Send ETH @no-mmi', function () { it('submits a transaction successfully', async function () { await withFixtures( getSwapSendFixtures(this.test?.fullTitle()), - async ({ - driver, - ganacheServer, - }: { - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - driver: any; - ganacheServer: Ganache; - }) => { + async ({ driver, ganacheServer }) => { const swapSendPage = new SwapSendPage(driver); await logInWithBalanceValidation(driver, ganacheServer); @@ -90,15 +81,7 @@ describe('Swap-Send ETH @no-mmi', function () { it('sets max amount', async function () { await withFixtures( getSwapSendFixtures(this.test?.fullTitle()), - async ({ - driver, - ganacheServer, - }: { - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - driver: any; - ganacheServer: Ganache; - }) => { + async ({ driver, ganacheServer }) => { const swapSendPage = new SwapSendPage(driver); await logInWithBalanceValidation(driver, ganacheServer); diff --git a/test/e2e/tests/swap-send/swap-send-test-utils.ts b/test/e2e/tests/swap-send/swap-send-test-utils.ts index a78cd2a27057..c5f4d8bb3d9c 100644 --- a/test/e2e/tests/swap-send/swap-send-test-utils.ts +++ b/test/e2e/tests/swap-send/swap-send-test-utils.ts @@ -266,9 +266,7 @@ export const getSwapSendFixtures = ( ) => { const ETH_CONVERSION_RATE_USD = 3010; return { - driverOptions: { - openDevToolsForTabs: true, - }, + driverOptions: { responsive: true }, fixtures: new FixtureBuilder() .withPreferencesController({ featureFlags: {}, diff --git a/test/e2e/tests/tokens/increase-token-allowance.spec.js b/test/e2e/tests/tokens/increase-token-allowance.spec.js index ed35d314228b..ef1ed8e19998 100644 --- a/test/e2e/tests/tokens/increase-token-allowance.spec.js +++ b/test/e2e/tests/tokens/increase-token-allowance.spec.js @@ -104,7 +104,7 @@ describe('Increase Token Allowance', function () { tag: 'button', text: 'Next', }); - driver.waitForSelector({ + await driver.waitForSelector({ css: '.box--display-flex > h6', text: `10 TST`, }); @@ -158,10 +158,7 @@ describe('Increase Token Allowance', function () { await transferFromRecipientInputEl.clear(); await transferFromRecipientInputEl.fill(recipientAccount); - await driver.clickElement({ - text: 'Transfer From Tokens', - tag: 'button', - }); + await driver.clickElement('#transferFromTokens'); await driver.delay(2000); } @@ -283,7 +280,7 @@ describe('Increase Token Allowance', function () { text: `${finalSpendingCap} TST`, css: '.mm-box > h6', }); - await driver.clickElement({ + await driver.clickElementAndWaitForWindowToClose({ tag: 'button', text: 'Approve', }); @@ -296,6 +293,8 @@ describe('Increase Token Allowance', function () { css: '.transaction-list__completed-transactions .activity-list-item [data-testid="activity-list-item-action"]', text: 'Increase TST spending cap', }); + + await driver.delay(2000); } async function confirmTransferFromTokensSuccess(driver) { diff --git a/test/e2e/tests/tokens/token-list.spec.ts b/test/e2e/tests/tokens/token-list.spec.ts index 75f3a1e4db91..32b5ea85e3ae 100644 --- a/test/e2e/tests/tokens/token-list.spec.ts +++ b/test/e2e/tests/tokens/token-list.spec.ts @@ -97,7 +97,7 @@ describe('Token List', function () { ); }); - it.only('shows percentage increase for an ERC20 token with prices available', async function () { + it('shows percentage increase for an ERC20 token with prices available', async function () { const ethConversionInUsd = 10000; // Prices are in ETH diff --git a/test/e2e/tests/transaction/edit-gas-fee.spec.js b/test/e2e/tests/transaction/edit-gas-fee.spec.js index 07944e0e973c..85ae4da3a31f 100644 --- a/test/e2e/tests/transaction/edit-gas-fee.spec.js +++ b/test/e2e/tests/transaction/edit-gas-fee.spec.js @@ -1,11 +1,14 @@ const { strict: assert } = require('assert'); +const { + createInternalTransaction, +} = require('../../page-objects/flows/transaction'); + const { withFixtures, openDapp, unlockWallet, generateGanacheOptions, WINDOW_TITLES, - createInternalTransaction, } = require('../../helpers'); const FixtureBuilder = require('../../fixture-builder'); diff --git a/test/e2e/tests/transaction/navigate-transactions.spec.js b/test/e2e/tests/transaction/navigate-transactions.spec.js index 151e22ba55b9..45c27d11e76e 100644 --- a/test/e2e/tests/transaction/navigate-transactions.spec.js +++ b/test/e2e/tests/transaction/navigate-transactions.spec.js @@ -1,3 +1,11 @@ +const { + createDappTransaction, +} = require('../../page-objects/flows/transaction'); + +const { + default: ConfirmationNavigation, +} = require('../../page-objects/pages/confirmations/legacy/navigation'); + const { withFixtures, openDapp, @@ -5,7 +13,6 @@ const { unlockWallet, generateGanacheOptions, WINDOW_TITLES, - createDappTransactionTypeTwo, } = require('../../helpers'); const FixtureBuilder = require('../../fixture-builder'); @@ -27,26 +34,28 @@ describe('Navigate transactions', function () { await unlockWallet(driver); await createMultipleTransactions(driver, TRANSACTION_COUNT); - await clickNextPage(driver); - await expectPageNumber(driver, 2, 4); + const navigation = new ConfirmationNavigation(driver); + + await navigation.clickNextPage(); + await navigation.check_pageNumbers(2, 4); - await clickNextPage(driver); - await expectPageNumber(driver, 3, 4); + await navigation.clickNextPage(); + await navigation.check_pageNumbers(3, 4); - await clickNextPage(driver); - await expectPageNumber(driver, 4, 4); + await navigation.clickNextPage(); + await navigation.check_pageNumbers(4, 4); - await clickFirstPage(driver); - await expectPageNumber(driver, 1, 4); + await navigation.clickFirstPage(); + await navigation.check_pageNumbers(1, 4); - await clickLastPage(driver); - await expectPageNumber(driver, 4, 4); + await navigation.clickLastPage(); + await navigation.check_pageNumbers(4, 4); - await clickPreviousPage(driver); - await expectPageNumber(driver, 3, 4); + await navigation.clickPreviousPage(); + await navigation.check_pageNumbers(3, 4); - await clickPreviousPage(driver); - await expectPageNumber(driver, 2, 4); + await navigation.clickPreviousPage(); + await navigation.check_pageNumbers(2, 4); }, ); }); @@ -66,8 +75,10 @@ describe('Navigate transactions', function () { await unlockWallet(driver); await createMultipleTransactions(driver, TRANSACTION_COUNT); - await clickNextPage(driver); - await expectPageNumber(driver, 2, 4); + const navigation = new ConfirmationNavigation(driver); + + await navigation.clickNextPage(); + await navigation.check_pageNumbers(2, 4); await driver.switchToWindowWithTitle( WINDOW_TITLES.ExtensionInFullScreenView, @@ -77,7 +88,7 @@ describe('Navigate transactions', function () { await driver.clickElement({ text: 'Send', tag: 'button' }); await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - await expectPageNumber(driver, 2, 5); + await navigation.check_pageNumbers(2, 5); }, ); }); @@ -100,7 +111,8 @@ describe('Navigate transactions', function () { // reject transaction await driver.clickElement({ text: 'Reject', tag: 'button' }); - await expectPageNumber(driver, 1, 3); + const navigation = new ConfirmationNavigation(driver); + await navigation.check_pageNumbers(1, 3); }, ); }); @@ -123,7 +135,8 @@ describe('Navigate transactions', function () { // confirm transaction await driver.clickElement({ text: 'Confirm', tag: 'button' }); - await expectPageNumber(driver, 1, 3); + const navigation = new ConfirmationNavigation(driver); + await navigation.check_pageNumbers(1, 3); }, ); }); @@ -146,6 +159,7 @@ describe('Navigate transactions', function () { // reject transactions await driver.clickElement({ text: 'Reject 4', tag: 'a' }); await driver.clickElement({ text: 'Reject all', tag: 'button' }); + await driver.switchToWindowWithTitle( WINDOW_TITLES.ExtensionInFullScreenView, ); @@ -157,7 +171,7 @@ describe('Navigate transactions', function () { async function createMultipleTransactions(driver, count) { for (let i = 0; i < count; i++) { - await createDappTransactionTypeTwo(driver); + await createDappTransaction(driver); } await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); @@ -168,26 +182,3 @@ async function createMultipleTransactions(driver, count) { text: '0.001', }); } - -async function clickFirstPage(driver) { - await driver.clickElement('[data-testid="first-page"]'); -} - -async function clickLastPage(driver) { - await driver.clickElement('[data-testid="last-page"]'); -} - -async function clickNextPage(driver) { - await driver.clickElement('[data-testid="next-page"]'); -} - -async function clickPreviousPage(driver) { - await driver.clickElement('[data-testid="previous-page"]'); -} - -async function expectPageNumber(driver, current, total) { - await driver.findElement({ - css: '.confirm-page-container-navigation', - text: `${current} of ${total}`, - }); -} diff --git a/test/e2e/tests/transaction/send-edit.spec.js b/test/e2e/tests/transaction/send-edit.spec.js index 6e372ad2d64a..6fffe1d6b300 100644 --- a/test/e2e/tests/transaction/send-edit.spec.js +++ b/test/e2e/tests/transaction/send-edit.spec.js @@ -1,4 +1,5 @@ const { strict: assert } = require('assert'); + const { defaultGanacheOptions, withFixtures, diff --git a/test/e2e/tests/transaction/send-eth.spec.js b/test/e2e/tests/transaction/send-eth.spec.js index 4dce3c5567d9..5c49bba0cf4e 100644 --- a/test/e2e/tests/transaction/send-eth.spec.js +++ b/test/e2e/tests/transaction/send-eth.spec.js @@ -303,7 +303,7 @@ describe('Send ETH', function () { }); // the transaction has the expected gas price - driver.clickElement( + await driver.clickElement( '[data-testid="transaction-list-item-primary-currency"]', ); await driver.waitForSelector({ diff --git a/test/e2e/vault-decryption-chrome.spec.js b/test/e2e/vault-decryption-chrome.spec.js index a04e25f176de..25f79a3e10af 100644 --- a/test/e2e/vault-decryption-chrome.spec.js +++ b/test/e2e/vault-decryption-chrome.spec.js @@ -43,6 +43,18 @@ async function getExtensionStorageFilePath(driver) { return path.resolve(extensionStoragePath, logFiles[0]); } +/** + * Gets the size of a file in bytes. + * + * @param {string} filePath - The path to the file. + * @returns {Promise} A promise that resolves to the size of the file in bytes. + */ +async function getFileSize(filePath) { + const stats = await fs.promises.stat(filePath); + console.log(`File Size =========================: ${stats.size} bytes`); + return stats.size; +} + /** * Closes the announcements popover if present * @@ -121,7 +133,29 @@ describe('Vault Decryptor Page', function () { // fill the input field with storage recovered from filesystem await driver.clickElement('[name="vault-source"]'); const inputField = await driver.findElement('#fileinput'); - inputField.press(await getExtensionStorageFilePath(driver)); + + const filePath = await getExtensionStorageFilePath(driver); + + // Retry-logic to ensure the file is ready before uploading it + // to mitigate flakiness when Chrome hasn't finished writing + const MAX_RETRIES = 3; + const MIN_FILE_SIZE = 1000000; // bytes + + for (let attempt = 0; attempt < MAX_RETRIES; attempt++) { + const fileSize = await getFileSize(filePath); + if (fileSize > MIN_FILE_SIZE) { + break; + } else { + console.log(`File size is too small (${fileSize} bytes)`); + if (attempt < MAX_RETRIES - 1) { + console.log(`Waiting for 2 seconds before retrying...`); + await driver.delay(2000); + } + } + } + + await inputField.press(filePath); + // fill in the password await driver.fill('#passwordinput', WALLET_PASSWORD); // decrypt diff --git a/test/e2e/webdriver/chrome.js b/test/e2e/webdriver/chrome.js index 4a8d41afe9ec..fa56c107439e 100644 --- a/test/e2e/webdriver/chrome.js +++ b/test/e2e/webdriver/chrome.js @@ -24,6 +24,7 @@ function getProxyServer(proxyPort) { class ChromeDriver { static async build({ openDevToolsForTabs, + responsive, constrainWindowSize, port, proxyPort, @@ -43,7 +44,9 @@ class ChromeDriver { args.push(`load-extension=${process.cwd()}/dist/chrome`); } - if (openDevToolsForTabs) { + // When "responsive" is enabled, open dev tools to force a smaller viewport + // The minimum window width on Chrome is too large, this is how we're forcing the viewport to be smaller + if (openDevToolsForTabs || responsive) { args.push('--auto-open-devtools-for-tabs'); } diff --git a/test/e2e/webdriver/driver.js b/test/e2e/webdriver/driver.js index 06d0774b09cc..74ff8f19b9cc 100644 --- a/test/e2e/webdriver/driver.js +++ b/test/e2e/webdriver/driver.js @@ -124,8 +124,8 @@ class Driver { /** * @param {!ThenableWebDriver} driver - A {@code WebDriver} instance * @param {string} browser - The type of browser this driver is controlling - * @param extensionUrl - * @param {number} timeout + * @param {string} extensionUrl + * @param {number} timeout - Defaults to 10000 milliseconds (10 seconds) */ constructor(driver, browser, extensionUrl, timeout = 10 * 1000) { this.driver = driver; @@ -862,6 +862,47 @@ class Driver { return await this.windowHandles.getAllWindowHandles(); } + /** + * Function that aims to simulate a click action on a specified web element + * within a web page and waits for the current window to close. + * + * @param {string | object} rawLocator - Element locator + * @param {number} [retries] - The number of times to retry the click action if it fails + * @returns {Promise} promise that resolves to the WebElement + */ + async clickElementAndWaitForWindowToClose(rawLocator, retries = 3) { + const handle = await this.driver.getWindowHandle(); + await this.clickElement(rawLocator, retries); + await this.waitForWindowToClose(handle); + } + + /** + * Waits for the specified window handle to close before returning. + * + * @param {string} handle - The handle of the window or tab we'll wait for. + * @param {number} [timeout] - The amount of time in milliseconds to wait + * before timing out. Defaults to `this.timeout`. + * @throws {Error} throws an error if the window handle doesn't close within + * the timeout. + */ + async waitForWindowToClose(handle, timeout = this.timeout) { + const start = Date.now(); + // eslint-disable-next-line no-constant-condition + while (true) { + const handles = await this.getAllWindowHandles(); + if (!handles.includes(handle)) { + return; + } + + const timeElapsed = Date.now() - start; + if (timeElapsed > timeout) { + throw new Error( + `waitForWindowToClose timed out waiting for window handle '${handle}' to close.`, + ); + } + } + } + /** * Waits until the specified number of window handles are present. * diff --git a/test/e2e/webdriver/index.js b/test/e2e/webdriver/index.js index ddc0d5543822..782877dfea39 100644 --- a/test/e2e/webdriver/index.js +++ b/test/e2e/webdriver/index.js @@ -4,9 +4,10 @@ const ChromeDriver = require('./chrome'); const FirefoxDriver = require('./firefox'); async function buildWebDriver({ + responsive, openDevToolsForTabs, - port, constrainWindowSize, + port, timeOut, proxyPort, } = {}) { @@ -17,6 +18,7 @@ async function buildWebDriver({ extensionId, extensionUrl, } = await buildBrowserWebDriver(browser, { + responsive, openDevToolsForTabs, port, constrainWindowSize, diff --git a/test/integration/confirmations/transactions/alerts.test.tsx b/test/integration/confirmations/transactions/alerts.test.tsx new file mode 100644 index 000000000000..a2c2dda0e78b --- /dev/null +++ b/test/integration/confirmations/transactions/alerts.test.tsx @@ -0,0 +1,240 @@ +import { act, fireEvent } from '@testing-library/react'; +import { ApprovalType } from '@metamask/controller-utils'; +import nock from 'nock'; +import mockMetaMaskState from '../../data/integration-init-state.json'; +import { integrationTestRender } from '../../../lib/render-helpers'; +import * as backgroundConnection from '../../../../ui/store/background-connection'; +import { createMockImplementation, mock4byte } from '../../helpers'; +import { getUnapprovedApproveTransaction } from './transactionDataHelpers'; + +jest.mock('../../../../ui/store/background-connection', () => ({ + ...jest.requireActual('../../../../ui/store/background-connection'), + submitRequestToBackground: jest.fn(), + callBackgroundMethod: jest.fn(), +})); + +const mockedBackgroundConnection = jest.mocked(backgroundConnection); + +const backgroundConnectionMocked = { + onNotification: jest.fn(), +}; +export const pendingTransactionId = '48a75190-45ca-11ef-9001-f3886ec2397c'; +export const pendingTransactionTime = new Date().getTime(); + +const getMetaMaskStateWithUnapprovedApproveTransaction = ( + accountAddress: string, +) => { + return { + ...mockMetaMaskState, + preferences: { + ...mockMetaMaskState.preferences, + redesignedConfirmationsEnabled: true, + }, + pendingApprovals: { + [pendingTransactionId]: { + id: pendingTransactionId, + origin: 'origin', + time: pendingTransactionTime, + type: ApprovalType.Transaction, + requestData: { + txId: pendingTransactionId, + }, + requestState: null, + expectsResult: false, + }, + }, + pendingApprovalCount: 1, + knownMethodData: { + '0x3b4b1381': { + name: 'Mint NFTs', + params: [ + { + type: 'uint256', + }, + ], + }, + }, + transactions: [ + getUnapprovedApproveTransaction( + accountAddress, + pendingTransactionId, + pendingTransactionTime, + ), + ], + }; +}; + +const setupSubmitRequestToBackgroundMocks = ( + mockRequests?: Record, +) => { + mockedBackgroundConnection.submitRequestToBackground.mockImplementation( + createMockImplementation({ + ...(mockRequests ?? {}), + }), + ); +}; + +describe('Contract Interaction Confirmation', () => { + beforeEach(() => { + jest.resetAllMocks(); + setupSubmitRequestToBackgroundMocks(); + const APPROVE_NFT_HEX_SIG = '0x095ea7b3'; + mock4byte(APPROVE_NFT_HEX_SIG); + }); + + afterEach(() => { + nock.cleanAll(); + }); + + it('displays the alert when network is busy', async () => { + const account = + mockMetaMaskState.internalAccounts.accounts[ + mockMetaMaskState.internalAccounts + .selectedAccount as keyof typeof mockMetaMaskState.internalAccounts.accounts + ]; + + const mockedMetaMaskState = + getMetaMaskStateWithUnapprovedApproveTransaction(account.address); + + const { findByTestId, getByTestId, queryByTestId } = + await integrationTestRender({ + preloadedState: { + ...mockedMetaMaskState, + gasFeeEstimatesByChainId: { + '0x5': { + gasFeeEstimates: { + networkCongestion: 1.0005, + }, + }, + }, + }, + backgroundConnection: backgroundConnectionMocked, + }); + + act(() => { + fireEvent.click(getByTestId('inline-alert')); + }); + + expect(await findByTestId('alert-modal')).toBeInTheDocument(); + + expect( + await findByTestId('alert-modal__selected-alert'), + ).toBeInTheDocument(); + + expect(await findByTestId('alert-modal__selected-alert')).toHaveTextContent( + 'Gas prices are high and estimates are less accurate.', + ); + + expect(await findByTestId('alert-modal-button')).toBeInTheDocument(); + const alertModalConfirmButton = await findByTestId('alert-modal-button'); + + act(() => { + fireEvent.click(alertModalConfirmButton); + }); + + expect(queryByTestId('alert-modal')).not.toBeInTheDocument(); + }); + + it('displays the alert when gas estimate fails', async () => { + const account = + mockMetaMaskState.internalAccounts.accounts[ + mockMetaMaskState.internalAccounts + .selectedAccount as keyof typeof mockMetaMaskState.internalAccounts.accounts + ]; + + const mockedMetaMaskState = + getMetaMaskStateWithUnapprovedApproveTransaction(account.address); + + const transactions = { + ...mockedMetaMaskState.transactions[0], + simulationFails: { + reason: 'Internal JSON-RPC error.', + debug: { + blockNumber: '0x3a3c20d', + blockGasLimit: '0x1c9c380', + }, + }, + }; + + const { findByTestId, getByTestId, queryByTestId } = + await integrationTestRender({ + preloadedState: { + ...mockedMetaMaskState, + transactions: [transactions], + }, + backgroundConnection: backgroundConnectionMocked, + }); + + act(() => { + fireEvent.click(getByTestId('inline-alert')); + }); + + expect(await findByTestId('alert-modal')).toBeInTheDocument(); + + expect( + await findByTestId('alert-modal__selected-alert'), + ).toBeInTheDocument(); + + expect(await findByTestId('alert-modal__selected-alert')).toHaveTextContent( + 'We’re unable to provide an accurate fee and this estimate might be high. We suggest you to input a custom gas limit, but there’s a risk the transaction will still fail.', + ); + + expect(await findByTestId('alert-modal-button')).toBeInTheDocument(); + const alertModalConfirmButton = await findByTestId('alert-modal-button'); + + act(() => { + fireEvent.click(alertModalConfirmButton); + }); + + expect(queryByTestId('alert-modal')).not.toBeInTheDocument(); + }); + + it('displays the alert for insufficient gas', async () => { + const account = + mockMetaMaskState.internalAccounts.accounts[ + mockMetaMaskState.internalAccounts + .selectedAccount as keyof typeof mockMetaMaskState.internalAccounts.accounts + ]; + + const mockedMetaMaskState = + getMetaMaskStateWithUnapprovedApproveTransaction(account.address); + const transaction = mockedMetaMaskState.transactions[0]; + transaction.txParams.gas = '0x0'; + + const { findByTestId, getByTestId } = await integrationTestRender({ + preloadedState: { + ...mockedMetaMaskState, + gasFeeEstimatesByChainId: { + '0x5': { + gasFeeEstimates: { + networkCongestion: 0.0005, + }, + }, + }, + transactions: [transaction], + }, + backgroundConnection: backgroundConnectionMocked, + }); + + act(() => { + fireEvent.click(getByTestId('inline-alert')); + }); + + expect(await findByTestId('alert-modal')).toBeInTheDocument(); + + expect( + await findByTestId('alert-modal__selected-alert'), + ).toBeInTheDocument(); + + expect(await findByTestId('alert-modal__selected-alert')).toHaveTextContent( + 'To continue with this transaction, you’ll need to increase the gas limit to 21000 or higher.', + ); + + expect( + await findByTestId('alert-modal-action-showAdvancedGasModal'), + ).toBeInTheDocument(); + expect( + await findByTestId('alert-modal-action-showAdvancedGasModal'), + ).toHaveTextContent('Update gas limit'); + }); +}); diff --git a/test/integration/confirmations/transactions/contract-interaction.test.tsx b/test/integration/confirmations/transactions/contract-interaction.test.tsx index 75fbc41ce81d..4ea08aa70491 100644 --- a/test/integration/confirmations/transactions/contract-interaction.test.tsx +++ b/test/integration/confirmations/transactions/contract-interaction.test.tsx @@ -61,7 +61,7 @@ const getMetaMaskStateWithUnapprovedContractInteraction = ({ pendingApprovals: { [pendingTransactionId]: { id: pendingTransactionId, - origin: 'origin', + origin: 'local:http://localhost:8086/', time: pendingTransactionTime, type: ApprovalType.Transaction, requestData: { @@ -145,7 +145,8 @@ describe('Contract Interaction Confirmation', () => { beforeEach(() => { jest.resetAllMocks(); setupSubmitRequestToBackgroundMocks(); - mock4byte(); + const MINT_NFT_HEX_SIG = '0x3b4b1381'; + mock4byte(MINT_NFT_HEX_SIG); }); afterEach(() => { @@ -244,11 +245,6 @@ describe('Contract Interaction Confirmation', () => { }); expect(getByText('Transaction request')).toBeInTheDocument(); - expect( - getByText( - 'Only confirm this transaction if you fully understand the content and trust the requesting site.', - ), - ).toBeInTheDocument(); const simulationSection = getByTestId('simulation-details-layout'); expect(simulationSection).toBeInTheDocument(); @@ -269,8 +265,6 @@ describe('Contract Interaction Confirmation', () => { expect(transactionDetailsSection).toBeInTheDocument(); expect(transactionDetailsSection).toHaveTextContent('Request from'); expect(transactionDetailsSection).toHaveTextContent('Interacting with'); - expect(transactionDetailsSection).toHaveTextContent('Method'); - expect(transactionDetailsSection).toHaveTextContent('Mint NFTs'); const gasFeesSection = getByTestId('gas-fee-section'); expect(gasFeesSection).toBeInTheDocument(); diff --git a/test/integration/confirmations/transactions/erc721-approve.test.tsx b/test/integration/confirmations/transactions/erc721-approve.test.tsx new file mode 100644 index 000000000000..9d9a270963b1 --- /dev/null +++ b/test/integration/confirmations/transactions/erc721-approve.test.tsx @@ -0,0 +1,138 @@ +import { ApprovalType } from '@metamask/controller-utils'; +import { waitFor } from '@testing-library/react'; +import nock from 'nock'; +import * as backgroundConnection from '../../../../ui/store/background-connection'; +import { integrationTestRender } from '../../../lib/render-helpers'; +import mockMetaMaskState from '../../data/integration-init-state.json'; +import { createMockImplementation, mock4byte } from '../../helpers'; +import { getUnapprovedApproveTransaction } from './transactionDataHelpers'; + +jest.mock('../../../../ui/store/background-connection', () => ({ + ...jest.requireActual('../../../../ui/store/background-connection'), + submitRequestToBackground: jest.fn(), +})); + +const mockedBackgroundConnection = jest.mocked(backgroundConnection); + +const backgroundConnectionMocked = { + onNotification: jest.fn(), +}; +export const pendingTransactionId = '48a75190-45ca-11ef-9001-f3886ec2397c'; +export const pendingTransactionTime = new Date().getTime(); + +const getMetaMaskStateWithUnapprovedApproveTransaction = ( + accountAddress: string, +) => { + return { + ...mockMetaMaskState, + preferences: { + ...mockMetaMaskState.preferences, + redesignedConfirmationsEnabled: true, + }, + pendingApprovals: { + [pendingTransactionId]: { + id: pendingTransactionId, + origin: 'origin', + time: pendingTransactionTime, + type: ApprovalType.Transaction, + requestData: { + txId: pendingTransactionId, + }, + requestState: null, + expectsResult: false, + }, + }, + pendingApprovalCount: 1, + knownMethodData: { + '0x3b4b1381': { + name: 'Mint NFTs', + params: [ + { + type: 'uint256', + }, + ], + }, + }, + transactions: [ + getUnapprovedApproveTransaction( + accountAddress, + pendingTransactionId, + pendingTransactionTime, + ), + ], + }; +}; + +const advancedDetailsMockedRequests = { + getGasFeeTimeEstimate: { + lowerTimeBound: new Date().getTime(), + upperTimeBound: new Date().getTime(), + }, + getNextNonce: '9', + decodeTransactionData: { + data: [ + { + name: 'approve', + params: [ + { + type: 'address', + value: '0x2e0D7E8c45221FcA00d74a3609A0f7097035d09B', + }, + { + type: 'uint256', + value: 1, + }, + ], + }, + ], + source: 'FourByte', + }, +}; + +const setupSubmitRequestToBackgroundMocks = ( + mockRequests?: Record, +) => { + mockedBackgroundConnection.submitRequestToBackground.mockImplementation( + createMockImplementation({ + ...advancedDetailsMockedRequests, + ...(mockRequests ?? {}), + }), + ); +}; + +describe('ERC721 Approve Confirmation', () => { + beforeEach(() => { + jest.resetAllMocks(); + setupSubmitRequestToBackgroundMocks(); + const APPROVE_NFT_HEX_SIG = '0x095ea7b3'; + mock4byte(APPROVE_NFT_HEX_SIG); + }); + + afterEach(() => { + nock.cleanAll(); + }); + + it('displays approve details with correct data', async () => { + const account = + mockMetaMaskState.internalAccounts.accounts[ + mockMetaMaskState.internalAccounts + .selectedAccount as keyof typeof mockMetaMaskState.internalAccounts.accounts + ]; + + const mockedMetaMaskState = + getMetaMaskStateWithUnapprovedApproveTransaction(account.address); + + const { getByText } = await integrationTestRender({ + preloadedState: mockedMetaMaskState, + backgroundConnection: backgroundConnectionMocked, + }); + + await waitFor(() => { + expect(getByText('Allowance request')).toBeInTheDocument(); + }); + + await waitFor(() => { + expect(getByText('Request from')).toBeInTheDocument(); + }); + }); +}); diff --git a/test/integration/confirmations/transactions/transactionDataHelpers.tsx b/test/integration/confirmations/transactions/transactionDataHelpers.tsx index 0ee2f6f65738..72fc0d2289f3 100644 --- a/test/integration/confirmations/transactions/transactionDataHelpers.tsx +++ b/test/integration/confirmations/transactions/transactionDataHelpers.tsx @@ -12,7 +12,7 @@ export const getUnapprovedTransaction = ( gas: '0x16a92', }, id: pendingTransactionId, - origin: 'origin', + origin: 'local:http://localhost:8086/', securityAlertResponse: {}, status: 'unapproved', time: pendingTransactionTime, @@ -70,6 +70,30 @@ export const getUnapprovedTransaction = ( }; }; +export const getUnapprovedApproveTransaction = ( + accountAddress: string, + pendingTransactionId: string, + pendingTransactionTime: number, +) => { + return { + ...getUnapprovedTransaction( + accountAddress, + pendingTransactionId, + pendingTransactionTime, + ), + txParams: { + from: accountAddress, + data: '0x095ea7b30000000000000000000000002e0d7e8c45221fca00d74a3609a0f7097035d09b0000000000000000000000000000000000000000000000000000000000000001', + gas: '0x16a92', + to: '0x076146c765189d51be3160a2140cf80bfc73ad68', + value: '0x0', + maxFeePerGas: '0x5b06b0c0d', + maxPriorityFeePerGas: '0x59682f00', + }, + type: TransactionType.tokenMethodApprove, + }; +}; + export const getMaliciousUnapprovedTransaction = ( accountAddress: string, pendingTransactionId: string, diff --git a/test/integration/data/integration-init-state.json b/test/integration/data/integration-init-state.json index 733465a4043d..0e935a7a9a8e 100644 --- a/test/integration/data/integration-init-state.json +++ b/test/integration/data/integration-init-state.json @@ -392,7 +392,7 @@ "latestPriorityFeeRange": ["1", "40"], "historicalPriorityFeeRange": ["0.1458417", "700.156384646"], "priorityFeeTrend": "down", - "networkCongestion": 0.90625 + "networkCongestion": 0.10625 }, "gasFeeEstimatesByChainId": { "0x5": { @@ -426,7 +426,7 @@ "latestPriorityFeeRange": ["1", "40"], "historicalPriorityFeeRange": ["0.1458417", "700.156384646"], "priorityFeeTrend": "down", - "networkCongestion": 0.90625 + "networkCongestion": 0.10625 } } }, @@ -662,16 +662,16 @@ "type": "rpc", "id": "testNetworkConfigurationId" }, - "chain5": { + "goerli-network-id": { "type": "rpc", "chainId": "0x5", "ticker": "ETH", "nickname": "Chain 5", - "id": "chain5" + "id": "goerli-network-id" } }, "networksMetadata": { - "goerli": { + "goerli-network-id": { "EIPS": { "1559": true }, @@ -758,7 +758,7 @@ "securityAlertsEnabled": true, "seedPhraseBackedUp": true, "selectedAddress": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc", - "selectedNetworkClientId": "goerli", + "selectedNetworkClientId": "goerli-network-id", "showAccountBanner": false, "showBetaHeader": false, "showNetworkBanner": true, diff --git a/test/integration/helpers.tsx b/test/integration/helpers.tsx index ec88c05437f2..d975c5adc594 100644 --- a/test/integration/helpers.tsx +++ b/test/integration/helpers.tsx @@ -9,20 +9,20 @@ export const createMockImplementation = (requests: Record) => { }; }; -export function mock4byte() { +export function mock4byte(hexSignature: string) { const mockEndpoint = nock('https://www.4byte.directory:443', { encodedQueryParams: true, }) .persist() .get('/api/v1/signatures/') - .query({ hex_signature: '0x3b4b1381' }) + .query({ hex_signature: hexSignature }) .reply(200, { results: [ { id: 235447, created_at: '2021-09-14T02:07:09.805000Z', text_signature: 'mintNFTs(uint256)', - hex_signature: '0x3b4b1381', + hex_signature: hexSignature, bytes_signature: ';K\u0013 ', }, ], diff --git a/test/jest/mock-store.js b/test/jest/mock-store.js index de37f3d7ff2c..e61ae0fdce81 100644 --- a/test/jest/mock-store.js +++ b/test/jest/mock-store.js @@ -1,9 +1,8 @@ -import { NetworkType } from '@metamask/controller-utils'; -import { NetworkStatus } from '@metamask/network-controller'; import { EthAccountType } from '@metamask/keyring-api'; import { CHAIN_IDS, CURRENCY_SYMBOLS } from '../../shared/constants/network'; import { KeyringType } from '../../shared/constants/keyring'; import { ETH_EOA_METHODS } from '../../shared/constants/eth-methods'; +import { mockNetworkState } from '../stub/networks'; export const createGetSmartTransactionFeesApiResponse = () => { return { @@ -136,19 +135,6 @@ export const createSwapsMockStore = () => { swapsSTXLoading: false, }, metamask: { - selectedNetworkClientId: NetworkType.mainnet, - networksMetadata: { - [NetworkType.mainnet]: { - EIPS: { - 1559: false, - }, - status: NetworkStatus.Available, - }, - }, - providerConfig: { - chainId: CHAIN_IDS.MAINNET, - ticker: 'ETH', - }, preferences: { showFiatInTestnets: true, smartTransactionsOptInStatus: true, @@ -373,13 +359,12 @@ export const createSwapsMockStore = () => { accounts: ['0xd85a4b6a394794842887b8284293d69163007bbb'], }, ], - networkConfigurations: { - 'network-configuration-id-1': { - chainId: CHAIN_IDS.MAINNET, - ticker: CURRENCY_SYMBOLS.ETH, - rpcUrl: 'https://mainnet.infura.io/v3/', - }, - }, + ...mockNetworkState({ + chainId: CHAIN_IDS.MAINNET, + ticker: CURRENCY_SYMBOLS.ETH, + rpcUrl: 'https://mainnet.infura.io/v3/', + blockExplorerUrl: 'https://etherscan.io', + }), tokens: [ { erc20: true, @@ -667,12 +652,16 @@ export const createSwapsMockStore = () => { }; }; -export const createBridgeMockStore = (featureFlagOverrides = {}) => { +export const createBridgeMockStore = ( + featureFlagOverrides = {}, + bridgeSliceOverrides = {}, +) => { const swapsStore = createSwapsMockStore(); return { ...swapsStore, bridge: { toChain: null, + ...bridgeSliceOverrides, }, metamask: { ...swapsStore.metamask, @@ -680,6 +669,8 @@ export const createBridgeMockStore = (featureFlagOverrides = {}) => { ...(swapsStore.metamask.bridgeState ?? {}), bridgeFeatureFlags: { extensionSupport: false, + srcNetworkAllowlist: [], + destNetworkAllowlist: [], ...featureFlagOverrides, }, }, diff --git a/test/lib/confirmations/render-helpers.tsx b/test/lib/confirmations/render-helpers.tsx new file mode 100644 index 000000000000..c123b3117ff8 --- /dev/null +++ b/test/lib/confirmations/render-helpers.tsx @@ -0,0 +1,31 @@ +import React, { ReactElement } from 'react'; + +import { ConfirmContextProvider } from '../../../ui/pages/confirmations/context/confirm'; +import { renderHookWithProvider, renderWithProvider } from '../render-helpers'; + +export function renderWithConfirmContextProvider( + component: ReactElement, + store: unknown, + pathname = '/', +) { + return renderWithProvider( + {component}, + store, + pathname, + ); +} + +export function renderHookWithConfirmContextProvider( + hook: () => unknown, + state: Record, + pathname = '/', + Container?: ReactElement, +) { + const contextContainer = Container ? ( + {Container} + ) : ( + ConfirmContextProvider + ); + + return renderHookWithProvider(hook, state, pathname, contextContainer); +} diff --git a/test/manual-scenarios/transactions/send transactions with custom nonce.csv b/test/manual-scenarios/transactions/send transactions with custom nonce.csv index 7cffbe1742f4..60516b8d9ccf 100644 --- a/test/manual-scenarios/transactions/send transactions with custom nonce.csv +++ b/test/manual-scenarios/transactions/send transactions with custom nonce.csv @@ -20,4 +20,4 @@ Step,Test steps,Test data,Expected result,Notes 19,"Click ""Next"".",,"Metamask popup with the recipient's address, amount, gas and total fees is displayed. The ""Custom nonce"" field with the next suggested nonce value is shown on the confirmation screen.", 20,Reduce the suggested nonce value by one.,,"In the ""Custom nonce"" field, enter the suggested value minus one.", 21,Proceed to confirm the transaction with low nonce value.,,, -22,Low nonce transaction fails.,,"Popup notification about failed transaction appears: ""Transaction *** failed! Nonce too low: next nonce ***, tx nonce ***."" Current transaction appears as ""Failed"" on the activity list.",Note that low nonce failed transaction is visible on the activity only in case when previous successful transaction with the same nonce is not shown on the Activity list. \ No newline at end of file +22,Low nonce transaction fails.,,"Popup notification about failed transaction appears: ""Transaction *** failed! Nonce too low: next nonce ***, tx nonce ***."",, diff --git a/test/manual-scenarios/upgrade-testing/upgrade-testing.md b/test/manual-scenarios/upgrade-testing/upgrade-testing.md new file mode 100644 index 000000000000..df5ca3d1fc50 --- /dev/null +++ b/test/manual-scenarios/upgrade-testing/upgrade-testing.md @@ -0,0 +1,38 @@ +# MetaMask Extension Upgrade Testing from Master to Release Branch + +## Feature: Validate Functionality Post-Upgrade + +To ensure MetaMask extension's upgrade process is seamless and retains user data and functionality, we need to validate the transition from the previously shipped version (master branch) to the upcoming release (release branch). + +## Scenario: Validate Upgrade Retains Data and Functionality + +### Pre-Upgrade Actions on Master Branch + +- **Given** the user checks out the master branch, runs `yarn` and `yarn start` to build locally, and has loaded the MetaMask extension. For instructions on how to load extension on Chrome and Firefox, check the guidelines [here for Chrome](https://github.com/MetaMask/metamask-extension/blob/develop/docs/add-to-chrome.md) and [here for Firefox](https://github.com/MetaMask/metamask-extension/blob/develop/docs/add-to-firefox.md). +- **And** the user has successfully onboarded. +- **And** the user creates two accounts. +- **And** the user sends a transaction between these accounts. +- **And** the user uses TestDapp to create an ERC20 token and imports it into MetaMask. + +### Upgrade Actions + +1. **When** the user stops the previous `yarn start` in the console from the master branch. +2. **And** uses `git checkout` to switch to the release branch. +3. **And** updates dependencies and restarts extension using `yarn` and `yarn start`. +4. **Then** the user should see a reload button on extension management page. +5. **When** the user clicks the reload button to complete the upgrade. +6. **Then** the user should verify that the displayed extension version number is updated. + +### Post-Upgrade Validation on Release Branch + +7. **Then** the user should verify that both accounts created pre-upgrade are present and correctly displayed. +8. **And** the previously sent transaction is visible in the transaction history. +9. **And** the ERC20 token created and imported pre-upgrade is still available. +10. **And** the user can successfully send another transaction. +11. **And** any popup modals related to the new version are appropriately displayed and functional. + +### Expected Outcome + +After upgrading from the master branch to the release branch, the MetaMask extension should: +- Retain all user data including accounts, transactions, and imported tokens. +- Maintain full functionality, enabling the user to continue using extension without any issues. \ No newline at end of file diff --git a/test/stub/networks.ts b/test/stub/networks.ts new file mode 100644 index 000000000000..fe58a0dfe285 --- /dev/null +++ b/test/stub/networks.ts @@ -0,0 +1,70 @@ +import { + NetworkMetadata, + NetworkState, + NetworkStatus, +} from '@metamask/network-controller'; +import { v4 as uuidv4 } from 'uuid'; +import { Hex } from '@metamask/utils'; +import { + NETWORK_TO_NAME_MAP, + CHAIN_ID_TO_CURRENCY_SYMBOL_MAP, +} from '../../shared/constants/network'; + +export const mockNetworkState = ( + ...networks: { + id?: string; + type?: string; + chainId: Hex; + rpcUrl?: string; + nickname?: string; + ticker?: string; + blockExplorerUrl?: string; + metadata?: NetworkMetadata; + }[] +): NetworkState => { + const networkConfigurations = networks.map((network) => ({ + id: network.id ?? uuidv4(), + chainId: network.chainId, + rpcUrl: + 'rpcUrl' in network + ? network.rpcUrl + : `https://localhost/rpc/${network.chainId}`, + nickname: + 'nickname' in network + ? network.nickname + : (NETWORK_TO_NAME_MAP as Record)[network.chainId], + ticker: + 'ticker' in network + ? network.ticker + : (CHAIN_ID_TO_CURRENCY_SYMBOL_MAP as Record)[ + network.chainId + ], + ...((!('blockExplorerUrl' in network) || network.blockExplorerUrl) && { + rpcPrefs: { + blockExplorerUrl: + network.blockExplorerUrl ?? + `https://localhost/blockExplorer/${network.chainId}`, + }, + }), + })); + + const networksMetadata = networks.reduce( + (acc, network, i) => ({ + ...acc, + [networkConfigurations[i].id]: network.metadata ?? { + EIPS: {}, + status: NetworkStatus.Available, + }, + }), + {}, + ); + + return { + selectedNetworkClientId: networkConfigurations[0].id, + networkConfigurations: networkConfigurations.reduce( + (acc, network) => ({ ...acc, [network.id]: network }), + {}, + ), + networksMetadata, + }; +}; diff --git a/test/unit-global/protect-intrinsics.test.js b/test/unit-global/protect-intrinsics.test.js deleted file mode 100644 index 8640945e4c37..000000000000 --- a/test/unit-global/protect-intrinsics.test.js +++ /dev/null @@ -1,18 +0,0 @@ -/** - * @jest-environment node - */ -import 'ses'; -import '../../app/scripts/lockdown-run'; -import '../../app/scripts/lockdown-more'; -import { - getGlobalProperties, - testIntrinsic, -} from '../helpers/protect-intrinsics-helpers'; - -describe('non-modifiable intrinsics', function () { - getGlobalProperties().forEach((propertyName) => { - it(`intrinsic globalThis["${propertyName}"]`, function () { - expect(() => testIntrinsic(propertyName)).not.toThrow(); - }); - }); -}); diff --git a/ui/__mocks__/ethereumjs-util.js b/ui/__mocks__/ethereumjs-util.js new file mode 100644 index 000000000000..d7af4a81162c --- /dev/null +++ b/ui/__mocks__/ethereumjs-util.js @@ -0,0 +1,6 @@ +// eslint-disable-next-line import/no-extraneous-dependencies, node/no-extraneous-require +const util = require('ethereumjs-util'); + +module.exports = { + ...util, +}; diff --git a/ui/components/app/alert-system/alert-modal/alert-modal.tsx b/ui/components/app/alert-system/alert-modal/alert-modal.tsx index fa31cb018091..85bb7b87f7d4 100644 --- a/ui/components/app/alert-system/alert-modal/alert-modal.tsx +++ b/ui/components/app/alert-system/alert-modal/alert-modal.tsx @@ -287,9 +287,11 @@ function ActionButton({ } const { key, label } = action; + const dataTestId = `alert-modal-action-${key}`; return ( diff --git a/ui/components/app/snaps/snap-home-page/snap-home-renderer.js b/ui/components/app/snaps/snap-home-page/snap-home-renderer.js index 03359b81b1ed..305dd28ae2ff 100644 --- a/ui/components/app/snaps/snap-home-page/snap-home-renderer.js +++ b/ui/components/app/snaps/snap-home-page/snap-home-renderer.js @@ -66,12 +66,14 @@ export const SnapHomeRenderer = ({ snapId }) => { return ( {error && ( - - - {t('snapsUIError', [{snapName}])} - - - + + + + {t('snapsUIError', [{snapName}])} + + + + )} {(interfaceId || loading) && ( { interfaceId={interfaceId} isLoading={loading} useDelineator={false} + useFooter /> )} diff --git a/ui/components/app/snaps/snap-insight/snap-insight.js b/ui/components/app/snaps/snap-insight/snap-insight.js index c13f3206f62e..4f243e265183 100644 --- a/ui/components/app/snaps/snap-insight/snap-insight.js +++ b/ui/components/app/snaps/snap-insight/snap-insight.js @@ -21,12 +21,12 @@ import { Copyable } from '../copyable'; import { getSnapMetadata } from '../../../../selectors'; import { trackInsightSnapUsage } from '../../../../store/actions'; -export const SnapInsight = ({ snapId, data, loading }) => { +export const SnapInsight = ({ snapId, data }) => { const dispatch = useDispatch(); const t = useI18nContext(); - const isLoading = loading; + const isLoading = data?.loading; const error = data?.error; - const interfaceId = data?.response?.id; + const interfaceId = data?.interfaceId; useEffect(() => { const trackInsightUsage = async () => { @@ -88,7 +88,7 @@ export const SnapInsight = ({ snapId, data, loading }) => { {t('snapsUIError', [{snapName}])} - + )} diff --git a/ui/components/app/snaps/snap-ui-card/snap-ui-card.tsx b/ui/components/app/snaps/snap-ui-card/snap-ui-card.tsx index d2f2a6a1e8de..b7a0468b5315 100644 --- a/ui/components/app/snaps/snap-ui-card/snap-ui-card.tsx +++ b/ui/components/app/snaps/snap-ui-card/snap-ui-card.tsx @@ -6,6 +6,7 @@ import { TextAlign, TextColor, TextVariant, + AlignItems, } from '../../../../helpers/constants/design-system'; import { Box, Text } from '../../../component-library'; import { SnapUIImage } from '../snap-ui-image'; @@ -30,8 +31,9 @@ export const SnapUICard: FunctionComponent = ({ className="snap-ui-renderer__card" display={Display.Flex} justifyContent={JustifyContent.spaceBetween} + alignItems={AlignItems.center} > - + {image && ( = ({ style={{ borderRadius: '999px' }} /> )} - - {title} - {description} + + + {title} + + {description && ( + + {description} + + )} - {value} - {extra} + + {value} + + {extra && ( + + {extra} + + )} ); diff --git a/ui/components/app/snaps/snap-ui-icon/index.ts b/ui/components/app/snaps/snap-ui-icon/index.ts new file mode 100644 index 000000000000..29e8a7c5b0b3 --- /dev/null +++ b/ui/components/app/snaps/snap-ui-icon/index.ts @@ -0,0 +1 @@ +export * from './snap-ui-icon'; diff --git a/ui/components/app/snaps/snap-ui-icon/snap-ui-icon.tsx b/ui/components/app/snaps/snap-ui-icon/snap-ui-icon.tsx new file mode 100644 index 000000000000..48587ecc3ff2 --- /dev/null +++ b/ui/components/app/snaps/snap-ui-icon/snap-ui-icon.tsx @@ -0,0 +1,20 @@ +import React from 'react'; +import { Icon, IconName, IconSize } from '../../../component-library/icon'; +import { IconColor } from '../../../../helpers/constants/design-system'; + +export type SnapUIIconProps = { + name: IconName; + color?: IconColor; + size?: IconSize; +}; + +export const SnapUIIcon = ({ name, color, size }: SnapUIIconProps) => { + return ( + + ); +}; diff --git a/ui/components/app/snaps/snap-ui-input/index.scss b/ui/components/app/snaps/snap-ui-input/index.scss new file mode 100644 index 000000000000..47106f2d7423 --- /dev/null +++ b/ui/components/app/snaps/snap-ui-input/index.scss @@ -0,0 +1,13 @@ +.snap-ui-renderer__input { + .snap-ui-button { + &:has(.snap-ui-renderer__image) { + display: flex; + } + + .snap-ui-renderer__image { + vertical-align: middle; + max-height: 20px; + max-width: 20px; + } + } +} diff --git a/ui/components/app/snaps/snap-ui-renderer/components/button.ts b/ui/components/app/snaps/snap-ui-renderer/components/button.ts index 32a642424e68..047491472305 100644 --- a/ui/components/app/snaps/snap-ui-renderer/components/button.ts +++ b/ui/components/app/snaps/snap-ui-renderer/components/button.ts @@ -1,7 +1,13 @@ -import { ButtonElement } from '@metamask/snaps-sdk/jsx'; +import { ButtonElement, JSXElement } from '@metamask/snaps-sdk/jsx'; +import { getJsxChildren } from '@metamask/snaps-utils'; +import { NonEmptyArray } from '@metamask/utils'; +import { mapTextToTemplate } from '../utils'; import { UIComponentFactory } from './types'; -export const button: UIComponentFactory = ({ element }) => ({ +export const button: UIComponentFactory = ({ + element, + ...params +}) => ({ element: 'SnapUIButton', props: { type: element.props.type, @@ -9,5 +15,8 @@ export const button: UIComponentFactory = ({ element }) => ({ name: element.props.name, disabled: element.props.disabled, }, - children: element.props.children, + children: mapTextToTemplate( + getJsxChildren(element) as NonEmptyArray, + params, + ), }); diff --git a/ui/components/app/snaps/snap-ui-renderer/components/container.ts b/ui/components/app/snaps/snap-ui-renderer/components/container.ts index e6496d32fbe5..152a01eb652d 100644 --- a/ui/components/app/snaps/snap-ui-renderer/components/container.ts +++ b/ui/components/app/snaps/snap-ui-renderer/components/container.ts @@ -52,7 +52,8 @@ export const container: UIComponentFactory = ({ } // Injects the default footer if the dialog uses default footer but none was provided. - if (useFooter && !children[1]) { + // If onCancel is omitted by the caller we assume that it is safe to not display the default footer. + if (useFooter && onCancel && !children[1]) { templateChildren.push({ ...DEFAULT_FOOTER, props: { diff --git a/ui/components/app/snaps/snap-ui-renderer/components/field.ts b/ui/components/app/snaps/snap-ui-renderer/components/field.ts index 61a7cab9466a..4794cb063f61 100644 --- a/ui/components/app/snaps/snap-ui-renderer/components/field.ts +++ b/ui/components/app/snaps/snap-ui-renderer/components/field.ts @@ -6,15 +6,21 @@ import { DropdownElement, RadioGroupElement, CheckboxElement, + SelectorElement, } from '@metamask/snaps-sdk/jsx'; import { getJsxChildren } from '@metamask/snaps-utils'; import { button as buttonFn } from './button'; import { dropdown as dropdownFn } from './dropdown'; import { radioGroup as radioGroupFn } from './radioGroup'; import { checkbox as checkboxFn } from './checkbox'; +import { selector as selectorFn } from './selector'; import { UIComponentFactory, UIComponentParams } from './types'; -export const field: UIComponentFactory = ({ element, form }) => { +export const field: UIComponentFactory = ({ + element, + form, + ...params +}) => { // For fields we don't render the Input itself, we just adapt SnapUIInput. const children = getJsxChildren(element); const child = children[0] as JSXElement; @@ -40,7 +46,10 @@ export const field: UIComponentFactory = ({ element, form }) => { const button = children[1] as ButtonElement; const buttonMapped = button && - buttonFn({ element: button } as UIComponentParams); + buttonFn({ + ...params, + element: button, + } as UIComponentParams); return { element: 'SnapUIInput', @@ -120,6 +129,24 @@ export const field: UIComponentFactory = ({ element, form }) => { }; } + case 'Selector': { + const selector = child as SelectorElement; + const selectorMapped = selectorFn({ + ...params, + element: selector, + } as UIComponentParams); + return { + ...selectorMapped, + element: 'SnapUISelector', + props: { + ...selectorMapped.props, + label: element.props.label, + form, + error: element.props.error, + }, + }; + } + default: throw new Error(`Invalid Field child: ${child.type}`); } diff --git a/ui/components/app/snaps/snap-ui-renderer/components/footer.ts b/ui/components/app/snaps/snap-ui-renderer/components/footer.ts index f71f3f3ba362..3d400c4fa918 100644 --- a/ui/components/app/snaps/snap-ui-renderer/components/footer.ts +++ b/ui/components/app/snaps/snap-ui-renderer/components/footer.ts @@ -7,7 +7,8 @@ import { Display, FlexDirection, } from '../../../../../helpers/constants/design-system'; -import { UIComponentFactory } from './types'; +import { UIComponentFactory, UIComponentParams } from './types'; +import { button as buttonFn } from './button'; export const DEFAULT_FOOTER = { element: 'Box', @@ -28,11 +29,12 @@ export const DEFAULT_FOOTER = { const getDefaultButtons = ( footer: FooterElement, t: (value: string) => string, - onCancel: () => void, + onCancel?: () => void, ) => { const children = getJsxChildren(footer); - if (children.length === 1) { + // If onCancel is omitted by the caller we assume that it is safe to not display the default footer. + if (children.length === 1 && onCancel) { return { element: 'SnapFooterButton', key: 'default-button', @@ -51,20 +53,24 @@ export const footer: UIComponentFactory = ({ element, t, onCancel, + ...params }) => { const defaultButtons = getDefaultButtons(element, t, onCancel); const footerChildren = (getJsxChildren(element) as ButtonElement[]).map( - (children) => { - const { children: buttonChildren, ...props } = children.props; + (children, index) => { + const buttonMapped = buttonFn({ + ...params, + element: children, + } as UIComponentParams); return { element: 'SnapFooterButton', - key: `snap-footer-button-${props.name}`, + key: `snap-footer-button-${buttonMapped.props?.name ?? index}`, props: { - ...props, + ...buttonMapped.props, isSnapAction: true, }, - children: buttonChildren, + children: buttonMapped.children, }; }, ); diff --git a/ui/components/app/snaps/snap-ui-renderer/components/icon.ts b/ui/components/app/snaps/snap-ui-renderer/components/icon.ts new file mode 100644 index 000000000000..f1fd2cef4138 --- /dev/null +++ b/ui/components/app/snaps/snap-ui-renderer/components/icon.ts @@ -0,0 +1,44 @@ +import { IconElement } from '@metamask/snaps-sdk/jsx'; +import { IconColor } from '../../../../../helpers/constants/design-system'; +import { IconName, IconSize } from '../../../../component-library'; +import { UIComponentFactory } from './types'; + +const ICON_NAMES = new Set(Object.values(IconName)); + +export const icon: UIComponentFactory = ({ element }) => { + const getIconName = () => { + if (ICON_NAMES.has(element.props.name as IconName)) { + return element.props.name as IconName; + } + return IconName.Danger; + }; + + const getIconColor = () => { + switch (element.props.color) { + case 'muted': + return IconColor.iconMuted; + case 'primary': + return IconColor.primaryDefault; + default: + return IconColor.iconDefault; + } + }; + + const getIconSize = () => { + switch (element.props.size) { + case 'md': + return IconSize.Md; + default: + return IconSize.Inherit; + } + }; + + return { + element: 'SnapUIIcon', + props: { + name: getIconName(), + color: getIconColor(), + size: getIconSize(), + }, + }; +}; diff --git a/ui/components/app/snaps/snap-ui-renderer/components/index.ts b/ui/components/app/snaps/snap-ui-renderer/components/index.ts index e68a5e15b7c2..063a68a1d03a 100644 --- a/ui/components/app/snaps/snap-ui-renderer/components/index.ts +++ b/ui/components/app/snaps/snap-ui-renderer/components/index.ts @@ -23,6 +23,8 @@ import { tooltip } from './tooltip'; import { card } from './card'; import { footer } from './footer'; import { container } from './container'; +import { selector } from './selector'; +import { icon } from './icon'; export const COMPONENT_MAPPING = { Box: box, @@ -30,6 +32,7 @@ export const COMPONENT_MAPPING = { Text: text, Divider: divider, Spinner: spinner, + Icon: icon, Image: image, Copyable: copyable, Row: row, @@ -50,4 +53,5 @@ export const COMPONENT_MAPPING = { Card: card, Footer: footer, Container: container, + Selector: selector, }; diff --git a/ui/components/app/snaps/snap-ui-renderer/components/selector.ts b/ui/components/app/snaps/snap-ui-renderer/components/selector.ts new file mode 100644 index 000000000000..0ad51fc20b35 --- /dev/null +++ b/ui/components/app/snaps/snap-ui-renderer/components/selector.ts @@ -0,0 +1,41 @@ +import { + JSXElement, + SelectorElement, + SelectorOptionElement, +} from '@metamask/snaps-sdk/jsx'; +import { getJsxChildren } from '@metamask/snaps-utils'; + +import { mapToTemplate } from '../utils'; +import { UIComponentFactory } from './types'; + +export const selector: UIComponentFactory = ({ + element, + form, + ...params +}) => { + const children = getJsxChildren(element) as SelectorOptionElement[]; + + const options = children.map((child) => child.props.value); + + const optionComponents = children.map((child) => + mapToTemplate({ + ...params, + form, + element: child.props.children as JSXElement, + }), + ); + + return { + element: 'SnapUISelector', + props: { + id: element.props.name, + name: element.props.name, + title: element.props.title, + form, + options, + }, + propComponents: { + optionComponents, + }, + }; +}; diff --git a/ui/components/app/snaps/snap-ui-renderer/components/text.ts b/ui/components/app/snaps/snap-ui-renderer/components/text.ts index 9502df1aabaa..b0e30a6d15d1 100644 --- a/ui/components/app/snaps/snap-ui-renderer/components/text.ts +++ b/ui/components/app/snaps/snap-ui-renderer/components/text.ts @@ -12,17 +12,38 @@ import { UIComponentFactory } from './types'; export const text: UIComponentFactory = ({ element, ...params -}) => ({ - element: 'Text', - children: mapTextToTemplate( - getJsxChildren(element) as NonEmptyArray, - params, - ), - props: { - variant: TextVariant.bodyMd, - overflowWrap: OverflowWrap.Anywhere, - color: TextColor.inherit, - className: 'snap-ui-renderer__text', - textAlign: element.props.alignment, - }, -}); +}) => { + const getTextColor = () => { + switch (element.props.color) { + case 'default': + return TextColor.textDefault; + case 'alternative': + return TextColor.textAlternative; + case 'muted': + return TextColor.textMuted; + case 'error': + return TextColor.errorDefault; + case 'success': + return TextColor.successDefault; + case 'warning': + return TextColor.warningDefault; + default: + return TextColor.inherit; + } + }; + + return { + element: 'Text', + children: mapTextToTemplate( + getJsxChildren(element) as NonEmptyArray, + params, + ), + props: { + variant: TextVariant.bodyMd, + overflowWrap: OverflowWrap.Anywhere, + color: getTextColor(), + className: 'snap-ui-renderer__text', + textAlign: element.props.alignment, + }, + }; +}; diff --git a/ui/components/app/snaps/snap-ui-renderer/components/types.ts b/ui/components/app/snaps/snap-ui-renderer/components/types.ts index f2eff8560ea9..78d354f805a0 100644 --- a/ui/components/app/snaps/snap-ui-renderer/components/types.ts +++ b/ui/components/app/snaps/snap-ui-renderer/components/types.ts @@ -5,8 +5,8 @@ export type UIComponentParams = { map: Record; element: T; form?: string; - useFooter: boolean; - onCancel: () => void; + useFooter?: boolean; + onCancel?: () => void; promptLegacyProps?: { onInputChange: (event: ReactChangeEvent) => void; inputValue: string; diff --git a/ui/components/app/snaps/snap-ui-renderer/snap-ui-renderer.js b/ui/components/app/snaps/snap-ui-renderer/snap-ui-renderer.js index d012af7ce677..5bde7fba7aa8 100644 --- a/ui/components/app/snaps/snap-ui-renderer/snap-ui-renderer.js +++ b/ui/components/app/snaps/snap-ui-renderer/snap-ui-renderer.js @@ -106,6 +106,7 @@ const SnapUIRendererComponent = ({ isCollapsed={isCollapsed} onClick={onClick} boxProps={boxProps} + disablePadding > { it('supports all exposed components', () => { diff --git a/ui/components/app/snaps/snap-ui-renderer/utils.ts b/ui/components/app/snaps/snap-ui-renderer/utils.ts index b3d0e1eb57ae..cb8bd74a6d2d 100644 --- a/ui/components/app/snaps/snap-ui-renderer/utils.ts +++ b/ui/components/app/snaps/snap-ui-renderer/utils.ts @@ -98,7 +98,7 @@ export const mapToTemplate = (params: MapToTemplateParams): UIComponent => { const { type, key } = params.element; const elementKey = key ?? generateKey(params.map, params.element); const mapped = COMPONENT_MAPPING[ - type as Exclude + type as Exclude // TODO: Replace `any` with type // eslint-disable-next-line @typescript-eslint/no-explicit-any ](params as any); diff --git a/ui/components/app/snaps/snap-ui-selector/index.scss b/ui/components/app/snaps/snap-ui-selector/index.scss new file mode 100644 index 000000000000..96830bf6c9d5 --- /dev/null +++ b/ui/components/app/snaps/snap-ui-selector/index.scss @@ -0,0 +1,33 @@ +.snap-ui-renderer { + &__selector { + width: 100%; + + & > span:first-child { + width: 100%; + } + + & > span:first-child > *:first-child { + width: 100%; + } + + &:hover { + background-color: var(--color-background-alternative-hover); + } + } + + &__selector-item { + width: 100%; + + & > span:first-child { + width: 100%; + } + + & > span:first-child > *:first-child { + width: 100%; + } + + &:hover { + background-color: var(--color-background-alternative-hover); + } + } +} diff --git a/ui/components/app/snaps/snap-ui-selector/index.ts b/ui/components/app/snaps/snap-ui-selector/index.ts new file mode 100644 index 000000000000..4b4c429f4809 --- /dev/null +++ b/ui/components/app/snaps/snap-ui-selector/index.ts @@ -0,0 +1 @@ +export * from './snap-ui-selector'; diff --git a/ui/components/app/snaps/snap-ui-selector/snap-ui-selector.stories.tsx b/ui/components/app/snaps/snap-ui-selector/snap-ui-selector.stories.tsx new file mode 100644 index 000000000000..582cf0e58b64 --- /dev/null +++ b/ui/components/app/snaps/snap-ui-selector/snap-ui-selector.stories.tsx @@ -0,0 +1,110 @@ +import React from 'react'; +import { Provider } from 'react-redux'; +import { SnapUISelector } from './snap-ui-selector'; +import { Box, Text } from '../../../component-library'; +import { + Display, + FlexDirection, + JustifyContent, +} from '../../../../helpers/constants/design-system'; +import { shortenAddress } from '../../../../helpers/utils/util'; +import { SnapUICard } from '../snap-ui-card/snap-ui-card'; +import configureStore from '../../../../store/store'; +import testData from '../../../../../.storybook/test-data'; +import { SnapInterfaceContextProvider } from '../../../../contexts/snaps'; + +const store = configureStore(testData); + +export default { + title: 'Components/App/Snaps/SnapUISelector', + component: SnapUISelector, + argTypes: {}, + decorators: [ + (story) => ( + + + {story()} + + + ), + ], +}; + +export const DefaultStory = (args) => ; + +DefaultStory.storyName = 'Default'; + +DefaultStory.args = { + options: ['foo', 'bar'], + optionComponents: [ + + Foo + , + + Bar + , + ], +}; + +export const AdvancedStory = (args) => ; + +AdvancedStory.storyName = 'Advanced'; + +AdvancedStory.args = { + options: ['foo', 'bar'], + optionComponents: [ + + + Account 1 + + {shortenAddress('0xc0ffee254729296a45a3885639AC7E10F9d54979')} + + + + 3000 USD + 1 ETH + + , + + + Account 2 + + {shortenAddress('0xc0ffee254729296a45a3885639AC7E10F9d54979')} + + + + 0 USD + 0 ETH + + , + ], +}; + +export const CardStory = (args) => ; + +CardStory.storyName = 'Card'; + +CardStory.args = { + options: ['foo', 'bar'], + optionComponents: [ + `} + title="Title" + description="Description" + value="Value" + extra="Extra" + />, + `} + title="Title" + description="Description" + value="Value" + extra="Extra" + />, + ], +}; diff --git a/ui/components/app/snaps/snap-ui-selector/snap-ui-selector.tsx b/ui/components/app/snaps/snap-ui-selector/snap-ui-selector.tsx new file mode 100644 index 000000000000..f3f3f81493e1 --- /dev/null +++ b/ui/components/app/snaps/snap-ui-selector/snap-ui-selector.tsx @@ -0,0 +1,192 @@ +import React, { useState, useEffect } from 'react'; +import { + Box, + ButtonBase, + HelpText, + HelpTextSeverity, + IconName, + IconSize, + Label, + Modal, + ModalBody, + ModalContent, + ModalHeader, + ModalOverlay, + Text, +} from '../../../component-library'; +import { + BackgroundColor, + BlockSize, + BorderRadius, + Display, + FlexDirection, + IconColor, + TextAlign, + TextVariant, +} from '../../../../helpers/constants/design-system'; +import { useSnapInterfaceContext } from '../../../../contexts/snaps'; + +export type SnapUISelectorProps = { + name: string; + title: string; + options: string[]; + optionComponents: React.ReactNode[]; + form?: string; + label?: string; + error?: string; + disabled?: boolean; +}; + +type SelectorItemProps = { + value: string; + children: React.ReactNode; + onSelect: (value: string) => void; +}; + +const SelectorItem: React.FunctionComponent = ({ + value, + children, + onSelect, +}) => { + const handleClick = () => { + onSelect(value); + }; + + return ( + + {children} + + ); +}; + +export const SnapUISelector: React.FunctionComponent = ({ + name, + title, + options, + optionComponents, + form, + label, + error, + disabled, +}) => { + const { handleInputChange, getValue } = useSnapInterfaceContext(); + + const initialValue = getValue(name, form) as string; + + const [selectedOptionValue, setSelectedOption] = useState(initialValue); + const [isModalOpen, setIsModalOpen] = useState(false); + + useEffect(() => { + if (initialValue) { + setSelectedOption(initialValue); + } + }, [initialValue]); + + const handleModalOpen = () => setIsModalOpen(true); + + const handleModalClose = () => setIsModalOpen(false); + + const handleSelect = (value: string) => { + setSelectedOption(value); + handleInputChange(name, value, form); + handleModalClose(); + }; + + const selectedOptionIndex = options.findIndex( + (option) => option === selectedOptionValue, + ); + + const selectedOption = optionComponents[selectedOptionIndex]; + + return ( + <> + + {label && } + + {selectedOption} + + {error && ( + + {error} + + )} + + + + + + + {title} + + + + + {optionComponents.map((component, index) => ( + + {component} + + ))} + + + + + + ); +}; diff --git a/ui/components/app/transaction-activity-log/transaction-activity-log.container.test.js b/ui/components/app/transaction-activity-log/transaction-activity-log.container.test.js index f252f744d250..0b227967ae8c 100644 --- a/ui/components/app/transaction-activity-log/transaction-activity-log.container.test.js +++ b/ui/components/app/transaction-activity-log/transaction-activity-log.container.test.js @@ -1,3 +1,6 @@ +import { CHAIN_IDS } from '../../../../shared/constants/network'; +import { mockNetworkState } from '../../../../test/stub/networks'; + /* eslint-disable import/unambiguous */ let mapStateToProps; @@ -20,10 +23,11 @@ describe('TransactionActivityLog container', () => { conversionRate: 280.45, }, }, - networkConfigurations: {}, - providerConfig: { - ticker: 'ETH', - }, + + ...mockNetworkState({ + chainId: CHAIN_IDS.MAINNET, + blockExplorerUrl: undefined, + }), }, }; @@ -42,18 +46,11 @@ describe('TransactionActivityLog container', () => { conversionRate: 280.45, }, }, - networkConfigurations: { - networkConfigurationId: { - rpcUrl: 'https://customnetwork.com/', - }, - }, - providerConfig: { + ...mockNetworkState({ + chainId: CHAIN_IDS.MAINNET, rpcUrl: 'https://customnetwork.com/', - ticker: 'ETH', - rpcPrefs: { - blockExplorerUrl: 'https://customblockexplorer.com/', - }, - }, + blockExplorerUrl: 'https://customblockexplorer.com/', + }), }, }; diff --git a/ui/components/app/transaction-breakdown/transaction-breakdown.test.js b/ui/components/app/transaction-breakdown/transaction-breakdown.test.js index 8e5b78d3993f..664ac3611728 100644 --- a/ui/components/app/transaction-breakdown/transaction-breakdown.test.js +++ b/ui/components/app/transaction-breakdown/transaction-breakdown.test.js @@ -3,11 +3,8 @@ import configureMockStore from 'redux-mock-store'; import { within } from '@testing-library/react'; import { renderWithProvider } from '../../../../test/jest/rendering'; import mockState from '../../../../test/data/mock-state.json'; -import { - MAINNET_DISPLAY_NAME, - NETWORK_TYPES, - CHAIN_IDS, -} from '../../../../shared/constants/network'; +import { mockNetworkState } from '../../../../test/stub/networks'; +import { CHAIN_IDS } from '../../../../shared/constants/network'; import TransactionBreakdown from '.'; function getActualDataFrom(transactionBreakdownRows) { @@ -27,11 +24,8 @@ describe('TransactionBreakdown', () => { metamask: { currencyRates: {}, preferences: {}, - providerConfig: { - chainId: CHAIN_IDS.MAINNET, - nickname: MAINNET_DISPLAY_NAME, - type: NETWORK_TYPES.MAINNET, - }, + ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), + internalAccounts: mockState.metamask.internalAccounts, completedOnboarding: true, }, diff --git a/ui/components/app/user-preferenced-currency-display/user-preferenced-currency-display.test.js b/ui/components/app/user-preferenced-currency-display/user-preferenced-currency-display.test.js index 3b37732cda78..51ee63d40c17 100644 --- a/ui/components/app/user-preferenced-currency-display/user-preferenced-currency-display.test.js +++ b/ui/components/app/user-preferenced-currency-display/user-preferenced-currency-display.test.js @@ -2,11 +2,8 @@ import React from 'react'; import configureMockStore from 'redux-mock-store'; import mockState from '../../../../test/data/mock-state.json'; import { renderWithProvider } from '../../../../test/lib/render-helpers'; -import { - MAINNET_DISPLAY_NAME, - NETWORK_TYPES, - CHAIN_IDS, -} from '../../../../shared/constants/network'; +import { mockNetworkState } from '../../../../test/stub/networks'; +import { CHAIN_IDS } from '../../../../shared/constants/network'; import UserPreferencedCurrencyDisplay from '.'; describe('UserPreferencedCurrencyDisplay Component', () => { @@ -14,11 +11,7 @@ describe('UserPreferencedCurrencyDisplay Component', () => { const defaultState = { metamask: { ...mockState.metamask, - providerConfig: { - chainId: CHAIN_IDS.MAINNET, - nickname: MAINNET_DISPLAY_NAME, - type: NETWORK_TYPES.MAINNET, - }, + ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), currencyRates: {}, preferences: { useNativeCurrencyAsPrimaryCurrency: true, diff --git a/ui/components/app/wallet-overview/btc-overview.test.tsx b/ui/components/app/wallet-overview/btc-overview.test.tsx index f71164c673ea..5adbe8dcc927 100644 --- a/ui/components/app/wallet-overview/btc-overview.test.tsx +++ b/ui/components/app/wallet-overview/btc-overview.test.tsx @@ -1,6 +1,6 @@ import React from 'react'; import configureMockStore from 'redux-mock-store'; -import { fireEvent, waitFor } from '@testing-library/react'; +import { fireEvent } from '@testing-library/react'; import thunk from 'redux-thunk'; import { Cryptocurrency } from '@metamask/assets-controllers'; import { BtcAccountType, BtcMethod } from '@metamask/keyring-api'; @@ -17,7 +17,7 @@ const PORTOFOLIO_URL = 'https://portfolio.test'; const BTC_OVERVIEW_BUY = 'coin-overview-buy'; const BTC_OVERVIEW_BRIDGE = 'coin-overview-bridge'; -const BTC_OVERVIEW_PORTFOLIO = 'coin-overview-portfolio'; +const BTC_OVERVIEW_RECEIVE = 'coin-overview-receive'; const BTC_OVERVIEW_SWAP = 'token-overview-button-swap'; const BTC_OVERVIEW_SEND = 'coin-overview-send'; const BTC_OVERVIEW_PRIMARY_CURRENCY = 'coin-overview__primary-currency'; @@ -218,27 +218,9 @@ describe('BtcOverview', () => { }); }); - it('always show the Portfolio button', () => { + it('always show the Receive button', () => { const { queryByTestId } = renderWithProvider(, getStore()); - const portfolioButton = queryByTestId(BTC_OVERVIEW_PORTFOLIO); - expect(portfolioButton).toBeInTheDocument(); - }); - - it('open the Portfolio URI when clicking on Portfolio button', async () => { - const { queryByTestId } = renderWithProvider(, getStore()); - const openTabSpy = jest.spyOn(global.platform, 'openTab'); - - const portfolioButton = queryByTestId(BTC_OVERVIEW_PORTFOLIO); - expect(portfolioButton).toBeInTheDocument(); - fireEvent.click(portfolioButton as HTMLElement); - - expect(openTabSpy).toHaveBeenCalledTimes(1); - await waitFor(() => - expect(openTabSpy).toHaveBeenCalledWith({ - url: expect.stringContaining( - `?metamaskEntry=ext_portfolio_button&metametricsId=${mockMetaMetricsId}`, - ), - }), - ); + const receiveButton = queryByTestId(BTC_OVERVIEW_RECEIVE); + expect(receiveButton).toBeInTheDocument(); }); }); diff --git a/ui/components/app/wallet-overview/coin-buttons.tsx b/ui/components/app/wallet-overview/coin-buttons.tsx index cf9a60e45da2..73b0679b49a7 100644 --- a/ui/components/app/wallet-overview/coin-buttons.tsx +++ b/ui/components/app/wallet-overview/coin-buttons.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useContext } from 'react'; +import React, { useCallback, useContext, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { useHistory, @@ -36,11 +36,9 @@ import { ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) SwapsEthToken, getCurrentKeyring, - getDataCollectionForMarketing, - getMetaMetricsId, - getParticipateInMetaMetrics, ///: END:ONLY_INCLUDE_IF getUseExternalServices, + getSelectedAccount, } from '../../../selectors'; import Tooltip from '../../ui/tooltip'; ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) @@ -65,9 +63,9 @@ import { import { Box, Icon, IconName } from '../../component-library'; import IconButton from '../../ui/icon-button'; ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) -import { getPortfolioUrl } from '../../../helpers/utils/portfolio'; import useRamps from '../../../hooks/ramps/useRamps/useRamps'; import useBridging from '../../../hooks/bridge/useBridging'; +import { ReceiveModal } from '../../multichain/receive-modal'; ///: END:ONLY_INCLUDE_IF const CoinButtons = ({ @@ -95,12 +93,12 @@ const CoinButtons = ({ const dispatch = useDispatch(); const trackEvent = useContext(MetaMetricsContext); + const [showReceiveModal, setShowReceiveModal] = useState(false); + + const { address: selectedAddress } = useSelector(getSelectedAccount); const history = useHistory(); ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) const location = useLocation(); - const metaMetricsId = useSelector(getMetaMetricsId); - const isMetaMetricsEnabled = useSelector(getParticipateInMetaMetrics); - const isMarketingEnabled = useSelector(getDataCollectionForMarketing); const keyring = useSelector(getCurrentKeyring); const usingHardwareWallet = isHardwareKeyring(keyring?.type); ///: END:ONLY_INCLUDE_IF @@ -300,27 +298,6 @@ const CoinButtons = ({ location.pathname.includes('asset') ? '&token=native' : '', ); }, [defaultSwapsToken, location, openBridgeExperience]); - - const handlePortfolioOnClick = useCallback(() => { - const url = getPortfolioUrl( - '', - 'ext_portfolio_button', - metaMetricsId, - isMetaMetricsEnabled, - isMarketingEnabled, - ); - global.platform.openTab({ url }); - trackEvent({ - category: MetaMetricsEventCategory.Navigation, - event: MetaMetricsEventName.PortfolioLinkClicked, - properties: { - location: 'Home', - text: 'Portfolio', - chain_id: chainId, - token_symbol: 'ETH', - }, - }); - }, [chainId, metaMetricsId]); ///: END:ONLY_INCLUDE_IF return ( @@ -349,22 +326,6 @@ const CoinButtons = ({ ///: END:ONLY_INCLUDE_IF } - - } - disabled={!isSigningEnabled} - label={t('send')} - onClick={handleSendOnClick} - tooltipRender={(contents: React.ReactElement) => - generateTooltip('sendButton', contents) - } - /> ///: END:ONLY_INCLUDE_IF } + + } + disabled={!isSigningEnabled} + label={t('send')} + onClick={handleSendOnClick} + tooltipRender={(contents: React.ReactElement) => + generateTooltip('sendButton', contents) + } + /> { - ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) - - } - label={t('portfolio')} - onClick={handlePortfolioOnClick} - /> - ///: END:ONLY_INCLUDE_IF + <> + {showReceiveModal && ( + setShowReceiveModal(false)} + /> + )} + + } + label={t('receive')} + onClick={() => { + setShowReceiveModal(true); + }} + /> + } ); diff --git a/ui/components/app/wallet-overview/coin-overview.tsx b/ui/components/app/wallet-overview/coin-overview.tsx index 53d98f17039d..1a5e408bcac5 100644 --- a/ui/components/app/wallet-overview/coin-overview.tsx +++ b/ui/components/app/wallet-overview/coin-overview.tsx @@ -1,15 +1,25 @@ -import React, { useContext } from 'react'; +import React, { useContext, useCallback } from 'react'; import { useSelector } from 'react-redux'; import classnames from 'classnames'; import { zeroAddress } from 'ethereumjs-util'; - import { CaipChainId } from '@metamask/utils'; import type { Hex } from '@metamask/utils'; +import { Icon, IconName, IconSize } from '../../component-library'; +import { IconColor } from '../../../helpers/constants/design-system'; +import { MetaMetricsContext } from '../../../contexts/metametrics'; +import { + MetaMetricsEventCategory, + MetaMetricsEventName, +} from '../../../../shared/constants/metametrics'; +import { getPortfolioUrl } from '../../../helpers/utils/portfolio'; import { I18nContext } from '../../../contexts/i18n'; import Tooltip from '../../ui/tooltip'; import UserPreferencedCurrencyDisplay from '../user-preferenced-currency-display'; import { PRIMARY, SECONDARY } from '../../../helpers/constants/common'; import { + getDataCollectionForMarketing, + getMetaMetricsId, + getParticipateInMetaMetrics, getPreferences, getTokensMarketData, ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) @@ -66,6 +76,12 @@ export const CoinOverview = ({ ///: END:ONLY_INCLUDE_IF const t = useContext(I18nContext); + const trackEvent = useContext(MetaMetricsContext); + + const metaMetricsId = useSelector(getMetaMetricsId); + const isMetaMetricsEnabled = useSelector(getParticipateInMetaMetrics); + const isMarketingEnabled = useSelector(getDataCollectionForMarketing); + const isEvm = useSelector(getMultichainIsEvm); const showFiat = useSelector(getMultichainShouldShowFiat); const { useNativeCurrencyAsPrimaryCurrency } = useSelector(getPreferences); @@ -78,6 +94,25 @@ export const CoinOverview = ({ ); const tokensMarketData = useSelector(getTokensMarketData); + const handlePortfolioOnClick = useCallback(() => { + const url = getPortfolioUrl( + '', + 'ext_portfolio_button', + metaMetricsId, + isMetaMetricsEnabled, + isMarketingEnabled, + ); + global.platform.openTab({ url }); + trackEvent({ + category: MetaMetricsEventCategory.Navigation, + event: MetaMetricsEventName.PortfolioLinkClicked, + properties: { + location: 'Home', + text: 'Portfolio', + }, + }); + }, [isMarketingEnabled, isMetaMetricsEnabled, metaMetricsId, trackEvent]); + return ( )} - {showFiat && isOriginalNativeSymbol && balance && ( - - )} +
+ {showFiat && isOriginalNativeSymbol && balance && ( + + )} +
+ {t('portfolio')} + +
+
{isEvm && ( { @@ -38,21 +34,7 @@ describe('EthOverview', () => { const mockStore = { metamask: { - providerConfig: { - chainId: CHAIN_IDS.MAINNET, - nickname: MAINNET_DISPLAY_NAME, - type: NETWORK_TYPES.MAINNET, - ticker: 'ETH', - }, - networkConfigurations: { - testNetworkConfigurationId: { - rpcUrl: 'https://testrpc.com', - chainId: '0x89', - nickname: 'Custom Mainnet RPC', - type: 'rpc', - id: 'custom-mainnet', - }, - }, + ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), accountsByChainId: { [CHAIN_IDS.MAINNET]: { '0x1': { address: '0x1', balance: '0x1F4' }, @@ -132,7 +114,7 @@ describe('EthOverview', () => { const store = configureMockStore([thunk])(mockStore); const ETH_OVERVIEW_BUY = 'eth-overview-buy'; const ETH_OVERVIEW_BRIDGE = 'eth-overview-bridge'; - const ETH_OVERVIEW_PORTFOLIO = 'eth-overview-portfolio'; + const ETH_OVERVIEW_RECEIVE = 'eth-overview-receive'; const ETH_OVERVIEW_SWAP = 'token-overview-button-swap'; const ETH_OVERVIEW_SEND = 'eth-overview-send'; const ETH_OVERVIEW_PRIMARY_CURRENCY = 'eth-overview__primary-currency'; @@ -206,19 +188,7 @@ describe('EthOverview', () => { ...mockStore, metamask: { ...mockStore.metamask, - providerConfig: { - ...mockStore.metamask.providerConfig, - chainId: '0xa86a', - }, - networkConfigurations: { - testNetworkConfigurationId: { - rpcUrl: 'https://testrpc.com', - chainId: '0x89', - nickname: 'Custom Mainnet RPC', - type: 'rpc', - id: 'custom-mainnet', - }, - }, + ...mockNetworkState({ chainId: '0xa86a' }), }, }; const mockedStore = configureMockStore([thunk])(mockedAvalancheStore); @@ -305,19 +275,7 @@ describe('EthOverview', () => { ...mockStore, metamask: { ...mockStore.metamask, - providerConfig: { - ...mockStore.metamask.providerConfig, - chainId: '0xfa', - }, - networkConfigurations: { - testNetworkConfigurationId: { - rpcUrl: 'https://testrpc.com', - chainId: '0x89', - nickname: 'Custom Mainnet RPC', - type: 'rpc', - id: 'custom-mainnet', - }, - }, + ...mockNetworkState({ chainId: CHAIN_IDS.SEPOLIA }), }, }; const mockedStore = configureMockStore([thunk])(mockedFantomStore); @@ -337,28 +295,10 @@ describe('EthOverview', () => { it('should always show the Portfolio button', () => { const { queryByTestId } = renderWithProvider(, store); - const portfolioButton = queryByTestId(ETH_OVERVIEW_PORTFOLIO); + const portfolioButton = queryByTestId(ETH_OVERVIEW_RECEIVE); expect(portfolioButton).toBeInTheDocument(); }); - it('should open the Portfolio URI when clicking on Portfolio button', async () => { - const { queryByTestId } = renderWithProvider(, store); - - const portfolioButton = queryByTestId(ETH_OVERVIEW_PORTFOLIO); - - expect(portfolioButton).toBeInTheDocument(); - expect(portfolioButton).not.toBeDisabled(); - - fireEvent.click(portfolioButton); - expect(openTabSpy).toHaveBeenCalledTimes(1); - - await waitFor(() => - expect(openTabSpy).toHaveBeenCalledWith({ - url: expect.stringContaining(`?metamaskEntry=ext`), - }), - ); - }); - it('should always show the Buy button regardless of current chain Id', () => { const { queryByTestId } = renderWithProvider(, store); const buyButton = queryByTestId(ETH_OVERVIEW_BUY); @@ -370,11 +310,7 @@ describe('EthOverview', () => { ...mockStore, metamask: { ...mockStore.metamask, - providerConfig: { - type: 'test', - chainId: CHAIN_IDS.GOERLI, - nickname: GOERLI_DISPLAY_NAME, - }, + ...mockNetworkState({ chainId: CHAIN_IDS.GOERLI }), }, }; const mockedStore = configureMockStore([thunk])( @@ -395,20 +331,7 @@ describe('EthOverview', () => { ...mockStore, metamask: { ...mockStore.metamask, - providerConfig: { - chainId: '0x89', - type: 'rpc', - id: 'custom-mainnet', - }, - networkConfigurations: { - testNetworkConfigurationId: { - rpcUrl: 'https://testrpc.com', - chainId: '0x89', - nickname: 'Custom Mainnet RPC', - type: 'rpc', - id: 'custom-mainnet', - }, - }, + ...mockNetworkState({ chainId: CHAIN_IDS.POLYGON }), }, }; const mockedStore = configureMockStore([thunk])( @@ -429,20 +352,7 @@ describe('EthOverview', () => { ...mockStore, metamask: { ...mockStore.metamask, - providerConfig: { - chainId: '0x89', - type: 'rpc', - id: 'custom-mainnet', - }, - networkConfigurations: { - testNetworkConfigurationId: { - rpcUrl: 'https://testrpc.com', - chainId: '0x89', - nickname: 'Custom Mainnet RPC', - type: 'rpc', - id: 'custom-mainnet', - }, - }, + ...mockNetworkState({ chainId: CHAIN_IDS.POLYGON }), }, }; const mockedStore = configureMockStore([thunk])( diff --git a/ui/components/app/wallet-overview/index.scss b/ui/components/app/wallet-overview/index.scss index e6ca04fcebd9..bbbf57075c24 100644 --- a/ui/components/app/wallet-overview/index.scss +++ b/ui/components/app/wallet-overview/index.scss @@ -14,6 +14,7 @@ &__balance { flex: 1; display: flex; + gap: 4px; flex-direction: column; align-items: center; width: 100%; @@ -25,6 +26,21 @@ height: 68px; margin-bottom: 24px; } + + &__portfolio_button { + display: flex; + flex-direction: row; + gap: 6px; + cursor: pointer; + align-items: center; + color: var(--color-primary-default); + } + + &__currency-wrapper { + display: flex; + flex-direction: row; + gap: 10px; + } } %asset-buttons { @@ -45,6 +61,7 @@ display: flex; flex-direction: column; min-width: 0; + gap: 4px; position: relative; align-items: center; margin: 16px 0; diff --git a/ui/components/component-library/modal-header/modal-header.types.ts b/ui/components/component-library/modal-header/modal-header.types.ts index 8b09318bf622..eac7ba56d8db 100644 --- a/ui/components/component-library/modal-header/modal-header.types.ts +++ b/ui/components/component-library/modal-header/modal-header.types.ts @@ -2,6 +2,14 @@ import React from 'react'; import type { ButtonIconProps } from '../button-icon/button-icon.types'; import type { HeaderBaseStyleUtilityProps } from '../header-base'; +/** + * Makes all props optional so that if a prop object is used not ALL required props need to be passed + * TODO: Move to appropriate place in app as this will be highly reusable + */ +type MakePropsOptional = { + [K in keyof T]?: T[K]; +}; + // TODO: Convert to a `type` in a future major version. // eslint-disable-next-line @typescript-eslint/consistent-type-definitions export interface ModalHeaderProps extends HeaderBaseStyleUtilityProps { @@ -35,7 +43,7 @@ export interface ModalHeaderProps extends HeaderBaseStyleUtilityProps { /** * The props to pass to the close `ButtonIcon` */ - closeButtonProps?: ButtonIconProps<'button'>; + closeButtonProps?: MakePropsOptional>; /** * The end (right) content area of ModalHeader * Default to have the close `ButtonIcon` when `onClose` is passed, but passing a `endAccessory` will override this diff --git a/ui/components/institutional/custody-confirm-link-modal/custody-confirm-link-modal.stories.tsx b/ui/components/institutional/custody-confirm-link-modal/custody-confirm-link-modal.stories.tsx index 3f0dc3055022..fa10bebaec40 100644 --- a/ui/components/institutional/custody-confirm-link-modal/custody-confirm-link-modal.stories.tsx +++ b/ui/components/institutional/custody-confirm-link-modal/custody-confirm-link-modal.stories.tsx @@ -52,9 +52,6 @@ const customData = { custodianName: 'saturn-dev', }, }, - providerConfig: { - type: 'test', - }, }, }; diff --git a/ui/components/institutional/custody-confirm-link-modal/custody-confirm-link-modal.test.tsx b/ui/components/institutional/custody-confirm-link-modal/custody-confirm-link-modal.test.tsx index 52feba07ac47..1f4fae5168e9 100644 --- a/ui/components/institutional/custody-confirm-link-modal/custody-confirm-link-modal.test.tsx +++ b/ui/components/institutional/custody-confirm-link-modal/custody-confirm-link-modal.test.tsx @@ -83,9 +83,6 @@ describe('Custody Confirm Link', () => { custodianName: mockedCustodianName, }, }, - providerConfig: { - type: 'test', - }, }, }; diff --git a/ui/components/institutional/interactive-replacement-token-modal/interactive-replacement-token-modal.stories.tsx b/ui/components/institutional/interactive-replacement-token-modal/interactive-replacement-token-modal.stories.tsx index dac449cce66a..862b98d350f3 100644 --- a/ui/components/institutional/interactive-replacement-token-modal/interactive-replacement-token-modal.stories.tsx +++ b/ui/components/institutional/interactive-replacement-token-modal/interactive-replacement-token-modal.stories.tsx @@ -42,9 +42,6 @@ const customData = { custodianName: 'saturn-dev', }, }, - providerConfig: { - type: 'test', - }, isUnlocked: true, interactiveReplacementToken: { oldRefreshToken: 'abc', diff --git a/ui/components/institutional/interactive-replacement-token-modal/interactive-replacement-token-modal.test.tsx b/ui/components/institutional/interactive-replacement-token-modal/interactive-replacement-token-modal.test.tsx index 6d71c4681de6..535b21f2e69b 100644 --- a/ui/components/institutional/interactive-replacement-token-modal/interactive-replacement-token-modal.test.tsx +++ b/ui/components/institutional/interactive-replacement-token-modal/interactive-replacement-token-modal.test.tsx @@ -43,9 +43,6 @@ describe('Interactive Replacement Token Modal', function () { custodianName: 'saturn-dev', }, }, - providerConfig: { - type: 'test', - }, isUnlocked: true, interactiveReplacementToken: { oldRefreshToken: 'abc', diff --git a/ui/components/institutional/interactive-replacement-token-notification/interactive-replacement-token-notification.stories.tsx b/ui/components/institutional/interactive-replacement-token-notification/interactive-replacement-token-notification.stories.tsx index a92ebadc04e5..fd169d23881f 100644 --- a/ui/components/institutional/interactive-replacement-token-notification/interactive-replacement-token-notification.stories.tsx +++ b/ui/components/institutional/interactive-replacement-token-notification/interactive-replacement-token-notification.stories.tsx @@ -8,9 +8,6 @@ const customData = { ...testData, metamask: { ...testData.metamask, - providerConfig: { - type: 'test', - }, isUnlocked: true, interactiveReplacementToken: { oldRefreshToken: diff --git a/ui/components/institutional/interactive-replacement-token-notification/interactive-replacement-token-notification.test.tsx b/ui/components/institutional/interactive-replacement-token-notification/interactive-replacement-token-notification.test.tsx index a6d2a417ea03..77424a95e86e 100644 --- a/ui/components/institutional/interactive-replacement-token-notification/interactive-replacement-token-notification.test.tsx +++ b/ui/components/institutional/interactive-replacement-token-notification/interactive-replacement-token-notification.test.tsx @@ -52,9 +52,6 @@ describe('Interactive Replacement Token Notification', () => { const mockStore = { metamask: { - providerConfig: { - type: 'test', - }, internalAccounts: { accounts: { 'cf8dace4-9439-4bd4-b3a8-88c821c8fcb3': { diff --git a/ui/components/institutional/jwt-url-form/jwt-url-form.test.tsx b/ui/components/institutional/jwt-url-form/jwt-url-form.test.tsx index 16f315f6fbae..9f3e0a610aa9 100644 --- a/ui/components/institutional/jwt-url-form/jwt-url-form.test.tsx +++ b/ui/components/institutional/jwt-url-form/jwt-url-form.test.tsx @@ -6,11 +6,7 @@ import JwtUrlForm from './jwt-url-form'; describe('JwtUrlForm', function () { const mockStore = { - metamask: { - providerConfig: { - type: 'test', - }, - }, + metamask: {}, }; const store = configureMockStore()(mockStore); diff --git a/ui/components/institutional/wrong-network-notification/wrong-network-notification.stories.tsx b/ui/components/institutional/wrong-network-notification/wrong-network-notification.stories.tsx index f3cc5e769a62..01ba40676f93 100644 --- a/ui/components/institutional/wrong-network-notification/wrong-network-notification.stories.tsx +++ b/ui/components/institutional/wrong-network-notification/wrong-network-notification.stories.tsx @@ -4,15 +4,13 @@ import { toHex } from '@metamask/controller-utils'; import configureStore from '../../../store/store'; import testData from '../../../../.storybook/test-data'; import WrongNetworkNotification from '.'; +import { mockNetworkState } from '../../../../test/stub/networks'; const customData = { ...testData, metamask: { ...testData.metamask, - providerConfig: { - type: 'test', - chainId: toHex(3), - }, + ...mockNetworkState({ chainId: toHex(3) }), accountsByChainId: { [toHex(3)]: { '0x5Ab19e7091dD208F352F8E727B6DCC6F8aBB6275': { balance: '0x0' }, diff --git a/ui/components/institutional/wrong-network-notification/wrong-network-notification.test.tsx b/ui/components/institutional/wrong-network-notification/wrong-network-notification.test.tsx index c87f1399e7e7..e95d94290dfc 100644 --- a/ui/components/institutional/wrong-network-notification/wrong-network-notification.test.tsx +++ b/ui/components/institutional/wrong-network-notification/wrong-network-notification.test.tsx @@ -5,6 +5,7 @@ import { toHex } from '@metamask/controller-utils'; import { renderWithProvider } from '../../../../test/lib/render-helpers'; import testData from '../../../../.storybook/test-data'; import { ETH_EOA_METHODS } from '../../../../shared/constants/eth-methods'; +import { mockNetworkState } from '../../../../test/stub/networks'; import WrongNetworkNotification from '.'; jest.mock('../../../../shared/modules/hash.utils'); @@ -14,10 +15,7 @@ describe('Wrong Network Notification', function () { ...testData, metamask: { ...testData.metamask, - providerConfig: { - type: 'test', - chainId: toHex(3), - }, + ...mockNetworkState({ chainId: toHex(3) }), accountsByChainId: { [toHex(1)]: { '0x5Ab19e7091dD208F352F8E727B6DCC6F8aBB6275': { balance: '0x0' }, @@ -76,10 +74,6 @@ describe('Wrong Network Notification', function () { ...mockStore, metamask: { ...mockStore.metamask, - providerConfig: { - type: 'test', - chainId: toHex(3), - }, accountsByChainId: { [toHex(1)]: { '0x5Ab19e7091dD208F352F8E727B6DCC6F8aBB6275': { balance: '0x0' }, @@ -102,10 +96,6 @@ describe('Wrong Network Notification', function () { ...mockStore, metamask: { ...mockStore.metamask, - providerConfig: { - type: 'test', - chainId: toHex(3), - }, accountsByChainId: { [toHex(3)]: { '0x5Ab19e7091dD208F352F8E727B6DCC6F8aBB6275': { balance: '0x0' }, diff --git a/ui/components/institutional/wrong-network-notification/wrong-network-notification.tsx b/ui/components/institutional/wrong-network-notification/wrong-network-notification.tsx index faf74c61d4e4..4de11e4c153b 100644 --- a/ui/components/institutional/wrong-network-notification/wrong-network-notification.tsx +++ b/ui/components/institutional/wrong-network-notification/wrong-network-notification.tsx @@ -1,6 +1,5 @@ import React from 'react'; import { useSelector } from 'react-redux'; -import { ProviderConfig } from '@metamask/network-controller'; import { Display, AlignItems, @@ -17,7 +16,10 @@ import { Icon, IconName, IconSize, Box, Text } from '../../component-library'; const WrongNetworkNotification: React.FC = () => { const t = useI18nContext(); - const providerConfig = useSelector(getProviderConfig); + const providerConfig = useSelector< + object, + { nickname: string; type: string } + >(getProviderConfig); const balance = useSelector(getSelectedAccountCachedBalance); const isCustodianSupportedChain = useSelector(getIsCustodianSupportedChain); diff --git a/ui/components/multichain/account-list-item/account-list-item.test.js b/ui/components/multichain/account-list-item/account-list-item.test.js index f5e3ecc4c92c..09febefb3d83 100644 --- a/ui/components/multichain/account-list-item/account-list-item.test.js +++ b/ui/components/multichain/account-list-item/account-list-item.test.js @@ -11,6 +11,7 @@ import { SEPOLIA_DISPLAY_NAME, CHAIN_IDS, } from '../../../../shared/constants/network'; +import { mockNetworkState } from '../../../../test/stub/networks'; import { AccountListItem, AccountListItemMenuTypes } from '.'; const mockAccount = { @@ -207,10 +208,7 @@ describe('AccountListItem', () => { }, { metamask: { - providerConfig: { - chainId: CHAIN_IDS.SEPOLIA, - nickname: SEPOLIA_DISPLAY_NAME, - }, + ...mockNetworkState({ chainId: CHAIN_IDS.SEPOLIA }), preferences: { showFiatInTestnets: false, }, @@ -246,10 +244,11 @@ describe('AccountListItem', () => { }, { metamask: { - providerConfig: { + ...mockNetworkState({ chainId: CHAIN_IDS.SEPOLIA, nickname: SEPOLIA_DISPLAY_NAME, - }, + ticker: 'ETH', + }), preferences: { showFiatInTestnets: true, }, diff --git a/ui/components/multichain/account-list-menu/account-list-menu.tsx b/ui/components/multichain/account-list-menu/account-list-menu.tsx index f74d1c12a524..ce87e6e0e34e 100644 --- a/ui/components/multichain/account-list-menu/account-list-menu.tsx +++ b/ui/components/multichain/account-list-menu/account-list-menu.tsx @@ -57,6 +57,7 @@ import { getIsAddSnapAccountEnabled, ///: END:ONLY_INCLUDE_IF ///: BEGIN:ONLY_INCLUDE_IF(build-flask) + getIsWatchEthereumAccountEnabled, getIsBitcoinSupportEnabled, getIsBitcoinTestnetSupportEnabled, ///: END:ONLY_INCLUDE_IF @@ -81,6 +82,10 @@ import { getEnvironmentType } from '../../../../app/scripts/lib/util'; import { ENVIRONMENT_TYPE_POPUP } from '../../../../shared/constants/app'; import { getAccountLabel } from '../../../helpers/utils/accounts'; ///: BEGIN:ONLY_INCLUDE_IF(build-flask) +import { + ACCOUNT_WATCHER_NAME, + ACCOUNT_WATCHER_SNAP_ID, +} from '../../../../app/scripts/lib/snap-keyring/account-watcher-snap'; import { hasCreatedBtcMainnetAccount, hasCreatedBtcTestnetAccount, @@ -102,6 +107,8 @@ const ACTION_MODES = { // Displays the add account form controls ADD: 'add', ///: BEGIN:ONLY_INCLUDE_IF(build-flask) + // Displays the add account form controls (for watch-only account) + ADD_WATCH_ONLY: 'add-watch-only', // Displays the add account form controls (for bitcoin account) ADD_BITCOIN: 'add-bitcoin', // Same but for testnet @@ -124,15 +131,15 @@ export const getActionTitle = ( ) => { switch (actionMode) { case ACTION_MODES.ADD: + case ACTION_MODES.MENU: return t('addAccount'); ///: BEGIN:ONLY_INCLUDE_IF(build-flask) + case ACTION_MODES.ADD_WATCH_ONLY: case ACTION_MODES.ADD_BITCOIN: return t('addAccount'); case ACTION_MODES.ADD_BITCOIN_TESTNET: return t('addAccount'); ///: END:ONLY_INCLUDE_IF - case ACTION_MODES.MENU: - return t('addAccount'); case ACTION_MODES.IMPORT: return t('importAccount'); default: @@ -219,6 +226,24 @@ export const AccountListMenu = ({ const addSnapAccountEnabled = useSelector(getIsAddSnapAccountEnabled); ///: END:ONLY_INCLUDE_IF ///: BEGIN:ONLY_INCLUDE_IF(build-flask) + const isAddWatchEthereumAccountEnabled = useSelector( + getIsWatchEthereumAccountEnabled, + ); + const handleAddWatchAccount = useCallback(async () => { + await trackEvent({ + category: MetaMetricsEventCategory.Navigation, + event: MetaMetricsEventName.AccountAddSelected, + properties: { + account_type: MetaMetricsEventAccountType.Snap, + snap_id: ACCOUNT_WATCHER_SNAP_ID, + snap_name: ACCOUNT_WATCHER_NAME, + location: 'Main Menu', + }, + }); + onClose(); + history.push(`/snaps/view/${encodeURIComponent(ACCOUNT_WATCHER_SNAP_ID)}`); + }, [trackEvent, onClose, history]); + const bitcoinSupportEnabled = useSelector(getIsBitcoinSupportEnabled); const bitcoinTestnetSupportEnabled = useSelector( getIsBitcoinTestnetSupportEnabled, @@ -520,6 +545,23 @@ export const AccountListMenu = ({
///: END:ONLY_INCLUDE_IF } + { + ///: BEGIN:ONLY_INCLUDE_IF(build-flask) + isAddWatchEthereumAccountEnabled && ( + + + {t('addEthereumWatchOnlyAccount')} + + + ) + ///: END:ONLY_INCLUDE_IF + } ) : null} {actionMode === ACTION_MODES.LIST ? ( diff --git a/ui/components/multichain/account-overview/account-overview-btc.test.tsx b/ui/components/multichain/account-overview/account-overview-btc.test.tsx index 7a4390f013cb..34cbed54c127 100644 --- a/ui/components/multichain/account-overview/account-overview-btc.test.tsx +++ b/ui/components/multichain/account-overview/account-overview-btc.test.tsx @@ -40,7 +40,6 @@ describe('AccountOverviewBtc', () => { const { queryByTestId } = render(); expect(queryByTestId('account-overview__asset-tab')).toBeInTheDocument(); - expect(queryByTestId('receive-token-button')).not.toBeInTheDocument(); expect(queryByTestId('import-token-button')).not.toBeInTheDocument(); // TODO: This one might be required, but we do not really handle tokens for BTC yet... expect(queryByTestId('refresh-list-button')).not.toBeInTheDocument(); diff --git a/ui/components/multichain/account-overview/account-overview-tabs.tsx b/ui/components/multichain/account-overview/account-overview-tabs.tsx index 33de033fa103..554d9fb0acfc 100644 --- a/ui/components/multichain/account-overview/account-overview-tabs.tsx +++ b/ui/components/multichain/account-overview/account-overview-tabs.tsx @@ -61,7 +61,6 @@ export const AccountOverviewTabs = ({ const t = useI18nContext(); const trackEvent = useContext(MetaMetricsContext); - const tabPadding = 4; const tabProps = useMemo( () => ({ activeClassName: 'account-overview__tab--active', @@ -128,7 +127,7 @@ export const AccountOverviewTabs = ({ ///: END:ONLY_INCLUDE_IF return ( - + { const store = configureStore({ metamask: { ...mockState.metamask, - providerConfig: { - chainId: '0x99', - }, ...state, }, }); diff --git a/ui/components/multichain/address-copy-button/address-copy-button.test.js b/ui/components/multichain/address-copy-button/address-copy-button.test.js index 2de16d491c0d..62158712753a 100644 --- a/ui/components/multichain/address-copy-button/address-copy-button.test.js +++ b/ui/components/multichain/address-copy-button/address-copy-button.test.js @@ -103,7 +103,7 @@ describe('AccountListItem', () => { ); const tooltipTitle = container.querySelector( - '[data-original-title="This account is not set up for use with goerli"]', + '[data-original-title="This account is not set up for use with Chain 5"]', ); const button = queryByTestId('address-copy-button-text'); diff --git a/ui/components/multichain/app-header/app-header.test.js b/ui/components/multichain/app-header/app-header.test.js index d72d65510d96..025bbb81e255 100644 --- a/ui/components/multichain/app-header/app-header.test.js +++ b/ui/components/multichain/app-header/app-header.test.js @@ -6,6 +6,7 @@ import mockState from '../../../../test/data/mock-state.json'; import { SEND_STAGES } from '../../../ducks/send'; import { getEnvironmentType } from '../../../../app/scripts/lib/util'; import { ENVIRONMENT_TYPE_POPUP } from '../../../../shared/constants/app'; +import { mockNetworkState } from '../../../../test/stub/networks'; import { AppHeader } from '.'; jest.mock('../../../../app/scripts/lib/util', () => ({ @@ -23,8 +24,7 @@ jest.mock('react-router-dom', () => ({ const render = ({ stateChanges = {}, - provider = {}, - networkConfigurations = {}, + network = { chainId: '0x5', nickname: 'Chain 5', ticker: 'ETH' }, location = {}, isUnlocked = true, } = {}) => { @@ -32,14 +32,7 @@ const render = ({ ...mockState, metamask: { ...mockState.metamask, - providerConfig: { - ...mockState.metamask.providerConfig, - ...(provider ?? {}), - }, - networkConfigurations: { - ...mockState.metamask.networkConfigurations, - ...(networkConfigurations ?? {}), - }, + ...mockNetworkState(network), isUnlocked: isUnlocked ?? true, }, activeTab: { @@ -189,20 +182,13 @@ describe('App Header', () => { describe('network picker', () => { it('shows custom rpc if it has the same chainId as a default network', () => { const mockProviderConfig = { - id: 'custom-rpc-localhost', - type: 'rpc', - ticker: 'ETH', chainId: '0x1', rpcUrl: 'https://localhost:8545', nickname: 'Localhost', }; - const mockNetworkConfigurations = { - [mockProviderConfig.id]: mockProviderConfig, - }; const { getByText } = render({ - provider: mockProviderConfig, - networkConfigurations: mockNetworkConfigurations, + network: mockProviderConfig, isUnlocked: true, }); expect(getByText(mockProviderConfig.nickname)).toBeInTheDocument(); @@ -210,20 +196,13 @@ describe('App Header', () => { it("shows rpc url as nickname if there isn't a nickname set", () => { const mockProviderConfig = { - id: 'custom-rpc-localhost', - type: 'rpc', - ticker: 'ETH', chainId: '0x1', rpcUrl: 'https://localhost:8545', nickname: null, }; - const mockNetworkConfigurations = { - [mockProviderConfig.id]: mockProviderConfig, - }; const { getByText } = render({ - provider: mockProviderConfig, - networkConfigurations: mockNetworkConfigurations, + network: mockProviderConfig, isUnlocked: true, }); expect(getByText(mockProviderConfig.rpcUrl)).toBeInTheDocument(); diff --git a/ui/components/multichain/asset-picker-amount/asset-balance/asset-balance-text.tsx b/ui/components/multichain/asset-picker-amount/asset-balance/asset-balance-text.tsx index 2ed83803bdc5..67def7ee82b1 100644 --- a/ui/components/multichain/asset-picker-amount/asset-balance/asset-balance-text.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-balance/asset-balance-text.tsx @@ -20,7 +20,7 @@ import { useTokenFiatAmount } from '../../../../hooks/useTokenFiatAmount'; import { getIsFiatPrimary } from '../utils'; import { useI18nContext } from '../../../../hooks/useI18nContext'; import { hexToDecimal } from '../../../../../shared/modules/conversion.utils'; -import { Token } from '../asset-picker-modal/types'; +import { TokenWithBalance } from '../asset-picker-modal/types'; export type AssetBalanceTextProps = { asset: Asset; @@ -38,7 +38,7 @@ export function AssetBalanceText({ const isFiatPrimary = useSelector(getIsFiatPrimary); - const { tokensWithBalances }: { tokensWithBalances: Token[] } = + const { tokensWithBalances }: { tokensWithBalances: TokenWithBalance[] } = useTokenTracker({ tokens: asset.details?.address && !asset.balance diff --git a/ui/components/multichain/asset-picker-amount/asset-picker-amount.stories.tsx b/ui/components/multichain/asset-picker-amount/asset-picker-amount.stories.tsx index 1f22ad544f0d..c087853d0c93 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker-amount.stories.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker-amount.stories.tsx @@ -6,6 +6,7 @@ import { AssetType } from '../../../../shared/constants/transaction'; import mockState from '../../../../test/data/mock-state.json'; import { AssetPickerAmount } from './asset-picker-amount'; import { INSUFFICIENT_FUNDS_ERROR_KEY } from '../../../helpers/constants/error-keys'; +import { mockNetworkState } from '../../../../test/stub/networks'; const noop = () => {}; @@ -54,10 +55,7 @@ const store = configureStore({ decimals: '18', }, ], - providerConfig: { - chainId: '0x1', - ticker: 'ETH', - }, + ...mockNetworkState({ chainId: '0x1' }), preferences: { showFiatInTestnets: true, }, diff --git a/ui/components/multichain/asset-picker-amount/asset-picker-amount.test.tsx b/ui/components/multichain/asset-picker-amount/asset-picker-amount.test.tsx index a5215202db1c..3347102ae649 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker-amount.test.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker-amount.test.tsx @@ -39,6 +39,7 @@ describe('AssetPickerAmount', () => { const onAmountChangeMock = jest.fn(); const defaultProps = { + header: 'testHeader', asset: { type: AssetType.token, details: { address: '0xToken', symbol: 'TOKEN', decimals: 18 }, diff --git a/ui/components/multichain/asset-picker-amount/asset-picker-amount.tsx b/ui/components/multichain/asset-picker-amount/asset-picker-amount.tsx index 12be182cfbd3..f6966e6f0328 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker-amount.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker-amount.tsx @@ -1,5 +1,6 @@ import React, { useCallback, useEffect, useState } from 'react'; import { useSelector } from 'react-redux'; +import { TokenListMap } from '@metamask/assets-controllers'; import { useI18nContext } from '../../../hooks/useI18nContext'; import { Box, Text } from '../../component-library'; import { @@ -12,7 +13,12 @@ import { TextColor, TextVariant, } from '../../../helpers/constants/design-system'; -import { getSelectedInternalAccount } from '../../../selectors'; +import { + getIpfsGateway, + getNativeCurrencyImage, + getSelectedInternalAccount, + getTokenList, +} from '../../../selectors'; import { AssetType, @@ -26,6 +32,8 @@ import { type Asset, } from '../../../ducks/send'; import { NEGATIVE_OR_ZERO_AMOUNT_TOKENS_ERROR } from '../../../pages/confirmations/send/send.constants'; +import { getAssetImageURL } from '../../../helpers/utils/util'; +import { getNativeCurrency } from '../../../ducks/metamask/metamask'; import MaxClearButton from './max-clear-button'; import { AssetPicker, @@ -73,6 +81,12 @@ export const AssetPickerAmount = ({ const isMaxMode = useSelector(getSendMaxModeState); const isNativeSendPossible = useSelector(getIsNativeSendPossible); + const nativeCurrencySymbol = useSelector(getNativeCurrency); + const nativeCurrencyImageUrl = useSelector(getNativeCurrencyImage); + const tokenList = useSelector(getTokenList) as TokenListMap; + + const ipfsGateway = useSelector(getIpfsGateway); + useEffect(() => { // if this input is immutable – avoids double fire if (isDisabled) { @@ -143,6 +157,37 @@ export const AssetPickerAmount = ({ sendAsset.type === AssetType.native && receiveAsset.type !== AssetType.native; + let standardizedAsset; + if (asset?.type === AssetType.native) { + standardizedAsset = { + type: asset.type, + image: nativeCurrencyImageUrl, + symbol: nativeCurrencySymbol as string, + }; + } else if (asset?.type === AssetType.token && asset?.details?.symbol) { + standardizedAsset = { + type: asset.type, + image: + getAssetImageURL(asset.details.image, ipfsGateway) || + (tokenList && + asset.details?.address && + tokenList[asset.details.address.toLowerCase()]?.iconUrl), + symbol: asset.details.symbol, + address: asset.details.address, + }; + } else if ( + asset?.type === AssetType.NFT && + asset?.details?.tokenId !== undefined && + asset?.details?.image + ) { + standardizedAsset = { + type: asset.type as AssetType.NFT, + tokenId: asset.details.tokenId, + image: asset.details.image, + symbol: asset.details.symbol, + }; + } + return ( - + ({ @@ -60,9 +61,13 @@ describe('Asset', () => { it('should render TokenListItem with correct props when address is provided', () => { const { getByText } = render( , ); @@ -80,23 +85,4 @@ describe('Asset', () => { {}, ); }); - - it('should render TokenListItem with correct props when address is not provided', () => { - const { getByText } = render( - , - ); - - expect(getByText('TokenListItem')).toBeInTheDocument(); - expect(TokenListItem).toHaveBeenCalledWith( - expect.objectContaining({ - tokenSymbol: 'WETH', - tokenImage: undefined, - primary: '10', - secondary: '$10.10', - title: 'WETH', - tooltipText: 'tooltip', - }), - {}, - ); - }); }); diff --git a/ui/components/multichain/asset-picker-amount/asset-picker-modal/Asset.tsx b/ui/components/multichain/asset-picker-amount/asset-picker-modal/Asset.tsx index f59b104c255b..83229689f055 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker-modal/Asset.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker-modal/Asset.tsx @@ -7,12 +7,9 @@ import { TokenListItem } from '../../token-list-item'; import { isEqualCaseInsensitive } from '../../../../../shared/modules/string-utils'; import { formatAmount } from '../../../../pages/confirmations/components/simulation-details/formatAmount'; import { getIntlLocale } from '../../../../ducks/locale/locale'; +import { AssetWithDisplayData, ERC20Asset } from './types'; -type AssetProps = { - address?: string | null; - image?: string; - symbol: string; - decimalTokenAmount?: string; +type AssetProps = AssetWithDisplayData & { tooltipText?: string; }; @@ -20,7 +17,7 @@ export default function Asset({ address, image, symbol, - decimalTokenAmount, + string: decimalTokenAmount, tooltipText, }: AssetProps) { const locale = useSelector(getIntlLocale); diff --git a/ui/components/multichain/asset-picker-amount/asset-picker-modal/AssetList.test.tsx b/ui/components/multichain/asset-picker-amount/asset-picker-modal/AssetList.test.tsx index 2335f68d8d43..c4e4ba0ceb29 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker-modal/AssetList.test.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker-modal/AssetList.test.tsx @@ -9,7 +9,9 @@ import { getNativeCurrency } from '../../../../ducks/metamask/metamask'; import { useUserPreferencedCurrency } from '../../../../hooks/useUserPreferencedCurrency'; import { useCurrencyDisplay } from '../../../../hooks/useCurrencyDisplay'; import { AssetType } from '../../../../../shared/constants/transaction'; +import { CHAIN_ID_TOKEN_IMAGE_MAP } from '../../../../../shared/constants/network'; import AssetList from './AssetList'; +import { AssetWithDisplayData, ERC20Asset, NativeAsset } from './types'; jest.mock('react-redux', () => ({ useSelector: jest.fn(), @@ -40,8 +42,11 @@ jest.mock('./Asset', () => jest.fn(() =>
AssetComponent
)); describe('AssetList', () => { const handleAssetChangeMock = jest.fn(); const nativeCurrency = 'ETH'; - const balanceValue = '1000000000000000000'; - const tokenList = [ + const balanceValue = '0x121'; + const tokenList: ( + | AssetWithDisplayData + | AssetWithDisplayData + )[] = [ { address: '0xToken1', symbol: 'TOKEN1', @@ -61,13 +66,13 @@ describe('AssetList', () => { balance: '10', }, { - address: '0xToken3', - symbol: 'TOKEN3', + address: null, + symbol: 'ETH', type: AssetType.native, - image: 'image3.png', + image: CHAIN_ID_TOKEN_IMAGE_MAP['0x1'], string: '30', decimals: 18, - balance: '20', + balance: '0x121', }, ]; const primaryCurrency = 'USD'; @@ -108,9 +113,12 @@ describe('AssetList', () => { render( , ); @@ -122,9 +130,12 @@ describe('AssetList', () => { render( , ); @@ -151,10 +162,13 @@ describe('AssetList', () => { render( token.address === '0xToken1'} />, ); diff --git a/ui/components/multichain/asset-picker-amount/asset-picker-modal/AssetList.tsx b/ui/components/multichain/asset-picker-amount/asset-picker-modal/AssetList.tsx index 5efebe781a1c..1ea66e917437 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker-modal/AssetList.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker-modal/AssetList.tsx @@ -19,26 +19,30 @@ import { FlexWrap, } from '../../../../helpers/constants/design-system'; import { TokenListItem } from '../..'; -import { isEqualCaseInsensitive } from '../../../../../shared/modules/string-utils'; -import { Asset, Token } from './types'; import AssetComponent from './Asset'; +import { AssetWithDisplayData, ERC20Asset, NativeAsset } from './types'; type AssetListProps = { - handleAssetChange: (token: Token) => void; - asset: Asset; - tokenList: Token[]; - sendingAssetSymbol?: string; - memoizedSwapsBlockedTokens: Set; + handleAssetChange: ( + token: AssetWithDisplayData | AssetWithDisplayData, + ) => void; + asset?: ERC20Asset | NativeAsset; + tokenList: ( + | AssetWithDisplayData + | AssetWithDisplayData + )[]; + isTokenDisabled?: ( + token: AssetWithDisplayData | AssetWithDisplayData, + ) => boolean; }; export default function AssetList({ handleAssetChange, asset, tokenList, - sendingAssetSymbol, - memoizedSwapsBlockedTokens, + isTokenDisabled, }: AssetListProps) { - const selectedToken = asset.details?.address; + const selectedToken = asset?.address; const nativeCurrency = useSelector(getNativeCurrency); const balanceValue = useSelector(getSelectedAccountCachedBalance); @@ -71,10 +75,7 @@ export default function AssetList({ {tokenList.map((token) => { const tokenAddress = token.address?.toLowerCase(); const isSelected = tokenAddress === selectedToken?.toLowerCase(); - const isDisabled = sendingAssetSymbol - ? !isEqualCaseInsensitive(sendingAssetSymbol, token.symbol) && - memoizedSwapsBlockedTokens.has(tokenAddress as string) - : false; + const isDisabled = isTokenDisabled?.(token) ?? false; return ( void; renderSearch: () => void; }; export function AssetPickerModalNftTab({ - collectionDataFiltered, - previouslyOwnedCollection, + searchQuery, onClose, renderSearch, }: AssetPickerModalNftTabProps) { const t = useI18nContext(); + const { + collections, + previouslyOwnedCollection, + }: { + collections: Record; + previouslyOwnedCollection: PreviouslyOwnedCollections; + } = useNftsCollections(); + + const collectionsKeys = Object.keys(collections); + + const collectionsData = collectionsKeys.reduce((acc: unknown[], key) => { + // TODO: Replace `any` with type + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const collection = (collections as any)[key]; + + const isMatchingQuery = collection.collectionName + ?.toLowerCase() + .includes(searchQuery.toLowerCase()); + + if (isMatchingQuery) { + acc.push(collection); + return acc; + } + return acc; + }, []); + + // filter and exclude ERC1155 + const collectionDataFiltered = (collectionsData as Collection[]).filter( + (collection) => collection.nfts.length > 0, + ); + const hasAnyNfts = Object.keys(collectionDataFiltered).length > 0; const useNftDetection = useSelector(getUseNftDetection); const isMainnet = useSelector(getIsMainnet); diff --git a/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal-search.tsx b/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal-search.tsx new file mode 100644 index 000000000000..c7c0d699793e --- /dev/null +++ b/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal-search.tsx @@ -0,0 +1,62 @@ +import React from 'react'; +import { + Box, + ButtonIconSize, + TextFieldSearch, + TextFieldSearchSize, +} from '../../../component-library'; +import { + BlockSize, + BorderRadius, +} from '../../../../helpers/constants/design-system'; +import { useI18nContext } from '../../../../hooks/useI18nContext'; + +/** + * Renders a search component for the asset picker modal. + * + * @param props + * @param props.searchQuery - The current search query. + * @param props.onChange - The function to handle search query changes. + * @param props.isNFTSearch - Indicates if the search is for NFTs. + * @param props.props - Additional props for the containing Box component. + * @returns The rendered search component. + */ +export const Search = ({ + searchQuery, + onChange, + isNFTSearch = false, + props, +}: { + searchQuery: string; + onChange: (value: string) => void; + isNFTSearch?: boolean; + props?: React.ComponentProps; +}) => { + const t = useI18nContext(); + + return ( + + onChange(e.target.value)} + error={false} + autoFocus + autoComplete={false} + width={BlockSize.Full} + clearButtonOnClick={() => onChange('')} + clearButtonProps={{ + size: ButtonIconSize.Sm, + }} + showClearButton + className="asset-picker-modal__search-list" + inputProps={{ + 'data-testid': 'asset-picker-modal-search-input', + }} + endAccessory={null} + size={TextFieldSearchSize.Lg} + /> + + ); +}; diff --git a/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal-tabs.tsx b/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal-tabs.tsx new file mode 100644 index 000000000000..8ef783ef07f9 --- /dev/null +++ b/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal-tabs.tsx @@ -0,0 +1,60 @@ +import React, { ReactElement } from 'react'; +import { useI18nContext } from '../../../../hooks/useI18nContext'; +import { Tab, Tabs } from '../../../ui/tabs'; + +export enum TabName { + TOKENS = 'tokens', + NFTS = 'nfts', +} + +/** + * AssetPickerModalTabs component. + * + * @param props + * @param props.defaultActiveTabKey - The key of the default active tab. + * @param props.children - The child components to be displayed within tabs. + * @param props.visibleTabs - A list of visible tabs. + * @returns A Tabs instance with the provided visible children. + */ +export const AssetPickerModalTabs = ({ + defaultActiveTabKey = TabName.TOKENS, + children, + visibleTabs = [TabName.TOKENS, TabName.NFTS], +}: { + defaultActiveTabKey?: TabName; + children: ReactElement[]; + visibleTabs?: TabName[]; +}) => { + const t = useI18nContext(); + + if (visibleTabs.length > 1) { + return ( + + {visibleTabs.map((tabName) => { + return ( + + {children.find(({ key }) => key === tabName)} + + ); + })} + + ); + } + + return ( + <> + {visibleTabs.map((tabName) => + children.find(({ key }) => key === tabName), + )} + + ); +}; diff --git a/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.stories.tsx b/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.stories.tsx index 97bb1a4ebfa6..70ccdf170abd 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.stories.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.stories.tsx @@ -5,6 +5,8 @@ import configureStore from '../../../../store/store'; import mockState from '../../../../../test/data/mock-state.json'; import { AssetType } from '../../../../../shared/constants/transaction'; import { AssetPickerModal } from './asset-picker-modal'; +import { useI18nContext } from '../../../../hooks/useI18nContext'; +import { ERC20Asset } from './types'; const storybook = { title: 'Components/Multichain/AssetPickerModal', @@ -15,21 +17,35 @@ const props = { isOpen: true, onClose: () => ({}), asset: { - balance: null, - details: null, - error: null, + address: '0xAddress', + symbol: 'TOKEN', + image: 'image.png', type: AssetType.token, - } as unknown as Asset, + } as ERC20Asset, +}; +export const DefaultStory = () => { + const t = useI18nContext(); + return ( + ({})} + {...props} + /> + ); }; -export const DefaultStory = () => ( - ({})} {...props} /> -); DefaultStory.storyName = 'Default'; -export const TokenStory = () => ( - ({})} {...props} /> -); +export const TokenStory = () => { + const t = useI18nContext(); + return ( + ({})} + {...props} + /> + ); +}; TokenStory.storyName = 'Modal With Balance'; diff --git a/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.test.tsx b/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.test.tsx index 96177e397fd6..be0eb8282258 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.test.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.test.tsx @@ -29,9 +29,10 @@ import { import { getTopAssets } from '../../../../ducks/swaps/swaps'; import { getRenderableTokenData } from '../../../../hooks/useTokensToSearch'; import * as actions from '../../../../store/actions'; +import { getSwapsBlockedTokens } from '../../../../ducks/send'; import { AssetPickerModal } from './asset-picker-modal'; -import { Asset } from './types'; import AssetList from './AssetList'; +import { ERC20Asset } from './types'; jest.mock('./AssetList', () => jest.fn(() =>
AssetList
)); @@ -68,17 +69,20 @@ describe('AssetPickerModal', () => { const onCloseMock = jest.fn(); const defaultProps = { + header: 'sendSelectReceiveAsset', isOpen: true, onClose: onCloseMock, asset: { - balance: '0x0', - details: { address: '0xAddress', decimals: 18, symbol: 'TOKEN' }, - error: null, - type: 'TOKEN', - } as unknown as Asset, + address: '0xAddress', + symbol: 'TOKEN', + image: 'image.png', + type: AssetType.token, + } as ERC20Asset, onAssetChange: onAssetChangeMock, - sendingAssetImage: 'image.png', - sendingAssetSymbol: 'SYMB', + sendingAsset: { + image: 'image.png', + symbol: 'SYMB', + }, }; beforeEach(() => { @@ -105,7 +109,18 @@ describe('AssetPickerModal', () => { return {}; } if (selector === getTokenList) { - return { '0xAddress': { ...defaultProps.asset, symbol: 'TOKEN' } }; + return { + '0xAddress': { ...defaultProps.asset, symbol: 'TOKEN' }, + '0xtoken1': { + address: '0xToken1', + symbol: 'TOKEN1', + type: AssetType.token, + image: 'image1.png', + string: '10', + decimals: 18, + balance: '0', + }, + }; } if (selector === getConversionRate) { return 1; @@ -122,6 +137,9 @@ describe('AssetPickerModal', () => { if (selector === getPreferences) { return { useNativeCurrencyAsPrimaryCurrency: false }; } + if (selector === getSwapsBlockedTokens) { + return new Set(['0xtoken1']); + } return undefined; }); @@ -161,11 +179,11 @@ describe('AssetPickerModal', () => { , store, ); @@ -184,7 +202,7 @@ describe('AssetPickerModal', () => { expect( (AssetList as jest.Mock).mock.calls.slice(-1)[0][0].tokenList.length, - ).toBe(1); + ).toBe(2); fireEvent.change(screen.getByPlaceholderText('searchTokens'), { target: { value: 'UNAVAILABLE TOKEN' }, @@ -233,4 +251,47 @@ describe('AssetPickerModal', () => { expect(modalTitle).toBeInTheDocument(); expect(searchPlaceholder).toBeInTheDocument(); }); + + it('should disable the token if it is in the blocked tokens list', () => { + renderWithProvider( + , + store, + ); + + fireEvent.change(screen.getByPlaceholderText('searchTokens'), { + target: { value: 'TO' }, + }); + + expect( + (AssetList as jest.Mock).mock.calls.slice(-1)[0][0].tokenList.length, + ).toBe(2); + + fireEvent.change(screen.getByPlaceholderText('searchTokens'), { + target: { value: 'TOKEN1' }, + }); + + expect((AssetList as jest.Mock).mock.calls[1][0]).not.toEqual( + expect.objectContaining({ + asset: { + balance: '0x0', + details: { address: '0xAddress', decimals: 18, symbol: 'TOKEN' }, + error: null, + type: 'NATIVE', + }, + }), + ); + + expect( + (AssetList as jest.Mock).mock.calls.slice(-1)[0][0].tokenList.length, + ).toBe(1); + + expect( + (AssetList as jest.Mock).mock.calls[2][0].isTokenDisabled({ + address: '0xtoken1', + }), + ).toBe(true); + }); }); diff --git a/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.tsx b/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.tsx index e893524e0cbd..ac6f658f33a0 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.tsx @@ -1,23 +1,24 @@ -import React, { useState, useCallback, useMemo, useContext } from 'react'; +import React, { useState, useCallback, useMemo } from 'react'; import { useSelector } from 'react-redux'; import { isEqual, uniqBy } from 'lodash'; -import { Tab, Tabs } from '../../../ui/tabs'; +import { + Token, + TokenListMap, + TokenListToken, +} from '@metamask/assets-controllers'; +import { Hex } from '@metamask/utils'; import { Modal, ModalContent, ModalOverlay, ModalHeader, - TextFieldSearch, Box, - ButtonIconSize, - TextFieldSearchSize, AvatarTokenSize, AvatarToken, Text, } from '../../../component-library'; import { - BlockSize, BorderRadius, TextVariant, TextAlign, @@ -28,7 +29,6 @@ import { useI18nContext } from '../../../../hooks/useI18nContext'; import { AssetType } from '../../../../../shared/constants/transaction'; -import { useNftsCollections } from '../../../../hooks/useNftsCollections'; import { getAllTokens, getCurrentChainId, @@ -48,103 +48,59 @@ import { useTokenTracker } from '../../../../hooks/useTokenTracker'; import { getTopAssets } from '../../../../ducks/swaps/swaps'; import { getRenderableTokenData } from '../../../../hooks/useTokensToSearch'; import { useEqualityCheck } from '../../../../hooks/useEqualityCheck'; -import { - MetaMetricsEventName, - MetaMetricsEventCategory, -} from '../../../../../shared/constants/metametrics'; -import { MetaMetricsContext } from '../../../../contexts/metametrics'; -import { - getSendAnalyticProperties, - getSwapsBlockedTokens, -} from '../../../../ducks/send'; +import { getSwapsBlockedTokens } from '../../../../ducks/send'; import { isEqualCaseInsensitive } from '../../../../../shared/modules/string-utils'; -import { Asset, Collection, Token } from './types'; +import { + ERC20Asset, + NativeAsset, + NFT, + AssetWithDisplayData, + TokenWithBalance, +} from './types'; +import { AssetPickerModalTabs, TabName } from './asset-picker-modal-tabs'; import { AssetPickerModalNftTab } from './asset-picker-modal-nft-tab'; import AssetList from './AssetList'; +import { Search } from './asset-picker-modal-search'; type AssetPickerModalProps = { + header: JSX.Element | string | null; isOpen: boolean; onClose: () => void; - asset: Asset; - onAssetChange: (asset: Asset) => void; - sendingAssetImage?: string; - sendingAssetSymbol?: string; -}; + asset?: ERC20Asset | NativeAsset | Pick; + onAssetChange: ( + asset: AssetWithDisplayData | AssetWithDisplayData, + ) => void; + /** + * Sending asset for UI treatments; only for dest component + */ + sendingAsset?: { image: string; symbol: string } | undefined; +} & Pick< + React.ComponentProps, + 'visibleTabs' | 'defaultActiveTabKey' +>; const MAX_UNOWNED_TOKENS_RENDERED = 30; export function AssetPickerModal({ + header, isOpen, onClose, asset, onAssetChange, - sendingAssetImage, - sendingAssetSymbol, + sendingAsset, + ...tabProps }: AssetPickerModalProps) { const t = useI18nContext(); - const trackEvent = useContext(MetaMetricsContext); - const sendAnalytics = useSelector(getSendAnalyticProperties); const [searchQuery, setSearchQuery] = useState(''); - const { collections, previouslyOwnedCollection } = useNftsCollections(); - - const collectionsKeys = Object.keys(collections); - - const collectionsData = collectionsKeys.reduce((acc: unknown[], key) => { - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const collection = (collections as any)[key]; - - const isMatchingQuery = collection.collectionName - ?.toLowerCase() - .includes(searchQuery.toLowerCase()); - - if (isMatchingQuery) { - acc.push(collection); - return acc; - } - return acc; - }, []); - - // filter and exclude ERC1155 - const collectionDataFiltered = (collectionsData as Collection[]).filter( - (collection) => collection.nfts.length > 0, - ); - const swapsBlockedTokens = useSelector(getSwapsBlockedTokens); const memoizedSwapsBlockedTokens = useMemo(() => { return new Set(swapsBlockedTokens); }, [swapsBlockedTokens]); - const isDest = sendingAssetImage && sendingAssetSymbol; - - const handleAssetChange = useCallback( - (token: Token) => { - onAssetChange(token); - trackEvent( - { - event: MetaMetricsEventName.sendAssetSelected, - category: MetaMetricsEventCategory.Send, - properties: { - is_destination_asset_picker_modal: Boolean(isDest), - is_nft: false, - }, - sensitiveProperties: { - ...sendAnalytics, - new_asset_symbol: token.symbol, - new_asset_address: token.address, - }, - }, - { excludeMetaMetricsId: false }, - ); - onClose(); - }, - [onAssetChange], - ); - - const defaultActiveTabKey = asset?.type === AssetType.NFT ? 'nfts' : 'tokens'; + const handleAssetChange = useCallback(onAssetChange, [onAssetChange]); const chainId = useSelector(getCurrentChainId); @@ -161,47 +117,69 @@ export function AssetPickerModal({ getShouldHideZeroBalanceTokens, ); - const detectedTokens = useSelector(getAllTokens); + const detectedTokens: Record> = useSelector( + getAllTokens, + ); const tokens = detectedTokens?.[chainId]?.[selectedAddress] ?? []; - const { tokensWithBalances } = useTokenTracker({ - tokens, - address: selectedAddress, - hideZeroBalanceTokens: Boolean(shouldHideZeroBalanceTokens), - }); + const { tokensWithBalances }: { tokensWithBalances: TokenWithBalance[] } = + useTokenTracker({ + tokens, + address: selectedAddress, + hideZeroBalanceTokens: Boolean(shouldHideZeroBalanceTokens), + }); // Swaps token list - const tokenList = useSelector(getTokenList) as Record; + const tokenList = useSelector(getTokenList) as TokenListMap; const topTokens = useSelector(getTopAssets, isEqual); - const usersTokens = uniqBy([...tokensWithBalances, ...tokens], 'address'); + const usersTokens = uniqBy( + [...tokensWithBalances, ...tokens], + 'address', + ); + + const memoizedUsersTokens: TokenWithBalance[] = useEqualityCheck(usersTokens); + + const getIsDisabled = useCallback( + ({ + address, + symbol, + }: + | TokenListToken + | AssetWithDisplayData + | AssetWithDisplayData) => { + const isDisabled = sendingAsset?.symbol + ? !isEqualCaseInsensitive(sendingAsset.symbol, symbol) && + memoizedSwapsBlockedTokens.has(address || '') + : false; - const memoizedUsersTokens = useEqualityCheck(usersTokens); + return isDisabled; + }, + [sendingAsset?.symbol, memoizedSwapsBlockedTokens], + ); const filteredTokenList = useMemo(() => { - const nativeToken = { + const nativeToken: AssetWithDisplayData = { address: null, symbol: nativeCurrency, decimals: 18, image: nativeCurrencyImage, balance: balanceValue, + string: undefined, type: AssetType.native, }; - const filteredTokens: Token[] = []; + const filteredTokens: AssetWithDisplayData[] = []; // undefined would be the native token address const filteredTokensAddresses = new Set(); - const getIsDisabled = ({ address, symbol }: Token) => { - const isDisabled = sendingAssetSymbol - ? !isEqualCaseInsensitive(sendingAssetSymbol, symbol) && - memoizedSwapsBlockedTokens.has(address || '') - : false; - - return isDisabled; - }; - - function* tokenGenerator() { + function* tokenGenerator(): Generator< + | AssetWithDisplayData + | ((Token | TokenListToken) & { + balance?: string; + string?: string; + }) + > { yield nativeToken; const blockedTokens = []; @@ -214,7 +192,7 @@ export function AssetPickerModal({ for (const address of Object.keys(topTokens)) { const token = tokenList?.[address]; if (token) { - if (isDest && getIsDisabled(token)) { + if (getIsDisabled(token)) { blockedTokens.push(token); continue; } else { @@ -232,8 +210,7 @@ export function AssetPickerModal({ } } - let token: Token; - for (token of tokenGenerator()) { + for (const token of tokenGenerator()) { if ( token.symbol?.toLowerCase().includes(searchQuery.toLowerCase()) && !filteredTokensAddresses.has(token.address?.toLowerCase()) @@ -242,11 +219,11 @@ export function AssetPickerModal({ filteredTokens.push( getRenderableTokenData( token.address - ? { + ? ({ ...token, ...tokenList[token.address.toLowerCase()], type: AssetType.token, - } + } as AssetWithDisplayData) : token, tokenConversionRates, conversionRate, @@ -270,49 +247,18 @@ export function AssetPickerModal({ nativeCurrency, nativeCurrencyImage, balanceValue, + memoizedUsersTokens, + topTokens, + tokenList, + getIsDisabled, + searchQuery, tokenConversionRates, conversionRate, currentCurrency, chainId, tokenList, - sendingAssetSymbol, ]); - const Search = useCallback( - ({ - isNFTSearch = false, - props, - }: { - isNFTSearch?: boolean; - props?: React.ComponentProps; - }) => ( - - setSearchQuery(e.target.value)} - error={false} - autoFocus - autoComplete={false} - width={BlockSize.Full} - clearButtonOnClick={() => setSearchQuery('')} - clearButtonProps={{ - size: ButtonIconSize.Sm, - }} - showClearButton={true} - className="asset-picker-modal__search-list" - inputProps={{ - 'data-testid': 'asset-picker-modal-search-input', - }} - endAccessory={null} - size={TextFieldSearchSize.Lg} - /> - - ), - [searchQuery], - ); - return ( - + - {t(isDest ? 'sendSelectReceiveAsset' : 'sendSelectSendAsset')} + {header} - {isDest && ( + {sendingAsset?.image && sendingAsset?.symbol && ( - {t('sendingAsset', [sendingAssetSymbol])} + {t('sendingAsset', [sendingAsset.symbol])} )} - {isDest ? ( - <> - + + + setSearchQuery(value)} + /> - - ) : ( - - { - - - - - } - - { - - Search({ isNFTSearch: true })} - /> - - } - - )} + + ( + setSearchQuery(value)} + /> + )} + /> + diff --git a/ui/components/multichain/asset-picker-amount/asset-picker-modal/types.ts b/ui/components/multichain/asset-picker-amount/asset-picker-modal/types.ts index 7e328a7fc4b7..2e54b918b124 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker-modal/types.ts +++ b/ui/components/multichain/asset-picker-amount/asset-picker-modal/types.ts @@ -1,8 +1,14 @@ +import { Token, TokenListToken } from '@metamask/assets-controllers'; +import { Hex } from '@metamask/utils'; import type { AssetType, TokenStandard, } from '../../../../../shared/constants/transaction'; import type { Asset } from '../../../../ducks/send'; +import { + CHAIN_ID_TO_CURRENCY_SYMBOL_MAP, + CHAIN_ID_TOKEN_IMAGE_MAP, +} from '../../../../../shared/constants/network'; export type NFT = { address: string; @@ -12,19 +18,39 @@ export type NFT = { isCurrentlyOwned: boolean; name: string | null; standard: TokenStandard; - tokenId: string; + tokenId: number; tokenURI?: string; + type: AssetType.NFT; + symbol?: string; }; -export type Token = { - address: string | null; - symbol: string; - decimals: number; +/** + * Passed in to AssetPicker, AssetPickerModal and AssetList as a prop + * Since token interfaces can vary between experiences (i.e. send vs bridge, + * these fields need to be set before passing an asset to the AssetPicker + */ +export type ERC20Asset = Pick & { + type: AssetType.token; image: string; - balance: string; - string: string; - type: AssetType; }; +export type NativeAsset = { + type: AssetType.native; + address?: null; + image: typeof CHAIN_ID_TOKEN_IMAGE_MAP extends Record + ? V + : never; // only allow wallet's hardcoded images + symbol: typeof CHAIN_ID_TO_CURRENCY_SYMBOL_MAP extends Record + ? V + : never; // only allow wallet's hardcoded symbols +}; + +/** + * ERC20Asset or NativeAsset, plus additional fields for display purposes in the Asset component + */ +export type AssetWithDisplayData = T & { + balance: T['type'] extends AssetType.token ? string : Hex; // raw balance + string: string | undefined; // normalized balance as a stringified number +} & Pick; export type Collection = { collectionName: string; @@ -32,4 +58,9 @@ export type Collection = { nfts: NFT[]; }; +/** + * Type of useTokenTracker's tokensWithBalances result + */ +export type TokenWithBalance = Token & { balance?: string; string?: string }; + export { Asset }; diff --git a/ui/components/multichain/asset-picker-amount/asset-picker/__snapshots__/asset-picker.test.tsx.snap b/ui/components/multichain/asset-picker-amount/asset-picker/__snapshots__/asset-picker.test.tsx.snap index 0acf78dd5a46..c15007fc3b0d 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker/__snapshots__/asset-picker.test.tsx.snap +++ b/ui/components/multichain/asset-picker-amount/asset-picker/__snapshots__/asset-picker.test.tsx.snap @@ -33,7 +33,11 @@ exports[`AssetPicker matches snapshot 1`] = `
- ? + Ethereum Mainnet logo
diff --git a/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.stories.tsx b/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.stories.tsx new file mode 100644 index 000000000000..753ee2644f5a --- /dev/null +++ b/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.stories.tsx @@ -0,0 +1,91 @@ +import React from 'react'; +import { Provider } from 'react-redux'; +import configureStore from '../../../../store/store'; +import mockState from '../../../../../test/data/mock-state.json'; +import { AssetType } from '../../../../../shared/constants/transaction'; +import { AssetPicker } from './asset-picker'; +import { useI18nContext } from '../../../../hooks/useI18nContext'; +import { CHAIN_ID_TOKEN_IMAGE_MAP } from '../../../../../shared/constants/network'; +import { ERC20Asset } from '../asset-picker-modal/types'; + +const storybook = { + title: 'Components/Multichain/AssetPicker', + component: AssetPicker, +}; + +const props = { + asset: { + symbol: 'ETH', + address: '0xaddress1', + image: CHAIN_ID_TOKEN_IMAGE_MAP['0x1'], + type: AssetType.token, + } as ERC20Asset, +}; +export const DefaultStory = () => { + const t = useI18nContext(); + return ( + ({})} + {...props} + /> + ); +}; + +DefaultStory.storyName = 'Default'; + +export const DisabledStory = () => { + const t = useI18nContext(); + return ( + ({})} + {...props} + isDisabled + /> + ); +}; + +DisabledStory.storyName = 'Disabled'; + +export const SendDestStory = () => { + const t = useI18nContext(); + return ( + ({})} + {...props} + asset={{ + symbol: 'ETH', + image: CHAIN_ID_TOKEN_IMAGE_MAP['0x1'], + type: AssetType.native, + }} + sendingAsset={{ + image: 'token image', + symbol: 'ETH', + }} + /> + ); +}; + +function store() { + const defaultMockState = { ...mockState }; + defaultMockState.metamask = { + ...defaultMockState.metamask, + providerConfig: { + ...defaultMockState.metamask.providerConfig, + chainId: '0x1', + ticker: 'ETH', + nickname: 'Ethereum Mainnet', + }, + }; + return configureStore(defaultMockState); +} + +SendDestStory.decorators = [ + (story) => {story()}, +]; + +SendDestStory.storyName = 'With Sending Asset'; + +export default storybook; diff --git a/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.test.tsx b/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.test.tsx index 208395b2228b..262016ce4e69 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.test.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.test.tsx @@ -1,16 +1,35 @@ import React from 'react'; import { render } from '@testing-library/react'; import { Provider } from 'react-redux'; +import { Hex } from '@metamask/utils'; import { AssetType } from '../../../../../shared/constants/transaction'; import mockSendState from '../../../../../test/data/mock-send-state.json'; import configureStore from '../../../../store/store'; +import { + CHAIN_IDS, + CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP, + CHAIN_ID_TOKEN_IMAGE_MAP, +} from '../../../../../shared/constants/network'; +import { mockNetworkState } from '../../../../../test/stub/networks'; + +import { ERC20Asset, NativeAsset, NFT } from '../asset-picker-modal/types'; import { AssetPicker } from './asset-picker'; +const unknownChainId = '0x2489078'; +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useHistory: jest.fn(() => ({ + push: jest.fn(), + })), +})); + +const NATIVE_TICKER = 'NATIVE TICKER'; const store = ( - nativeTicker = 'NATIVE TICKER', + nativeTicker = NATIVE_TICKER, // TODO: Replace `any` with type // eslint-disable-next-line @typescript-eslint/no-explicit-any tokenList = {} as any, + chainId: Hex = CHAIN_IDS.MAINNET, ) => configureStore({ ...mockSendState, @@ -21,10 +40,10 @@ const store = ( conversionRate: 11.1, }, }, - providerConfig: { - chainId: '0x1', + ...mockNetworkState({ + chainId, ticker: nativeTicker, - }, + }), useTokenDetection: true, tokenList, }, @@ -34,123 +53,164 @@ describe('AssetPicker', () => { it('matches snapshot', () => { const asset = { type: AssetType.native, - balance: '1000000', - }; + image: CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP['0x1'], + symbol: NATIVE_TICKER, + } as NativeAsset; const mockAssetChange = jest.fn(); const { asFragment } = render( - mockAssetChange()} /> + mockAssetChange()} + /> , ); expect(asFragment()).toMatchSnapshot(); }); + it('calls onClick handler', () => { + const asset = { + type: AssetType.native, + image: CHAIN_ID_TOKEN_IMAGE_MAP['0x1'], + symbol: 'NATIVE', + } as NativeAsset; + const mockAssetChange = jest.fn(); + const mockOnClick = jest.fn(); + const { getByTestId } = render( + + mockAssetChange()} + onClick={mockOnClick} + /> + , + ); + getByTestId('asset-picker-button').click(); + expect(mockOnClick).toHaveBeenCalled(); + }); + it('native: renders symbol and image', () => { const asset = { type: AssetType.native, - balance: '1000000', - }; + image: CHAIN_ID_TOKEN_IMAGE_MAP['0x1'], + symbol: 'NATIVE', + } as NativeAsset; const mockAssetChange = jest.fn(); - const { getByText, getByRole } = render( + const { getByText, getByAltText } = render( - mockAssetChange()} /> + mockAssetChange()} + /> , ); expect(getByText('NATIVE')).toBeInTheDocument(); - expect(getByRole('img')).toBeInTheDocument(); - expect(getByRole('img')).toHaveAttribute('src', './images/eth_logo.svg'); + const img = getByAltText('Ethereum Mainnet logo'); + expect(img).toBeInTheDocument(); + expect(img).toHaveAttribute('src', './images/eth_logo.svg'); }); it('native: renders overflowing symbol and image', () => { const asset = { type: AssetType.native, - balance: '1000000', - }; + image: CHAIN_ID_TOKEN_IMAGE_MAP['0x1'], + symbol: NATIVE_TICKER, + } as NativeAsset; const mockAssetChange = jest.fn(); - const { getByText, getByRole } = render( - - mockAssetChange()} /> + const { getByText, getByAltText } = render( + + mockAssetChange()} + /> , ); expect(getByText('NATIVE...')).toBeInTheDocument(); - expect(getByRole('img')).toBeInTheDocument(); - expect(getByRole('img')).toHaveAttribute('src', './images/eth_logo.svg'); + const img = getByAltText('Ethereum Mainnet logo'); + expect(img).toBeInTheDocument(); + expect(img).toHaveAttribute('src', './images/eth_logo.svg'); }); it('token: renders symbol and image', () => { const asset = { type: AssetType.token, - details: { - address: 'token address', - decimals: 2, - symbol: 'symbol', - }, - balance: '100', - }; + address: 'token address', + image: 'token icon url', + symbol: 'symbol', + } as ERC20Asset; const mockAssetChange = jest.fn(); - const { getByText, getByRole } = render( - - mockAssetChange()} /> + const { getByText, getByAltText } = render( + + mockAssetChange()} + /> , ); expect(getByText('symbol')).toBeInTheDocument(); - expect(getByRole('img')).toBeInTheDocument(); - expect(getByRole('img')).toHaveAttribute('src', 'token icon url'); + const img = getByAltText('symbol logo'); + expect(img).toBeInTheDocument(); + expect(img).toHaveAttribute('src', 'token icon url'); }); it('token: renders symbol and image overflowing', () => { const asset = { type: AssetType.token, - details: { - address: 'token address', - decimals: 2, - symbol: 'symbol overflow', - }, - balance: '100', - }; + address: 'token address', + image: 'token icon url', + symbol: 'symbol overflow', + } as ERC20Asset; const mockAssetChange = jest.fn(); - const { getByText, getByRole } = render( + const { getByText, getByAltText } = render( - mockAssetChange()} /> + mockAssetChange()} + /> , ); expect(getByText('symbol...')).toBeInTheDocument(); - expect(getByRole('img')).toBeInTheDocument(); - expect(getByRole('img')).toHaveAttribute('src', 'token icon url'); + const img = getByAltText('symbol overflow logo'); + expect(img).toBeInTheDocument(); + expect(img).toHaveAttribute('src', 'token icon url'); }); it('token: renders symbol and image falls back', () => { const asset = { type: AssetType.token, - details: { - address: 'token address', - decimals: 2, - symbol: 'symbol', - }, - balance: '100', - }; + address: 'token address', + symbol: 'symbol', + } as ERC20Asset; const mockAssetChange = jest.fn(); const { getByText } = render( - mockAssetChange()} /> + mockAssetChange()} + /> , ); expect(getByText('symbol')).toBeInTheDocument(); @@ -160,18 +220,18 @@ describe('AssetPicker', () => { it('nft: does not truncates if token ID is under length 13', () => { const asset = { type: AssetType.NFT, - details: { - address: 'token address', - decimals: 2, - tokenId: 1234567890, - }, - balance: '100', - }; + address: 'token address', + tokenId: 1234567890, + } as NFT; const mockAssetChange = jest.fn(); const { getByText } = render( - mockAssetChange()} /> + mockAssetChange()} + /> , ); expect(getByText('#1234567890')).toBeInTheDocument(); @@ -180,18 +240,18 @@ describe('AssetPicker', () => { it('nft: truncates if token ID is too long', () => { const asset = { type: AssetType.NFT, - details: { - address: 'token address', - decimals: 2, - tokenId: 1234567890123456, - }, - balance: '100', - }; + address: 'token address', + tokenId: 1234567890123456, + } as NFT; const mockAssetChange = jest.fn(); const { getByText } = render( - mockAssetChange()} /> + mockAssetChange()} + /> , ); expect(getByText('#123456...3456')).toBeInTheDocument(); @@ -200,22 +260,21 @@ describe('AssetPicker', () => { it('render if disabled', () => { const asset = { type: AssetType.token, - details: { - address: 'token address', - decimals: 2, - symbol: 'symbol', - }, - balance: '100', - }; + address: 'token address', + symbol: 'symbol', + } as ERC20Asset; const mockAssetChange = jest.fn(); const { container } = render( mockAssetChange()} isDisabled diff --git a/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.tsx b/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.tsx index 2c1dcd5f9349..28ff01ba02b7 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.tsx @@ -1,4 +1,4 @@ -import React, { useContext, useState } from 'react'; +import React, { useState } from 'react'; import { useSelector } from 'react-redux'; import { AvatarTokenSize, @@ -11,7 +11,6 @@ import { BadgeWrapper, AvatarNetwork, } from '../../../component-library'; -import { Asset, getSendAnalyticProperties } from '../../../../ducks/send'; import { AlignItems, BackgroundColor, @@ -25,96 +24,71 @@ import { } from '../../../../helpers/constants/design-system'; import { AssetType } from '../../../../../shared/constants/transaction'; import { AssetPickerModal } from '../asset-picker-modal/asset-picker-modal'; -import { getNativeCurrency } from '../../../../ducks/metamask/metamask'; import { getCurrentNetwork, - getIpfsGateway, - getNativeCurrencyImage, getTestNetworkBackgroundColor, - getTokenList, } from '../../../../selectors'; import Tooltip from '../../../ui/tooltip'; import { LARGE_SYMBOL_LENGTH } from '../constants'; -import { getAssetImageURL } from '../../../../helpers/utils/util'; ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) import { useI18nContext } from '../../../../hooks/useI18nContext'; ///: END:ONLY_INCLUDE_IF -import { MetaMetricsContext } from '../../../../contexts/metametrics'; -import { - MetaMetricsEventCategory, - MetaMetricsEventName, -} from '../../../../../shared/constants/metametrics'; import { ellipsify } from '../../../../pages/confirmations/send/send.utils'; +import { + AssetWithDisplayData, + ERC20Asset, + NativeAsset, + NFT, +} from '../asset-picker-modal/types'; +import { TabName } from '../asset-picker-modal/asset-picker-modal-tabs'; const ELLIPSIFY_LENGTH = 13; // 6 (start) + 4 (end) + 3 (...) export type AssetPickerProps = { - asset: Asset; + asset?: + | ERC20Asset + | NativeAsset + | Pick + | undefined; /** * Needs to be wrapped in a callback */ - onAssetChange: (newAsset: Asset) => void; - /** - * Sending asset for UI treatments; only for dest component - */ - sendingAsset?: Asset; + onAssetChange: ( + newAsset: + | AssetWithDisplayData + | AssetWithDisplayData, + ) => void; + onClick?: () => void; isDisabled?: boolean; -}; +} & Pick< + React.ComponentProps, + 'visibleTabs' | 'header' | 'sendingAsset' +>; // A component that lets the user pick from a list of assets. export function AssetPicker({ + header, asset, onAssetChange, sendingAsset, + onClick, isDisabled = false, + visibleTabs, }: AssetPickerProps) { ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) const t = useI18nContext(); ///: END:ONLY_INCLUDE_IF - const trackEvent = useContext(MetaMetricsContext); - const sendAnalytics = useSelector(getSendAnalyticProperties); - - const nativeCurrencySymbol = useSelector(getNativeCurrency); - const nativeCurrencyImageUrl = useSelector(getNativeCurrencyImage); - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const tokenList: Record = useSelector(getTokenList); - - const ipfsGateway = useSelector(getIpfsGateway); const [showAssetPickerModal, setShowAssetPickerModal] = useState(false); - let primaryTokenImage: string | undefined; + const isNFT = asset?.type === AssetType.NFT; - if (asset.type === AssetType.native) { - primaryTokenImage = nativeCurrencyImageUrl; - } else if (tokenList && asset.details) { - primaryTokenImage = - getAssetImageURL(asset.details?.image, ipfsGateway) || - tokenList[asset.details.address?.toLowerCase()]?.iconUrl; - } - - let sendingTokenImage: string | undefined; - - if (sendingAsset) { - if (sendingAsset.type === AssetType.native) { - sendingTokenImage = nativeCurrencyImageUrl; - } else if (tokenList && sendingAsset.details) { - sendingTokenImage = - getAssetImageURL(sendingAsset.details?.image, ipfsGateway) || - tokenList[sendingAsset.details.address?.toLowerCase()]?.iconUrl; - } - } - - const symbol = - asset.type === AssetType.native - ? nativeCurrencySymbol - : asset.details?.symbol; - - const isSymbolLong = symbol?.length > LARGE_SYMBOL_LENGTH; - const isNFT = asset.type === AssetType.NFT; + // selected asset details + const primaryTokenImage = asset?.image; + const symbol = asset?.symbol; + const isSymbolLong = symbol && symbol.length > LARGE_SYMBOL_LENGTH; const formattedSymbol = isSymbolLong && !isNFT ? `${symbol.substring(0, LARGE_SYMBOL_LENGTH - 1)}...` @@ -138,13 +112,22 @@ export function AssetPicker({ <> {/* This is the Modal that ask to choose token to send */} setShowAssetPickerModal(false)} asset={asset} - onAssetChange={onAssetChange} - sendingAssetImage={sendingTokenImage} - sendingAssetSymbol={ - sendingAsset?.details?.symbol || nativeCurrencySymbol + onAssetChange={( + token: + | AssetWithDisplayData + | AssetWithDisplayData, + ) => { + onAssetChange(token); + setShowAssetPickerModal(false); + }} + sendingAsset={sendingAsset} + defaultActiveTabKey={ + asset?.type === AssetType.NFT ? TabName.NFTS : TabName.TOKENS } /> @@ -162,19 +145,7 @@ export function AssetPicker({ backgroundColor={BackgroundColor.transparent} onClick={() => { setShowAssetPickerModal(true); - trackEvent( - { - event: MetaMetricsEventName.sendTokenModalOpened, - category: MetaMetricsEventCategory.Send, - properties: { - is_destination_asset_picker_modal: Boolean(sendingAsset), - }, - sensitiveProperties: { - ...sendAnalytics, - }, - }, - { excludeMetaMetricsId: false }, - ); + onClick?.(); }} endIconName={IconName.ArrowDown} endIconProps={{ @@ -203,7 +174,7 @@ export function AssetPicker({ > {formattedSymbol} - {asset.details?.tokenId && ( + {isNFT && asset?.tokenId && ( # - {String(asset.details.tokenId).length < ELLIPSIFY_LENGTH - ? asset.details.tokenId - : ellipsify(String(asset.details.tokenId), 6, 4)} + {String(asset.tokenId).length < ELLIPSIFY_LENGTH + ? asset.tokenId + : ellipsify(String(asset.tokenId), 6, 4)} )} diff --git a/ui/components/multichain/badge-status/badge-status.test.js b/ui/components/multichain/badge-status/badge-status.test.js index 7c1212444c69..de646b353330 100644 --- a/ui/components/multichain/badge-status/badge-status.test.js +++ b/ui/components/multichain/badge-status/badge-status.test.js @@ -14,9 +14,6 @@ describe('Badge Status', () => { const store = configureStore({ metamask: { ...mockState.metamask, - providerConfig: { - chainId: '0x99', - }, ...state, }, }); diff --git a/ui/components/multichain/create-named-snap-account/create-named-snap-account.tsx b/ui/components/multichain/create-named-snap-account/create-named-snap-account.tsx index ac0650dc6d93..194a8c6399df 100644 --- a/ui/components/multichain/create-named-snap-account/create-named-snap-account.tsx +++ b/ui/components/multichain/create-named-snap-account/create-named-snap-account.tsx @@ -73,7 +73,7 @@ export const CreateNamedSnapAccount: React.FC = ({ }, []); return ( - + {t('addAccountToMetaMask')} diff --git a/ui/components/multichain/create-named-snap-account/index.scss b/ui/components/multichain/create-named-snap-account/index.scss new file mode 100644 index 000000000000..f799195dc1f8 --- /dev/null +++ b/ui/components/multichain/create-named-snap-account/index.scss @@ -0,0 +1,29 @@ +@use "design-system"; + +.name-snap-account-page { + $height-screen-sm-max: 100%; + $width-screen-sm-min: 85vw; + $width-screen-md-min: 80vw; + $width-screen-lg-min: 62vw; + + width: 100%; + flex-flow: column nowrap; + margin: 0 auto; + + @include design-system.screen-sm-max { + height: $height-screen-sm-max; + } + + @include design-system.screen-sm-min { + width: $width-screen-sm-min; + } + + @include design-system.screen-md-min { + width: $width-screen-md-min; + } + + @include design-system.screen-lg-min { + width: $width-screen-lg-min; + } +} + diff --git a/ui/components/multichain/detected-token-banner/detected-token-banner.stories.js b/ui/components/multichain/detected-token-banner/detected-token-banner.stories.js index 2558bef435bb..f34de33c3ad3 100644 --- a/ui/components/multichain/detected-token-banner/detected-token-banner.stories.js +++ b/ui/components/multichain/detected-token-banner/detected-token-banner.stories.js @@ -1,9 +1,23 @@ import React from 'react'; +import { Provider } from 'react-redux'; +import configureStore from '../../../store/store'; +import testData from '../../../../.storybook/test-data'; +import { CHAIN_IDS } from '../../../../shared/constants/network'; +import { mockNetworkState } from '../../../../test/stub/networks'; import { DetectedTokensBanner } from '.'; +const store = configureStore({ + ...testData, + metamask: { + ...testData.metamask, + ...mockNetworkState({ chainId: CHAIN_IDS.SEPOLIA }), + }, +}); + export default { title: 'Components/Multichain/DetectedTokensBanner', component: DetectedTokensBanner, + decorators: [(story) => {story()}], argTypes: { actionButtonOnClick: { action: 'setShowDetectedTokens' }, }, diff --git a/ui/components/multichain/detected-token-banner/detected-token-banner.test.js b/ui/components/multichain/detected-token-banner/detected-token-banner.test.js index f99d678e96ee..262147179ad1 100644 --- a/ui/components/multichain/detected-token-banner/detected-token-banner.test.js +++ b/ui/components/multichain/detected-token-banner/detected-token-banner.test.js @@ -3,6 +3,8 @@ import { fireEvent, renderWithProvider, screen } from '../../../../test/jest'; import configureStore from '../../../store/store'; import testData from '../../../../.storybook/test-data'; +import { CHAIN_IDS } from '../../../../shared/constants/network'; +import { mockNetworkState } from '../../../../test/stub/networks'; import { DetectedTokensBanner } from '.'; describe('DetectedTokensBanner', () => { @@ -10,13 +12,21 @@ describe('DetectedTokensBanner', () => { const args = {}; + const mockStore = { + ...testData, + metamask: { + ...testData.metamask, + ...mockNetworkState({ chainId: CHAIN_IDS.SEPOLIA }), + }, + }; + beforeEach(() => { setShowDetectedTokensSpy = jest.fn(); args.actionButtonOnClick = setShowDetectedTokensSpy; }); it('should render correctly', () => { - const store = configureStore(testData); + const store = configureStore(mockStore); const { getByTestId, container } = renderWithProvider( , store, @@ -26,7 +36,7 @@ describe('DetectedTokensBanner', () => { expect(container).toMatchSnapshot(); }); it('should render number of tokens detected link', () => { - const store = configureStore(testData); + const store = configureStore(mockStore); renderWithProvider(, store); expect( diff --git a/ui/components/multichain/edit-networks-modal/edit-networks-modal.js b/ui/components/multichain/edit-networks-modal/edit-networks-modal.js new file mode 100644 index 000000000000..3cce3266ad5e --- /dev/null +++ b/ui/components/multichain/edit-networks-modal/edit-networks-modal.js @@ -0,0 +1,84 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import { useSelector } from 'react-redux'; +import { TextVariant } from '../../../helpers/constants/design-system'; +import { useI18nContext } from '../../../hooks/useI18nContext'; +import { getNonTestNetworks, getTestNetworks } from '../../../selectors'; +import { + Modal, + ModalOverlay, + ModalContent, + ModalHeader, + Checkbox, + Text, + Box, +} from '../../component-library'; +import { NetworkListItem } from '..'; + +export const EditNetworksModal = ({ onClose }) => { + const t = useI18nContext(); + const nonTestNetworks = useSelector(getNonTestNetworks); + const testNetworks = useSelector(getTestNetworks); + return ( + { + onClose(); + }} + className="edit-networks-modal" + > + + + { + onClose(); + }} + > + {t('editNetworksTitle')} + + + (allAreSelected() ? deselectAll() : selectAll())} + // isIndeterminate={isIndeterminate} + /> + + {nonTestNetworks.map((network) => ( + { + console.log(network.id); + }} + startAccessory={} + /> + ))} + + {t('testnets')} + + {testNetworks.map((network) => ( + { + console.log(network.id); + }} + startAccessory={} + showEndAccessory={false} + /> + ))} + + + ); +}; + +EditNetworksModal.propTypes = { + /** + * Executes when the modal closes + */ + onClose: PropTypes.func.isRequired, +}; diff --git a/ui/components/multichain/edit-networks-modal/edit-networks-modal.stories.js b/ui/components/multichain/edit-networks-modal/edit-networks-modal.stories.js new file mode 100644 index 000000000000..10cf14383bba --- /dev/null +++ b/ui/components/multichain/edit-networks-modal/edit-networks-modal.stories.js @@ -0,0 +1,10 @@ +import React from 'react'; +import { EditNetworksModal } from '.'; + +export default { + title: 'Components/Multichain/EditNetworksModal', +}; + +export const DefaultStory = () => ; + +DefaultStory.storyName = 'Default'; diff --git a/ui/components/multichain/edit-networks-modal/index.js b/ui/components/multichain/edit-networks-modal/index.js new file mode 100644 index 000000000000..7a5b9f88ab7a --- /dev/null +++ b/ui/components/multichain/edit-networks-modal/index.js @@ -0,0 +1 @@ +export { EditNetworksModal } from './edit-networks-modal'; diff --git a/ui/components/multichain/import-nfts-modal/import-nfts-modal.js b/ui/components/multichain/import-nfts-modal/import-nfts-modal.js index cccf0a481f66..3d40209ba62b 100644 --- a/ui/components/multichain/import-nfts-modal/import-nfts-modal.js +++ b/ui/components/multichain/import-nfts-modal/import-nfts-modal.js @@ -114,7 +114,7 @@ export const ImportNftsModal = ({ onClose }) => { nftAddress, null, tokenId.toString(), - ); + ).catch(() => ({})); trackEvent({ event: MetaMetricsEventName.TokenAdded, diff --git a/ui/components/multichain/import-nfts-modal/import-nfts-modal.stories.js b/ui/components/multichain/import-nfts-modal/import-nfts-modal.stories.js index f6e6139ae1d6..0f9e72b23a21 100644 --- a/ui/components/multichain/import-nfts-modal/import-nfts-modal.stories.js +++ b/ui/components/multichain/import-nfts-modal/import-nfts-modal.stories.js @@ -3,6 +3,7 @@ import { Provider } from 'react-redux'; import configureStore from '../../../store/store'; import testData from '../../../../.storybook/test-data'; import { CHAIN_IDS } from '../../../../shared/constants/network'; +import { mockNetworkState } from '../../../../test/stub/networks'; import { ImportNftsModal } from '.'; const createStore = (chainId = CHAIN_IDS.MAINNET, useTokenDetection = true) => { @@ -11,7 +12,7 @@ const createStore = (chainId = CHAIN_IDS.MAINNET, useTokenDetection = true) => { metamask: { ...testData.metamask, useTokenDetection, - providerConfig: { chainId }, + ...mockNetworkState({ chainId }), }, }); }; diff --git a/ui/components/multichain/import-token-link/import-token-link.test.js b/ui/components/multichain/import-token-link/import-token-link.test.js index e15b4ed09aa4..722d2b4106ff 100644 --- a/ui/components/multichain/import-token-link/import-token-link.test.js +++ b/ui/components/multichain/import-token-link/import-token-link.test.js @@ -4,6 +4,7 @@ import { fireEvent, screen } from '@testing-library/react'; import { detectTokens } from '../../../store/actions'; import { renderWithProvider } from '../../../../test/lib/render-helpers'; import { CHAIN_IDS } from '../../../../shared/constants/network'; +import { mockNetworkState } from '../../../../test/stub/networks'; import { ImportTokenLink } from '.'; const mockPushHistory = jest.fn(); @@ -30,9 +31,7 @@ describe('Import Token Link', () => { it('should match snapshot for goerli chainId', () => { const mockState = { metamask: { - providerConfig: { - chainId: '0x5', - }, + ...mockNetworkState({ chainId: CHAIN_IDS.GOERLI }), }, }; @@ -46,9 +45,7 @@ describe('Import Token Link', () => { it('should match snapshot for mainnet chainId', () => { const mockState = { metamask: { - providerConfig: { - chainId: CHAIN_IDS.MAINNET, - }, + ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), }, }; @@ -62,9 +59,7 @@ describe('Import Token Link', () => { it('should detectTokens when clicking refresh', () => { const mockState = { metamask: { - providerConfig: { - chainId: '0x5', - }, + ...mockNetworkState({ chainId: CHAIN_IDS.GOERLI }), }, }; @@ -81,9 +76,7 @@ describe('Import Token Link', () => { it('should push import token route', () => { const mockState = { metamask: { - providerConfig: { - chainId: '0x5', - }, + ...mockNetworkState({ chainId: CHAIN_IDS.GOERLI }), }, }; diff --git a/ui/components/multichain/import-tokens-modal/import-tokens-modal-confirm.stories.js b/ui/components/multichain/import-tokens-modal/import-tokens-modal-confirm.stories.js index 2aedaaff099f..928768616fd6 100644 --- a/ui/components/multichain/import-tokens-modal/import-tokens-modal-confirm.stories.js +++ b/ui/components/multichain/import-tokens-modal/import-tokens-modal-confirm.stories.js @@ -3,6 +3,7 @@ import { Provider } from 'react-redux'; import configureStore from '../../../store/store'; import testData from '../../../../.storybook/test-data'; import { CHAIN_IDS } from '../../../../shared/constants/network'; +import { mockNetworkState } from '../../../../test/stub/networks'; import { ImportTokensModalConfirm } from './import-tokens-modal-confirm'; const createStore = ( @@ -15,7 +16,7 @@ const createStore = ( metamask: { ...testData.metamask, useTokenDetection, - providerConfig: { chainId }, + ...mockNetworkState({ chainId }), pendingTokens: { '0x0000000de40dfa9b17854cbc7869d80f9f98d823': { address: '0x0000000de40dfa9b17854cbc7869d80f9f98d823', diff --git a/ui/components/multichain/import-tokens-modal/import-tokens-modal.stories.js b/ui/components/multichain/import-tokens-modal/import-tokens-modal.stories.js index 04505d00b196..807b5c45e70e 100644 --- a/ui/components/multichain/import-tokens-modal/import-tokens-modal.stories.js +++ b/ui/components/multichain/import-tokens-modal/import-tokens-modal.stories.js @@ -3,6 +3,7 @@ import { Provider } from 'react-redux'; import configureStore from '../../../store/store'; import testData from '../../../../.storybook/test-data'; import { CHAIN_IDS } from '../../../../shared/constants/network'; +import { mockNetworkState } from '../../../../test/stub/networks'; import { ImportTokensModal } from './import-tokens-modal'; const createStore = (chainId = CHAIN_IDS.MAINNET, useTokenDetection = true) => { @@ -11,7 +12,7 @@ const createStore = (chainId = CHAIN_IDS.MAINNET, useTokenDetection = true) => { metamask: { ...testData.metamask, useTokenDetection, - providerConfig: { chainId }, + ...mockNetworkState({ chainId }), }, }); }; diff --git a/ui/components/multichain/index.js b/ui/components/multichain/index.js index 4f4ca9468bac..143092a1de76 100644 --- a/ui/components/multichain/index.js +++ b/ui/components/multichain/index.js @@ -8,7 +8,6 @@ export { AppHeader } from './app-header'; export { DetectedTokensBanner } from './detected-token-banner'; export { GlobalMenu } from './global-menu'; export { ImportTokenLink } from './import-token-link'; -export { ReceiveTokenLink } from './receive-token-link'; export { TokenListItem } from './token-list-item'; export { AddressCopyButton } from './address-copy-button'; export { ConnectedSiteMenu } from './connected-site-menu'; @@ -49,3 +48,4 @@ export { NotificationListItemSnap } from './notification-list-item-snap'; export { NotificationsTagCounter } from './notifications-tag-counter'; export { Toast, ToastContainer } from './toast'; export { PermissionDetailsModal } from './permission-details-modal'; +export { EditNetworksModal } from './edit-networks-modal'; diff --git a/ui/components/multichain/multichain-components.scss b/ui/components/multichain/multichain-components.scss index c4b043c69b1f..5fda42a8141f 100644 --- a/ui/components/multichain/multichain-components.scss +++ b/ui/components/multichain/multichain-components.scss @@ -17,6 +17,7 @@ @import 'app-header'; @import 'connected-accounts-menu'; @import 'connected-site-menu'; +@import 'create-named-snap-account'; @import 'token-list-item'; @import 'network-list-item'; @import 'network-list-item-menu'; diff --git a/ui/components/multichain/network-list-item/network-list-item.js b/ui/components/multichain/network-list-item/network-list-item.js index 7135bebe90dd..5f52fcd19832 100644 --- a/ui/components/multichain/network-list-item/network-list-item.js +++ b/ui/components/multichain/network-list-item/network-list-item.js @@ -114,11 +114,7 @@ export const NetworkListItem = ({ width={BlockSize.Full} onClick={onClick} > - {startAccessory ? ( - - {startAccessory} - - ) : null} + {startAccessory ? {startAccessory} : null} {selected && ( { focus={isCurrentNetwork && !focusSearch} onClick={() => { dispatch(toggleNetworkMenu()); - if (network.providerType) { - dispatch(setProviderType(network.providerType)); - } else { - dispatch(setActiveNetwork(network.id)); - } + dispatch(setActiveNetwork(network.providerType || network.id)); // If presently on and connected to a dapp, communicate a change to // the dapp via silent switchEthereumChain that the network has diff --git a/ui/components/multichain/network-list-menu/network-list-menu.test.js b/ui/components/multichain/network-list-menu/network-list-menu.test.js index 4a170fc139d7..f7c346fb2070 100644 --- a/ui/components/multichain/network-list-menu/network-list-menu.test.js +++ b/ui/components/multichain/network-list-menu/network-list-menu.test.js @@ -12,14 +12,12 @@ import { import { NetworkListMenu } from '.'; const mockSetShowTestNetworks = jest.fn(); -const mockSetProviderType = jest.fn(); const mockToggleNetworkMenu = jest.fn(); const mockSetNetworkClientIdForDomain = jest.fn(); const mockSetActiveNetwork = jest.fn(); jest.mock('../../../store/actions.ts', () => ({ setShowTestNetworks: () => mockSetShowTestNetworks, - setProviderType: () => mockSetProviderType, setActiveNetwork: () => mockSetActiveNetwork, toggleNetworkMenu: () => mockToggleNetworkMenu, setNetworkClientIdForDomain: (network, id) => @@ -30,8 +28,7 @@ const MOCK_ORIGIN = 'https://portfolio.metamask.io'; const render = ({ showTestNetworks = false, - currentChainId = '0x5', - providerConfigId = 'chain5', + selectedNetworkClientId = 'goerli', isUnlocked = true, origin = MOCK_ORIGIN, selectedTabOriginInDomainsState = true, @@ -40,18 +37,14 @@ const render = ({ metamask: { ...mockState.metamask, isUnlocked, - providerConfig: { - ...mockState.metamask.providerConfig, - chainId: currentChainId, - id: providerConfigId, - }, + selectedNetworkClientId, preferences: { showTestNetworks, }, useRequestQueue: true, domains: { ...(selectedTabOriginInDomainsState - ? { [origin]: providerConfigId } + ? { [origin]: selectedNetworkClientId } : {}), }, }, @@ -108,7 +101,7 @@ describe('NetworkListMenu', () => { const { getByText } = render(); fireEvent.click(getByText(MAINNET_DISPLAY_NAME)); expect(mockToggleNetworkMenu).toHaveBeenCalled(); - expect(mockSetProviderType).toHaveBeenCalled(); + expect(mockSetActiveNetwork).toHaveBeenCalled(); }); it('shows the correct selected network when networks share the same chain ID', () => { @@ -116,7 +109,7 @@ describe('NetworkListMenu', () => { render({ showTestNetworks: false, currentChainId: CHAIN_IDS.MAINNET, - providerConfigId: 'testNetworkConfigurationId', + selectedNetworkClientId: 'testNetworkConfigurationId', }); // Contains Mainnet, Linea Mainnet and the two custom networks diff --git a/ui/components/multichain/network-list-menu/popular-network-list/popular-network-list.test.tsx b/ui/components/multichain/network-list-menu/popular-network-list/popular-network-list.test.tsx index f7354e0acefc..468563214c6d 100644 --- a/ui/components/multichain/network-list-menu/popular-network-list/popular-network-list.test.tsx +++ b/ui/components/multichain/network-list-menu/popular-network-list/popular-network-list.test.tsx @@ -18,11 +18,7 @@ jest.mock('react-redux', () => ({ })); const STATE_MOCK = { - metamask: { - providerConfig: { - chainId: '0x1', - }, - }, + metamask: {}, }; describe('PopularNetworkList', () => { diff --git a/ui/components/multichain/notification-detail-block-explorer-button/notification-detail-block-explorer-button.test.tsx b/ui/components/multichain/notification-detail-block-explorer-button/notification-detail-block-explorer-button.test.tsx index fd63e34dffee..7b9ff70969be 100644 --- a/ui/components/multichain/notification-detail-block-explorer-button/notification-detail-block-explorer-button.test.tsx +++ b/ui/components/multichain/notification-detail-block-explorer-button/notification-detail-block-explorer-button.test.tsx @@ -1,11 +1,13 @@ import React from 'react'; +import type { NotificationServicesController } from '@metamask/notification-services-controller'; import { render, screen } from '@testing-library/react'; import { Provider } from 'react-redux'; import configureStore from 'redux-mock-store'; -import type { Notification } from '../../../../app/scripts/controllers/metamask-notifications/types/notification/notification'; import mockState from '../../../../test/data/mock-state.json'; import { NotificationDetailBlockExplorerButton } from './notification-detail-block-explorer-button'; +type Notification = NotificationServicesController.Types.INotification; + const mockStore = configureStore(); const store = mockStore({ ...mockState, diff --git a/ui/components/multichain/notification-detail-block-explorer-button/notification-detail-block-explorer-button.tsx b/ui/components/multichain/notification-detail-block-explorer-button/notification-detail-block-explorer-button.tsx index 949a2edd2255..02250c4ec36b 100644 --- a/ui/components/multichain/notification-detail-block-explorer-button/notification-detail-block-explorer-button.tsx +++ b/ui/components/multichain/notification-detail-block-explorer-button/notification-detail-block-explorer-button.tsx @@ -1,7 +1,7 @@ import React, { useMemo } from 'react'; import { useSelector } from 'react-redux'; import type { NetworkConfiguration } from '@metamask/network-controller'; -import type { Notification } from '../../../../app/scripts/controllers/metamask-notifications/types/types'; +import type { NotificationServicesController } from '@metamask/notification-services-controller'; import { getAllNetworks } from '../../../selectors'; import { CHAIN_IDS } from '../../../../shared/constants/network'; import { ButtonVariant } from '../../component-library'; @@ -10,6 +10,8 @@ import { useI18nContext } from '../../../hooks/useI18nContext'; import { getNetworkDetailsByChainId } from '../../../helpers/utils/notification.util'; import { NotificationDetailButton } from '../notification-detail-button'; +type Notification = NotificationServicesController.Types.INotification; + type NotificationDetailBlockExplorerButtonProps = { notification: Notification; chainId: number; @@ -26,7 +28,6 @@ export const NotificationDetailBlockExplorerButton = ({ const t = useI18nContext(); const chainIdHex = decimalToHex(chainId); - const { blockExplorerConfig } = getNetworkDetailsByChainId( `0x${chainIdHex}` as keyof typeof CHAIN_IDS, ); diff --git a/ui/components/multichain/notification-detail-button/notification-detail-button.test.tsx b/ui/components/multichain/notification-detail-button/notification-detail-button.test.tsx index fb08ebbfda98..46b80df1213e 100644 --- a/ui/components/multichain/notification-detail-button/notification-detail-button.test.tsx +++ b/ui/components/multichain/notification-detail-button/notification-detail-button.test.tsx @@ -1,10 +1,13 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; -import type { Notification } from '../../../../app/scripts/controllers/metamask-notifications/types/notification/notification'; +import { NotificationServicesController } from '@metamask/notification-services-controller'; import { ButtonVariant } from '../../component-library'; -import { TRIGGER_TYPES } from '../../../../app/scripts/controllers/metamask-notifications/constants/notification-schema'; import { NotificationDetailButton } from './notification-detail-button'; +const { TRIGGER_TYPES } = NotificationServicesController.Constants; + +type Notification = NotificationServicesController.Types.INotification; + describe('NotificationDetailButton', () => { const defaultProps = { variant: ButtonVariant.Primary, diff --git a/ui/components/multichain/notification-detail-button/notification-detail-button.tsx b/ui/components/multichain/notification-detail-button/notification-detail-button.tsx index b4c243ef9b7d..e9b2f8d21903 100644 --- a/ui/components/multichain/notification-detail-button/notification-detail-button.tsx +++ b/ui/components/multichain/notification-detail-button/notification-detail-button.tsx @@ -1,11 +1,10 @@ import React, { useContext } from 'react'; -import type { Notification } from '../../../../app/scripts/controllers/metamask-notifications/types/types'; +import { NotificationServicesController } from '@metamask/notification-services-controller'; import { MetaMetricsContext } from '../../../contexts/metametrics'; import { MetaMetricsEventCategory, MetaMetricsEventName, } from '../../../../shared/constants/metametrics'; -import { TRIGGER_TYPES } from '../../../../app/scripts/controllers/metamask-notifications/constants/notification-schema'; import { Button, ButtonVariant, @@ -14,6 +13,10 @@ import { } from '../../component-library'; import { BlockSize } from '../../../helpers/constants/design-system'; +type Notification = NotificationServicesController.Types.INotification; + +const { TRIGGER_TYPES } = NotificationServicesController.Constants; + type NotificationDetailButtonProps = { notification: Notification; variant: ButtonVariant; diff --git a/ui/components/multichain/notification-detail-copy-button/notification-detail-copy-button.tsx b/ui/components/multichain/notification-detail-copy-button/notification-detail-copy-button.tsx index f70189266f8d..9bddb9256744 100644 --- a/ui/components/multichain/notification-detail-copy-button/notification-detail-copy-button.tsx +++ b/ui/components/multichain/notification-detail-copy-button/notification-detail-copy-button.tsx @@ -1,7 +1,6 @@ import React, { useContext } from 'react'; import type { FC } from 'react'; -import type { Notification } from '../../../../app/scripts/controllers/metamask-notifications/types/types'; -import { TRIGGER_TYPES } from '../../../../app/scripts/controllers/metamask-notifications/constants/notification-schema'; +import { NotificationServicesController } from '@metamask/notification-services-controller'; import { MetaMetricsContext } from '../../../contexts/metametrics'; import { MetaMetricsEventCategory, @@ -26,6 +25,9 @@ import Tooltip from '../../ui/tooltip/tooltip'; import { useI18nContext } from '../../../hooks/useI18nContext'; import { MINUTE } from '../../../../shared/constants/time'; +type Notification = NotificationServicesController.Types.INotification; +const { TRIGGER_TYPES } = NotificationServicesController.Constants; + export type NotificationDetailCopyButtonProps = { notification?: Notification; text: string; diff --git a/ui/components/multichain/notification-detail-network-fee/notification-detail-network-fee.tsx b/ui/components/multichain/notification-detail-network-fee/notification-detail-network-fee.tsx index fb19f04558c5..773e4c14bfcd 100644 --- a/ui/components/multichain/notification-detail-network-fee/notification-detail-network-fee.tsx +++ b/ui/components/multichain/notification-detail-network-fee/notification-detail-network-fee.tsx @@ -1,6 +1,6 @@ import React, { useContext, useState, useEffect } from 'react'; import type { FC } from 'react'; -import type { OnChainRawNotificationsWithNetworkFields } from '../../../../app/scripts/controllers/metamask-notifications/types/on-chain-notification/on-chain-notification'; +import type { NotificationServicesController } from '@metamask/notification-services-controller'; import { useI18nContext } from '../../../hooks/useI18nContext'; import { MetaMetricsContext } from '../../../contexts/metametrics'; @@ -38,6 +38,9 @@ import { } from '../../../helpers/constants/design-system'; import Preloader from '../../ui/icon/preloader/preloader-icon.component'; +type OnChainRawNotificationsWithNetworkFields = + NotificationServicesController.Types.OnChainRawNotificationsWithNetworkFields; + type NetworkFees = { transactionFee: { transactionFeeInEther: string; diff --git a/ui/components/multichain/notification-list-item-snap/notification-list-item-snap.tsx b/ui/components/multichain/notification-list-item-snap/notification-list-item-snap.tsx index 08e63fcf0bb6..fbe021861b91 100644 --- a/ui/components/multichain/notification-list-item-snap/notification-list-item-snap.tsx +++ b/ui/components/multichain/notification-list-item-snap/notification-list-item-snap.tsx @@ -22,6 +22,7 @@ import SnapAvatar from '../../app/snaps/snap-avatar'; export type NotificationListItemSnapProps = { id: string; + snapId: string; isRead: boolean; title: NotificationListItemTextProps; snapMessage: string; @@ -38,6 +39,7 @@ export type NotificationListItemSnapProps = { * @param props.title - The title of the notification. * @param props.createdAt - The date of the notification. * @param props.id - The id of the notification. + * @param props.snapId - The id of the Snap that created the notification. * @param props.handleSnapClick - The function to call when the notification is clicked. * @param props.handleSnapButton - The function to call when the snap button is clicked. * @param props.snapMessage - The snap message to display on the notification. @@ -49,6 +51,7 @@ export const NotificationListItemSnap = ({ title, snapMessage, createdAt, + snapId, handleSnapClick, handleSnapButton, }: NotificationListItemSnapProps) => { @@ -105,7 +108,7 @@ export const NotificationListItemSnap = ({ diff --git a/ui/components/multichain/pages/send/components/domain-input-resolution-cell.tsx b/ui/components/multichain/pages/send/components/domain-input-resolution-cell.tsx index 54d80a4e6c5a..914c904e76dd 100644 --- a/ui/components/multichain/pages/send/components/domain-input-resolution-cell.tsx +++ b/ui/components/multichain/pages/send/components/domain-input-resolution-cell.tsx @@ -1,35 +1,22 @@ -import React, { - ///: BEGIN:ONLY_INCLUDE_IF(build-flask) - useContext, - ///: END:ONLY_INCLUDE_IF - useRef, - useEffect, - useState, -} from 'react'; +import React, { useContext, useRef, useEffect, useState } from 'react'; import PropTypes from 'prop-types'; -///: BEGIN:ONLY_INCLUDE_IF(build-flask) import { I18nContext } from '../../../../../contexts/i18n'; -///: END:ONLY_INCLUDE_IF import Confusable from '../../../../ui/confusable'; import { AvatarAccount, Box, - ///: BEGIN:ONLY_INCLUDE_IF(build-flask) AvatarIcon, AvatarIconSize, BadgeWrapper, IconName, - ///: END:ONLY_INCLUDE_IF Text, } from '../../../../component-library'; import { AlignItems, Display, - ///: BEGIN:ONLY_INCLUDE_IF(build-flask) BackgroundColor, BorderColor, IconColor, - ///: END:ONLY_INCLUDE_IF TextColor, TextVariant, } from '../../../../../helpers/constants/design-system'; @@ -49,15 +36,11 @@ export const DomainInputResolutionCell = ({ domainType, address, domainName, - ///: BEGIN:ONLY_INCLUDE_IF(build-flask) resolvingSnap = '', - ///: END:ONLY_INCLUDE_IF onClick, protocol, }: DomainInputResolutionCellArgs) => { - ///: BEGIN:ONLY_INCLUDE_IF(build-flask) const t: (key: string, params: unknown[]) => string = useContext(I18nContext); - ///: END:ONLY_INCLUDE_IF const titleRef = useRef(null); const breakpointRef = useRef(null); const [isTitleOverflowing, setIsTitleOverflowing] = useState(false); @@ -106,7 +89,6 @@ export const DomainInputResolutionCell = ({ ); - ///: BEGIN:ONLY_INCLUDE_IF(build-flask) if (domainType === 'Other') { // Snap provided resolution. return ( @@ -180,7 +162,6 @@ export const DomainInputResolutionCell = ({ ); } - ///: END:ONLY_INCLUDE_IF const getTitle = () => { if (domainName && isTitleOverflowing) { return ; @@ -242,9 +223,7 @@ DomainInputResolutionCell.propTypes = { domainType: PropTypes.string.isRequired, address: PropTypes.string.isRequired, domainName: PropTypes.string.isRequired, - ///: BEGIN:ONLY_INCLUDE_IF(build-flask) resolvingSnap: PropTypes.string.isRequired, - ///: END:ONLY_INCLUDE_IF onClick: PropTypes.func, protocol: PropTypes.string, }; diff --git a/ui/components/multichain/pages/send/components/recipient-content.test.tsx b/ui/components/multichain/pages/send/components/recipient-content.test.tsx index f2bab84491aa..d738aee3cb8a 100644 --- a/ui/components/multichain/pages/send/components/recipient-content.test.tsx +++ b/ui/components/multichain/pages/send/components/recipient-content.test.tsx @@ -17,6 +17,14 @@ import { AssetType } from '../../../../../../shared/constants/transaction'; import { getSendHexDataFeatureFlagState } from '../../../../../ducks/metamask/metamask'; import { SendPageRecipientContent } from './recipient-content'; +jest.mock('reselect', () => ({ + createSelector: jest.fn(), +})); + +jest.mock('../../../../../selectors/util', () => ({ + createDeepEqualSelector: jest.fn(), +})); + jest.mock('react-redux', () => ({ useSelector: jest.fn(), useDispatch: jest.fn(), @@ -65,6 +73,7 @@ describe('SendPageRecipientContent', () => { const defaultProps = { requireContractAddressAcknowledgement: false, onAssetChange: onAssetChangeMock, + onClick: jest.fn(), }; beforeEach(() => { diff --git a/ui/components/multichain/pages/send/components/recipient-content.tsx b/ui/components/multichain/pages/send/components/recipient-content.tsx index 419cba588b47..d6c8f00b446c 100644 --- a/ui/components/multichain/pages/send/components/recipient-content.tsx +++ b/ui/components/multichain/pages/send/components/recipient-content.tsx @@ -6,22 +6,30 @@ import React, { useRef, } from 'react'; import { useDispatch, useSelector } from 'react-redux'; +///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) +import { TokenListMap } from '@metamask/assets-controllers'; +///: END:ONLY_INCLUDE_IF import { BannerAlert, BannerAlertSeverity, Box, } from '../../../../component-library'; -import { getSendHexDataFeatureFlagState } from '../../../../../ducks/metamask/metamask'; +import { + ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) + getNativeCurrency, + ///: END:ONLY_INCLUDE_IF + getSendHexDataFeatureFlagState, +} from '../../../../../ducks/metamask/metamask'; import { Asset, acknowledgeRecipientWarning, getBestQuote, getCurrentDraftTransaction, - getSendAsset, ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) getIsSwapAndSendDisabledForNetwork, getSwapsBlockedTokens, ///: END:ONLY_INCLUDE_IF + getSendAsset, } from '../../../../../ducks/send'; import { AssetType } from '../../../../../../shared/constants/transaction'; import { CONTRACT_ADDRESS_LINK } from '../../../../../helpers/constants/common'; @@ -31,21 +39,29 @@ import { AssetPickerAmount } from '../../..'; import { decimalToHex } from '../../../../../../shared/modules/conversion.utils'; ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) import { + getIpfsGateway, getIsSwapsChain, + getNativeCurrencyImage, + getTokenList, getUseExternalServices, } from '../../../../../selectors'; ///: END:ONLY_INCLUDE_IF import type { Quote } from '../../../../../ducks/send/swap-and-send-utils'; import { isEqualCaseInsensitive } from '../../../../../../shared/modules/string-utils'; +import { AssetPicker } from '../../../asset-picker-amount/asset-picker'; +import { TabName } from '../../../asset-picker-amount/asset-picker-modal/asset-picker-modal-tabs'; +import { getAssetImageURL } from '../../../../../helpers/utils/util'; import { SendHexData, SendPageRow, QuoteCard } from '.'; export const SendPageRecipientContent = ({ requireContractAddressAcknowledgement, onAssetChange, + onClick, }: { requireContractAddressAcknowledgement: boolean; onAssetChange: (newAsset: Asset, isReceived: boolean) => void; + onClick: () => React.ComponentProps['onClick']; }) => { const t = useI18nContext(); @@ -73,6 +89,11 @@ export const SendPageRecipientContent = ({ return new Set(swapsBlockedTokens); }, [swapsBlockedTokens]); + const nativeCurrencySymbol = useSelector(getNativeCurrency); + const nativeCurrencyImageUrl = useSelector(getNativeCurrencyImage); + const tokenList = useSelector(getTokenList) as TokenListMap; + const ipfsGateway = useSelector(getIpfsGateway); + isSwapAllowed = isSwapsChain && !isSwapAndSendDisabledForNetwork && @@ -138,8 +159,22 @@ export const SendPageRecipientContent = ({ ) : null} onAssetChange(newAsset, isSwapAllowed), [onAssetChange, isSwapAllowed], @@ -147,6 +182,8 @@ export const SendPageRecipientContent = ({ isAmountLoading={isLoadingInitialQuotes} amount={amount} isDisabled={!isSwapAllowed} + onClick={onClick} + visibleTabs={[TabName.TOKENS]} /> diff --git a/ui/components/multichain/pages/send/components/recipient.tsx b/ui/components/multichain/pages/send/components/recipient.tsx index 651067d4bf8c..7458083b9d3f 100644 --- a/ui/components/multichain/pages/send/components/recipient.tsx +++ b/ui/components/multichain/pages/send/components/recipient.tsx @@ -24,7 +24,6 @@ import { MetaMetricsEventCategory, MetaMetricsEventName, } from '../../../../../../shared/constants/metametrics'; - import { MetaMetricsContext } from '../../../../../contexts/metametrics'; import { DomainInputResolutionCell } from './domain-input-resolution-cell'; import { SendPageAddressBook, SendPageRow, SendPageYourAccounts } from '.'; diff --git a/ui/components/multichain/pages/send/send.js b/ui/components/multichain/pages/send/send.js index ca9b3275e5eb..3fe8ef3652eb 100644 --- a/ui/components/multichain/pages/send/send.js +++ b/ui/components/multichain/pages/send/send.js @@ -146,9 +146,45 @@ export const SendPage = () => { }), ); } + + trackEvent( + { + event: MetaMetricsEventName.sendAssetSelected, + category: MetaMetricsEventCategory.Send, + properties: { + is_destination_asset_picker_modal: Boolean(isReceived), + is_nft: false, + }, + sensitiveProperties: { + ...sendAnalytics, + new_asset_symbol: token.symbol, + new_asset_address: token.address, + }, + }, + { excludeMetaMetricsId: false }, + ); history.push(SEND_ROUTE); }, - [dispatch, history], + [dispatch, history, sendAnalytics, trackEvent], + ); + + const handleAssetPickerClick = useCallback( + (isDest) => { + trackEvent( + { + event: MetaMetricsEventName.sendTokenModalOpened, + category: MetaMetricsEventCategory.Send, + properties: { + is_destination_asset_picker_modal: Boolean(isDest), + }, + sensitiveProperties: { + ...sendAnalytics, + }, + }, + { excludeMetaMetricsId: false }, + ); + }, + [sendAnalytics, trackEvent], ); const cleanup = useCallback(() => { @@ -340,10 +376,12 @@ export const SendPage = () => { {isSendFormShown && ( handleAssetPickerClick(false)} /> )} @@ -354,6 +392,7 @@ export const SendPage = () => { requireContractAddressAcknowledgement } onAssetChange={handleSelectToken} + onClick={() => handleAssetPickerClick(true)} /> ) : ( diff --git a/ui/components/multichain/pages/send/send.test.js b/ui/components/multichain/pages/send/send.test.js index 4b23ac89e2cb..164fe2b8bb3c 100644 --- a/ui/components/multichain/pages/send/send.test.js +++ b/ui/components/multichain/pages/send/send.test.js @@ -1,7 +1,6 @@ import React from 'react'; import thunk from 'redux-thunk'; import configureMockStore from 'redux-mock-store'; -import { NetworkType } from '@metamask/controller-utils'; import { EthAccountType } from '@metamask/keyring-api'; import { act } from '@testing-library/react'; import { @@ -14,15 +13,12 @@ import { INITIAL_SEND_STATE_FOR_EXISTING_DRAFT } from '../../../../../test/jest/ import { GasEstimateTypes } from '../../../../../shared/constants/gas'; import { SEND_STAGES, startNewDraftTransaction } from '../../../../ducks/send'; import { AssetType } from '../../../../../shared/constants/transaction'; -import { - CHAIN_IDS, - GOERLI_DISPLAY_NAME, - NETWORK_TYPES, -} from '../../../../../shared/constants/network'; +import { CHAIN_IDS } from '../../../../../shared/constants/network'; import mockSendState from '../../../../../test/data/mock-send-state.json'; import { useIsOriginalNativeTokenSymbol } from '../../../../hooks/useIsOriginalNativeTokenSymbol'; import { KeyringType } from '../../../../../shared/constants/keyring'; import { ETH_EOA_METHODS } from '../../../../../shared/constants/eth-methods'; +import { mockNetworkState } from '../../../../../test/stub/networks'; import { SendPage } from '.'; jest.mock('@ethersproject/providers', () => { @@ -165,24 +161,16 @@ const baseStore = { accounts: ['0x0'], }, ], - selectedNetworkClientId: NetworkType.goerli, - networksMetadata: { - [NetworkType.goerli]: { - EIPS: {}, - status: 'available', - }, - }, + ...mockNetworkState({ + chainId: CHAIN_IDS.GOERLI, + ticker: 'ETH', + }), tokens: [], preferences: { useNativeCurrencyAsPrimaryCurrency: false, showFiatInTestnets: true, }, currentCurrency: 'USD', - providerConfig: { - chainId: CHAIN_IDS.GOERLI, - nickname: GOERLI_DISPLAY_NAME, - ticker: 'ETH', - }, nativeCurrency: 'ETH', featureFlags: { sendHexData: false, @@ -378,12 +366,7 @@ describe('SendPage', () => { metamask: { ...mockSendState.metamask, gasEstimateType: 'none', - providerConfig: { - chainId: CHAIN_IDS.GOERLI, - nickname: GOERLI_DISPLAY_NAME, - type: NETWORK_TYPES.GOERLI, - ticker: 'ETH', - }, + ...mockNetworkState({ chainId: CHAIN_IDS.GOERLI }), }, }; diff --git a/ui/components/multichain/receive-modal/receive-modal.js b/ui/components/multichain/receive-modal/receive-modal.js index 8fe57cc9de5a..bc90f6497505 100644 --- a/ui/components/multichain/receive-modal/receive-modal.js +++ b/ui/components/multichain/receive-modal/receive-modal.js @@ -2,32 +2,23 @@ import React from 'react'; import { useSelector } from 'react-redux'; import PropTypes from 'prop-types'; import { - AvatarAccount, - AvatarAccountSize, - AvatarAccountVariant, Box, Modal, ModalOverlay, - Text, ModalContent, ModalHeader, } from '../../component-library'; import QrCodeView from '../../ui/qr-code-view'; import { useI18nContext } from '../../../hooks/useI18nContext'; -import { getInternalAccountByAddress, getUseBlockie } from '../../../selectors'; +import { getInternalAccountByAddress } from '../../../selectors'; import { AlignItems, - BlockSize, Display, FlexDirection, - JustifyContent, - TextAlign, - TextVariant, } from '../../../helpers/constants/design-system'; export const ReceiveModal = ({ address, onClose }) => { const t = useI18nContext(); - const useBlockie = useSelector(getUseBlockie); const { metadata: { name }, } = useSelector((state) => getInternalAccountByAddress(state, address)); @@ -39,28 +30,6 @@ export const ReceiveModal = ({ address, onClose }) => { {t('receive')} - - - - - {name} - { paddingInlineEnd={4} paddingInlineStart={4} > - + diff --git a/ui/components/multichain/receive-modal/receive-modal.test.js b/ui/components/multichain/receive-modal/receive-modal.test.js index 48bafe095e3e..16cc5b1e5f28 100644 --- a/ui/components/multichain/receive-modal/receive-modal.test.js +++ b/ui/components/multichain/receive-modal/receive-modal.test.js @@ -3,7 +3,6 @@ import { screen } from '@testing-library/react'; import mockState from '../../../../test/data/mock-state.json'; import { renderWithProvider } from '../../../../test/jest'; import configureStore from '../../../store/store'; -import { toChecksumHexAddress } from '../../../../shared/modules/hexstring-utils'; import { ReceiveModal } from '.'; describe('ReceiveModal', () => { @@ -16,10 +15,6 @@ describe('ReceiveModal', () => { it('should show the correct account address and name', () => { const address = '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc'; render(address); - // Check for the copy button - expect( - screen.queryByText(toChecksumHexAddress(address)), - ).toBeInTheDocument(); // Check for the title expect(screen.queryByText('Test Account')).toBeInTheDocument(); }); diff --git a/ui/components/multichain/receive-token-link/index.ts b/ui/components/multichain/receive-token-link/index.ts deleted file mode 100644 index f9f98f23d8d3..000000000000 --- a/ui/components/multichain/receive-token-link/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { ReceiveTokenLink } from './receive-token-link'; diff --git a/ui/components/multichain/receive-token-link/receive-token-link.tsx b/ui/components/multichain/receive-token-link/receive-token-link.tsx deleted file mode 100644 index 41416833b5aa..000000000000 --- a/ui/components/multichain/receive-token-link/receive-token-link.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import React, { useContext, useEffect, useState } from 'react'; -import { useSelector } from 'react-redux'; -import type { BoxProps } from '../../component-library'; -import { - Box, - ButtonLink, - ButtonLinkSize, - IconName, -} from '../../component-library'; -import { AlignItems, Display } from '../../../helpers/constants/design-system'; -import { useI18nContext } from '../../../hooks/useI18nContext'; -import { MetaMetricsContext } from '../../../contexts/metametrics'; -import { - MetaMetricsEventCategory, - MetaMetricsEventName, -} from '../../../../shared/constants/metametrics'; -import { getCurrentNetwork, getSelectedAccount } from '../../../selectors'; -import { ReceiveModal } from '../receive-modal'; -import { ORIGIN_METAMASK } from '../../../../shared/constants/app'; -import { getCurrentLocale } from '../../../ducks/locale/locale'; - -export const ReceiveTokenLink: React.FC> = ({ - ...props -}): JSX.Element => { - const trackEvent = useContext(MetaMetricsContext); - const t = useI18nContext(); - const currentNetwork = useSelector(getCurrentNetwork); - const currentLocale = useSelector(getCurrentLocale); - const { address: selectedAddress } = useSelector(getSelectedAccount); - - const [showReceiveModal, setShowReceiveModal] = useState(false); - - useEffect(() => { - trackEvent({ - event: MetaMetricsEventName.EmptyReceiveBannerDisplayed, - category: MetaMetricsEventCategory.Navigation, - properties: { - chain_id: currentNetwork.chainId, - locale: currentLocale, - network: currentNetwork.nickname, - referrer: ORIGIN_METAMASK, - }, - }); - }, []); - - return ( - <> - {showReceiveModal && ( - setShowReceiveModal(false)} - /> - )} - - { - setShowReceiveModal(true); - }} - > - {t('receiveTokensCamelCase')} - - - - ); -}; diff --git a/ui/components/multichain/token-list-item/__snapshots__/token-list-item.test.js.snap b/ui/components/multichain/token-list-item/__snapshots__/token-list-item.test.js.snap index bc795c07b57b..5c75df62eef1 100644 --- a/ui/components/multichain/token-list-item/__snapshots__/token-list-item.test.js.snap +++ b/ui/components/multichain/token-list-item/__snapshots__/token-list-item.test.js.snap @@ -7,15 +7,15 @@ exports[`TokenListItem should render correctly 1`] = ` data-testid="multichain-token-list-item" >
?
@@ -23,10 +23,10 @@ exports[`TokenListItem should render correctly 1`] = ` class="mm-box mm-badge-wrapper__badge-container mm-badge-wrapper__badge-container--circular-top-right" >
network logo diff --git a/ui/components/multichain/token-list-item/index.scss b/ui/components/multichain/token-list-item/index.scss index f40fcf5a1df6..d73591ddcdba 100644 --- a/ui/components/multichain/token-list-item/index.scss +++ b/ui/components/multichain/token-list-item/index.scss @@ -10,5 +10,9 @@ &__badge { align-self: center; + + &__avatar-network { + box-sizing: content-box; + } } } diff --git a/ui/components/multichain/token-list-item/token-list-item.js b/ui/components/multichain/token-list-item/token-list-item.js index ed69d6638b37..f28a53bcd075 100644 --- a/ui/components/multichain/token-list-item/token-list-item.js +++ b/ui/components/multichain/token-list-item/token-list-item.js @@ -8,7 +8,6 @@ import { AlignItems, BackgroundColor, BlockSize, - BorderColor, Display, FlexDirection, FontWeight, @@ -46,7 +45,6 @@ import { getMultichainCurrentChainId, getMultichainCurrentNetwork, getMultichainIsEvm, - getMultichainNativeCurrencyImage, } from '../../../selectors/multichain'; import Tooltip from '../../ui/tooltip'; import { useI18nContext } from '../../../hooks/useI18nContext'; @@ -82,7 +80,6 @@ export const TokenListItem = ({ }) => { const t = useI18nContext(); const isEvm = useSelector(getMultichainIsEvm); - const primaryTokenImage = useSelector(getMultichainNativeCurrencyImage); const trackEvent = useContext(MetaMetricsContext); const chainId = useSelector(getMultichainCurrentChainId); const metaMetricsId = useSelector(getMetaMetricsId); @@ -128,11 +125,10 @@ export const TokenListItem = ({ as="button" backgroundColor={BackgroundColor.transparent} data-testid={`staking-entrypoint-${chainId}`} - display={Display.InlineFlex} - flexDirection={FlexDirection.Row} - alignItems={AlignItems.center} gap={1} paddingInline={0} + paddingInlineStart={1} + paddingInlineEnd={1} tabIndex="0" onClick={(e) => { e.preventDefault(); @@ -159,7 +155,13 @@ export const TokenListItem = ({ }} > - + {t('stake')} } - marginRight={3} + marginRight={4} className="multichain-token-list-item__badge" > - + { ); }; @@ -49,14 +49,16 @@ const Header = ({ isCollapsible, isExpanded, isLoading, + isDisabled, onHeaderClick, type, }: { headerComponent: DelineatorProps['headerComponent']; - iconName: IconName; + iconName?: IconName; isCollapsible: boolean; isExpanded: boolean; isLoading: boolean; + isDisabled: boolean; onHeaderClick: () => void; type?: DelineatorType; }) => { @@ -67,18 +69,19 @@ const Header = ({ delineator__header: true, 'delineator__header--expanded': isExpanded, 'delineator__header--loading': isLoading, + 'delineator__header--disabled': isDisabled, })} display={Display.Flex} alignItems={AlignItems.center} justifyContent={JustifyContent.spaceBetween} paddingTop={2} paddingRight={4} - paddingBottom={2} + paddingBottom={isExpanded ? 0 : 2} paddingLeft={4} onClick={onHeaderClick} > - + {iconName && } {overrideTextComponentColorByType({ component: headerComponent, type, @@ -89,7 +92,13 @@ const Header = ({ ); }; -const Content = ({ children }: { children: React.ReactNode }) => { +const Content = ({ + children, + contentBoxProps, +}: { + children: React.ReactNode; + contentBoxProps: DelineatorProps['contentBoxProps']; +}) => { return ( { paddingBottom={4} paddingLeft={4} flexDirection={FlexDirection.Column} + {...contentBoxProps} > {children} @@ -131,21 +141,23 @@ export const Delineator: React.FC = ({ isCollapsible = true, isExpanded: isExpandedProp, isLoading = false, + isDisabled = false, onExpandChange, type, wrapperBoxProps, + contentBoxProps, }) => { const [isExpanded, setIsExpanded] = useState(isExpandedProp || false); const shouldShowContent = !isCollapsible || (isCollapsible && isExpanded); const handleHeaderClick = useCallback(() => { - if (isLoading || !isCollapsible) { + if (isDisabled || isLoading || !isCollapsible) { return; } const newExpandedState = !isExpanded; onExpandChange?.(newExpandedState); setIsExpanded(newExpandedState); - }, [isLoading, isCollapsible, isExpanded, onExpandChange]); + }, [isLoading, isCollapsible, isExpanded, isDisabled, onExpandChange]); return ( @@ -155,10 +167,13 @@ export const Delineator: React.FC = ({ isCollapsible={isCollapsible} isExpanded={isExpanded} isLoading={isLoading} + isDisabled={isDisabled} onHeaderClick={handleHeaderClick} type={type} /> - {shouldShowContent && !isLoading && {children}} + {shouldShowContent && !isLoading && ( + {children} + )} ); }; diff --git a/ui/components/ui/delineator/delineator.types.ts b/ui/components/ui/delineator/delineator.types.ts index 3f49790fae98..dc83f55a289a 100644 --- a/ui/components/ui/delineator/delineator.types.ts +++ b/ui/components/ui/delineator/delineator.types.ts @@ -3,13 +3,15 @@ import { Box, IconName, Text } from '../../component-library'; export type DelineatorProps = { children?: React.ReactNode; headerComponent: React.ReactElement; - iconName: IconName; + iconName?: IconName; isCollapsible?: boolean; isExpanded?: boolean; isLoading?: boolean; + isDisabled?: boolean; onExpandChange?: (isExpanded: boolean) => void; type?: DelineatorType; wrapperBoxProps?: React.ComponentProps; + contentBoxProps?: React.ComponentProps; }; export enum DelineatorType { diff --git a/ui/components/ui/delineator/index.scss b/ui/components/ui/delineator/index.scss index 4d222a35b951..46bcfe11050d 100644 --- a/ui/components/ui/delineator/index.scss +++ b/ui/components/ui/delineator/index.scss @@ -5,5 +5,10 @@ &--loading { cursor: default; } + + &--disabled { + cursor: default; + opacity: 0.5; + } } } diff --git a/ui/components/ui/delineator/utils.ts b/ui/components/ui/delineator/utils.ts index 5675042e8772..666d3ad5e990 100644 --- a/ui/components/ui/delineator/utils.ts +++ b/ui/components/ui/delineator/utils.ts @@ -42,7 +42,7 @@ const getTextColorByType = (type?: DelineatorType) => { case DelineatorType.Error: return TextColor.errorDefault; default: - return TextColor.textAlternative; + return TextColor.textDefault; } }; diff --git a/ui/components/ui/deprecated-networks/deprecated-networks.stories.js b/ui/components/ui/deprecated-networks/deprecated-networks.stories.js index 4b6836440fc6..cf2d096aa786 100644 --- a/ui/components/ui/deprecated-networks/deprecated-networks.stories.js +++ b/ui/components/ui/deprecated-networks/deprecated-networks.stories.js @@ -4,6 +4,7 @@ import testData from '../../../../.storybook/test-data'; import configureStore from '../../../store/store'; import { CHAIN_IDS } from '../../../../shared/constants/network'; +import { mockNetworkState } from '../../../../test/stub/networks'; import DeprecatedNetworks from './deprecated-networks'; const store = (chainId, rpcUrl) => @@ -12,8 +13,7 @@ const store = (chainId, rpcUrl) => metamask: { ...testData.metamask, completedOnboarding: true, - providerConfig: { chainId }, - networkConfigurations: { id: { chainId, rpcUrl } }, + ...mockNetworkState({ chainId, rpcUrl }), }, }); diff --git a/ui/components/ui/identicon/identicon.component.test.js b/ui/components/ui/identicon/identicon.component.test.js index ac953ec62860..244499cb9556 100644 --- a/ui/components/ui/identicon/identicon.component.test.js +++ b/ui/components/ui/identicon/identicon.component.test.js @@ -19,9 +19,6 @@ const ADDRESS_MOCK = '0x0000000000000000000000000000000000000000'; const mockState = { metamask: { - providerConfig: { - chainId: '0x99', - }, useBlockie: false, }, }; diff --git a/ui/components/ui/new-network-info/new-network-info.test.js b/ui/components/ui/new-network-info/new-network-info.test.js index 33fe59dafe70..027c0e2721a5 100644 --- a/ui/components/ui/new-network-info/new-network-info.test.js +++ b/ui/components/ui/new-network-info/new-network-info.test.js @@ -3,6 +3,8 @@ import { waitFor } from '@testing-library/react'; import configureMockStore from 'redux-mock-store'; import nock from 'nock'; import { renderWithProvider } from '../../../../test/lib/render-helpers'; +import { mockNetworkState } from '../../../../test/stub/networks'; +import { CHAIN_IDS } from '../../../../shared/constants/network'; import NewNetworkInfo from './new-network-info'; const fetchWithCache = @@ -39,12 +41,7 @@ describe('NewNetworkInfo', () => { describe('fetch token successfully', () => { const state = { metamask: { - providerConfig: { - ticker: 'ETH', - nickname: '', - chainId: '0x1', - type: 'mainnet', - }, + ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), useExternalServices: true, useTokenDetection: false, currencyRates: {}, @@ -111,8 +108,6 @@ describe('NewNetworkInfo', () => { .get('/tokens/0x3?occurrenceFloor=100&includeNativeAssets=false') .reply(200, '{"error":"ChainId 0x3 is not supported"}'); - state.metamask.providerConfig.ticker = null; - const store = configureMockStore()(state); const { container, getByTestId } = renderWithProvider( , @@ -133,12 +128,8 @@ describe('NewNetworkInfo', () => { describe('add token link', () => { const newState = { metamask: { - providerConfig: { - ticker: 'ETH', - nickname: '', - chainId: '0x1', - type: 'mainnet', - }, + ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), + useExternalServices: true, useTokenDetection: true, currencyRates: {}, diff --git a/ui/components/ui/qr-code-view/index.scss b/ui/components/ui/qr-code-view/index.scss index 0bb183297402..77890b155564 100644 --- a/ui/components/ui/qr-code-view/index.scss +++ b/ui/components/ui/qr-code-view/index.scss @@ -6,6 +6,39 @@ justify-content: center; align-items: center; + &__wrapper { + position: relative; + display: inline-block; + } + + &__image { + position: relative; + z-index: 1; + border: 1px solid var(--color-border-muted); + border-radius: 16px; + background-color: var(--brand-colors-white); // stay white regardless of theme + } + + &__logo { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + z-index: 2; + background-color: var(--brand-colors-white); // stay white regardless of theme + display: flex; + overflow: hidden; + justify-content: center; + align-items: center; + height: 40px; + width: 45px; + } + + &__logo img { + height: 35px; + width: 35px; + } + &__message-container > div:first-child { @include design-system.Paragraph; @@ -13,6 +46,20 @@ color: var(--color-text-muted); } + &__address-segments { + word-break: break-all; + width: 240px; + text-align: center; + } + + &__address-inner-segment { + display: inline; + } + + &__copy-button { + cursor: pointer; + } + &__error { display: flex; justify-content: center; @@ -20,15 +67,4 @@ color: var(--color-error-default); margin-bottom: 9px; } - - &__address-container { - display: flex; - justify-content: center; - cursor: pointer; - align-items: center; - - &__tooltip-wrapper { - width: 100%; - } - } } diff --git a/ui/components/ui/qr-code-view/qr-code-view.tsx b/ui/components/ui/qr-code-view/qr-code-view.tsx index 22b7e0f80205..ca535d00dc65 100644 --- a/ui/components/ui/qr-code-view/qr-code-view.tsx +++ b/ui/components/ui/qr-code-view/qr-code-view.tsx @@ -4,51 +4,66 @@ import qrCode from 'qrcode-generator'; import { connect } from 'react-redux'; import { isHexPrefixed } from 'ethereumjs-util'; import { normalizeSafeAddress } from '../../../../app/scripts/lib/multichain/address'; -import { AddressCopyButton } from '../../multichain'; -import Box from '../box/box'; +import { Box, Icon, IconName, IconSize, Text } from '../../component-library'; import { MetaMetricsContext } from '../../../contexts/metametrics'; -import { - MetaMetricsEventCategory, - MetaMetricsEventName, -} from '../../../../shared/constants/metametrics'; import type { CombinedBackgroundAndReduxState } from '../../../store/store'; -import { Text } from '../../component-library'; import { - TextVariant, + AlignItems, + Display, + IconColor, + TextAlign, TextColor, + TextVariant, } from '../../../helpers/constants/design-system'; - -export default connect(mapStateToProps)(QrCodeView); +import { useI18nContext } from '../../../hooks/useI18nContext'; +import { MINUTE } from '../../../../shared/constants/time'; +import { + MetaMetricsEventCategory, + MetaMetricsEventName, +} from '../../../../shared/constants/metametrics'; +import { useCopyToClipboard } from '../../../hooks/useCopyToClipboard'; function mapStateToProps(state: CombinedBackgroundAndReduxState) { const { buyView, warning } = state.appState; return { - // Qr code is not fetched from state. 'message' and 'data' props are passed instead. buyView, warning, }; } +const PREFIX_LEN = 6; +const SUFFIX_LEN = 5; function QrCodeView({ Qr, warning, + accountName, }: { Qr: { message: string; data: string }; warning: null | string; + accountName?: string; }) { const trackEvent = useContext(MetaMetricsContext); + const [copied, handleCopy] = useCopyToClipboard(MINUTE); + const t = useI18nContext(); const { message, data } = Qr; + const checksummedAddress = normalizeSafeAddress(data); const address = `${ isHexPrefixed(data) ? 'ethereum:' : '' - }${normalizeSafeAddress(data)}`; + }${checksummedAddress}`; const qrImage = qrCode(4, 'M'); qrImage.addData(address); qrImage.make(); - const header = message ? (
{message}
) : null; + const addressStart = data.substring(0, PREFIX_LEN); + const addressMiddle: string = data.substring( + PREFIX_LEN, + data.length - SUFFIX_LEN, + ); + const addressEnd: string = data.substring(data.length - SUFFIX_LEN); + return (
{Array.isArray(message) ? ( @@ -67,27 +82,67 @@ function QrCodeView({ header )} {warning ? {warning} : null} -
- - { - trackEvent({ - category: MetaMetricsEventCategory.Accounts, - event: MetaMetricsEventName.PublicAddressCopied, - properties: { - location: 'Account Details Modal', - }, - }); + + + + Logo + + + {accountName ? ( + + {accountName} + + ) : null} + + {addressStart} + + {addressMiddle} + + {addressEnd} + + { + handleCopy(checksummedAddress); + trackEvent({ + category: MetaMetricsEventCategory.Accounts, + event: MetaMetricsEventName.PublicAddressCopied, + properties: { + location: 'Account Details Modal', + }, + }); + }} + > + + {t('copyAddressShort')}
); @@ -103,3 +158,5 @@ QrCodeView.propTypes = { data: PropTypes.string.isRequired, }).isRequired, }; + +export default connect(mapStateToProps)(QrCodeView); diff --git a/ui/components/ui/token-input/token-input.component.test.js b/ui/components/ui/token-input/token-input.component.test.js index 8f6e278b3d0e..8a91d5301b3c 100644 --- a/ui/components/ui/token-input/token-input.component.test.js +++ b/ui/components/ui/token-input/token-input.component.test.js @@ -4,10 +4,10 @@ import { fireEvent } from '@testing-library/react'; import { renderWithProvider } from '../../../../test/lib/render-helpers'; import mockState from '../../../../test/data/mock-state.json'; import { - NETWORK_TYPES, CHAIN_IDS, CURRENCY_SYMBOLS, } from '../../../../shared/constants/network'; +import { mockNetworkState } from '../../../../test/stub/networks'; import TokenInput from '.'; describe('TokenInput Component', () => { @@ -74,11 +74,7 @@ describe('TokenInput Component', () => { ...mockState.metamask.preferences, showFiatInTestnets: true, }, - providerConfig: { - chainId: CHAIN_IDS.POLYGON, - type: NETWORK_TYPES.MAINNET, - ticker: CURRENCY_SYMBOLS.POL, - }, + ...mockNetworkState({ chainId: CHAIN_IDS.POLYGON }), }, }; const mockStore = configureMockStore()(showFiatState); diff --git a/ui/components/ui/truncated-definition-list/truncated-definition-list.js b/ui/components/ui/truncated-definition-list/truncated-definition-list.js index c928f3b1112c..ae1782979866 100644 --- a/ui/components/ui/truncated-definition-list/truncated-definition-list.js +++ b/ui/components/ui/truncated-definition-list/truncated-definition-list.js @@ -18,57 +18,77 @@ export default function TruncatedDefinitionList({ const [isPopoverOpen, setIsPopoverOpen] = useState(false); const t = useI18nContext(); - return ( - <> - ( + + ); + + const renderButton = () => ( + + ); + + const renderPopover = () => + isPopoverOpen && ( + setIsPopoverOpen(false)} + footer={ + + } > - - - - {isPopoverOpen && ( - setIsPopoverOpen(false)} - footer={ - <> -
- - - } - > - - - - - )} - + + {renderDefinitionList(true)} + + + ); + + const renderContent = () => { + if (process.env.CHAIN_PERMISSIONS) { + return isPopoverOpen ? ( + renderDefinitionList(true) + ) : ( + <> + {renderDefinitionList(false)} + {renderButton()} + + ); + } + return ( + <> + {renderDefinitionList(false)} + {renderButton()} + {renderPopover()} + + ); + }; + + return ( + + {renderContent()} + ); } diff --git a/ui/contexts/metamask-notifications/metamask-notifications.tsx b/ui/contexts/metamask-notifications/metamask-notifications.tsx index ed40b9d7c55a..249fc0b742dc 100644 --- a/ui/contexts/metamask-notifications/metamask-notifications.tsx +++ b/ui/contexts/metamask-notifications/metamask-notifications.tsx @@ -1,11 +1,13 @@ import React, { createContext, useContext, useEffect, useMemo } from 'react'; import { useSelector } from 'react-redux'; +import type { NotificationServicesController } from '@metamask/notification-services-controller'; import { useListNotifications } from '../../hooks/metamask-notifications/useNotifications'; import { selectIsProfileSyncingEnabled } from '../../selectors/metamask-notifications/profile-syncing'; import { selectIsMetamaskNotificationsEnabled } from '../../selectors/metamask-notifications/metamask-notifications'; -import type { Notification } from '../../../app/scripts/controllers/metamask-notifications/types/notification/notification'; import { getUseExternalServices } from '../../selectors'; +type Notification = NotificationServicesController.Types.INotification; + type MetamaskNotificationsContextType = { listNotifications: () => void; notificationsData?: Notification[]; diff --git a/ui/contexts/snaps/snap-interface.tsx b/ui/contexts/snaps/snap-interface.tsx index 0feff5d00966..6c4340f27cd0 100644 --- a/ui/contexts/snaps/snap-interface.tsx +++ b/ui/contexts/snaps/snap-interface.tsx @@ -6,7 +6,6 @@ import { UserInputEventType, } from '@metamask/snaps-sdk'; import { encodeBase64 } from '@metamask/snaps-utils'; - import { Json } from '@metamask/utils'; import { debounce, throttle } from 'lodash'; import React, { @@ -103,7 +102,7 @@ export const SnapInterfaceContextProvider: FunctionComponent< name?: string, value?: unknown, ) => { - handleSnapRequest({ + handleSnapRequest[0]>({ snapId, origin: '', handler: 'onUserInput', @@ -196,7 +195,7 @@ export const SnapInterfaceContextProvider: FunctionComponent< }; const uploadFile = (name: string, file: FileObject | null) => { - handleSnapRequest({ + handleSnapRequest[0]>({ snapId, origin: '', handler: 'onUserInput', @@ -206,8 +205,8 @@ export const SnapInterfaceContextProvider: FunctionComponent< params: { event: { type: UserInputEventType.FileUploadEvent, - name, - file, + ...(name === undefined ? {} : { name }), + ...(file === undefined ? {} : { file }), }, id: interfaceId, context, diff --git a/ui/ducks/bridge/bridge.ts b/ui/ducks/bridge/bridge.ts index 2f6b4aabd775..2f7c2c3482cd 100644 --- a/ui/ducks/bridge/bridge.ts +++ b/ui/ducks/bridge/bridge.ts @@ -1,9 +1,11 @@ import { createSlice } from '@reduxjs/toolkit'; + import { swapsSlice } from '../swaps/swaps'; +import { RPCDefinition } from '../../../shared/constants/network'; // Only states that are not in swaps slice -type BridgeState = { - toChain: string | null; +export type BridgeState = { + toChain: RPCDefinition | null; }; const initialState: BridgeState = { diff --git a/ui/ducks/bridge/selectors.test.ts b/ui/ducks/bridge/selectors.test.ts new file mode 100644 index 000000000000..0962ea4bcde9 --- /dev/null +++ b/ui/ducks/bridge/selectors.test.ts @@ -0,0 +1,262 @@ +import { createBridgeMockStore } from '../../../test/jest/mock-store'; +import { CHAIN_IDS, FEATURED_RPCS } from '../../../shared/constants/network'; +import { ALLOWED_BRIDGE_CHAIN_IDS } from '../../../shared/constants/bridge'; +import { getProviderConfig } from '../metamask/metamask'; +import { mockNetworkState } from '../../../test/stub/networks'; +import { + getAllBridgeableNetworks, + getFromChain, + getFromChains, + getIsBridgeTx, + getToChain, + getToChains, +} from './selectors'; + +describe('Bridge selectors', () => { + describe('getFromChain', () => { + it('returns the fromChain from the state', () => { + const state = { + metamask: { ...mockNetworkState({ chainId: '0x1' }) }, + }; + const result = getFromChain(state as never); + expect(result).toStrictEqual(getProviderConfig(state)); + }); + }); + + describe('getToChain', () => { + it('returns the toChain from the state', () => { + const state = { + bridge: { + toChain: { chainId: '0x1' } as unknown, + }, + }; + + const result = getToChain(state as never); + + expect(result).toStrictEqual({ chainId: '0x1' }); + }); + }); + + describe('getAllBridgeableNetworks', () => { + it('returns list of ALLOWED_BRIDGE_CHAIN_IDS networks', () => { + const state = createBridgeMockStore(); + const result = getAllBridgeableNetworks(state as never); + + expect(result).toHaveLength(9); + expect(result[0]).toStrictEqual( + expect.objectContaining({ chainId: CHAIN_IDS.MAINNET }), + ); + expect(result[1]).toStrictEqual( + expect.objectContaining({ chainId: CHAIN_IDS.LINEA_MAINNET }), + ); + expect(result.slice(2)).toStrictEqual(FEATURED_RPCS); + result.forEach(({ chainId }) => { + expect(ALLOWED_BRIDGE_CHAIN_IDS).toContain(chainId); + }); + ALLOWED_BRIDGE_CHAIN_IDS.forEach((allowedChainId) => { + expect( + result.findIndex(({ chainId }) => chainId === allowedChainId), + ).toBeGreaterThan(-1); + }); + }); + + it('uses config from allNetworks if network is in both FEATURED_RPCS and allNetworks', () => { + const addedFeaturedNetwork = { + ...FEATURED_RPCS[FEATURED_RPCS.length - 1], + id: 'testid', + }; + const state = { + ...createBridgeMockStore(), + metamask: { + networkConfigurations: [addedFeaturedNetwork], + }, + }; + const result = getAllBridgeableNetworks(state as never); + + expect(result).toHaveLength(9); + expect(result[0]).toStrictEqual( + expect.objectContaining({ chainId: CHAIN_IDS.MAINNET }), + ); + expect(result[1]).toStrictEqual( + expect.objectContaining({ chainId: CHAIN_IDS.LINEA_MAINNET }), + ); + expect(result[2]).toStrictEqual({ + ...addedFeaturedNetwork, + removable: true, + blockExplorerUrl: 'https://basescan.org', + }); + expect(result.slice(3)).toStrictEqual(FEATURED_RPCS.slice(0, -1)); + }); + + it('returns network if included in ALLOWED_BRIDGE_CHAIN_IDS', () => { + const addedFeaturedNetwork = { + chainId: '0x11212131241523151', + nickname: 'scroll', + rpcUrl: 'https://a', + ticker: 'ETH', + rpcPrefs: { + blockExplorerUrl: 'https://a', + imageUrl: 'https://a', + }, + }; + const state = { + ...createBridgeMockStore(), + metamask: { + networkConfigurations: [addedFeaturedNetwork], + }, + }; + const result = getAllBridgeableNetworks(state as never); + + expect(result).toHaveLength(9); + expect(result[0]).toStrictEqual( + expect.objectContaining({ chainId: CHAIN_IDS.MAINNET }), + ); + expect(result[1]).toStrictEqual( + expect.objectContaining({ chainId: CHAIN_IDS.LINEA_MAINNET }), + ); + expect(result.slice(2)).toStrictEqual(FEATURED_RPCS); + }); + }); + + describe('getFromChains', () => { + it('excludes selected toChain and disabled chains from options', () => { + const state = createBridgeMockStore( + { + srcNetworkAllowlist: [ + CHAIN_IDS.MAINNET, + CHAIN_IDS.OPTIMISM, + CHAIN_IDS.POLYGON, + ], + }, + { toChain: { chainId: CHAIN_IDS.MAINNET } }, + ); + const result = getFromChains(state as never); + + expect(result).toHaveLength(3); + expect(result[0]).toStrictEqual( + expect.objectContaining({ chainId: CHAIN_IDS.MAINNET }), + ); + expect(result[1]).toStrictEqual( + expect.objectContaining({ chainId: CHAIN_IDS.OPTIMISM }), + ); + expect(result[2]).toStrictEqual( + expect.objectContaining({ chainId: CHAIN_IDS.POLYGON }), + ); + }); + + it('returns empty list when bridgeFeatureFlags are not set', () => { + const state = createBridgeMockStore(); + const result = getFromChains(state as never); + + expect(result).toHaveLength(0); + }); + }); + + describe('getToChains', () => { + it('excludes selected providerConfig and disabled chains from options', () => { + const state = createBridgeMockStore({ + destNetworkAllowlist: [ + CHAIN_IDS.MAINNET, + CHAIN_IDS.OPTIMISM, + CHAIN_IDS.POLYGON, + ], + }); + const result = getToChains(state as never); + + expect(result).toHaveLength(3); + expect(result[0]).toStrictEqual( + expect.objectContaining({ chainId: CHAIN_IDS.MAINNET }), + ); + expect(result[1]).toStrictEqual( + expect.objectContaining({ chainId: CHAIN_IDS.OPTIMISM }), + ); + expect(result[2]).toStrictEqual( + expect.objectContaining({ chainId: CHAIN_IDS.POLYGON }), + ); + }); + + it('returns empty list when bridgeFeatureFlags are not set', () => { + const state = createBridgeMockStore(); + const result = getToChains(state as never); + + expect(result).toHaveLength(0); + }); + }); + + describe('getIsBridgeTx', () => { + it('returns false if bridge is not enabled', () => { + const state = { + metamask: { + ...mockNetworkState({ chainId: '0x1' }), + useExternalServices: true, + bridgeState: { bridgeFeatureFlags: { extensionSupport: false } }, + }, + bridge: { toChain: { chainId: '0x38' } as unknown }, + }; + + const result = getIsBridgeTx(state as never); + + expect(result).toBe(false); + }); + + it('returns false if toChain is null', () => { + const state = { + metamask: { + ...mockNetworkState({ chainId: '0x1' }), + useExternalServices: true, + bridgeState: { bridgeFeatureFlags: { extensionSupport: true } }, + }, + bridge: { toChain: null }, + }; + + const result = getIsBridgeTx(state as never); + + expect(result).toBe(false); + }); + + it('returns false if fromChain and toChain have the same chainId', () => { + const state = { + metamask: { + ...mockNetworkState({ chainId: '0x1' }), + useExternalServices: true, + bridgeState: { bridgeFeatureFlags: { extensionSupport: true } }, + }, + bridge: { toChain: { chainId: '0x1' } }, + }; + + const result = getIsBridgeTx(state as never); + + expect(result).toBe(false); + }); + + it('returns false if useExternalServices is not enabled', () => { + const state = { + metamask: { + ...mockNetworkState({ chainId: '0x1' }), + useExternalServices: false, + bridgeState: { bridgeFeatureFlags: { extensionSupport: true } }, + }, + bridge: { toChain: { chainId: '0x38' } }, + }; + + const result = getIsBridgeTx(state as never); + + expect(result).toBe(false); + }); + + it('returns true if bridge is enabled and fromChain and toChain have different chainIds', () => { + const state = { + metamask: { + ...mockNetworkState({ chainId: '0x1' }), + useExternalServices: true, + bridgeState: { bridgeFeatureFlags: { extensionSupport: true } }, + }, + bridge: { toChain: { chainId: '0x38' } }, + }; + + const result = getIsBridgeTx(state as never); + + expect(result).toBe(true); + }); + }); +}); diff --git a/ui/ducks/bridge/selectors.ts b/ui/ducks/bridge/selectors.ts new file mode 100644 index 000000000000..9a5cdc18151f --- /dev/null +++ b/ui/ducks/bridge/selectors.ts @@ -0,0 +1,69 @@ +import { NetworkState } from '@metamask/network-controller'; +import { uniqBy } from 'lodash'; +import { getAllNetworks, getIsBridgeEnabled } from '../../selectors'; +import { ALLOWED_BRIDGE_CHAIN_IDS } from '../../../shared/constants/bridge'; +import { + BridgeControllerState, + BridgeFeatureFlagsKey, +} from '../../../app/scripts/controllers/bridge/types'; +import { + FEATURED_RPCS, + RPCDefinition, +} from '../../../shared/constants/network'; +import { createDeepEqualSelector } from '../../selectors/util'; +import { getProviderConfig } from '../metamask/metamask'; +import { BridgeState } from './bridge'; + +// TODO add swaps state +type BridgeAppState = { + metamask: NetworkState & { bridgeState: BridgeControllerState } & { + useExternalServices: boolean; + }; + bridge: BridgeState; +}; + +export const getFromChain = (state: BridgeAppState) => getProviderConfig(state); +export const getToChain = (state: BridgeAppState) => state.bridge.toChain; + +export const getAllBridgeableNetworks = createDeepEqualSelector( + (state: BridgeAppState) => + // includes networks user has added + getAllNetworks({ + metamask: { networkConfigurations: state.metamask.networkConfigurations }, + }), + (allNetworks): RPCDefinition[] => { + return uniqBy([...allNetworks, ...FEATURED_RPCS], 'chainId').filter( + ({ chainId }) => ALLOWED_BRIDGE_CHAIN_IDS.includes(chainId), + ); + }, +); +export const getFromChains = createDeepEqualSelector( + getAllBridgeableNetworks, + (state: BridgeAppState) => state.metamask.bridgeState?.bridgeFeatureFlags, + (allBridgeableNetworks, bridgeFeatureFlags): RPCDefinition[] => + allBridgeableNetworks.filter(({ chainId }) => + bridgeFeatureFlags[BridgeFeatureFlagsKey.NETWORK_SRC_ALLOWLIST].includes( + chainId, + ), + ), +); +export const getToChains = createDeepEqualSelector( + getAllBridgeableNetworks, + (state: BridgeAppState) => state.metamask.bridgeState?.bridgeFeatureFlags, + (allBridgeableNetworks, bridgeFeatureFlags): RPCDefinition[] => + allBridgeableNetworks.filter(({ chainId }) => + bridgeFeatureFlags[BridgeFeatureFlagsKey.NETWORK_DEST_ALLOWLIST].includes( + chainId, + ), + ), +); + +export const getIsBridgeTx = createDeepEqualSelector( + getFromChain, + getToChain, + (state: BridgeAppState) => getIsBridgeEnabled(state), + (fromChain, toChain, isBridgeEnabled: boolean) => + isBridgeEnabled && + toChain !== null && + fromChain.chainId !== toChain.chainId, +); diff --git a/ui/ducks/confirm-transaction/confirm-transaction.duck.test.js b/ui/ducks/confirm-transaction/confirm-transaction.duck.test.js index 69f6d20e207c..4420db36df80 100644 --- a/ui/ducks/confirm-transaction/confirm-transaction.duck.test.js +++ b/ui/ducks/confirm-transaction/confirm-transaction.duck.test.js @@ -1,10 +1,10 @@ import configureMockStore from 'redux-mock-store'; import thunk from 'redux-thunk'; import sinon from 'sinon'; -import { NetworkStatus } from '@metamask/network-controller'; -import { NetworkType } from '@metamask/controller-utils'; import { TransactionStatus } from '@metamask/transaction-controller'; +import { CHAIN_IDS } from '../../../shared/constants/network'; +import { mockNetworkState } from '../../../test/stub/networks'; import ConfirmTransactionReducer, * as actions from './confirm-transaction.duck'; const initialState = { @@ -328,9 +328,7 @@ describe('Confirm Transaction Duck', () => { conversionRate: 468.58, }, }, - providerConfig: { - ticker: 'ETH', - }, + ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), }, confirmTransaction: { ethTransactionAmount: '1', @@ -385,17 +383,7 @@ describe('Confirm Transaction Duck', () => { conversionRate: 468.58, }, }, - selectedNetworkClientId: NetworkType.goerli, - networksMetadata: { - [NetworkType.goerli]: { - EIPS: {}, - status: NetworkStatus.Available, - }, - }, - providerConfig: { - chainId: '0x5', - ticker: 'ETH', - }, + ...mockNetworkState({ chainId: CHAIN_IDS.GOERLI }), transactions: [ { history: [], diff --git a/ui/ducks/metamask/metamask.js b/ui/ducks/metamask/metamask.js index cfac6f3cc463..e3d16b6fa78d 100644 --- a/ui/ducks/metamask/metamask.js +++ b/ui/ducks/metamask/metamask.js @@ -17,10 +17,15 @@ import { getAddressBook, getSelectedNetworkClientId, getSelectedInternalAccount, + getNetworkConfigurations, } from '../../selectors'; import * as actionConstants from '../../store/actionConstants'; import { updateTransactionGasFees } from '../../store/actions'; import { setCustomGasLimit, setCustomGasPrice } from '../gas/gas.duck'; +import { + BUILT_IN_INFURA_NETWORKS, + NETWORK_TYPES, +} from '../../../shared/constants/network'; const initialState = { isInitialized: false, @@ -62,9 +67,6 @@ const initialState = { conversionRate: null, }, }, - providerConfig: { - ticker: 'ETH', - }, }; /** @@ -278,10 +280,20 @@ export const getAlertEnabledness = (state) => state.metamask.alertEnabledness; * Get the provider configuration for the current selected network. * * @param {object} state - Redux state object. - * @returns {import('../../../app/scripts/controllers/network/network-controller').NetworkControllerState['providerConfig']} The provider configuration for the current selected network. */ export function getProviderConfig(state) { - return state.metamask.providerConfig; + const networkClientId = getSelectedNetworkClientId(state); + const builtInNetwork = BUILT_IN_INFURA_NETWORKS[networkClientId]; + return builtInNetwork + ? { + ...builtInNetwork, + type: networkClientId, + rpcPrefs: { blockExplorerUrl: builtInNetwork.blockExplorerUrl }, + } + : { + ...getNetworkConfigurations(state)[networkClientId], + type: NETWORK_TYPES.RPC, + }; } export const getUnconnectedAccountAlertEnabledness = (state) => diff --git a/ui/ducks/metamask/metamask.test.js b/ui/ducks/metamask/metamask.test.js index c4f4df187721..d36dac4f87bb 100644 --- a/ui/ducks/metamask/metamask.test.js +++ b/ui/ducks/metamask/metamask.test.js @@ -1,5 +1,3 @@ -import { NetworkType } from '@metamask/controller-utils'; -import { NetworkStatus } from '@metamask/network-controller'; import { EthAccountType } from '@metamask/keyring-api'; import { GasFeeEstimateType, @@ -9,6 +7,8 @@ import { import { GAS_ESTIMATE_TYPES } from '@metamask/gas-fee-controller'; import * as actionConstants from '../../store/actionConstants'; import { ETH_EOA_METHODS } from '../../../shared/constants/eth-methods'; +import { CHAIN_IDS } from '../../../shared/constants/network'; +import { mockNetworkState } from '../../../test/stub/networks'; import reduceMetamask, { getBlockGasLimit, getConversionRate, @@ -122,22 +122,11 @@ describe('MetaMask Reducers', () => { }, useCurrencyRateCheck: true, currencyRates: { - TestETH: { + GoerliETH: { conversionRate: 1200.88200327, }, }, - selectedNetworkClientId: NetworkType.goerli, - networksMetadata: { - [NetworkType.goerli]: { - EIPS: {}, - status: NetworkStatus.Available, - }, - }, - providerConfig: { - type: 'testnet', - chainId: '0x5', - ticker: 'TestETH', - }, + ...mockNetworkState({ chainId: CHAIN_IDS.GOERLI }), accounts: { '0xfdea65c8e26263f6d9a1b5de9555d2931a33b825': { code: '0x', @@ -379,7 +368,7 @@ describe('MetaMask Reducers', () => { describe('getNativeCurrency()', () => { it('should return nativeCurrency when useCurrencyRateCheck is true', () => { - expect(getNativeCurrency(mockState)).toStrictEqual('TestETH'); + expect(getNativeCurrency(mockState)).toStrictEqual('GoerliETH'); }); it('should return the ticker symbol of the selected network when useCurrencyRateCheck is false', () => { @@ -391,7 +380,7 @@ describe('MetaMask Reducers', () => { useCurrencyRateCheck: false, }, }), - ).toStrictEqual('TestETH'); + ).toStrictEqual('GoerliETH'); }); }); @@ -485,15 +474,10 @@ describe('MetaMask Reducers', () => { ...mockState, metamask: { ...mockState.metamask, - selectedNetworkClientId: NetworkType.mainnet, - networksMetadata: { - [NetworkType.mainnet]: { - EIPS: { - 1559: false, - }, - status: 'available', - }, - }, + ...mockNetworkState({ + chainId: CHAIN_IDS.MAINNET, + metadata: { EIPS: { 1559: false } }, + }), }, }), ).toStrictEqual(true); @@ -507,13 +491,10 @@ describe('MetaMask Reducers', () => { ...mockState, metamask: { ...mockState.metamask, - selectedNetworkClientId: NetworkType.mainnet, - networksMetadata: { - [NetworkType.mainnet]: { - EIPS: { 1559: true }, - status: 'available', - }, - }, + ...mockNetworkState({ + chainId: CHAIN_IDS.MAINNET, + metadata: { EIPS: { 1559: true } }, + }), }, }), ).toStrictEqual(false); @@ -526,9 +507,6 @@ describe('MetaMask Reducers', () => { getIsNetworkBusyByChainId( { metamask: { - providerConfig: { - chainId: '0x2', - }, gasFeeEstimatesByChainId: { '0x1': { gasFeeEstimates: { networkCongestion: 0.67 }, @@ -546,9 +524,6 @@ describe('MetaMask Reducers', () => { getIsNetworkBusyByChainId( { metamask: { - providerConfig: { - chainId: '0x2', - }, gasFeeEstimatesByChainId: { '0x1': { gasFeeEstimates: { networkCongestion: 0.66 }, @@ -566,9 +541,6 @@ describe('MetaMask Reducers', () => { getIsNetworkBusyByChainId( { metamask: { - providerConfig: { - chainId: '0x2', - }, gasFeeEstimatesByChainId: { '0x1': { gasFeeEstimates: { networkCongestion: 0.65 }, diff --git a/ui/ducks/send/helpers.test.js b/ui/ducks/send/helpers.test.js index 75a9365e062f..7129ffca8194 100644 --- a/ui/ducks/send/helpers.test.js +++ b/ui/ducks/send/helpers.test.js @@ -61,6 +61,7 @@ jest.mock('../../store/actions', () => ({ })); jest.mock('../metamask/metamask', () => ({ + ...jest.requireActual('../metamask/metamask'), getGasFeeEstimates: jest.fn(), getNativeCurrency: jest.fn(), })); diff --git a/ui/ducks/send/send.js b/ui/ducks/send/send.js index b915907f5b42..672ee0ea4395 100644 --- a/ui/ducks/send/send.js +++ b/ui/ducks/send/send.js @@ -3548,15 +3548,15 @@ export function getSwapsBlockedTokens(state) { } export const getIsSwapAndSendDisabledForNetwork = createSelector( - (state) => state.metamask.providerConfig, + (state) => getCurrentChainId(state), (state) => state[name]?.disabledSwapAndSendNetworks ?? [], - ({ chainId }, disabledSwapAndSendNetworks) => { + (chainId, disabledSwapAndSendNetworks) => { return disabledSwapAndSendNetworks.includes(chainId); }, ); export const getSendAnalyticProperties = createSelector( - (state) => state.metamask.providerConfig, + getProviderConfig, getCurrentDraftTransaction, getBestQuote, ({ chainId, ticker: nativeCurrencySymbol }, draftTransaction, bestQuote) => { diff --git a/ui/ducks/send/send.test.js b/ui/ducks/send/send.test.js index bcb6117cbaeb..70ca9cde62c7 100644 --- a/ui/ducks/send/send.test.js +++ b/ui/ducks/send/send.test.js @@ -2,8 +2,6 @@ import sinon from 'sinon'; import createMockStore from 'redux-mock-store'; import thunk from 'redux-thunk'; import { BigNumber } from '@ethersproject/bignumber'; -import { NetworkType } from '@metamask/controller-utils'; -import { NetworkStatus } from '@metamask/network-controller'; import { EthAccountType } from '@metamask/keyring-api'; import { TransactionEnvelopeType } from '@metamask/transaction-controller'; import { waitFor } from '@testing-library/react'; @@ -35,6 +33,7 @@ import { INITIAL_SEND_STATE_FOR_EXISTING_DRAFT, } from '../../../test/jest/mocks'; import { ETH_EOA_METHODS } from '../../../shared/constants/eth-methods'; +import { mockNetworkState } from '../../../test/stub/networks'; import * as Utils from './swap-and-send-utils'; import sendReducer, { initialState, @@ -1548,15 +1547,7 @@ describe('Send Slice', () => { metamask: { gasEstimateType: GasEstimateTypes.none, gasFeeEstimates: {}, - selectedNetworkClientId: NetworkType.goerli, - networksMetadata: { - [NetworkType.goerli]: { - EIPS: { - 1559: true, - }, - status: NetworkStatus.Available, - }, - }, + ...mockNetworkState({ chainId: CHAIN_IDS.GOERLI }), internalAccounts: { accounts: { 'cf8dace4-9439-4bd4-b3a8-88c821c8fcb3': { @@ -1592,10 +1583,7 @@ describe('Send Slice', () => { [mockAddress1]: { balance: '0x0' }, }, }, - providerConfig: { - chainId: '0x5', - ticker: 'ETH', - }, + ...mockNetworkState({ chainId: CHAIN_IDS.GOERLI }), useTokenDetection: true, tokenList: { '0x514910771af9ca656af840dff83e8264ecf986ca': { @@ -1763,9 +1751,7 @@ describe('Send Slice', () => { selectedAccount: 'mock-id', }, accounts: {}, - providerConfig: { - chainId: '0x1', - }, + ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), }, send: getInitialSendStateWithExistingTxState({ sendAsset: { @@ -1836,9 +1822,7 @@ describe('Send Slice', () => { selectedAccount: 'mock-id', }, accounts: {}, - providerConfig: { - chainId: '0x1', - }, + ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), }, send: getInitialSendStateWithExistingTxState({ sendAsset: { @@ -1903,9 +1887,7 @@ describe('Send Slice', () => { selectedAccount: 'mock-id', }, accounts: {}, - providerConfig: { - chainId: '0x1', - }, + ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), }, send: getInitialSendStateWithExistingTxState({ sendAsset: { @@ -1981,9 +1963,7 @@ describe('Send Slice', () => { selectedAccount: 'mock-id', }, accounts: {}, - providerConfig: { - chainId: '0x1', - }, + ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), }, send: getInitialSendStateWithExistingTxState({ sendAsset: { @@ -2053,9 +2033,7 @@ describe('Send Slice', () => { selectedAccount: 'mock-id', }, accounts: {}, - providerConfig: { - chainId: '0x1', - }, + ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), }, send: getInitialSendStateWithExistingTxState({ sendAsset: { @@ -2116,9 +2094,7 @@ describe('Send Slice', () => { }, selectedAccount: 'cf8dace4-9439-4bd4-b3a8-88c821c8fcb3', }, - providerConfig: { - chainId: CHAIN_IDS.GOERLI, - }, + ...mockNetworkState({ chainId: CHAIN_IDS.GOERLI }), accountsByChainId: { [CHAIN_IDS.GOERLI]: { [mockAddress1]: { balance: '0x0' }, @@ -2168,7 +2144,8 @@ describe('Send Slice', () => { expect(actionResult[0]).toMatchObject({ type: 'send/addHistoryEntry', - payload: 'sendFlow - user set asset of type NATIVE with symbol ETH', + payload: + 'sendFlow - user set asset of type NATIVE with symbol GoerliETH', }); expect(actionResult[1].type).toStrictEqual('send/updateAsset'); expect(actionResult[1].payload).toStrictEqual({ @@ -2363,9 +2340,7 @@ describe('Send Slice', () => { describe('updateRecipientUserInput', () => { const updateRecipientUserInputState = { metamask: { - providerConfig: { - chainId: '', - }, + ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), tokens: [], useTokenDetection: true, tokenList: { @@ -2447,7 +2422,7 @@ describe('Send Slice', () => { 'send/validateRecipientUserInput', ); expect(actionResult[4].payload).toStrictEqual({ - chainId: '', + chainId: '0x1', tokens: [], useTokenDetection: true, isProbablyAnAssetContract: false, @@ -2521,9 +2496,7 @@ describe('Send Slice', () => { accounts: {}, selectedAccount: '', }, - providerConfig: { - chainId: '0x1', - }, + ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), }, send: { account: { @@ -2580,9 +2553,7 @@ describe('Send Slice', () => { }, ], }, - providerConfig: { - chainId: '0x1', - }, + ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), }, send: { account: { @@ -2633,9 +2604,7 @@ describe('Send Slice', () => { selectedAccount: '', }, blockGasLimit: '', - providerConfig: { - chainId: '0x1', - }, + ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), }, send: { account: { @@ -2680,9 +2649,8 @@ describe('Send Slice', () => { const updateRecipientState = { metamask: { addressBook: {}, - providerConfig: { - chainId: '', - }, + ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), + tokens: [], useTokenDetection: true, tokenList: { @@ -2824,9 +2792,7 @@ describe('Send Slice', () => { userInputHexData: '', }, metamask: { - providerConfig: { - chainId: CHAIN_IDS.GOERLI, - }, + ...mockNetworkState({ chainId: CHAIN_IDS.GOERLI }), }, }; @@ -2935,9 +2901,8 @@ describe('Send Slice', () => { amountMode: AMOUNT_MODES.MAX, }, metamask: { - providerConfig: { - chainId: CHAIN_IDS.GOERLI, - }, + ...mockNetworkState({ chainId: CHAIN_IDS.GOERLI }), + internalAccounts: { accounts: { 'mock-id': { @@ -3023,9 +2988,8 @@ describe('Send Slice', () => { it('should pass the correct transaction parameters to addTransactionAndRouteToConfirmationPage', async () => { const tokenTransferTxState = { metamask: { - providerConfig: { - chainId: CHAIN_IDS.GOERLI, - }, + ...mockNetworkState({ chainId: CHAIN_IDS.GOERLI }), + transactions: [ { id: 1, @@ -3156,9 +3120,8 @@ describe('Send Slice', () => { it('should pass the correct transaction parameters to addTransactionAndWaitForPublish', async () => { const swapAndSendState = { metamask: { - providerConfig: { - chainId: CHAIN_IDS.GOERLI, - }, + ...mockNetworkState({ chainId: CHAIN_IDS.GOERLI }), + transactions: [ { id: 1, @@ -3251,9 +3214,8 @@ describe('Send Slice', () => { it('should create actions for updateTransaction rejecting', async () => { const editStageSignTxState = { metamask: { - providerConfig: { - chainId: '0x1', - }, + ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), + transactions: [ { id: 1, @@ -3301,9 +3263,8 @@ describe('Send Slice', () => { metamask: { gasEstimateType: GasEstimateTypes.none, gasFeeEstimates: {}, - providerConfig: { - chainId: CHAIN_IDS.GOERLI, - }, + ...mockNetworkState({ chainId: CHAIN_IDS.GOERLI }), + tokens: [], addressBook: { [CHAIN_IDS.GOERLI]: {}, @@ -3475,9 +3436,8 @@ describe('Send Slice', () => { const editTransactionState = { metamask: { blockGasLimit: '0x3a98', - providerConfig: { - chainId: CHAIN_IDS.GOERLI, - }, + ...mockNetworkState({ chainId: CHAIN_IDS.GOERLI }), + tokens: [], addressBook: { [CHAIN_IDS.GOERLI]: {}, @@ -3705,9 +3665,8 @@ describe('Send Slice', () => { }, selectedAccount: 'cf8dace4-9439-4bd4-b3a8-88c821c8fcb3', }, - providerConfig: { - chainId: CHAIN_IDS.GOERLI, - }, + ...mockNetworkState({ chainId: CHAIN_IDS.GOERLI }), + tokens: [ { address: '0xTokenAddress', @@ -3932,9 +3891,7 @@ describe('Send Slice', () => { }, selectedAccount: 'cf8dace4-9439-4bd4-b3a8-88c821c8fcb3', }, - providerConfig: { - chainId: CHAIN_IDS.GOERLI, - }, + ...mockNetworkState({ chainId: CHAIN_IDS.GOERLI }), tokens: [ { address: '0xTokenAddress', @@ -4169,7 +4126,8 @@ describe('Send Slice', () => { expect( getGasInputMode({ metamask: { - providerConfig: { chainId: CHAIN_IDS.MAINNET }, + ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), + featureFlags: { advancedInlineGas: false }, }, send: initialState, @@ -4182,7 +4140,7 @@ describe('Send Slice', () => { expect( getGasInputMode({ metamask: { - providerConfig: { chainId: '0x539' }, + ...mockNetworkState({ chainId: '0x539' }), featureFlags: { advancedInlineGas: false }, }, send: initialState, @@ -4195,7 +4153,8 @@ describe('Send Slice', () => { expect( getGasInputMode({ metamask: { - providerConfig: { chainId: CHAIN_IDS.MAINNET }, + ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), + featureFlags: { advancedInlineGas: true }, }, send: initialState, @@ -4207,7 +4166,8 @@ describe('Send Slice', () => { expect( getGasInputMode({ metamask: { - providerConfig: { chainId: CHAIN_IDS.MAINNET }, + ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), + featureFlags: { advancedInlineGas: false }, gasEstimateType: GasEstimateTypes.ethGasPrice, }, @@ -4221,7 +4181,8 @@ describe('Send Slice', () => { expect( getGasInputMode({ metamask: { - providerConfig: { chainId: CHAIN_IDS.MAINNET }, + ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), + featureFlags: { advancedInlineGas: false }, gasEstimateType: GasEstimateTypes.ethGasPrice, }, @@ -4235,7 +4196,8 @@ describe('Send Slice', () => { expect( getGasInputMode({ metamask: { - providerConfig: { chainId: CHAIN_IDS.MAINNET }, + ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), + featureFlags: { advancedInlineGas: true }, }, send: { @@ -4378,9 +4340,7 @@ describe('Send Slice', () => { selectedAccount: '', }, addressBook: {}, - providerConfig: { - chainId: '0x5', - }, + ...mockNetworkState({ chainId: CHAIN_IDS.GOERLI }), }, }), ).toBe(undefined); @@ -4396,9 +4356,7 @@ describe('Send Slice', () => { accounts: {}, selectedAccount: '', }, - providerConfig: { - chainId: '0x5', - }, + ...mockNetworkState({ chainId: CHAIN_IDS.GOERLI }), }, }), ).toBe('0xb'); @@ -4415,9 +4373,7 @@ describe('Send Slice', () => { selectedAccount: '', }, addressBook: {}, - providerConfig: { - chainId: '0x5', - }, + ...mockNetworkState({ chainId: CHAIN_IDS.GOERLI }), }, }), ).toBe(''); @@ -4433,9 +4389,7 @@ describe('Send Slice', () => { accounts: {}, selectedAccount: '', }, - providerConfig: { - chainId: '0x5', - }, + ...mockNetworkState({ chainId: CHAIN_IDS.GOERLI }), }, }), ).toBe('0xb'); @@ -4484,9 +4438,7 @@ describe('Send Slice', () => { selectedAccount: '', }, addressBook: {}, - providerConfig: { - chainId: '0x5', - }, + ...mockNetworkState({ chainId: CHAIN_IDS.GOERLI }), }, }), ).toMatchObject( @@ -4618,13 +4570,11 @@ describe('Send Slice', () => { expect( getIsSwapAndSendDisabledForNetwork({ metamask: { - providerConfig: { - chainId: 'disabled network', - }, + ...mockNetworkState({ chainId: '0x123' }), }, send: { ...INITIAL_SEND_STATE_FOR_EXISTING_DRAFT, - disabledSwapAndSendNetworks: ['disabled network'], + disabledSwapAndSendNetworks: ['0x123'], }, }), ).toStrictEqual(true); @@ -4632,13 +4582,11 @@ describe('Send Slice', () => { expect( getIsSwapAndSendDisabledForNetwork({ metamask: { - providerConfig: { - chainId: 'enabled network', - }, + ...mockNetworkState({ chainId: '0x123' }), }, send: { ...INITIAL_SEND_STATE_FOR_EXISTING_DRAFT, - disabledSwapAndSendNetworks: ['disabled network'], + disabledSwapAndSendNetworks: ['0x456'], }, }), ).toStrictEqual(false); diff --git a/ui/ducks/swaps/swaps.js b/ui/ducks/swaps/swaps.js index fab4e3288237..3f251089c2e2 100644 --- a/ui/ducks/swaps/swaps.js +++ b/ui/ducks/swaps/swaps.js @@ -1027,6 +1027,7 @@ export const signAndSendSwapsSmartTransaction = ({ history.push(SMART_TRANSACTION_STATUS_ROUTE); } catch (e) { console.log('signAndSendSwapsSmartTransaction error', e); + dispatch(setSwapsSTXSubmitLoading(false)); const { swaps: { isFeatureFlagLoaded }, } = getState(); diff --git a/ui/ducks/swaps/swaps.test.js b/ui/ducks/swaps/swaps.test.js index 140982c03a85..0bba5e5a68be 100644 --- a/ui/ducks/swaps/swaps.test.js +++ b/ui/ducks/swaps/swaps.test.js @@ -7,6 +7,7 @@ import { setSwapsLiveness, setSwapsFeatureFlags } from '../../store/actions'; import { CHAIN_IDS } from '../../../shared/constants/network'; import { setStorageItem } from '../../../shared/lib/storage-helpers'; import { createMockInternalAccount } from '../../../test/jest/mocks'; +import { mockNetworkState } from '../../../test/stub/networks'; import swapsReducer, * as swaps from './swaps'; const middleware = [thunk]; @@ -20,15 +21,6 @@ jest.mock('../../store/actions.ts', () => ({ }), })); -const providerConfigState = { - chainId: '0x1', - nickname: '', - rpcPrefs: {}, - rpcUrl: '', - ticker: 'ETH', - type: 'mainnet', -}; - describe('Ducks - Swaps', () => { afterEach(() => { nock.cleanAll(); @@ -67,7 +59,7 @@ describe('Ducks - Swaps', () => { return () => ({ metamask: { - providerConfig: { ...providerConfigState }, + ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), from: '0x64a845a5b02460acf8a3d84503b0d68d028b4bb4', internalAccounts: { accounts: { diff --git a/ui/helpers/utils/build-types.js b/ui/helpers/utils/build-types.js index af004297d3ae..e0b003527ccb 100644 --- a/ui/helpers/utils/build-types.js +++ b/ui/helpers/utils/build-types.js @@ -31,6 +31,10 @@ export function isBeta() { return process.env.METAMASK_BUILD_TYPE === 'beta'; } +export function isMMI() { + return process.env.METAMASK_BUILD_TYPE === 'mmi'; +} + // Returns a specific version of an asset based on // the current metamask version (i.e. main, beta, etc.) export function getBuildSpecificAsset(assetName) { diff --git a/ui/helpers/utils/notification.util.ts b/ui/helpers/utils/notification.util.ts index 1efe78adf3bb..b48893ee8e0e 100644 --- a/ui/helpers/utils/notification.util.ts +++ b/ui/helpers/utils/notification.util.ts @@ -1,5 +1,6 @@ import { BigNumber } from 'bignumber.js'; import { JsonRpcProvider } from '@ethersproject/providers'; +import type { NotificationServicesController } from '@metamask/notification-services-controller'; import { TextVariant } from '../constants/design-system'; import { CHAIN_IDS, @@ -17,10 +18,6 @@ import { } from '../../../shared/constants/network'; import { SUPPORTED_NOTIFICATION_BLOCK_EXPLORERS } from '../constants/metamask-notifications/metamask-notifications'; import { calcTokenAmount } from '../../../shared/lib/transactions-controller-utils'; -import type { - OnChainRawNotification, - OnChainRawNotificationsWithNetworkFields, -} from '../../../app/scripts/controllers/metamask-notifications/types/on-chain-notification/on-chain-notification'; import type { BlockExplorerConfig } from '../constants/metamask-notifications/metamask-notifications'; import { hexWEIToDecGWEI, @@ -28,6 +25,11 @@ import { decimalToHex, } from '../../../shared/modules/conversion.utils'; +type OnChainRawNotification = + NotificationServicesController.Types.OnChainRawNotification; +type OnChainRawNotificationsWithNetworkFields = + NotificationServicesController.Types.OnChainRawNotificationsWithNetworkFields; + /** * Type guard to ensure a key is present in an object. * @@ -429,9 +431,11 @@ export const getNetworkFees = async (notification: OnChainRawNotification) => { } try { - const receipt = await provider.getTransactionReceipt(notification.tx_hash); - const transaction = await provider.getTransaction(notification.tx_hash); - const block = await provider.getBlock(notification.block_number); + const [receipt, transaction, block] = await Promise.all([ + provider.getTransactionReceipt(notification.tx_hash), + provider.getTransaction(notification.tx_hash), + provider.getBlock(notification.block_number), + ]); const calculateUsdAmount = (value: string, decimalPlaces?: number) => formatAmount( diff --git a/ui/helpers/utils/permission.js b/ui/helpers/utils/permission.js index 2a0720d815fe..d6a6a3bcd670 100644 --- a/ui/helpers/utils/permission.js +++ b/ui/helpers/utils/permission.js @@ -507,14 +507,12 @@ export const PERMISSION_DESCRIPTIONS = deepFreeze({ weight: PermissionWeight.endowment_keyring, }), ///: END:ONLY_INCLUDE_IF - ///: BEGIN:ONLY_INCLUDE_IF(build-flask) [EndowmentPermissions['endowment:name-lookup']]: ({ t }) => ({ label: t('permission_nameLookup'), description: t('permission_nameLookupDescription'), leftIcon: IconName.Search, weight: PermissionWeight.endowment_nameLookup, }), - ///: END:ONLY_INCLUDE_IF [EndowmentPermissions['endowment:signature-insight']]: ({ t, permissionValue, diff --git a/ui/hooks/bridge/useBridging.test.ts b/ui/hooks/bridge/useBridging.test.ts index 9701ca4732d8..9e2f205c28dc 100644 --- a/ui/hooks/bridge/useBridging.test.ts +++ b/ui/hooks/bridge/useBridging.test.ts @@ -3,6 +3,8 @@ import { MetaMetricsSwapsEventSource } from '../../../shared/constants/metametri import { ETH_SWAPS_TOKEN_OBJECT } from '../../../shared/constants/swaps'; import { renderHookWithProvider } from '../../../test/lib/render-helpers'; import { BRIDGE_API_BASE_URL } from '../../../shared/constants/bridge'; +import { mockNetworkState } from '../../../test/stub/networks'; +import { CHAIN_IDS } from '../../../shared/constants/network'; import useBridging from './useBridging'; const mockHistoryPush = jest.fn(); @@ -78,9 +80,7 @@ describe('useBridging', () => { const { result } = renderUseBridging({ metamask: { useExternalServices: true, - providerConfig: { - chainId: '0x1', - }, + ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), metaMetricsId: MOCK_METAMETRICS_ID, bridgeState: { bridgeFeatureFlags: { @@ -149,9 +149,7 @@ describe('useBridging', () => { const { result } = renderUseBridging({ metamask: { useExternalServices: true, - providerConfig: { - chainId: '0x1', - }, + ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), metaMetricsId: MOCK_METAMETRICS_ID, bridgeState: { bridgeFeatureFlags: { diff --git a/ui/hooks/bridge/useBridging.ts b/ui/hooks/bridge/useBridging.ts index 8c88257df7ee..d11aaeb821a9 100644 --- a/ui/hooks/bridge/useBridging.ts +++ b/ui/hooks/bridge/useBridging.ts @@ -1,4 +1,4 @@ -import { useEffect, useCallback, useContext } from 'react'; +import { useCallback, useContext, useEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { useHistory } from 'react-router-dom'; import { setBridgeFeatureFlags } from '../../ducks/bridge/actions'; @@ -61,6 +61,7 @@ const useBridging = () => { if (!isBridgeChain) { return; } + if (isBridgeSupported) { trackEvent({ event: MetaMetricsEventName.BridgeLinkClicked, diff --git a/ui/hooks/metamask-notifications/useCounter.test.tsx b/ui/hooks/metamask-notifications/useCounter.test.tsx index db7689fe10b6..3c3137bfbe9d 100644 --- a/ui/hooks/metamask-notifications/useCounter.test.tsx +++ b/ui/hooks/metamask-notifications/useCounter.test.tsx @@ -1,14 +1,16 @@ import React, { ReactNode } from 'react'; import { Provider } from 'react-redux'; import { renderHook } from '@testing-library/react-hooks'; +import { NotificationServicesController } from '@metamask/notification-services-controller'; import configureStore from 'redux-mock-store'; import thunk from 'redux-thunk'; -import { TRIGGER_TYPES } from '../../../app/scripts/controllers/metamask-notifications/constants/notification-schema'; import { useUnreadNotificationsCounter, useReadNotificationsCounter, } from './useCounter'; +const { TRIGGER_TYPES } = NotificationServicesController.Constants; + const middlewares = [thunk]; const mockStore = configureStore(middlewares); diff --git a/ui/hooks/metamask-notifications/useNotifications.ts b/ui/hooks/metamask-notifications/useNotifications.ts index 68418a4f3945..62367cdbe310 100644 --- a/ui/hooks/metamask-notifications/useNotifications.ts +++ b/ui/hooks/metamask-notifications/useNotifications.ts @@ -1,11 +1,9 @@ import { useState, useCallback } from 'react'; import { useDispatch } from 'react-redux'; import type { InternalAccount } from '@metamask/keyring-api'; +import type { NotificationServicesController } from '@metamask/notification-services-controller'; import log from 'loglevel'; -import type { - Notification, - MarkAsReadNotificationsParam, -} from '../../../app/scripts/controllers/metamask-notifications/types/notification/notification'; + import { createOnChainTriggers, fetchAndUpdateMetamaskNotifications, @@ -14,6 +12,10 @@ import { disableMetamaskNotifications, } from '../../store/actions'; +type Notification = NotificationServicesController.Types.INotification; +type MarkAsReadNotificationsParam = + NotificationServicesController.Types.MarkAsReadNotificationsParam; + // Define KeyringType interface type KeyringType = { type: string; diff --git a/ui/hooks/ramps/useRamps/useRamps.test.tsx b/ui/hooks/ramps/useRamps/useRamps.test.tsx index 3477827562d4..2f1739fd36e8 100644 --- a/ui/hooks/ramps/useRamps/useRamps.test.tsx +++ b/ui/hooks/ramps/useRamps/useRamps.test.tsx @@ -1,16 +1,17 @@ import React, { FC } from 'react'; import { Provider } from 'react-redux'; import { renderHook } from '@testing-library/react-hooks'; +import { Hex } from '@metamask/utils'; import configureStore from '../../../store/store'; +import { mockNetworkState } from '../../../../test/stub/networks'; +import { CHAIN_IDS } from '../../../../shared/constants/network'; import useRamps, { RampsMetaMaskEntry } from './useRamps'; const mockedMetametricsId = '0xtestMetaMetricsId'; let mockStoreState = { metamask: { - providerConfig: { - chainId: '0x1', - }, + ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), metaMetricsId: mockedMetametricsId, }, }; @@ -37,9 +38,6 @@ describe('useRamps', () => { ...mockStoreState, metamask: { ...mockStoreState.metamask, - providerConfig: { - chainId: mockChainId, - }, }, }; @@ -62,9 +60,6 @@ describe('useRamps', () => { ...mockStoreState, metamask: { ...mockStoreState.metamask, - providerConfig: { - chainId: mockChainId, - }, }, }; @@ -86,14 +81,12 @@ describe('useRamps', () => { // @ts-ignore it.each(['0x1', '0x38', '0xa'])( 'should open the buy crypto URL with the currently connected chain ID', - (mockChainId: string) => { + (mockChainId: Hex) => { mockStoreState = { ...mockStoreState, metamask: { ...mockStoreState.metamask, - providerConfig: { - chainId: mockChainId, - }, + ...mockNetworkState({ chainId: mockChainId }), }, }; diff --git a/ui/hooks/snaps/useInsightSnaps.js b/ui/hooks/snaps/useInsightSnaps.js new file mode 100644 index 000000000000..061e04cb4fc7 --- /dev/null +++ b/ui/hooks/snaps/useInsightSnaps.js @@ -0,0 +1,15 @@ +import { useSelector } from 'react-redux'; +import { SeverityLevel } from '@metamask/snaps-sdk'; +import { getSnapInsights } from '../../selectors'; + +export function useInsightSnaps(id) { + const insight = useSelector((state) => getSnapInsights(state, id)); + + const data = insight ? Object.values(insight) : []; + + const warnings = data.filter( + (result) => result.severity === SeverityLevel.Critical, + ); + + return { data, warnings }; +} diff --git a/ui/hooks/snaps/useSafeWebsite.ts b/ui/hooks/snaps/useSafeWebsite.ts index 34e4333d99b0..0bd40c7f314a 100644 --- a/ui/hooks/snaps/useSafeWebsite.ts +++ b/ui/hooks/snaps/useSafeWebsite.ts @@ -1,5 +1,5 @@ import { useEffect, useState } from 'react'; -import { EthPhishingDetectResult } from '@metamask/phishing-controller'; +import { PhishingDetectorResult } from '@metamask/phishing-controller'; import { getPhishingResult } from '../../store/actions'; /** @@ -15,7 +15,7 @@ export const useSafeWebsite = (website: string) => { const performPhishingCheck = async () => { const phishingResult = (await getPhishingResult( website, - )) as EthPhishingDetectResult; + )) as PhishingDetectorResult; if (!phishingResult.result) { setSafeWebsite(new URL(website)); diff --git a/ui/hooks/snaps/useSignatureInsights.js b/ui/hooks/snaps/useSignatureInsights.js deleted file mode 100644 index c0e297fcb216..000000000000 --- a/ui/hooks/snaps/useSignatureInsights.js +++ /dev/null @@ -1,130 +0,0 @@ -import { useEffect, useState } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; -import { getSignatureOriginCaveat } from '@metamask/snaps-rpc-methods'; -import { SeverityLevel } from '@metamask/snaps-sdk'; -import { - deleteInterface, - handleSnapRequest, - forceUpdateMetamaskState, -} from '../../store/actions'; -import { - getSignatureInsightSnapIds, - getPermissionSubjectsDeepEqual, -} from '../../selectors'; - -const SIGNATURE_INSIGHT_PERMISSION = 'endowment:signature-insight'; - -export function useSignatureInsights({ txData }) { - const dispatch = useDispatch(); - const subjects = useSelector(getPermissionSubjectsDeepEqual); - const snapIds = useSelector(getSignatureInsightSnapIds); - const [loading, setLoading] = useState(true); - const [data, setData] = useState(undefined); - const [warnings, setWarnings] = useState([]); - - useEffect(() => { - let cancelled = false; - - async function fetchInsight() { - setLoading(true); - - const { - msgParams: { from, data: msgData, signatureMethod, origin }, - } = txData; - - /** - * Both eth_signTypedData_v3 and eth_signTypedData_v4 methods - * need to be parsed because their data is stringified. All other - * signature methods do not, so they are ignored. - */ - const shouldParse = - signatureMethod === 'eth_signTypedData_v3' || - signatureMethod === 'eth_signTypedData_v4'; - - const signature = { - from, - data: shouldParse ? JSON.parse(msgData) : msgData, - signatureMethod, - }; - - const newData = await Promise.allSettled( - snapIds.map((snapId) => { - const permission = - subjects[snapId]?.permissions[SIGNATURE_INSIGHT_PERMISSION]; - if (!permission) { - return Promise.reject( - new Error( - 'This Snap does not have the signature insight endowment.', - ), - ); - } - - const hasSignatureOriginCaveat = getSignatureOriginCaveat(permission); - const signatureOrigin = hasSignatureOriginCaveat ? origin : null; - return handleSnapRequest({ - snapId, - origin: '', - handler: 'onSignature', - request: { - jsonrpc: '2.0', - method: '', - params: { signature, signatureOrigin }, - }, - }); - }), - ); - - const reformattedData = newData.map((promise, idx) => { - const snapId = snapIds[idx]; - if (promise.status === 'rejected') { - return { - error: promise.reason, - snapId, - }; - } - return { - snapId, - response: promise.value, - }; - }); - - const insightWarnings = reformattedData.reduce((warningsArr, promise) => { - if (promise.response?.severity === SeverityLevel.Critical) { - const { - snapId, - response: { id }, - } = promise; - warningsArr.push({ snapId, id }); - } - return warningsArr; - }, []); - - if (!cancelled) { - setData(reformattedData); - setWarnings(insightWarnings); - setLoading(false); - if (reformattedData.length > 0) { - forceUpdateMetamaskState(dispatch); - } - } - } - if (Object.keys(txData).length > 0 && txData.msgParams !== undefined) { - fetchInsight(); - } - return () => { - cancelled = true; - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [txData, JSON.stringify(snapIds), subjects]); - - useEffect(() => { - return () => { - data?.map( - ({ response }) => - response?.id && dispatch(deleteInterface(response.id)), - ); - }; - }, [data]); - - return { data, loading, warnings }; -} diff --git a/ui/hooks/snaps/useTransactionInsightSnaps.js b/ui/hooks/snaps/useTransactionInsightSnaps.js deleted file mode 100644 index 970964e4bce0..000000000000 --- a/ui/hooks/snaps/useTransactionInsightSnaps.js +++ /dev/null @@ -1,115 +0,0 @@ -import { useEffect, useState } from 'react'; -import { useSelector, useDispatch } from 'react-redux'; -import { getTransactionOriginCaveat } from '@metamask/snaps-rpc-methods'; -import { SeverityLevel } from '@metamask/snaps-sdk'; -import { - handleSnapRequest, - forceUpdateMetamaskState, -} from '../../store/actions'; -import { getPermissionSubjectsDeepEqual } from '../../selectors'; - -const INSIGHT_PERMISSION = 'endowment:transaction-insight'; - -export function useTransactionInsightSnaps({ - transaction, - chainId, - origin, - insightSnaps, -}) { - const dispatch = useDispatch(); - const subjects = useSelector(getPermissionSubjectsDeepEqual); - - const [loading, setLoading] = useState(true); - const [data, setData] = useState(undefined); - const [hasFetchedInsight, setHasFetchedInsight] = useState(false); - - useEffect(() => { - let cancelled = false; - - async function fetchInsight() { - if (hasFetchedInsight) { - setLoading(false); - return; - } - - setLoading(true); - - const newData = await Promise.allSettled( - insightSnaps.map((snapId) => { - const permission = subjects[snapId]?.permissions[INSIGHT_PERMISSION]; - if (!permission) { - return Promise.reject( - new Error( - 'This Snap does not have the transaction insight endowment.', - ), - ); - } - - const hasTransactionOriginCaveat = - getTransactionOriginCaveat(permission); - const transactionOrigin = hasTransactionOriginCaveat ? origin : null; - return handleSnapRequest({ - snapId, - origin: '', - handler: 'onTransaction', - request: { - jsonrpc: '2.0', - method: '', - params: { transaction, chainId, transactionOrigin }, - }, - }); - }), - ); - - const reformattedData = newData.map((promise, idx) => { - const snapId = insightSnaps[idx]; - if (promise.status === 'rejected') { - return { - error: promise.reason, - snapId, - }; - } - return { - snapId, - response: promise.value, - }; - }); - - if (!cancelled) { - setData(reformattedData); - setLoading(false); - setHasFetchedInsight(true); - if (reformattedData.length > 0) { - forceUpdateMetamaskState(dispatch); - } - } - } - if (transaction && Object.keys(transaction).length > 0) { - fetchInsight(); - } - return () => { - cancelled = true; - }; - }, [ - transaction, - chainId, - origin, - subjects, - // TODO: Figure out how to improve this - JSON.stringify(insightSnaps), - hasFetchedInsight, - ]); - - const warnings = data?.reduce((warningsArr, promise) => { - if (promise.response?.severity === SeverityLevel.Critical) { - const { - snapId, - response: { id }, - } = promise; - warningsArr.push({ snapId, id }); - } - return warningsArr; - }, []); - - return { data, loading, warnings }; -} diff --git a/ui/hooks/useAccountTotalFiatBalance.test.js b/ui/hooks/useAccountTotalFiatBalance.test.js index 56b029d87f91..c883eb37cc1e 100644 --- a/ui/hooks/useAccountTotalFiatBalance.test.js +++ b/ui/hooks/useAccountTotalFiatBalance.test.js @@ -7,6 +7,7 @@ import configureStore from '../store/store'; import { CHAIN_IDS } from '../../shared/constants/network'; import { createMockInternalAccount } from '../../test/jest/mocks'; +import { mockNetworkState } from '../../test/stub/networks'; import { useAccountTotalFiatBalance } from './useAccountTotalFiatBalance'; const mockAccount = createMockInternalAccount({ @@ -75,10 +76,8 @@ const renderUseAccountTotalFiatBalance = (address) => { }, }, }, - providerConfig: { - chainId: CHAIN_IDS.MAINNET, - ticker: 'ETH', - }, + ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), + detectedTokens: { '0x1': { '0x0836f5ed6b62baf60706fe3adc0ff0fd1df833da': [ diff --git a/ui/hooks/useAddressDetails.test.js b/ui/hooks/useAddressDetails.test.js index 2a4e03eee343..4a04cb8f50ea 100644 --- a/ui/hooks/useAddressDetails.test.js +++ b/ui/hooks/useAddressDetails.test.js @@ -5,15 +5,15 @@ import { EthAccountType } from '@metamask/keyring-api'; import configureStore from '../store/store'; import { ETH_EOA_METHODS } from '../../shared/constants/eth-methods'; +import { CHAIN_IDS } from '../../shared/constants/network'; +import { mockNetworkState } from '../../test/stub/networks'; import useAddressDetails from './useAddressDetails'; const renderUseAddressDetails = (toAddress, stateVariables = {}) => { const mockState = { metamask: { - providerConfig: { - type: 'test', - chainId: '0x5', - }, + ...mockNetworkState({ chainId: CHAIN_IDS.GOERLI }), + tokenList: {}, internalAccounts: { accounts: { diff --git a/ui/hooks/useAssetDetails.test.js b/ui/hooks/useAssetDetails.test.js index f14725bdd4b8..b8701672b0b4 100644 --- a/ui/hooks/useAssetDetails.test.js +++ b/ui/hooks/useAssetDetails.test.js @@ -8,6 +8,8 @@ import configureStore from '../store/store'; import * as Actions from '../store/actions'; import { TokenStandard } from '../../shared/constants/transaction'; import { ETH_EOA_METHODS } from '../../shared/constants/eth-methods'; +import { CHAIN_IDS } from '../../shared/constants/network'; +import { mockNetworkState } from '../../test/stub/networks'; const renderUseAssetDetails = ({ tokenAddress, @@ -16,10 +18,7 @@ const renderUseAssetDetails = ({ }) => { const mockState = { metamask: { - providerConfig: { - type: 'test', - chainId: '0x5', - }, + ...mockNetworkState({ chainId: CHAIN_IDS.GOERLI }), tokenList: {}, tokens: [], internalAccounts: { diff --git a/ui/hooks/useCurrencyDisplay.test.js b/ui/hooks/useCurrencyDisplay.test.js index b05cda9657cc..4198a71843f9 100644 --- a/ui/hooks/useCurrencyDisplay.test.js +++ b/ui/hooks/useCurrencyDisplay.test.js @@ -129,7 +129,6 @@ const renderUseCurrencyDisplay = (value, restProps) => { ...mockState.metamask, completedOnboarding: true, currentCurrency: 'usd', - providerConfig: { chainId: '0x1', ticker: 'ETH' }, currencyRates: { ETH: { conversionRate: 280.45 } }, }, }; diff --git a/ui/hooks/useMultichainAccountTotalFiatBalance.test.tsx b/ui/hooks/useMultichainAccountTotalFiatBalance.test.tsx index 677453104e15..888d70e1d62c 100644 --- a/ui/hooks/useMultichainAccountTotalFiatBalance.test.tsx +++ b/ui/hooks/useMultichainAccountTotalFiatBalance.test.tsx @@ -6,6 +6,7 @@ import mockState from '../../test/data/mock-state.json'; import configureStore from '../store/store'; import { createMockInternalAccount } from '../../test/jest/mocks'; import { CHAIN_IDS } from '../../shared/constants/network'; +import { mockNetworkState } from '../../test/stub/networks'; import { useMultichainAccountTotalFiatBalance } from './useMultichainAccountTotalFiatBalance'; const mockTokenBalances = [ @@ -101,10 +102,8 @@ const renderUseMultichainAccountTotalFiatBalance = ( }, }, }, - providerConfig: { - chainId: CHAIN_IDS.MAINNET, - ticker: 'ETH', - }, + ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), + detectedTokens: { '0x1': { '0x0836f5ed6b62baf60706fe3adc0ff0fd1df833da': [ diff --git a/ui/hooks/useMultichainSelector.test.ts b/ui/hooks/useMultichainSelector.test.ts index 0f66030ae100..35ed5cf7b155 100644 --- a/ui/hooks/useMultichainSelector.test.ts +++ b/ui/hooks/useMultichainSelector.test.ts @@ -3,14 +3,16 @@ import { createMockInternalAccount } from '../../test/jest/mocks'; import { renderHookWithProvider } from '../../test/lib/render-helpers'; import { getSelectedNetworkClientId } from '../selectors'; import { MultichainState, getMultichainIsEvm } from '../selectors/multichain'; +import { CHAIN_IDS } from '../../shared/constants/network'; +import { mockNetworkState } from '../../test/stub/networks'; import { useMultichainSelector } from './useMultichainSelector'; const mockAccount = createMockInternalAccount(); -const mockNetworkId = '0x1'; +const mockNetworkId = 'network-client-id'; const mockState = { metamask: { - selectedNetworkClientId: mockNetworkId, + ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET, id: mockNetworkId }), completedOnboarding: true, internalAccounts: { accounts: { diff --git a/ui/hooks/useTransactionDisplayData.test.js b/ui/hooks/useTransactionDisplayData.test.js index ee3da7383d45..55cb05c256b6 100644 --- a/ui/hooks/useTransactionDisplayData.test.js +++ b/ui/hooks/useTransactionDisplayData.test.js @@ -13,10 +13,11 @@ import configureStore from '../store/store'; import transactions from '../../test/data/transaction-data.json'; import messages from '../../app/_locales/en/messages.json'; import { ASSET_ROUTE, DEFAULT_ROUTE } from '../helpers/constants/routes'; -import { CHAIN_IDS } from '../../shared/constants/network'; import { TransactionGroupCategory } from '../../shared/constants/transaction'; import { formatDateWithYearContext } from '../helpers/utils/util'; import { getMessage } from '../helpers/utils/i18n-helper'; +import { mockNetworkState } from '../../test/stub/networks'; +import { CHAIN_IDS } from '../../shared/constants/network'; import * as i18nhooks from './useI18nContext'; import * as useTokenFiatAmountHooks from './useTokenFiatAmount'; import { useTransactionDisplayData } from './useTransactionDisplayData'; @@ -204,7 +205,7 @@ const renderHookWithRouter = (cb, tokenAddress) => { metamask: { ...mockState.metamask, completeOnboarding: true, - providerConfig: { chainId: CHAIN_IDS.MAINNET, ticker: 'ETH' }, + ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), currentCurrency: 'ETH', useCurrencyRateCheck: false, // to force getShouldShowFiat to return false preferences: { diff --git a/ui/hooks/useTransactionInsights.js b/ui/hooks/useTransactionInsights.js index b6d1a851aec1..997f74061194 100644 --- a/ui/hooks/useTransactionInsights.js +++ b/ui/hooks/useTransactionInsights.js @@ -2,7 +2,6 @@ import React, { useEffect, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { TransactionType } from '@metamask/transaction-controller'; -import { stripHexPrefix } from '../../shared/modules/hexstring-utils'; import { Tab } from '../components/ui/tabs'; import DropdownTab from '../components/ui/tabs/snaps/dropdown-tab'; import { SnapInsight } from '../components/app/snaps/snap-insight/snap-insight'; @@ -13,7 +12,7 @@ import { } from '../selectors'; import { deleteInterface } from '../store/actions'; import { getSnapName } from '../helpers/utils/util'; -import { useTransactionInsightSnaps } from './snaps/useTransactionInsightSnaps'; +import { useInsightSnaps } from './snaps/useInsightSnaps'; const isAllowedTransactionTypes = (transactionType) => transactionType === TransactionType.contractInteraction || @@ -27,8 +26,6 @@ const isAllowedTransactionTypes = (transactionType) => // Thus it is not possible to use React Component here const useTransactionInsights = ({ txData }) => { const dispatch = useDispatch(); - const { txParams, chainId, origin } = txData; - const caip2ChainId = `eip155:${stripHexPrefix(chainId)}`; const insightSnaps = useSelector(getInsightSnaps); const insightSnapIds = useSelector(getInsightSnapIds); const snapsMetadata = useSelector(getSnapsMetadata); @@ -39,15 +36,7 @@ const useTransactionInsights = ({ txData }) => { insightSnaps[0]?.id, ); - const insightHookParams = { - transaction: txParams, - chainId: caip2ChainId, - origin, - insightSnaps: insightSnapIds, - }; - - const { data, loading, warnings } = - useTransactionInsightSnaps(insightHookParams); + const { data, warnings } = useInsightSnaps(txData.id); useEffect(() => { if (insightSnapIds.length > 0 && !selectedInsightSnapId) { @@ -82,11 +71,7 @@ const useTransactionInsights = ({ txData }) => { className="confirm-page-container-content__tab" name={snapsNameGetter(selectedSnap.id)} > - + ); } else if (insightSnaps.length > 1) { @@ -99,7 +84,7 @@ const useTransactionInsights = ({ txData }) => { }); const selectedSnapData = data?.find( - (promise) => promise?.snapId === selectedInsightSnapId, + (result) => result?.snapId === selectedInsightSnapId, ); insightComponent = ( @@ -109,11 +94,7 @@ const useTransactionInsights = ({ txData }) => { selectedOption={selectedInsightSnapId} onChange={(snapId) => setSelectedInsightSnapId(snapId)} > - + ); } diff --git a/ui/hooks/useUserPreferencedCurrency.test.js b/ui/hooks/useUserPreferencedCurrency.test.js index ae9881a92c41..c4818d9e980d 100644 --- a/ui/hooks/useUserPreferencedCurrency.test.js +++ b/ui/hooks/useUserPreferencedCurrency.test.js @@ -4,6 +4,8 @@ import { Provider } from 'react-redux'; import mockState from '../../test/data/mock-state.json'; import configureStore from '../store/store'; +import { mockNetworkState } from '../../test/stub/networks'; +import { CHAIN_IDS } from '../../shared/constants/network'; import { useUserPreferencedCurrency } from './useUserPreferencedCurrency'; const tests = [ @@ -120,11 +122,11 @@ const renderUseUserPreferencedCurrency = (state, value, restProps) => { metamask: { ...mockState.metamask, completedOnboarding: true, - currentCurrency: state.currentCurrency, - providerConfig: { - chainId: state.showFiat ? '0x1' : '0x539', + ...mockNetworkState({ + chainId: state.showFiat ? CHAIN_IDS.MAINNET : CHAIN_IDS.LOCALHOST, ticker: state?.nativeCurrency, - }, + }), + currentCurrency: state.currentCurrency, currencyRates: { ETH: { conversionRate: 280.45 } }, preferences: { useNativeCurrencyAsPrimaryCurrency: diff --git a/ui/index.js b/ui/index.js index 741fce6dc283..7eb8a46709a9 100644 --- a/ui/index.js +++ b/ui/index.js @@ -24,6 +24,7 @@ import { getNetworkToAutomaticallySwitchTo, getSwitchedNetworkDetails, getUseRequestQueue, + getCurrentChainId, } from './selectors'; import { ALERT_STATE } from './ducks/alerts'; import { @@ -170,7 +171,7 @@ export async function setupInitialStore( metamaskState.unapprovedEncryptionPublicKeyMsgs, metamaskState.unapprovedTypedMessages, metamaskState.networkId, - metamaskState.providerConfig.chainId, + getCurrentChainId({ metamask: metamaskState }), ); const numberOfUnapprovedTx = unapprovedTxsAll.length; if (numberOfUnapprovedTx > 0) { @@ -198,9 +199,6 @@ async function startApp(metamaskState, backgroundConnection, opts) { updateCurrentLocale: (code) => { store.dispatch(actions.updateCurrentLocale(code)); }, - setProviderType: (type) => { - store.dispatch(actions.setProviderType(type)); - }, setFeatureFlag: (key, value) => { store.dispatch(actions.setFeatureFlag(key, value)); }, diff --git a/ui/pages/asset/components/__snapshots__/asset-page.test.tsx.snap b/ui/pages/asset/components/__snapshots__/asset-page.test.tsx.snap index 8e1446dc6461..10c36fe31236 100644 --- a/ui/pages/asset/components/__snapshots__/asset-page.test.tsx.snap +++ b/ui/pages/asset/components/__snapshots__/asset-page.test.tsx.snap @@ -87,76 +87,76 @@ exports[`AssetPage should render a native asset 1`] = `

@@ -174,14 +174,14 @@ exports[`AssetPage should render a native asset 1`] = ` data-testid="multichain-token-list-item" >
T
@@ -189,10 +189,10 @@ exports[`AssetPage should render a native asset 1`] = ` class="mm-box mm-badge-wrapper__badge-container mm-badge-wrapper__badge-container--circular-top-right" >
network logo @@ -458,14 +458,14 @@ exports[`AssetPage should render an ERC20 asset without prices 1`] = ` data-testid="multichain-token-list-item" >
T
@@ -473,10 +473,10 @@ exports[`AssetPage should render an ERC20 asset without prices 1`] = ` class="mm-box mm-badge-wrapper__badge-container mm-badge-wrapper__badge-container--circular-top-right" >
network logo @@ -935,14 +935,14 @@ exports[`AssetPage should render an ERC20 token with prices 1`] = ` data-testid="multichain-token-list-item" >
T
@@ -950,10 +950,10 @@ exports[`AssetPage should render an ERC20 token with prices 1`] = ` class="mm-box mm-badge-wrapper__badge-container mm-badge-wrapper__badge-container--circular-top-right" >
network logo diff --git a/ui/pages/asset/components/asset-page.test.tsx b/ui/pages/asset/components/asset-page.test.tsx index a067bf8d6cb0..bf616d0aaac9 100644 --- a/ui/pages/asset/components/asset-page.test.tsx +++ b/ui/pages/asset/components/asset-page.test.tsx @@ -10,6 +10,7 @@ import { KeyringType } from '../../../../shared/constants/keyring'; import { AssetType } from '../../../../shared/constants/transaction'; import { ETH_EOA_METHODS } from '../../../../shared/constants/eth-methods'; import { setBackgroundConnection } from '../../../store/background-connection'; +import { mockNetworkState } from '../../../../test/stub/networks'; import AssetPage from './asset-page'; // Mock the price chart @@ -41,18 +42,7 @@ describe('AssetPage', () => { tokenList: {}, currentCurrency: 'usd', accounts: {}, - networkConfigurations: { - test: { - id: 'test', - chainId: CHAIN_IDS.MAINNET, - }, - }, - providerConfig: { - id: '1', - type: 'test', - ticker: 'ETH', - chainId: CHAIN_IDS.MAINNET, - }, + ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), currencyRates: { ETH: { conversionRate: 123, @@ -194,12 +184,14 @@ describe('AssetPage', () => { }); it('should disable the buy button on unsupported chains', () => { - const chainId = CHAIN_IDS.SEPOLIA; const { queryByTestId } = renderWithProvider( , configureMockStore([thunk])({ ...mockStore, - metamask: { ...mockStore.metamask, providerConfig: { chainId } }, + metamask: { + ...mockStore.metamask, + ...mockNetworkState({ chainId: CHAIN_IDS.SEPOLIA }), + }, }), ); const buyButton = queryByTestId('token-overview-buy'); @@ -212,7 +204,7 @@ describe('AssetPage', () => { ...mockStore, metamask: { ...mockStore.metamask, - providerConfig: { type: 'test', chainId: CHAIN_IDS.POLYGON }, + ...mockNetworkState({ chainId: CHAIN_IDS.POLYGON }), }, }; const mockedStore = configureMockStore([thunk])( @@ -257,12 +249,14 @@ describe('AssetPage', () => { }); it('should not show the Bridge button if chain id is not supported', async () => { - const chainId = CHAIN_IDS.SEPOLIA; const { queryByTestId } = renderWithProvider( , configureMockStore([thunk])({ ...mockStore, - metamask: { ...mockStore.metamask, providerConfig: { chainId } }, + metamask: { + ...mockStore.metamask, + ...mockNetworkState({ chainId: CHAIN_IDS.SEPOLIA }), + }, }), ); const bridgeButton = queryByTestId('token-overview-bridge'); diff --git a/ui/pages/bridge/bridge.util.test.ts b/ui/pages/bridge/bridge.util.test.ts index d693f92dc956..da2637b1f5f4 100644 --- a/ui/pages/bridge/bridge.util.test.ts +++ b/ui/pages/bridge/bridge.util.test.ts @@ -1,59 +1,82 @@ import fetchWithCache from '../../../shared/lib/fetch-with-cache'; +import { CHAIN_IDS } from '../../../shared/constants/network'; import { fetchBridgeFeatureFlags } from './bridge.util'; jest.mock('../../../shared/lib/fetch-with-cache'); describe('Bridge utils', () => { - it('should fetch bridge feature flags successfully', async () => { - const mockResponse = { - 'extension-support': true, - }; - - (fetchWithCache as jest.Mock).mockResolvedValue(mockResponse); - - const result = await fetchBridgeFeatureFlags(); - - expect(fetchWithCache).toHaveBeenCalledWith({ - url: 'https://bridge.api.cx.metamask.io/getAllFeatureFlags', - fetchOptions: { - method: 'GET', - headers: { 'X-Client-Id': 'extension' }, - }, - cacheOptions: { cacheRefreshTime: 600000 }, - functionName: 'fetchBridgeFeatureFlags', + describe('fetchBridgeFeatureFlags', () => { + it('should fetch bridge feature flags successfully', async () => { + const mockResponse = { + 'extension-support': true, + 'src-network-allowlist': [1, 10, 59144, 120], + 'dest-network-allowlist': [1, 137, 59144, 11111], + }; + + (fetchWithCache as jest.Mock).mockResolvedValue(mockResponse); + + const result = await fetchBridgeFeatureFlags(); + + expect(fetchWithCache).toHaveBeenCalledWith({ + url: 'https://bridge.api.cx.metamask.io/getAllFeatureFlags', + fetchOptions: { + method: 'GET', + headers: { 'X-Client-Id': 'extension' }, + }, + cacheOptions: { cacheRefreshTime: 600000 }, + functionName: 'fetchBridgeFeatureFlags', + }); + + expect(result).toStrictEqual({ + extensionSupport: true, + srcNetworkAllowlist: [ + CHAIN_IDS.MAINNET, + CHAIN_IDS.OPTIMISM, + CHAIN_IDS.LINEA_MAINNET, + '0x78', + ], + destNetworkAllowlist: [ + CHAIN_IDS.MAINNET, + CHAIN_IDS.POLYGON, + CHAIN_IDS.LINEA_MAINNET, + '0x2b67', + ], + }); }); - expect(result).toEqual({ extensionSupport: true }); - }); + it('should use fallback bridge feature flags if response is unexpected', async () => { + const mockResponse = { + flag1: true, + flag2: false, + }; - it('should use fallback bridge feature flags if response is unexpected', async () => { - const mockResponse = { - flag1: true, - flag2: false, - }; + (fetchWithCache as jest.Mock).mockResolvedValue(mockResponse); - (fetchWithCache as jest.Mock).mockResolvedValue(mockResponse); + const result = await fetchBridgeFeatureFlags(); - const result = await fetchBridgeFeatureFlags(); + expect(fetchWithCache).toHaveBeenCalledWith({ + url: 'https://bridge.api.cx.metamask.io/getAllFeatureFlags', + fetchOptions: { + method: 'GET', + headers: { 'X-Client-Id': 'extension' }, + }, + cacheOptions: { cacheRefreshTime: 600000 }, + functionName: 'fetchBridgeFeatureFlags', + }); - expect(fetchWithCache).toHaveBeenCalledWith({ - url: 'https://bridge.api.cx.metamask.io/getAllFeatureFlags', - fetchOptions: { - method: 'GET', - headers: { 'X-Client-Id': 'extension' }, - }, - cacheOptions: { cacheRefreshTime: 600000 }, - functionName: 'fetchBridgeFeatureFlags', + expect(result).toStrictEqual({ + extensionSupport: false, + srcNetworkAllowlist: [], + destNetworkAllowlist: [], + }); }); - expect(result).toEqual({ extensionSupport: false }); - }); - - it('should handle fetch error', async () => { - const mockError = new Error('Failed to fetch'); + it('should handle fetch error', async () => { + const mockError = new Error('Failed to fetch'); - (fetchWithCache as jest.Mock).mockRejectedValue(mockError); + (fetchWithCache as jest.Mock).mockRejectedValue(mockError); - await expect(fetchBridgeFeatureFlags()).rejects.toThrowError(mockError); + await expect(fetchBridgeFeatureFlags()).rejects.toThrowError(mockError); + }); }); }); diff --git a/ui/pages/bridge/bridge.util.ts b/ui/pages/bridge/bridge.util.ts index 0c3e54d1dc79..e7506ede2066 100644 --- a/ui/pages/bridge/bridge.util.ts +++ b/ui/pages/bridge/bridge.util.ts @@ -1,3 +1,4 @@ +import { add0x } from '@metamask/utils'; import { BridgeFeatureFlagsKey, BridgeFeatureFlags, @@ -9,6 +10,7 @@ import { import { MINUTE } from '../../../shared/constants/time'; import fetchWithCache from '../../../shared/lib/fetch-with-cache'; import { validateData } from '../../../shared/lib/swaps-utils'; +import { decimalToHex } from '../../../shared/modules/conversion.utils'; const CLIENT_ID_HEADER = { 'X-Client-Id': BRIDGE_CLIENT_ID }; const CACHE_REFRESH_TEN_MINUTES = 10 * MINUTE; @@ -16,10 +18,14 @@ const CACHE_REFRESH_TEN_MINUTES = 10 * MINUTE; // Types copied from Metabridge API enum BridgeFlag { EXTENSION_SUPPORT = 'extension-support', + NETWORK_SRC_ALLOWLIST = 'src-network-allowlist', + NETWORK_DEST_ALLOWLIST = 'dest-network-allowlist', } export type FeatureFlagResponse = { [BridgeFlag.EXTENSION_SUPPORT]: boolean; + [BridgeFlag.NETWORK_SRC_ALLOWLIST]: number[]; + [BridgeFlag.NETWORK_DEST_ALLOWLIST]: number[]; }; // End of copied types @@ -54,6 +60,22 @@ export async function fetchBridgeFeatureFlags(): Promise { type: 'boolean', validator: (v) => typeof v === 'boolean', }, + { + property: BridgeFlag.NETWORK_SRC_ALLOWLIST, + type: 'object', + validator: (v): v is number[] => + Object.values(v as { [s: string]: unknown }).every( + (i) => typeof i === 'number', + ), + }, + { + property: BridgeFlag.NETWORK_DEST_ALLOWLIST, + type: 'object', + validator: (v): v is number[] => + Object.values(v as { [s: string]: unknown }).every( + (i) => typeof i === 'number', + ), + }, ], rawFeatureFlags, url, @@ -62,11 +84,21 @@ export async function fetchBridgeFeatureFlags(): Promise { return { [BridgeFeatureFlagsKey.EXTENSION_SUPPORT]: rawFeatureFlags[BridgeFlag.EXTENSION_SUPPORT], + [BridgeFeatureFlagsKey.NETWORK_SRC_ALLOWLIST]: rawFeatureFlags[ + BridgeFlag.NETWORK_SRC_ALLOWLIST + ].map((chainIdDec) => add0x(decimalToHex(chainIdDec))), + [BridgeFeatureFlagsKey.NETWORK_DEST_ALLOWLIST]: rawFeatureFlags[ + BridgeFlag.NETWORK_DEST_ALLOWLIST + ].map((chainIdDec) => add0x(decimalToHex(chainIdDec))), }; } return { // TODO set default to true once bridging is live [BridgeFeatureFlagsKey.EXTENSION_SUPPORT]: false, + // TODO set default to ALLOWED_BRIDGE_CHAIN_IDS once bridging is live + [BridgeFeatureFlagsKey.NETWORK_SRC_ALLOWLIST]: [], + // TODO set default to ALLOWED_BRIDGE_CHAIN_IDS once bridging is live + [BridgeFeatureFlagsKey.NETWORK_DEST_ALLOWLIST]: [], }; } diff --git a/ui/pages/confirm-add-suggested-nft/__snapshots__/confirm-add-suggested-nft.test.js.snap b/ui/pages/confirm-add-suggested-nft/__snapshots__/confirm-add-suggested-nft.test.js.snap index 908df23e73fa..565bd8d64b3f 100644 --- a/ui/pages/confirm-add-suggested-nft/__snapshots__/confirm-add-suggested-nft.test.js.snap +++ b/ui/pages/confirm-add-suggested-nft/__snapshots__/confirm-add-suggested-nft.test.js.snap @@ -181,12 +181,14 @@ exports[`ConfirmAddSuggestedNFT Component should match snapshot 1`] = `
-

CryptoKitty -

+

@@ -394,12 +396,14 @@ exports[`ConfirmAddSuggestedNFT Component should match snapshot 1`] = `

-

CryptoKitty -

+

diff --git a/ui/pages/confirm-add-suggested-nft/confirm-add-suggested-nft.test.js b/ui/pages/confirm-add-suggested-nft/confirm-add-suggested-nft.test.js index d92c7c22b1c2..177bffb7d3bd 100644 --- a/ui/pages/confirm-add-suggested-nft/confirm-add-suggested-nft.test.js +++ b/ui/pages/confirm-add-suggested-nft/confirm-add-suggested-nft.test.js @@ -9,6 +9,8 @@ import { import configureStore from '../../store/store'; import mockState from '../../../test/data/mock-state.json'; import { renderWithProvider } from '../../../test/jest/rendering'; +import { CHAIN_IDS } from '../../../shared/constants/network'; +import { mockNetworkState } from '../../../test/stub/networks'; import ConfirmAddSuggestedNFT from '.'; const PENDING_NFT_APPROVALS = { @@ -71,7 +73,7 @@ const renderComponent = (pendingNfts = {}) => { metamask: { ...mockState.metamask, pendingApprovals: pendingNfts, - providerConfig: { chainId: '0x1' }, + ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), }, history: { mostRecentOverviewPage: '/', diff --git a/ui/pages/confirm-add-suggested-token/confirm-add-suggested-token.test.js b/ui/pages/confirm-add-suggested-token/confirm-add-suggested-token.test.js index 295625196ded..1fc93886f752 100644 --- a/ui/pages/confirm-add-suggested-token/confirm-add-suggested-token.test.js +++ b/ui/pages/confirm-add-suggested-token/confirm-add-suggested-token.test.js @@ -10,6 +10,8 @@ import { import configureStore from '../../store/store'; import { renderWithProvider } from '../../../test/jest/rendering'; import { ETH_EOA_METHODS } from '../../../shared/constants/eth-methods'; +import { mockNetworkState } from '../../../test/stub/networks'; +import { CHAIN_IDS } from '../../../shared/constants/network'; import ConfirmAddSuggestedToken from '.'; const PENDING_APPROVALS = { @@ -69,7 +71,8 @@ const renderComponent = (tokens = []) => { metamask: { pendingApprovals: PENDING_APPROVALS, tokens, - providerConfig: { chainId: '0x1' }, + ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), + internalAccounts: { accounts: { 'cf8dace4-9439-4bd4-b3a8-88c821c8fcb3': { diff --git a/ui/pages/confirmations/components/advanced-gas-controls/advanced-gas-controls.test.js b/ui/pages/confirmations/components/advanced-gas-controls/advanced-gas-controls.test.js index 4320d592bb26..9e8aff0e7d4c 100644 --- a/ui/pages/confirmations/components/advanced-gas-controls/advanced-gas-controls.test.js +++ b/ui/pages/confirmations/components/advanced-gas-controls/advanced-gas-controls.test.js @@ -1,15 +1,11 @@ import React from 'react'; -import configureMockStore from 'redux-mock-store'; import { renderWithProvider } from '../../../../../test/jest/rendering'; import AdvancedGasControls from './advanced-gas-controls.component'; const renderComponent = (props) => { - const store = configureMockStore([])({ - metamask: { providerConfig: {} }, - }); - return renderWithProvider(, store); + return renderWithProvider(); }; describe('AdvancedGasControls Component', () => { diff --git a/ui/pages/confirmations/components/advanced-gas-fee-popover/advanced-gas-fee-defaults/advanced-gas-fee-defaults.test.js b/ui/pages/confirmations/components/advanced-gas-fee-popover/advanced-gas-fee-defaults/advanced-gas-fee-defaults.test.js index 3791a2d802cf..d6bcb4495950 100644 --- a/ui/pages/confirmations/components/advanced-gas-fee-popover/advanced-gas-fee-defaults/advanced-gas-fee-defaults.test.js +++ b/ui/pages/confirmations/components/advanced-gas-fee-popover/advanced-gas-fee-defaults/advanced-gas-fee-defaults.test.js @@ -19,7 +19,8 @@ import { CHAIN_IDS } from '../../../../../../shared/constants/network'; import { getSelectedInternalAccountFromMockState } from '../../../../../../test/jest/mocks'; import AdvancedGasFeeDefaults from './advanced-gas-fee-defaults'; -const TEXT_SELECTOR = 'Save these values as my default for the Goerli network.'; +const TEXT_SELECTOR = + 'Save these values as my default for the Chain 5 network.'; jest.mock('../../../../../store/actions', () => ({ gasFeeStartPollingByNetworkClientId: jest diff --git a/ui/pages/confirmations/components/advanced-gas-inputs/advanced-gas-inputs.test.js b/ui/pages/confirmations/components/advanced-gas-inputs/advanced-gas-inputs.test.js index 162e0e621d6a..92af1a3bd4d8 100644 --- a/ui/pages/confirmations/components/advanced-gas-inputs/advanced-gas-inputs.test.js +++ b/ui/pages/confirmations/components/advanced-gas-inputs/advanced-gas-inputs.test.js @@ -20,7 +20,7 @@ describe('AdvancedGasInputs', () => { minimumGasLimit: 21000, }; - const store = configureStore({ metamask: { providerConfig: {} } }); + const store = configureStore({}); beforeEach(() => { clock = sinon.useFakeTimers(); diff --git a/ui/pages/confirmations/components/confirm-gas-display/__snapshots__/confirm-gas-display.test.js.snap b/ui/pages/confirmations/components/confirm-gas-display/__snapshots__/confirm-gas-display.test.js.snap index 85e9ff66d381..c6d58247c683 100644 --- a/ui/pages/confirmations/components/confirm-gas-display/__snapshots__/confirm-gas-display.test.js.snap +++ b/ui/pages/confirmations/components/confirm-gas-display/__snapshots__/confirm-gas-display.test.js.snap @@ -82,12 +82,12 @@ exports[`ConfirmGasDisplay should match snapshot 1`] = ` class="mm-box mm-box--display-flex mm-box--flex-wrap-wrap" >

🦊 Market

({ @@ -101,15 +101,10 @@ describe('ConfirmGasDisplay', () => { await render({ contextProps: { metamask: { - selectedNetworkClientId: NetworkType.goerli, - networksMetadata: { - [NetworkType.goerli]: { - EIPS: { - 1559: false, - }, - status: NetworkStatus.Available, - }, - }, + ...mockNetworkState({ + chainId: CHAIN_IDS.GOERLI, + metadata: { EIPS: { 1559: false } }, + }), }, confirmTransaction: { txData: { diff --git a/ui/pages/confirmations/components/confirm-page-container/confirm-detail-row/__snapshots__/confirm-detail-row.component.test.js.snap b/ui/pages/confirmations/components/confirm-page-container/confirm-detail-row/__snapshots__/confirm-detail-row.component.test.js.snap index f6533ac17268..8a3053f67d88 100644 --- a/ui/pages/confirmations/components/confirm-page-container/confirm-detail-row/__snapshots__/confirm-detail-row.component.test.js.snap +++ b/ui/pages/confirmations/components/confirm-page-container/confirm-detail-row/__snapshots__/confirm-detail-row.component.test.js.snap @@ -15,6 +15,16 @@ exports[`Confirm Detail Row Component should match snapshot 1`] = ` class="mm-box currency-display-component confirm-detail-row__primary mm-box--display-flex mm-box--flex-wrap-wrap mm-box--align-items-center" title="0" > +

+
+ G +
+
@@ -25,6 +35,16 @@ exports[`Confirm Detail Row Component should match snapshot 1`] = ` class="mm-box currency-display-component confirm-detail-row__secondary mm-box--display-flex mm-box--flex-wrap-wrap mm-box--align-items-center" title="0" > +
+
+ G +
+
diff --git a/ui/pages/confirmations/components/confirm-page-container/confirm-detail-row/confirm-detail-row.component.test.js b/ui/pages/confirmations/components/confirm-page-container/confirm-detail-row/confirm-detail-row.component.test.js index 5cb7a8cc6b4c..5b1e505ddc14 100644 --- a/ui/pages/confirmations/components/confirm-page-container/confirm-detail-row/confirm-detail-row.component.test.js +++ b/ui/pages/confirmations/components/confirm-page-container/confirm-detail-row/confirm-detail-row.component.test.js @@ -1,35 +1,19 @@ import React from 'react'; import configureMockStore from 'redux-mock-store'; -import { NetworkStatus } from '@metamask/network-controller'; -import { NetworkType } from '@metamask/controller-utils'; import defaultMockState from '../../../../../../test/data/mock-state.json'; import { renderWithProvider } from '../../../../../../test/lib/render-helpers'; -import { - CHAIN_IDS, - GOERLI_DISPLAY_NAME, - NETWORK_TYPES, -} from '../../../../../../shared/constants/network'; +import { CHAIN_IDS } from '../../../../../../shared/constants/network'; +import { mockNetworkState } from '../../../../../../test/stub/networks'; import ConfirmDetailRow from '.'; describe('Confirm Detail Row Component', () => { const mockState = { metamask: { currencyRates: {}, - providerConfig: { - chainId: CHAIN_IDS.GOERLI, - nickname: GOERLI_DISPLAY_NAME, - type: NETWORK_TYPES.GOERLI, - }, + ...mockNetworkState({ chainId: CHAIN_IDS.GOERLI }), preferences: { useNativeCurrencyAsPrimaryCurrency: true, }, - selectedNetworkClientId: NetworkType.mainnet, - networksMetadata: { - [NetworkType.mainnet]: { - EIPS: {}, - status: NetworkStatus.Available, - }, - }, internalAccounts: defaultMockState.metamask.internalAccounts, }, }; diff --git a/ui/pages/confirmations/components/confirm-page-container/confirm-page-container-content/confirm-page-container-content.component.test.js b/ui/pages/confirmations/components/confirm-page-container/confirm-page-container-content/confirm-page-container-content.component.test.js index c7e68cf9eed9..5f852108b4c8 100644 --- a/ui/pages/confirmations/components/confirm-page-container/confirm-page-container-content/confirm-page-container-content.component.test.js +++ b/ui/pages/confirmations/components/confirm-page-container/confirm-page-container-content/confirm-page-container-content.component.test.js @@ -10,15 +10,14 @@ import { USER_OP_CONTRACT_DEPLOY_ERROR_KEY, } from '../../../../../helpers/constants/error-keys'; import { shortenAddress } from '../../../../../helpers/utils/util'; +import { CHAIN_IDS } from '../../../../../../shared/constants/network'; +import { mockNetworkState } from '../../../../../../test/stub/networks'; import ConfirmPageContainerContent from './confirm-page-container-content.component'; describe('Confirm Page Container Content', () => { const mockStore = { metamask: { - providerConfig: { - type: 'test', - chainId: '0x5', - }, + ...mockNetworkState({ chainId: CHAIN_IDS.GOERLI }), addressBook: { '0x5': { '0x06195827297c7A80a443b6894d3BDB8824b43896': { diff --git a/ui/pages/confirmations/components/confirm-page-container/confirm-page-container-header/__snapshots__/confirm-page-container-header.component.test.js.snap b/ui/pages/confirmations/components/confirm-page-container/confirm-page-container-header/__snapshots__/confirm-page-container-header.component.test.js.snap index ba0839bfd123..296e42db6bc5 100644 --- a/ui/pages/confirmations/components/confirm-page-container/confirm-page-container-header/__snapshots__/confirm-page-container-header.component.test.js.snap +++ b/ui/pages/confirmations/components/confirm-page-container/confirm-page-container-header/__snapshots__/confirm-page-container-header.component.test.js.snap @@ -30,12 +30,14 @@ exports[`Confirm Detail Row Component should match snapshot 1`] = `
- ? + G
+ > + Goerli + ({ @@ -15,17 +15,7 @@ jest.mock('../../../../../../app/scripts/lib/util', () => ({ describe('Confirm Detail Row Component', () => { const mockState = { metamask: { - providerConfig: { - type: 'rpc', - chainId: '0x5', - }, - selectedNetworkClientId: NetworkType.goerli, - networksMetadata: { - [NetworkType.goerli]: { - EIPS: {}, - status: NetworkStatus.Available, - }, - }, + ...mockNetworkState({ chainId: CHAIN_IDS.GOERLI }), }, }; diff --git a/ui/pages/confirmations/components/confirm-subtitle/confirm-subtitle.test.js b/ui/pages/confirmations/components/confirm-subtitle/confirm-subtitle.test.js index ac6095a28921..3c45b42cc717 100644 --- a/ui/pages/confirmations/components/confirm-subtitle/confirm-subtitle.test.js +++ b/ui/pages/confirmations/components/confirm-subtitle/confirm-subtitle.test.js @@ -50,7 +50,9 @@ describe('ConfirmSubTitle', () => { mockState.metamask.preferences.showFiatInTestnets = false; mockState.metamask.allNftContracts = { [mockSelectedInternalAccount.address]: { - [mockState.metamask.providerConfig.chainId]: [{ address: '0x9' }], + [mockState.metamask.networkConfigurations[ + mockState.metamask.selectedNetworkClientId + ].chainId]: [{ address: '0x9' }], }, }; store = configureStore(mockState); diff --git a/ui/pages/confirmations/components/confirm/footer/footer.test.tsx b/ui/pages/confirmations/components/confirm/footer/footer.test.tsx index 5fb6b7e5ac76..4122f82028e4 100644 --- a/ui/pages/confirmations/components/confirm/footer/footer.test.tsx +++ b/ui/pages/confirmations/components/confirm/footer/footer.test.tsx @@ -88,11 +88,23 @@ describe('ConfirmFooter', () => { // TODO: Replace `any` with type // eslint-disable-next-line @typescript-eslint/no-explicit-any .mockImplementation(() => ({} as any)); + const updateCustomNonceSpy = jest + .spyOn(Actions, 'updateCustomNonce') + // TODO: Replace `any` with type + // eslint-disable-next-line @typescript-eslint/no-explicit-any + .mockImplementation(() => ({} as any)); + const setNextNonceSpy = jest + .spyOn(Actions, 'setNextNonce') + // TODO: Replace `any` with type + // eslint-disable-next-line @typescript-eslint/no-explicit-any + .mockImplementation(() => ({} as any)); fireEvent.click(cancelButton); expect(rejectSpy).toHaveBeenCalled(); + expect(updateCustomNonceSpy).toHaveBeenCalledWith(''); + expect(setNextNonceSpy).toHaveBeenCalledWith(''); }); - it('invoke action resolvePendingApproval when submit button is clicked', () => { + it('invoke required actions when submit button is clicked', () => { const { getAllByRole } = render(); const submitButton = getAllByRole('button')[1]; const resolveSpy = jest @@ -100,8 +112,20 @@ describe('ConfirmFooter', () => { // TODO: Replace `any` with type // eslint-disable-next-line @typescript-eslint/no-explicit-any .mockImplementation(() => ({} as any)); + const updateCustomNonceSpy = jest + .spyOn(Actions, 'updateCustomNonce') + // TODO: Replace `any` with type + // eslint-disable-next-line @typescript-eslint/no-explicit-any + .mockImplementation(() => ({} as any)); + const setNextNonceSpy = jest + .spyOn(Actions, 'setNextNonce') + // TODO: Replace `any` with type + // eslint-disable-next-line @typescript-eslint/no-explicit-any + .mockImplementation(() => ({} as any)); fireEvent.click(submitButton); expect(resolveSpy).toHaveBeenCalled(); + expect(updateCustomNonceSpy).toHaveBeenCalledWith(''); + expect(setNextNonceSpy).toHaveBeenCalledWith(''); }); it('displays a danger "Confirm" button there are danger alerts', async () => { diff --git a/ui/pages/confirmations/components/confirm/footer/footer.tsx b/ui/pages/confirmations/components/confirm/footer/footer.tsx index 7c36c31c164e..e0d61f66f421 100644 --- a/ui/pages/confirmations/components/confirm/footer/footer.tsx +++ b/ui/pages/confirmations/components/confirm/footer/footer.tsx @@ -22,10 +22,14 @@ import useAlerts from '../../../../../hooks/useAlerts'; import { rejectPendingApproval, resolvePendingApproval, + setNextNonce, + ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) updateAndApproveTx, + ///: END:ONLY_INCLUDE_IF + updateCustomNonce, } from '../../../../../store/actions'; import { confirmSelector } from '../../../selectors'; -import { REDESIGN_TRANSACTION_TYPES } from '../../../utils'; +import { REDESIGN_DEV_TRANSACTION_TYPES } from '../../../utils'; import { getConfirmationSender } from '../utils'; import { MetaMetricsEventLocation } from '../../../../../../shared/constants/metametrics'; import { Severity } from '../../../../../helpers/constants/design-system'; @@ -61,7 +65,7 @@ const ConfirmButton = ({ alertOwnerId?: string; disabled: boolean; onSubmit: () => void; - onCancel: () => void; + onCancel: OnCancelHandler; }) => { const t = useI18nContext(); @@ -144,25 +148,30 @@ const Footer = () => { return false; }); - const onCancel = useCallback(() => { - if (!currentConfirmation) { - return; - } + const onCancel = useCallback( + ({ location }: { location?: MetaMetricsEventLocation }) => { + if (!currentConfirmation) { + return; + } - dispatch( - rejectPendingApproval( - currentConfirmation.id, - serializeError(ethErrors.provider.userRejectedRequest()), - ), - ); - }, [currentConfirmation]); + const error = ethErrors.provider.userRejectedRequest(); + error.data = { location }; + + dispatch( + rejectPendingApproval(currentConfirmation.id, serializeError(error)), + ); + dispatch(updateCustomNonce('')); + dispatch(setNextNonce('')); + }, + [currentConfirmation], + ); const onSubmit = useCallback(() => { if (!currentConfirmation) { return; } - const isTransactionConfirmation = REDESIGN_TRANSACTION_TYPES.find( + const isTransactionConfirmation = REDESIGN_DEV_TRANSACTION_TYPES.find( (type) => type === currentConfirmation?.type, ); if (isTransactionConfirmation) { @@ -183,14 +192,20 @@ const Footer = () => { mmiOnSignCallback(); ///: END:ONLY_INCLUDE_IF } + dispatch(updateCustomNonce('')); + dispatch(setNextNonce('')); }, [currentConfirmation, customNonceValue]); + const onFooterCancel = useCallback(() => { + onCancel({ location: MetaMetricsEventLocation.Confirmation }); + }, [currentConfirmation, onCancel]); + return (
+
+
+
+
+

+ Speed +

+
+
+
+

+ 🦊 Market +

+

+ + ~ + 0 sec + +

+
+
+
+
+
+`; + +exports[` renders component for approve request 2`] = ` +
+
+
+
+

+ Request from +

+
+
+ +
+
+
+
+

+ metamask.github.io +

+
+
+
+
+
+
+

+ Estimated fee +

+
+
+ +
+
+
+
+

+ 0.0001 ETH +

+

+ $0.04 +

+ +
+
+
+
+

+ Speed +

+
+
+
+

+ 🦊 Market +

+

+ + ~ + 0 sec + +

+
+
+
+
+
+`; + +exports[` renders component for approve request 3`] = `
`; diff --git a/ui/pages/confirmations/components/confirm/info/approve/approve-details/__snapshots__/approve-details.test.tsx.snap b/ui/pages/confirmations/components/confirm/info/approve/approve-details/__snapshots__/approve-details.test.tsx.snap new file mode 100644 index 000000000000..c31eb7dcfbd0 --- /dev/null +++ b/ui/pages/confirmations/components/confirm/info/approve/approve-details/__snapshots__/approve-details.test.tsx.snap @@ -0,0 +1,73 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders component for approve details 1`] = ` +
+
+
+
+
+

+ Data +

+
+
+ + + + + + + + + +
+
+
+
+
+`; diff --git a/ui/pages/confirmations/components/confirm/info/approve/approve-details/approve-details.stories.tsx b/ui/pages/confirmations/components/confirm/info/approve/approve-details/approve-details.stories.tsx new file mode 100644 index 000000000000..40523e396330 --- /dev/null +++ b/ui/pages/confirmations/components/confirm/info/approve/approve-details/approve-details.stories.tsx @@ -0,0 +1,43 @@ +import { Meta } from '@storybook/react'; +import React from 'react'; +import { Provider } from 'react-redux'; +import { + DEPOSIT_METHOD_DATA, + genUnapprovedContractInteractionConfirmation, +} from '../../../../../../../../test/data/confirmations/contract-interaction'; +import mockState from '../../../../../../../../test/data/mock-state.json'; +import configureStore from '../../../../../../../store/store'; +import { ApproveDetails } from './approve-details'; + +const store = configureStore({ + ...mockState, + metamask: { + ...mockState.metamask, + use4ByteResolution: true, + knownMethodData: { + [DEPOSIT_METHOD_DATA]: { + name: 'Deposit', + params: [], + }, + }, + }, + confirm: { + currentConfirmation: genUnapprovedContractInteractionConfirmation(), + }, +}); + +const Story = { + title: 'Pages/Confirmations/Components/Confirm/Info/Shared/ApproveDetails', + component: ApproveDetails, + decorators: [ + (story: () => Meta) => ( + {story()} + ), + ], +}; + +export default Story; + +export const DefaultStory = () => ; + +DefaultStory.storyName = 'Default'; diff --git a/ui/pages/confirmations/components/confirm/info/approve/approve-details/approve-details.test.tsx b/ui/pages/confirmations/components/confirm/info/approve/approve-details/approve-details.test.tsx new file mode 100644 index 000000000000..146fccb77b2d --- /dev/null +++ b/ui/pages/confirmations/components/confirm/info/approve/approve-details/approve-details.test.tsx @@ -0,0 +1,18 @@ +import React from 'react'; +import configureMockStore from 'redux-mock-store'; +import thunk from 'redux-thunk'; +import mockState from '../../../../../../../../test/data/mock-state.json'; +import { renderWithProvider } from '../../../../../../../../test/lib/render-helpers'; +import { ApproveDetails } from './approve-details'; + +describe('', () => { + const middleware = [thunk]; + + it('renders component for approve details', () => { + const state = mockState; + const mockStore = configureMockStore(middleware)(state); + const { container } = renderWithProvider(, mockStore); + + expect(container).toMatchSnapshot(); + }); +}); diff --git a/ui/pages/confirmations/components/confirm/info/approve/approve-details/approve-details.tsx b/ui/pages/confirmations/components/confirm/info/approve/approve-details/approve-details.tsx new file mode 100644 index 000000000000..279307965b5c --- /dev/null +++ b/ui/pages/confirmations/components/confirm/info/approve/approve-details/approve-details.tsx @@ -0,0 +1,64 @@ +import React from 'react'; +import { useSelector } from 'react-redux'; +import { + ConfirmInfoRow, + ConfirmInfoRowAddress, + ConfirmInfoRowDivider, +} from '../../../../../../../components/app/confirm/info/row'; +import { ConfirmInfoSection } from '../../../../../../../components/app/confirm/info/row/section'; +import { useI18nContext } from '../../../../../../../hooks/useI18nContext'; +import { selectConfirmationAdvancedDetailsOpen } from '../../../../../selectors/preferences'; +import { useDecodedTransactionData } from '../../hooks/useDecodedTransactionData'; +import { Container } from '../../shared/transaction-data/transaction-data'; +import { + MethodDataRow, + OriginRow, + RecipientRow, +} from '../../shared/transaction-details/transaction-details'; + +const Spender = () => { + const t = useI18nContext(); + + const decodedResponse = useDecodedTransactionData(); + + const { value, pending } = decodedResponse; + + if (pending) { + return ; + } + + if (!value) { + return null; + } + + const spender = value.data[0].params[0].value; + + return ( + <> + + + + + + + ); +}; + +export const ApproveDetails = () => { + const showAdvancedDetails = useSelector( + selectConfirmationAdvancedDetailsOpen, + ); + + return ( + + + + {showAdvancedDetails && ( + <> + + + + )} + + ); +}; diff --git a/ui/pages/confirmations/components/confirm/info/contract-interaction/contract-interaction.stories.tsx b/ui/pages/confirmations/components/confirm/info/approve/approve.stories.tsx similarity index 71% rename from ui/pages/confirmations/components/confirm/info/contract-interaction/contract-interaction.stories.tsx rename to ui/pages/confirmations/components/confirm/info/approve/approve.stories.tsx index 780b27ac3d1a..74a622e7b2af 100644 --- a/ui/pages/confirmations/components/confirm/info/contract-interaction/contract-interaction.stories.tsx +++ b/ui/pages/confirmations/components/confirm/info/approve/approve.stories.tsx @@ -4,7 +4,7 @@ import { Provider } from 'react-redux'; import { genUnapprovedContractInteractionConfirmation } from '../../../../../../../test/data/confirmations/contract-interaction'; import mockState from '../../../../../../../test/data/mock-state.json'; import configureStore from '../../../../../../store/store'; -import ContractInteractionInfo from './contract-interaction'; +import ApproveInfo from './approve'; const store = configureStore({ ...mockState, @@ -17,10 +17,10 @@ const store = configureStore({ }); const Story = { - title: 'Components/App/Confirm/info/ContractInteractionInfo', - component: ContractInteractionInfo, + title: 'Components/App/Confirm/info/ApproveInfo', + component: ApproveInfo, decorators: [ - (story: () => Meta) => ( + (story: () => Meta) => ( {story()} ), ], @@ -28,6 +28,6 @@ const Story = { export default Story; -export const DefaultStory = () => ; +export const DefaultStory = () => ; DefaultStory.storyName = 'Default'; diff --git a/ui/pages/confirmations/components/confirm/info/approve/approve.test.tsx b/ui/pages/confirmations/components/confirm/info/approve/approve.test.tsx new file mode 100644 index 000000000000..2eda7bf50c79 --- /dev/null +++ b/ui/pages/confirmations/components/confirm/info/approve/approve.test.tsx @@ -0,0 +1,62 @@ +import { waitFor } from '@testing-library/react'; +import React from 'react'; +import configureMockStore from 'redux-mock-store'; +import thunk from 'redux-thunk'; +import { genUnapprovedContractInteractionConfirmation } from '../../../../../../../test/data/confirmations/contract-interaction'; +import mockState from '../../../../../../../test/data/mock-state.json'; +import { renderWithProvider } from '../../../../../../../test/lib/render-helpers'; +import ApproveInfo from './approve'; + +jest.mock('../../../../../../store/actions', () => ({ + ...jest.requireActual('../../../../../../store/actions'), + getGasFeeTimeEstimate: jest.fn().mockResolvedValue({ + lowerTimeBound: 0, + upperTimeBound: 60000, + }), +})); + +jest.mock( + '../../../../../../components/app/alert-system/contexts/alertMetricsContext', + () => ({ + useAlertMetrics: jest.fn(() => ({ + trackAlertMetrics: jest.fn(), + })), + }), +); + +describe('', () => { + const middleware = [thunk]; + + it('renders component for approve request', async () => { + const state = { + ...mockState, + confirm: { + currentConfirmation: genUnapprovedContractInteractionConfirmation(), + }, + }; + const mockStore = configureMockStore(middleware)(state); + + const { container } = renderWithProvider(, mockStore); + + await waitFor(() => { + expect(container).toMatchSnapshot(); + }); + }); + + it('does not render if required data is not present in the transaction', () => { + const state = { + ...mockState, + confirm: { + currentConfirmation: { + id: '0050d5b0-c023-11ee-a0cb-3390a510a0ab', + status: 'unapproved', + time: new Date().getTime(), + type: 'json_request', + }, + }, + }; + const mockStore = configureMockStore(middleware)(state); + const { container } = renderWithProvider(, mockStore); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/ui/pages/confirmations/components/confirm/info/approve/approve.tsx b/ui/pages/confirmations/components/confirm/info/approve/approve.tsx new file mode 100644 index 000000000000..0dfd100a719a --- /dev/null +++ b/ui/pages/confirmations/components/confirm/info/approve/approve.tsx @@ -0,0 +1,107 @@ +import { NameType } from '@metamask/name-controller'; +import { TransactionMeta } from '@metamask/transaction-controller'; +import React from 'react'; +import { useSelector } from 'react-redux'; +import Name from '../../../../../../components/app/name'; +import { Box, Text } from '../../../../../../components/component-library'; +import { + AlignItems, + BackgroundColor, + BlockSize, + BorderRadius, + Display, + TextAlign, +} from '../../../../../../helpers/constants/design-system'; +import { useI18nContext } from '../../../../../../hooks/useI18nContext'; +import { currentConfirmationSelector } from '../../../../../../selectors'; +import { selectConfirmationAdvancedDetailsOpen } from '../../../../selectors/preferences'; +import { useDecodedTransactionData } from '../hooks/useDecodedTransactionData'; +import { AdvancedDetails } from '../shared/advanced-details/advanced-details'; +import { GasFeesSection } from '../shared/gas-fees-section/gas-fees-section'; +import StaticSimulation from '../shared/static-simulation/static-simulation'; +import { Container } from '../shared/transaction-data/transaction-data'; +import { ApproveDetails } from './approve-details/approve-details'; + +const ApproveStaticSimulation = () => { + const t = useI18nContext(); + + const transactionMeta = useSelector( + currentConfirmationSelector, + ) as TransactionMeta; + + const decodedResponse = useDecodedTransactionData(); + + const { value, pending } = decodedResponse; + + if (pending) { + return ; + } + + if (!value) { + return null; + } + + const tokenId = `#${value.data[0].params[1].value}`; + + const simulationElements = ( + <> + + + + {tokenId} + + + + + + ); + + return ( + + ); +}; + +const ApproveInfo = () => { + const transactionMeta = useSelector( + currentConfirmationSelector, + ) as TransactionMeta; + + const showAdvancedDetails = useSelector( + selectConfirmationAdvancedDetailsOpen, + ); + + if (!transactionMeta?.txParams) { + return null; + } + + return ( + <> + + + + {showAdvancedDetails && } + + ); +}; + +export default ApproveInfo; diff --git a/ui/pages/confirmations/components/confirm/info/contract-interaction/__snapshots__/contract-interaction.test.tsx.snap b/ui/pages/confirmations/components/confirm/info/base-transaction-info/__snapshots__/base-transaction-info.test.tsx.snap similarity index 91% rename from ui/pages/confirmations/components/confirm/info/contract-interaction/__snapshots__/contract-interaction.test.tsx.snap rename to ui/pages/confirmations/components/confirm/info/base-transaction-info/__snapshots__/base-transaction-info.test.tsx.snap index 1f5570a11688..dc0e1bd4f559 100644 --- a/ui/pages/confirmations/components/confirm/info/contract-interaction/__snapshots__/contract-interaction.test.tsx.snap +++ b/ui/pages/confirmations/components/confirm/info/base-transaction-info/__snapshots__/base-transaction-info.test.tsx.snap @@ -1,8 +1,8 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[` does not render if required data is not present in the transaction 1`] = `
`; +exports[` does not render if required data is not present in the transaction 1`] = `
`; -exports[` renders component for contract interaction request 1`] = ` +exports[` renders component for contract interaction request 1`] = `
renders component for contract interaction > Estimated changes

-
-
-
- - - -
+
+
+
@@ -101,10 +91,11 @@ exports[` renders component for contract interaction

renders component for contract interaction >

renders component for contract interaction >

renders component for contract interaction >

renders component for contract interaction class="mm-box mm-box--display-flex mm-box--flex-wrap-wrap" >

🦊 Market

renders component for contract interaction

`; -exports[` renders component for contract interaction request 2`] = ` +exports[` renders component for contract interaction request 2`] = `
renders component for contract interaction > Estimated changes

-
-
-
- - - -
+
+
+
@@ -467,10 +451,11 @@ exports[` renders component for contract interaction

renders component for contract interaction >

renders component for contract interaction >

renders component for contract interaction >

renders component for contract interaction class="mm-box mm-box--display-flex mm-box--flex-wrap-wrap" >

🦊 Market

renders component for contract interaction

`; -exports[` renders component for contract interaction request 3`] = ` +exports[` renders component for contract interaction request 3`] = `
renders component for contract interaction > Estimated changes

-
-
-
- - - -
+
+
+
@@ -830,10 +808,11 @@ exports[` renders component for contract interaction

renders component for contract interaction >

renders component for contract interaction >

renders component for contract interaction >

renders component for contract interaction class="mm-box mm-box--display-flex mm-box--flex-wrap-wrap" >

🦊 Market

Meta) => ( + {story()} + ), + ], +}; + +export default Story; + +export const DefaultStory = () => ; + +DefaultStory.storyName = 'Default'; diff --git a/ui/pages/confirmations/components/confirm/info/contract-interaction/contract-interaction.test.tsx b/ui/pages/confirmations/components/confirm/info/base-transaction-info/base-transaction-info.test.tsx similarity index 91% rename from ui/pages/confirmations/components/confirm/info/contract-interaction/contract-interaction.test.tsx rename to ui/pages/confirmations/components/confirm/info/base-transaction-info/base-transaction-info.test.tsx index 70de59dcd604..493cf7e522a4 100644 --- a/ui/pages/confirmations/components/confirm/info/contract-interaction/contract-interaction.test.tsx +++ b/ui/pages/confirmations/components/confirm/info/base-transaction-info/base-transaction-info.test.tsx @@ -5,7 +5,7 @@ import thunk from 'redux-thunk'; import { genUnapprovedContractInteractionConfirmation } from '../../../../../../../test/data/confirmations/contract-interaction'; import mockState from '../../../../../../../test/data/mock-state.json'; import { renderWithProvider } from '../../../../../../../test/lib/render-helpers'; -import ContractInteractionInfo from './contract-interaction'; +import BaseTransactionInfo from './base-transaction-info'; jest.mock('../../../../../../store/actions', () => ({ ...jest.requireActual('../../../../../../store/actions'), @@ -24,7 +24,7 @@ jest.mock( }), ); -describe('', () => { +describe('', () => { const middleware = [thunk]; it('renders component for contract interaction request', async () => { @@ -37,7 +37,7 @@ describe('', () => { const mockStore = configureMockStore(middleware)(state); const { container } = renderWithProvider( - , + , mockStore, ); @@ -60,7 +60,7 @@ describe('', () => { }; const mockStore = configureMockStore(middleware)(state); const { container } = renderWithProvider( - , + , mockStore, ); expect(container).toMatchSnapshot(); diff --git a/ui/pages/confirmations/components/confirm/info/contract-interaction/contract-interaction.tsx b/ui/pages/confirmations/components/confirm/info/base-transaction-info/base-transaction-info.tsx similarity index 94% rename from ui/pages/confirmations/components/confirm/info/contract-interaction/contract-interaction.tsx rename to ui/pages/confirmations/components/confirm/info/base-transaction-info/base-transaction-info.tsx index c736652a031e..e67b65848b6a 100644 --- a/ui/pages/confirmations/components/confirm/info/contract-interaction/contract-interaction.tsx +++ b/ui/pages/confirmations/components/confirm/info/base-transaction-info/base-transaction-info.tsx @@ -9,7 +9,7 @@ import { AdvancedDetails } from '../shared/advanced-details/advanced-details'; import { GasFeesSection } from '../shared/gas-fees-section/gas-fees-section'; import { TransactionDetails } from '../shared/transaction-details/transaction-details'; -const ContractInteractionInfo = () => { +const BaseTransactionInfo = () => { const transactionMeta = useSelector( currentConfirmationSelector, ) as TransactionMeta; @@ -38,4 +38,4 @@ const ContractInteractionInfo = () => { ); }; -export default ContractInteractionInfo; +export default BaseTransactionInfo; diff --git a/ui/pages/confirmations/components/confirm/info/hooks/useDecodedTransactionData.test.ts b/ui/pages/confirmations/components/confirm/info/hooks/useDecodedTransactionData.test.ts index c070e1ae0635..d211e97d80bb 100644 --- a/ui/pages/confirmations/components/confirm/info/hooks/useDecodedTransactionData.test.ts +++ b/ui/pages/confirmations/components/confirm/info/hooks/useDecodedTransactionData.test.ts @@ -65,6 +65,20 @@ describe('useDecodedTransactionData', () => { }, ); + it('returns undefined if no transaction to', async () => { + const result = await runHook({ + currentConfirmation: { + chainId: CHAIN_ID_MOCK, + txParams: { + data: TRANSACTION_DATA_UNISWAP, + to: undefined, + } as TransactionParams, + }, + }); + + expect(result).toStrictEqual({ pending: false, value: undefined }); + }); + it('returns the decoded data', async () => { decodeTransactionDataMock.mockResolvedValue(TRANSACTION_DECODE_SOURCIFY); diff --git a/ui/pages/confirmations/components/confirm/info/hooks/useDecodedTransactionData.ts b/ui/pages/confirmations/components/confirm/info/hooks/useDecodedTransactionData.ts index 23c8c7af3f61..e77b704abc69 100644 --- a/ui/pages/confirmations/components/confirm/info/hooks/useDecodedTransactionData.ts +++ b/ui/pages/confirmations/components/confirm/info/hooks/useDecodedTransactionData.ts @@ -20,9 +20,10 @@ export function useDecodedTransactionData(): AsyncResult< const chainId = currentConfirmation?.chainId as Hex; const contractAddress = currentConfirmation?.txParams?.to as Hex; const transactionData = currentConfirmation?.txParams?.data as Hex; + const transactionTo = currentConfirmation?.txParams?.to as Hex; return useAsyncResult(async () => { - if (!hasTransactionData(transactionData)) { + if (!hasTransactionData(transactionData) || !transactionTo) { return undefined; } @@ -31,5 +32,5 @@ export function useDecodedTransactionData(): AsyncResult< chainId, contractAddress, }); - }, [transactionData, chainId, contractAddress]); + }, [transactionData, transactionTo, chainId, contractAddress]); } diff --git a/ui/pages/confirmations/components/confirm/info/hooks/useFourByte.test.ts b/ui/pages/confirmations/components/confirm/info/hooks/useFourByte.test.ts index 5d4a023c82bb..9f1a848a96cd 100644 --- a/ui/pages/confirmations/components/confirm/info/hooks/useFourByte.test.ts +++ b/ui/pages/confirmations/components/confirm/info/hooks/useFourByte.test.ts @@ -34,7 +34,7 @@ describe('useFourByte', () => { expect(result.current.params).toEqual([]); }); - it('returns empty object if resolution is turned off', () => { + it('returns null if resolution disabled', () => { const currentConfirmation = genUnapprovedContractInteractionConfirmation({ address: CONTRACT_INTERACTION_SENDER_ADDRESS, txData: depositHexData, @@ -57,7 +57,7 @@ describe('useFourByte', () => { expect(result.current).toBeNull(); }); - it("returns undefined if it's not known even if resolution is enabled", () => { + it('returns null if not known even if resolution enabled', () => { const currentConfirmation = genUnapprovedContractInteractionConfirmation({ address: CONTRACT_INTERACTION_SENDER_ADDRESS, txData: depositHexData, @@ -77,4 +77,29 @@ describe('useFourByte', () => { expect(result.current).toBeNull(); }); + + it('returns null if no transaction to', () => { + const currentConfirmation = genUnapprovedContractInteractionConfirmation({ + address: CONTRACT_INTERACTION_SENDER_ADDRESS, + txData: depositHexData, + }) as TransactionMeta; + + currentConfirmation.txParams.to = undefined; + + const { result } = renderHookWithProvider( + () => useFourByte(currentConfirmation), + { + ...mockState, + metamask: { + ...mockState.metamask, + use4ByteResolution: true, + knownMethodData: { + [depositHexData]: { name: 'Deposit', params: [] }, + }, + }, + }, + ); + + expect(result.current).toBeNull(); + }); }); diff --git a/ui/pages/confirmations/components/confirm/info/hooks/useFourByte.ts b/ui/pages/confirmations/components/confirm/info/hooks/useFourByte.ts index 7e2b81b443bd..7fece13fb417 100644 --- a/ui/pages/confirmations/components/confirm/info/hooks/useFourByte.ts +++ b/ui/pages/confirmations/components/confirm/info/hooks/useFourByte.ts @@ -1,28 +1,41 @@ import { TransactionMeta } from '@metamask/transaction-controller'; import { useDispatch, useSelector } from 'react-redux'; import { useEffect } from 'react'; +import { Hex } from '@metamask/utils'; import { getKnownMethodData, use4ByteResolutionSelector, } from '../../../../../../selectors'; import { getContractMethodData } from '../../../../../../store/actions'; +import { hasTransactionData } from '../../../../../../../shared/modules/transaction.utils'; export const useFourByte = (currentConfirmation: TransactionMeta) => { const dispatch = useDispatch(); const isFourByteEnabled = useSelector(use4ByteResolutionSelector); - const transactionData = currentConfirmation?.txParams?.data; + const transactionTo = currentConfirmation?.txParams?.to; + const transactionData = currentConfirmation?.txParams?.data as + | Hex + | undefined; useEffect(() => { - if (!isFourByteEnabled || !transactionData) { + if ( + !isFourByteEnabled || + !hasTransactionData(transactionData) || + !transactionTo + ) { return; } dispatch(getContractMethodData(transactionData)); - }, [isFourByteEnabled, transactionData, dispatch]); + }, [isFourByteEnabled, transactionData, transactionTo, dispatch]); const methodData = useSelector((state) => getKnownMethodData(state, transactionData), ); + if (!transactionTo) { + return null; + } + return methodData; }; diff --git a/ui/pages/confirmations/components/confirm/info/info.tsx b/ui/pages/confirmations/components/confirm/info/info.tsx index 30ce579337ec..67691839f673 100644 --- a/ui/pages/confirmations/components/confirm/info/info.tsx +++ b/ui/pages/confirmations/components/confirm/info/info.tsx @@ -3,10 +3,11 @@ import React, { useMemo } from 'react'; import { useSelector } from 'react-redux'; import { currentConfirmationSelector } from '../../../../../selectors'; import { SignatureRequestType } from '../../../types/confirm'; -import ContractInteractionInfo from './contract-interaction/contract-interaction'; +import BaseTransactionInfo from './base-transaction-info/base-transaction-info'; import PersonalSignInfo from './personal-sign/personal-sign'; import TypedSignV1Info from './typed-sign-v1/typed-sign-v1'; import TypedSignInfo from './typed-sign/typed-sign'; +import ApproveInfo from './approve/approve'; const Info = () => { const currentConfirmation = useSelector(currentConfirmationSelector); @@ -22,7 +23,9 @@ const Info = () => { } return TypedSignInfo; }, - [TransactionType.contractInteraction]: () => ContractInteractionInfo, + [TransactionType.contractInteraction]: () => BaseTransactionInfo, + [TransactionType.deployContract]: () => BaseTransactionInfo, + [TransactionType.tokenMethodApprove]: () => ApproveInfo, }), [currentConfirmation], ); diff --git a/ui/pages/confirmations/components/confirm/info/personal-sign/__snapshots__/personal-sign.test.tsx.snap b/ui/pages/confirmations/components/confirm/info/personal-sign/__snapshots__/personal-sign.test.tsx.snap index 5d9f32748dab..c47e8efbae4e 100644 --- a/ui/pages/confirmations/components/confirm/info/personal-sign/__snapshots__/personal-sign.test.tsx.snap +++ b/ui/pages/confirmations/components/confirm/info/personal-sign/__snapshots__/personal-sign.test.tsx.snap @@ -11,6 +11,7 @@ exports[`PersonalSignInfo handle reverse string properly 1`] = ` >

{ )}

does not render component for advanced transaction >

does not render component for advanced transaction >

renders component for advanced transaction details >

renders component for advanced transaction details >

renders component 1`] = ` >

renders component for gas fees section 1`] = ` >

renders component for gas fees section 1`] = ` >

renders component for gas fees section 1`] = ` class="mm-box mm-box--display-flex mm-box--flex-wrap-wrap" >

🦊 Market

renders component 1`] = ` >

renders component for gas fees section 1`] = ` >

renders component for gas fees section 1`] = ` >

renders component for gas fees section 1`] = ` class="mm-box mm-box--display-flex mm-box--flex-wrap-wrap" >

🦊 Market

= ({ + title, + titleTooltip, + description, + simulationHeading, + simulationElements, +}) => { + return ( + + + + + + + {simulationElements} + + + + ); +}; + +export default StaticSimulation; diff --git a/ui/pages/confirmations/components/confirm/info/shared/transaction-data/__snapshots__/transaction-data.test.tsx.snap b/ui/pages/confirmations/components/confirm/info/shared/transaction-data/__snapshots__/transaction-data.test.tsx.snap index 79865c563231..02da6945d64a 100644 --- a/ui/pages/confirmations/components/confirm/info/shared/transaction-data/__snapshots__/transaction-data.test.tsx.snap +++ b/ui/pages/confirmations/components/confirm/info/shared/transaction-data/__snapshots__/transaction-data.test.tsx.snap @@ -16,6 +16,7 @@ exports[`TransactionData renders decoded data with names and descriptions 1`] = />

{ ); }; -function Container({ +export function Container({ children, isLoading, transactionData, diff --git a/ui/pages/confirmations/components/confirm/info/shared/transaction-details/__snapshots__/transaction-details.test.tsx.snap b/ui/pages/confirmations/components/confirm/info/shared/transaction-details/__snapshots__/transaction-details.test.tsx.snap index f0cc65797d7a..b65f3e74f8b8 100644 --- a/ui/pages/confirmations/components/confirm/info/shared/transaction-details/__snapshots__/transaction-details.test.tsx.snap +++ b/ui/pages/confirmations/components/confirm/info/shared/transaction-details/__snapshots__/transaction-details.test.tsx.snap @@ -18,10 +18,11 @@ exports[` renders component for transaction details 1`] =

renders component for transaction details 1`] = >

({ + useAlertMetrics: jest.fn(() => ({ + trackAlertMetrics: jest.fn(), + })), + }), +); + describe('', () => { const middleware = [thunk]; @@ -27,4 +38,26 @@ describe('', () => { const { container } = renderWithProvider(, mockStore); expect(container).toMatchSnapshot(); }); + + it('renders component for transaction details with amount', () => { + const simulationDataMock = { + error: { code: SimulationErrorCode.Disabled }, + tokenBalanceChanges: [], + }; + const contractInteraction = genUnapprovedContractInteractionConfirmation({ + simulationData: simulationDataMock, + }); + const state = { + ...mockState, + confirm: { + currentConfirmation: contractInteraction, + }, + }; + const mockStore = configureMockStore(middleware)(state); + const { getByTestId } = renderWithConfirmContextProvider( + , + mockStore, + ); + expect(getByTestId('transaction-details-amount-row')).toBeInTheDocument(); + }); }); diff --git a/ui/pages/confirmations/components/confirm/info/shared/transaction-details/transaction-details.tsx b/ui/pages/confirmations/components/confirm/info/shared/transaction-details/transaction-details.tsx index 1e5d84ef071c..aca636bf035d 100644 --- a/ui/pages/confirmations/components/confirm/info/shared/transaction-details/transaction-details.tsx +++ b/ui/pages/confirmations/components/confirm/info/shared/transaction-details/transaction-details.tsx @@ -8,13 +8,20 @@ import { ConfirmInfoRowText, ConfirmInfoRowUrl, } from '../../../../../../../components/app/confirm/info/row'; +import { ConfirmInfoAlertRow } from '../../../../../../../components/app/confirm/info/row/alert-row/alert-row'; +import { RowAlertKey } from '../../../../../../../components/app/confirm/info/row/constants'; import { ConfirmInfoSection } from '../../../../../../../components/app/confirm/info/row/section'; import { useI18nContext } from '../../../../../../../hooks/useI18nContext'; import { selectPaymasterAddress } from '../../../../../../../selectors/account-abstraction'; import { currentConfirmationSelector } from '../../../../../selectors'; +import { selectConfirmationAdvancedDetailsOpen } from '../../../../../selectors/preferences'; import { useFourByte } from '../../hooks/useFourByte'; +import { ConfirmInfoRowCurrency } from '../../../../../../../components/app/confirm/info/row/currency'; +import { PRIMARY } from '../../../../../../../helpers/constants/common'; +import { useUserPreferencedCurrency } from '../../../../../../../hooks/useUserPreferencedCurrency'; +import { HEX_ZERO } from '../constants'; -const OriginRow = () => { +export const OriginRow = () => { const t = useI18nContext(); const currentConfirmation = useSelector( @@ -28,17 +35,19 @@ const OriginRow = () => { } return ( - - + ); }; -const RecipientRow = () => { +export const RecipientRow = () => { const t = useI18nContext(); const currentConfirmation = useSelector( @@ -63,7 +72,7 @@ const RecipientRow = () => { ); }; -const MethodDataRow = () => { +export const MethodDataRow = () => { const t = useI18nContext(); const currentConfirmation = useSelector( @@ -87,6 +96,32 @@ const MethodDataRow = () => { ); }; +const AmountRow = () => { + const t = useI18nContext(); + const currentConfirmation = useSelector( + currentConfirmationSelector, + ) as TransactionMeta; + const { currency } = useUserPreferencedCurrency(PRIMARY); + + const value = currentConfirmation?.txParams?.value; + const simulationData = currentConfirmation?.simulationData; + + if (!value || value === HEX_ZERO || !simulationData?.error) { + return null; + } + + return ( + + + + + + ); +}; + const PaymasterRow = () => { const t = useI18nContext(); @@ -120,13 +155,18 @@ const PaymasterRow = () => { }; export const TransactionDetails = () => { + const showAdvancedDetails = useSelector( + selectConfirmationAdvancedDetailsOpen, + ); + return ( <> - + {showAdvancedDetails && } + ); diff --git a/ui/pages/confirmations/components/confirm/info/typed-sign-v1/__snapshots__/typed-sign-v1.test.tsx.snap b/ui/pages/confirmations/components/confirm/info/typed-sign-v1/__snapshots__/typed-sign-v1.test.tsx.snap index 78f79a65dae9..8228b021303c 100644 --- a/ui/pages/confirmations/components/confirm/info/typed-sign-v1/__snapshots__/typed-sign-v1.test.tsx.snap +++ b/ui/pages/confirmations/components/confirm/info/typed-sign-v1/__snapshots__/typed-sign-v1.test.tsx.snap @@ -7,10 +7,11 @@ exports[`TypedSignInfo correctly renders typed sign data request 1`] = ` >

({ + useAlertMetrics: jest.fn(() => ({ + trackAlertMetrics: jest.fn(), + })), + }), +); + describe('TypedSignInfo', () => { it('correctly renders typed sign data request', () => { const mockState = { confirm: { currentConfirmation: unapprovedTypedSignMsgV1, }, + confirmAlerts: { + alerts: [], + confirmed: [], + }, }; const mockStore = configureMockStore([])(mockState); const { container } = renderWithProvider(, mockStore); @@ -27,6 +40,10 @@ describe('TypedSignInfo', () => { type: 'json_request', }, }, + confirmAlerts: { + alerts: [], + confirmed: [], + }, }; const mockStore = configureMockStore([])(mockState); const { container } = renderWithProvider(, mockStore); diff --git a/ui/pages/confirmations/components/confirm/info/typed-sign-v1/typed-sign-v1.tsx b/ui/pages/confirmations/components/confirm/info/typed-sign-v1/typed-sign-v1.tsx index a2ed4e554e2f..a2817e8b5c98 100644 --- a/ui/pages/confirmations/components/confirm/info/typed-sign-v1/typed-sign-v1.tsx +++ b/ui/pages/confirmations/components/confirm/info/typed-sign-v1/typed-sign-v1.tsx @@ -1,10 +1,12 @@ import React from 'react'; import { useSelector } from 'react-redux'; +import { ConfirmInfoAlertRow } from '../../../../../../components/app/confirm/info/row/alert-row/alert-row'; import { ConfirmInfoRow, ConfirmInfoRowUrl, } from '../../../../../../components/app/confirm/info/row'; +import { RowAlertKey } from '../../../../../../components/app/confirm/info/row/constants'; import { useI18nContext } from '../../../../../../hooks/useI18nContext'; import { currentConfirmationSelector } from '../../../../../../selectors'; import { @@ -27,9 +29,14 @@ const TypedSignV1Info: React.FC = () => { return ( <> - + - + diff --git a/ui/pages/confirmations/components/confirm/info/typed-sign/__snapshots__/typed-sign.test.tsx.snap b/ui/pages/confirmations/components/confirm/info/typed-sign/__snapshots__/typed-sign.test.tsx.snap index 9e0218a9c9f5..54be67cf1fc2 100644 --- a/ui/pages/confirmations/components/confirm/info/typed-sign/__snapshots__/typed-sign.test.tsx.snap +++ b/ui/pages/confirmations/components/confirm/info/typed-sign/__snapshots__/typed-sign.test.tsx.snap @@ -11,6 +11,7 @@ exports[`TypedSignInfo correctly renders permit sign type 1`] = ` >

{ @@ -24,8 +22,6 @@ describe('PermitSimulation', () => { currentConfirmation: permitSignatureMsg, }, }; - // TODO: readd in 12.4.0 - // const state = getMockTypedSignConfirmStateForRequest(permitSignatureMsg); const mockStore = configureMockStore([])(state); await act(async () => { diff --git a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.tsx b/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.tsx index b246c2816d4d..a42bc0d60e0e 100644 --- a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.tsx +++ b/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.tsx @@ -1,21 +1,16 @@ import React from 'react'; import { useSelector } from 'react-redux'; - +import { Box } from '../../../../../../../components/component-library'; import { PrimaryType } from '../../../../../../../../shared/constants/signatures'; import { parseTypedDataMessage } from '../../../../../../../../shared/modules/transaction.utils'; -import { - ConfirmInfoRow, - ConfirmInfoRowText, -} from '../../../../../../../components/app/confirm/info/row'; -import { useI18nContext } from '../../../../../../../hooks/useI18nContext'; -import { currentConfirmationSelector } from '../../../../../../../selectors'; -import { Box } from '../../../../../../../components/component-library'; import { Display, FlexDirection, } from '../../../../../../../helpers/constants/design-system'; +import { useI18nContext } from '../../../../../../../hooks/useI18nContext'; import { SignatureRequestType } from '../../../../../types/confirm'; -import { ConfirmInfoSection } from '../../../../../../../components/app/confirm/info/row/section'; +import { currentConfirmationSelector } from '../../../../../../../selectors'; +import StaticSimulation from '../../shared/static-simulation/static-simulation'; import PermitSimulationValueDisplay from './value-display/value-display'; function extractTokenDetailsByPrimaryType( @@ -58,44 +53,40 @@ const PermitSimulation: React.FC = () => { const tokenDetails = extractTokenDetailsByPrimaryType(message, primaryType); return ( - - - - - - - {Array.isArray(tokenDetails) ? ( - - {tokenDetails.map( - ( - { token, amount }: { token: string; amount: string }, - i: number, - ) => ( - - ), - )} - - ) : ( - - )} - - - + + {tokenDetails.map( + ( + { token, amount }: { token: string; amount: string }, + i: number, + ) => ( + + ), + )} + + ) : ( + + ) + } + /> ); }; diff --git a/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign.test.tsx b/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign.test.tsx index 089590d89618..bc3fff028897 100644 --- a/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign.test.tsx +++ b/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign.test.tsx @@ -11,6 +11,15 @@ import { } from '../../../../../../../test/data/confirmations/typed_sign'; import TypedSignInfo from './typed-sign'; +jest.mock( + '../../../../../../components/app/alert-system/contexts/alertMetricsContext', + () => ({ + useAlertMetrics: jest.fn(() => ({ + trackAlertMetrics: jest.fn(), + })), + }), +); + jest.mock('../../../../../../store/actions', () => { return { getTokenStandardAndDetails: jest.fn().mockResolvedValue({ decimals: 2 }), diff --git a/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign.tsx b/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign.tsx index ddb56bf82800..589c3239e064 100644 --- a/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign.tsx +++ b/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign.tsx @@ -2,7 +2,9 @@ import React, { useEffect, useState } from 'react'; import { useSelector } from 'react-redux'; import { isValidAddress } from 'ethereumjs-util'; +import { ConfirmInfoAlertRow } from '../../../../../../components/app/confirm/info/row/alert-row/alert-row'; import { parseTypedDataMessage } from '../../../../../../../shared/modules/transaction.utils'; +import { RowAlertKey } from '../../../../../../components/app/confirm/info/row/constants'; import { ConfirmInfoRow, ConfirmInfoRowAddress, @@ -68,9 +70,14 @@ const TypedSignInfo: React.FC = () => { )} - + - + {isValidAddress(verifyingContract) && ( diff --git a/ui/pages/confirmations/components/confirm/network-change-toast/index.scss b/ui/pages/confirmations/components/confirm/network-change-toast/index.scss index 4b679dc6339a..7c7ee12e6008 100644 --- a/ui/pages/confirmations/components/confirm/network-change-toast/index.scss +++ b/ui/pages/confirmations/components/confirm/network-change-toast/index.scss @@ -8,4 +8,8 @@ position: fixed; width: 100%; z-index: design-system.$modal-z-index; + + @media screen and (min-width: design-system.$screen-sm-max) { + max-width: 408px; + } } diff --git a/ui/pages/confirmations/components/confirm/pluggable-section/pluggable-section.tsx b/ui/pages/confirmations/components/confirm/pluggable-section/pluggable-section.tsx index c167cdb83c0f..a8f1ecc34f6b 100644 --- a/ui/pages/confirmations/components/confirm/pluggable-section/pluggable-section.tsx +++ b/ui/pages/confirmations/components/confirm/pluggable-section/pluggable-section.tsx @@ -3,9 +3,10 @@ import { ReactComponentLike } from 'prop-types'; import { useSelector } from 'react-redux'; import { currentConfirmationSelector } from '../../../selectors'; +import { SnapsSection } from '../snaps/snaps-section'; // Components to be plugged into confirmation page can be added to the array below -const pluggedInSections: ReactComponentLike[] = []; +const pluggedInSections: ReactComponentLike[] = [SnapsSection]; const PluggableSection = () => { const currentConfirmation = useSelector(currentConfirmationSelector); diff --git a/ui/pages/confirmations/components/confirm/row/__snapshots__/dataTree.test.tsx.snap b/ui/pages/confirmations/components/confirm/row/__snapshots__/dataTree.test.tsx.snap index 1c341b09e71d..7f3bb467840e 100644 --- a/ui/pages/confirmations/components/confirm/row/__snapshots__/dataTree.test.tsx.snap +++ b/ui/pages/confirmations/components/confirm/row/__snapshots__/dataTree.test.tsx.snap @@ -11,6 +11,7 @@ exports[`DataTree correctly renders reverse strings 1`] = ` >

+

+
+
+
+

+ + + Insights from + + BIP-32 Test Snap + + + + +

+
+ +
+
+
+
+

+ Hello world again! +

+
+
+
+
+
+
+`; + +exports[`SnapsSection renders section personal sign request 1`] = ` +
+
+
+
+
+

+ + + Insights from + + BIP-32 Test Snap + + + + +

+
+ +
+
+
+
+

+ Hello world! +

+
+
+
+
+
+
+`; diff --git a/ui/pages/confirmations/components/confirm/snaps/snaps-section/index.ts b/ui/pages/confirmations/components/confirm/snaps/snaps-section/index.ts new file mode 100644 index 000000000000..1e898dac9479 --- /dev/null +++ b/ui/pages/confirmations/components/confirm/snaps/snaps-section/index.ts @@ -0,0 +1 @@ +export * from './snaps-section'; diff --git a/ui/pages/confirmations/components/confirm/snaps/snaps-section/snap-insight.tsx b/ui/pages/confirmations/components/confirm/snaps/snaps-section/snap-insight.tsx new file mode 100644 index 000000000000..b428ee8d158d --- /dev/null +++ b/ui/pages/confirmations/components/confirm/snaps/snaps-section/snap-insight.tsx @@ -0,0 +1,79 @@ +import React from 'react'; +import { useSelector } from 'react-redux'; +import { SnapUIRenderer } from '../../../../../../components/app/snaps/snap-ui-renderer'; +import { Delineator } from '../../../../../../components/ui/delineator'; +import { Text } from '../../../../../../components/component-library'; +import { + TextColor, + TextVariant, + FontWeight, +} from '../../../../../../helpers/constants/design-system'; +import { useI18nContext } from '../../../../../../hooks/useI18nContext'; +import { getSnapMetadata } from '../../../../../../selectors'; +import Tooltip from '../../../../../../components/ui/tooltip'; + +export type SnapInsightProps = { + snapId: string; + interfaceId: string; + loading: boolean; +}; + +export const SnapInsight: React.FunctionComponent = ({ + snapId, + interfaceId, + loading, +}) => { + const t = useI18nContext(); + const { name: snapName } = useSelector((state) => + /* @ts-expect-error wrong type on selector. */ + getSnapMetadata(state, snapId), + ); + + const headerComponent = ( + + {t('insightsFromSnap', [ + + {snapName} + , + ])} + + ); + + const hasNoInsight = !loading && !interfaceId; + + if (hasNoInsight) { + return ( + + + + ); + } + + return ( + + + + ); +}; diff --git a/ui/pages/confirmations/components/confirm/snaps/snaps-section/snaps-section.test.tsx b/ui/pages/confirmations/components/confirm/snaps/snaps-section/snaps-section.test.tsx new file mode 100644 index 000000000000..1c13c6eb4bdc --- /dev/null +++ b/ui/pages/confirmations/components/confirm/snaps/snaps-section/snaps-section.test.tsx @@ -0,0 +1,91 @@ +import React from 'react'; +import configureMockStore from 'redux-mock-store'; +import { Text } from '@metamask/snaps-sdk/jsx'; + +import { fireEvent } from '@testing-library/react'; +import mockState from '../../../../../../../test/data/mock-state.json'; +import { unapprovedPersonalSignMsg } from '../../../../../../../test/data/confirmations/personal_sign'; +import { unapprovedTypedSignMsgV3 } from '../../../../../../../test/data/confirmations/typed_sign'; +import { renderWithProvider } from '../../../../../../../test/lib/render-helpers'; +import { SnapsSection } from './snaps-section'; + +const additionalMockState = { + insights: { + [unapprovedPersonalSignMsg.id]: { + 'npm:@metamask/test-snap-bip32': { + snapId: 'npm:@metamask/test-snap-bip32', + loading: false, + interfaceId: 'interface-id', + }, + }, + [unapprovedTypedSignMsgV3.id]: { + 'npm:@metamask/test-snap-bip32': { + snapId: 'npm:@metamask/test-snap-bip32', + loading: false, + interfaceId: 'interface-id2', + }, + }, + }, + interfaces: { + 'interface-id': { + snapId: 'npm:@metamask/test-snap-bip32', + content: Text({ children: 'Hello world!' }), + state: {}, + context: null, + }, + 'interface-id2': { + snapId: 'npm:@metamask/test-snap-bip32', + content: Text({ children: 'Hello world again!' }), + state: {}, + context: null, + }, + }, +}; + +describe('SnapsSection', () => { + it('renders section personal sign request', () => { + const state = { + ...mockState, + confirm: { + currentConfirmation: unapprovedPersonalSignMsg, + }, + metamask: { + ...mockState.metamask, + ...additionalMockState, + }, + }; + const mockStore = configureMockStore([])(state); + const { container, getByText } = renderWithProvider( + , + mockStore, + ); + + fireEvent.click(getByText('Insights from')); + + expect(container).toMatchSnapshot(); + expect(getByText('Hello world!')).toBeDefined(); + }); + + it('renders section for typed sign request', () => { + const state = { + ...mockState, + confirm: { + currentConfirmation: unapprovedTypedSignMsgV3, + }, + metamask: { + ...mockState.metamask, + ...additionalMockState, + }, + }; + const mockStore = configureMockStore([])(state); + const { container, getByText } = renderWithProvider( + , + mockStore, + ); + + fireEvent.click(getByText('Insights from')); + + expect(container).toMatchSnapshot(); + expect(getByText('Hello world again!')).toBeDefined(); + }); +}); diff --git a/ui/pages/confirmations/components/confirm/snaps/snaps-section/snaps-section.tsx b/ui/pages/confirmations/components/confirm/snaps/snaps-section/snaps-section.tsx new file mode 100644 index 000000000000..f09b4acccf53 --- /dev/null +++ b/ui/pages/confirmations/components/confirm/snaps/snaps-section/snaps-section.tsx @@ -0,0 +1,37 @@ +import React from 'react'; +import { useSelector } from 'react-redux'; +import { currentConfirmationSelector } from '../../../../selectors'; +import { useInsightSnaps } from '../../../../../../hooks/snaps/useInsightSnaps'; +import { Box } from '../../../../../../components/component-library'; +import { + Display, + FlexDirection, +} from '../../../../../../helpers/constants/design-system'; +import { SnapInsight } from './snap-insight'; + +export const SnapsSection = () => { + const currentConfirmation = useSelector(currentConfirmationSelector); + const { data } = useInsightSnaps(currentConfirmation?.id); + + if (data.length === 0) { + return null; + } + + return ( + + {data.map(({ snapId, interfaceId, loading }) => ( + + ))} + + ); +}; diff --git a/ui/pages/confirmations/components/confirm/title/title.test.tsx b/ui/pages/confirmations/components/confirm/title/title.test.tsx index f0f330653e5c..15fe629243e4 100644 --- a/ui/pages/confirmations/components/confirm/title/title.test.tsx +++ b/ui/pages/confirmations/components/confirm/title/title.test.tsx @@ -76,11 +76,6 @@ describe('ConfirmTitle', () => { const { getByText } = renderWithProvider(, mockStore); expect(getByText('Transaction request')).toBeInTheDocument(); - expect( - getByText( - 'Only confirm this transaction if you fully understand the content and trust the requesting site.', - ), - ).toBeInTheDocument(); }); describe('Alert banner', () => { diff --git a/ui/pages/confirmations/components/confirm/title/title.tsx b/ui/pages/confirmations/components/confirm/title/title.tsx index dcdc2007a530..5d1bf2cf8c4e 100644 --- a/ui/pages/confirmations/components/confirm/title/title.tsx +++ b/ui/pages/confirmations/components/confirm/title/title.tsx @@ -60,6 +60,10 @@ const getTitle = (t: IntlFunction, confirmation?: Confirmation) => { switch (confirmation?.type) { case TransactionType.contractInteraction: return t('confirmTitleTransaction'); + case TransactionType.tokenMethodApprove: + return t('confirmTitleApproveTransaction'); + case TransactionType.deployContract: + return t('confirmTitleDeployContract'); case TransactionType.personalSign: if (isSIWESignatureRequest(confirmation as SignatureRequestType)) { return t('confirmTitleSIWESignature'); @@ -77,7 +81,11 @@ const getTitle = (t: IntlFunction, confirmation?: Confirmation) => { const getDescription = (t: IntlFunction, confirmation?: Confirmation) => { switch (confirmation?.type) { case TransactionType.contractInteraction: - return t('confirmTitleDescContractInteractionTransaction'); + return ''; + case TransactionType.tokenMethodApprove: + return t('confirmTitleDescApproveTransaction'); + case TransactionType.deployContract: + return t('confirmTitleDescDeployContract'); case TransactionType.personalSign: if (isSIWESignatureRequest(confirmation as SignatureRequestType)) { return t('confirmTitleDescSIWESignature'); diff --git a/ui/pages/confirmations/components/edit-gas-fee-popover/edit-gas-fee-popover.test.js b/ui/pages/confirmations/components/edit-gas-fee-popover/edit-gas-fee-popover.test.js index 35f01bf36f1b..a8eae416fc7a 100644 --- a/ui/pages/confirmations/components/edit-gas-fee-popover/edit-gas-fee-popover.test.js +++ b/ui/pages/confirmations/components/edit-gas-fee-popover/edit-gas-fee-popover.test.js @@ -11,12 +11,9 @@ import { renderWithProvider } from '../../../../../test/lib/render-helpers'; import configureStore from '../../../../store/store'; import { GasFeeContextProvider } from '../../../../contexts/gasFee'; -import { - NETWORK_TYPES, - CHAIN_IDS, - GOERLI_DISPLAY_NAME, -} from '../../../../../shared/constants/network'; +import { CHAIN_IDS } from '../../../../../shared/constants/network'; import { ETH_EOA_METHODS } from '../../../../../shared/constants/eth-methods'; +import { mockNetworkState } from '../../../../../test/stub/networks'; import EditGasFeePopover from './edit-gas-fee-popover'; jest.mock('../../../../store/actions', () => ({ @@ -67,20 +64,7 @@ const render = async ({ txProps, contextProps } = {}) => { const store = configureStore({ metamask: { currencyRates: {}, - providerConfig: { - chainId: CHAIN_IDS.GOERLI, - nickname: GOERLI_DISPLAY_NAME, - type: NETWORK_TYPES.GOERLI, - }, - selectedNetworkClientId: 'goerli', - networksMetadata: { - goerli: { - EIPS: { - 1559: true, - }, - status: 'available', - }, - }, + ...mockNetworkState({ chainId: CHAIN_IDS.GOERLI, ticker: 'ETH' }), accountsByChainId: { [CHAIN_IDS.GOERLI]: { '0xAddress': { address: '0xAddress', balance: '0x1F4' }, diff --git a/ui/pages/confirmations/components/edit-gas-fee-popover/edit-gas-item/edit-gas-item.test.js b/ui/pages/confirmations/components/edit-gas-fee-popover/edit-gas-item/edit-gas-item.test.js index c0c51d260e7a..95bd45506003 100644 --- a/ui/pages/confirmations/components/edit-gas-fee-popover/edit-gas-item/edit-gas-item.test.js +++ b/ui/pages/confirmations/components/edit-gas-fee-popover/edit-gas-item/edit-gas-item.test.js @@ -9,11 +9,8 @@ import { renderWithProvider } from '../../../../../../test/lib/render-helpers'; import configureStore from '../../../../../store/store'; import { GasFeeContextProvider } from '../../../../../contexts/gasFee'; -import { - CHAIN_IDS, - GOERLI_DISPLAY_NAME, - NETWORK_TYPES, -} from '../../../../../../shared/constants/network'; +import { CHAIN_IDS } from '../../../../../../shared/constants/network'; +import { mockNetworkState } from '../../../../../../test/stub/networks'; import EditGasItem from './edit-gas-item'; jest.mock('../../../../../store/actions', () => ({ @@ -65,20 +62,7 @@ const render = async ({ const store = configureStore({ metamask: { currencyRates: {}, - providerConfig: { - chainId: CHAIN_IDS.GOERLI, - nickname: GOERLI_DISPLAY_NAME, - type: NETWORK_TYPES.GOERLI, - }, - selectedNetworkClientId: 'goerli', - networkConfigurations: { - goerli: { - type: 'rpc', - chainId: '0x5', - ticker: 'ETH', - id: 'goerli', - }, - }, + ...mockNetworkState({ chainId: CHAIN_IDS.GOERLI, ticker: 'ETH' }), accountsByChainId: { [CHAIN_IDS.GOERLI]: { '0xAddress': { diff --git a/ui/pages/confirmations/components/edit-gas-fee-popover/edit-gas-tooltip/edit-gas-tooltip.test.js b/ui/pages/confirmations/components/edit-gas-fee-popover/edit-gas-tooltip/edit-gas-tooltip.test.js index 6d34b64ac371..558190bb7dd3 100644 --- a/ui/pages/confirmations/components/edit-gas-fee-popover/edit-gas-tooltip/edit-gas-tooltip.test.js +++ b/ui/pages/confirmations/components/edit-gas-fee-popover/edit-gas-tooltip/edit-gas-tooltip.test.js @@ -3,6 +3,8 @@ import { act } from '@testing-library/react'; import configureStore from '../../../../../store/store'; import { renderWithProvider } from '../../../../../../test/jest'; import { GasFeeContextProvider } from '../../../../../contexts/gasFee'; +import { mockNetworkState } from '../../../../../../test/stub/networks'; +import { CHAIN_IDS } from '../../../../../../shared/constants/network'; import EditGasToolTip from './edit-gas-tooltip'; jest.mock('../../../../../store/actions', () => ({ @@ -36,7 +38,7 @@ const HIGH_GAS_OPTION = { const render = async (componentProps) => { const mockStore = { metamask: { - providerConfig: {}, + ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), accountsByChainId: { '0x1': { '0xAddress': { diff --git a/ui/pages/confirmations/components/fee-details-component/fee-details-component.test.js b/ui/pages/confirmations/components/fee-details-component/fee-details-component.test.js index f6fc7ecdff13..99f7812a4978 100644 --- a/ui/pages/confirmations/components/fee-details-component/fee-details-component.test.js +++ b/ui/pages/confirmations/components/fee-details-component/fee-details-component.test.js @@ -1,9 +1,10 @@ import React from 'react'; import { act, screen } from '@testing-library/react'; import configureStore from 'redux-mock-store'; -import { CHAIN_IDS } from '../../../../../shared/constants/network'; import mockState from '../../../../../test/data/mock-state.json'; import { renderWithProvider } from '../../../../../test/lib/render-helpers'; +import { mockNetworkState } from '../../../../../test/stub/networks'; +import { CHAIN_IDS } from '../../../../../shared/constants/network'; import FeeDetailsComponent from './fee-details-component'; jest.mock('../../../../store/actions', () => ({ @@ -38,9 +39,7 @@ describe('FeeDetailsComponent', () => { ...mockState, metamask: { ...mockState.metamask, - providerConfig: { - chainId: CHAIN_IDS.OPTIMISM, - }, + ...mockNetworkState({ chainId: CHAIN_IDS.OPTIMISM }), }, }); expect(screen.queryByText('Fee details')).toBeInTheDocument(); @@ -51,9 +50,7 @@ describe('FeeDetailsComponent', () => { ...mockState, metamask: { ...mockState.metamask, - providerConfig: { - chainId: CHAIN_IDS.OPTIMISM, - }, + ...mockNetworkState({ chainId: CHAIN_IDS.OPTIMISM }), }, }); expect(screen.queryByTitle('0 ETH')).not.toBeInTheDocument(); @@ -74,17 +71,7 @@ describe('FeeDetailsComponent', () => { 1559: false, }, }, - networksMetadata: { - goerli: { - EIPS: { - 1559: false, - }, - status: 'available', - }, - }, - providerConfig: { - chainId: CHAIN_IDS.OPTIMISM, - }, + ...mockNetworkState({ chainId: CHAIN_IDS.OPTIMISM }), }, }); expect(screen.queryByText('Fee details')).toBeInTheDocument(); @@ -100,17 +87,7 @@ describe('FeeDetailsComponent', () => { 1559: false, }, }, - networksMetadata: { - goerli: { - EIPS: { - 1559: false, - }, - status: 'available', - }, - }, - providerConfig: { - chainId: CHAIN_IDS.OPTIMISM, - }, + ...mockNetworkState({ chainId: CHAIN_IDS.OPTIMISM }), }, }); await act(async () => { diff --git a/ui/pages/confirmations/components/gas-timing/gas-timing.component.js b/ui/pages/confirmations/components/gas-timing/gas-timing.component.js index e847c0baf1e2..4291797bbc2a 100644 --- a/ui/pages/confirmations/components/gas-timing/gas-timing.component.js +++ b/ui/pages/confirmations/components/gas-timing/gas-timing.component.js @@ -135,7 +135,6 @@ export default function GasTiming({ const estimateEmoji = PRIORITY_LEVEL_ICON_MAP[estimateToUse]; let text = `${estimateEmoji} ${t(estimateToUse)}`; let time = ''; - let attitude = 'positive'; if (estimateToUse === 'low') { text = `${estimateEmoji} ${t('gasTimingLow')}`; @@ -159,9 +158,6 @@ export default function GasTiming({ // If the user has chosen a value less than our low estimate, // calculate a potential wait time - if (estimateToUse === 'low') { - attitude = 'negative'; - } // If we didn't get any useful information, show the // "unknown processing time" message if ( @@ -170,7 +166,6 @@ export default function GasTiming({ customEstimatedTime?.upperTimeBound === 'unknown' ) { text = t('editGasTooLow'); - attitude = 'negative'; } else { time = toHumanReadableTime( Number(customEstimatedTime?.upperTimeBound), @@ -181,32 +176,21 @@ export default function GasTiming({ time = toHumanReadableTime(low.maxWaitTimeEstimate, t); } - const getColorFromAttitude = () => { - switch (attitude) { - case 'positive': - return TextColor.successDefault; - case 'warning': - return TextColor.warningDefault; - case 'negative': - return TextColor.errorDefault; - default: - return TextColor.successDefault; - } - }; - return ( {text} - - ~{time} - + {time && ( + + ~{time} + + )} ); } diff --git a/ui/pages/confirmations/components/signature-request-header/__snapshots__/signature-request-header.test.js.snap b/ui/pages/confirmations/components/signature-request-header/__snapshots__/signature-request-header.test.js.snap index c2df41b7f906..ef1797f6e8b5 100644 --- a/ui/pages/confirmations/components/signature-request-header/__snapshots__/signature-request-header.test.js.snap +++ b/ui/pages/confirmations/components/signature-request-header/__snapshots__/signature-request-header.test.js.snap @@ -62,7 +62,7 @@ exports[`SignatureRequestHeader should match snapshot 1`] = ` - G + C
@@ -73,7 +73,7 @@ exports[`SignatureRequestHeader should match snapshot 1`] = ` class="mm-box mm-text mm-text--body-sm mm-box--color-text-alternative" data-testid="signature-request-network-display" > - goerli + Chain 5
- G + C
@@ -149,7 +149,7 @@ exports[`SignatureRequestOriginal should match snapshot 1`] = ` class="mm-box mm-text mm-text--body-sm mm-box--color-text-alternative" data-testid="signature-request-network-display" > - goerli + Chain 5
- G + C
@@ -146,7 +146,7 @@ exports[`SignatureRequestSIWE (Sign in with Ethereum) should match snapshot 1`] class="mm-box mm-text mm-text--body-sm mm-box--color-text-alternative" data-testid="signature-request-network-display" > - goerli + Chain 5
{ transactions: [ ...mockStoreInitialState.metamask.transactions, { - chainId: mockStoreInitialState.metamask.providerConfig.chainId, + chainId: getCurrentChainId(mockStoreInitialState), status: 'unapproved', }, ], diff --git a/ui/pages/confirmations/components/signature-request/signature-request-data/signature-request-data.test.js b/ui/pages/confirmations/components/signature-request/signature-request-data/signature-request-data.test.js index 8d7714361349..3d0d118158c5 100644 --- a/ui/pages/confirmations/components/signature-request/signature-request-data/signature-request-data.test.js +++ b/ui/pages/confirmations/components/signature-request/signature-request-data/signature-request-data.test.js @@ -5,6 +5,8 @@ import { renderWithProvider } from '../../../../../../test/lib/render-helpers'; import { sanitizeMessage } from '../../../../../helpers/utils/util'; import Identicon from '../../../../../components/ui/identicon'; import { ETH_EOA_METHODS } from '../../../../../../shared/constants/eth-methods'; +import { CHAIN_IDS } from '../../../../../../shared/constants/network'; +import { mockNetworkState } from '../../../../../../test/stub/networks'; import SignatureRequestData from './signature-request-data'; describe('Signature Request Data', () => { @@ -36,10 +38,7 @@ describe('Signature Request Data', () => { unlisted: false, }, }, - providerConfig: { - type: 'test', - chainId: '0x5', - }, + ...mockNetworkState({ chainId: CHAIN_IDS.GOERLI }), internalAccounts: { accounts: { 'cf8dace4-9439-4bd4-b3a8-88c821c8fcb3': { diff --git a/ui/pages/confirmations/components/signature-request/signature-request.test.js b/ui/pages/confirmations/components/signature-request/signature-request.test.js index 7277be117a9e..ccaa34c749dc 100644 --- a/ui/pages/confirmations/components/signature-request/signature-request.test.js +++ b/ui/pages/confirmations/components/signature-request/signature-request.test.js @@ -26,6 +26,7 @@ import { pendingApprovalsSortedSelector, } from '../../../../selectors'; import { ETH_EOA_METHODS } from '../../../../../shared/constants/eth-methods'; +import { mockNetworkState } from '../../../../../test/stub/networks'; import SignatureRequest from './signature-request'; const baseProps = { @@ -37,14 +38,12 @@ const baseProps = { }; const mockStore = { metamask: { - providerConfig: { + ...mockNetworkState({ chainId: '0x539', nickname: 'Localhost 8545', - rpcPrefs: {}, rpcUrl: 'http://localhost:8545', ticker: 'ETH', - type: 'rpc', - }, + }), preferences: { useNativeCurrencyAsPrimaryCurrency: true, }, @@ -121,17 +120,17 @@ const generateUseSelectorRouter = (opts) => (selector) => { switch (selector) { case getProviderConfig: - return opts.metamask.providerConfig; + return getProviderConfig(opts); case getCurrentCurrency: return opts.metamask.currentCurrency; case getNativeCurrency: - return opts.metamask.providerConfig.ticker; + return getProviderConfig(opts).ticker; case getTotalUnapprovedMessagesCount: return opts.metamask.unapprovedTypedMessagesCount; case getPreferences: return opts.metamask.preferences; case conversionRateSelector: - return opts.metamask.currencyRates[opts.metamask.providerConfig.ticker] + return opts.metamask.currencyRates[getProviderConfig(opts).ticker] ?.conversionRate; case getSelectedAccount: return mockSelectedInternalAccount; diff --git a/ui/pages/confirmations/components/simulation-details/asset-pill.test.tsx b/ui/pages/confirmations/components/simulation-details/asset-pill.test.tsx index 0e32ca14ecb2..77d49ac6c561 100644 --- a/ui/pages/confirmations/components/simulation-details/asset-pill.test.tsx +++ b/ui/pages/confirmations/components/simulation-details/asset-pill.test.tsx @@ -7,6 +7,7 @@ import { renderWithProvider } from '../../../../../test/lib/render-helpers'; import configureStore from '../../../../store/store'; import { CHAIN_IDS } from '../../../../../shared/constants/network'; import { AvatarNetwork } from '../../../../components/component-library/avatar-network'; +import { mockNetworkState } from '../../../../../test/stub/networks'; import { AssetPill } from './asset-pill'; import { NATIVE_ASSET_IDENTIFIER, TokenAssetIdentifier } from './types'; @@ -54,7 +55,9 @@ describe('AssetPill', () => { expected: { ticker: string; imgSrc: string }; }) => { const store = configureStore({ - metamask: { providerConfig: { chainId, ticker: expected.ticker } }, + metamask: { + ...mockNetworkState({ chainId }), + }, }); renderWithProvider( diff --git a/ui/pages/confirmations/components/simulation-details/fiat-display.test.tsx b/ui/pages/confirmations/components/simulation-details/fiat-display.test.tsx index 0eee6aee8990..4110c2e9162c 100644 --- a/ui/pages/confirmations/components/simulation-details/fiat-display.test.tsx +++ b/ui/pages/confirmations/components/simulation-details/fiat-display.test.tsx @@ -5,15 +5,14 @@ import { merge } from 'lodash'; import { useFiatFormatter } from '../../../../hooks/useFiatFormatter'; import { renderWithProvider } from '../../../../../test/lib/render-helpers'; import mockState from '../../../../../test/data/mock-state.json'; +import { mockNetworkState } from '../../../../../test/stub/networks'; import { CHAIN_IDS } from '../../../../../shared/constants/network'; import { IndividualFiatDisplay, TotalFiatDisplay } from './fiat-display'; import { FIAT_UNAVAILABLE } from './types'; const mockStateWithTestnet = merge({}, mockState, { metamask: { - providerConfig: { - chainId: CHAIN_IDS.SEPOLIA, - }, + ...mockNetworkState({ chainId: CHAIN_IDS.SEPOLIA }), }, }); diff --git a/ui/pages/confirmations/components/simulation-details/fiat-display.tsx b/ui/pages/confirmations/components/simulation-details/fiat-display.tsx index 1481850770ba..c517db74d171 100644 --- a/ui/pages/confirmations/components/simulation-details/fiat-display.tsx +++ b/ui/pages/confirmations/components/simulation-details/fiat-display.tsx @@ -7,6 +7,7 @@ import { import { useI18nContext } from '../../../../hooks/useI18nContext'; import { Text } from '../../../../components/component-library'; import { SizeNumber } from '../../../../components/component-library/box/box.types'; +import Tooltip from '../../../../components/ui/tooltip'; import { useFiatFormatter } from '../../../../hooks/useFiatFormatter'; import { useHideFiatForTestnet } from '../../../../hooks/useHideFiatForTestnet'; import { FIAT_UNAVAILABLE, FiatAmount } from './types'; @@ -51,10 +52,17 @@ export const IndividualFiatDisplay: React.FC<{ return ; } const absFiat = Math.abs(fiatAmount); + const fiatDisplayValue = fiatFormatter(absFiat, { shorten }); - return ( + return shorten ? ( + + + {fiatDisplayValue} + + + ) : ( - {fiatFormatter(absFiat, { shorten })} + {fiatDisplayValue} ); }; diff --git a/ui/pages/confirmations/components/simulation-details/formatAmount.ts b/ui/pages/confirmations/components/simulation-details/formatAmount.ts index 8e63c44aa0bc..213b55d52083 100644 --- a/ui/pages/confirmations/components/simulation-details/formatAmount.ts +++ b/ui/pages/confirmations/components/simulation-details/formatAmount.ts @@ -4,51 +4,11 @@ import { DEFAULT_PRECISION, } from '../../../../hooks/useCurrencyDisplay'; -const MAX_ELLIPSIS_LEFT_DIGITS = 15; - // The number of significant decimals places to show for amounts less than 1. const MAX_SIGNIFICANT_DECIMAL_PLACES = 3; const ZERO_DISPLAY = '0'; -/** - * This function receives an formatted number and will append an ellipsis if the number of digits - * is greater than MAX_LEFT_DIGITS. Currently, we're only supporting en-US format. When we support - * i18n numbers, we'll need to update this method to support i18n. - * - * This method has been designed to receive results of formatAmount. - * - * There is no need to format the decimal portion because formatAmount shaves the portions off - * accordingly. - * - * @param amountText - * @param maxLeftDigits - */ -export function ellipsisAmountText( - amountText: string, - maxLeftDigits: number = MAX_ELLIPSIS_LEFT_DIGITS, -): string { - const [integerPart] = amountText.split('.'); - const cleanIntegerPart = integerPart.replace(/,/gu, ''); - - if (cleanIntegerPart.length > maxLeftDigits) { - let result = ''; - let digitCount = 0; - - for (let i = 0; digitCount < maxLeftDigits; i++) { - const integerChar = integerPart[i]; - result += integerChar; - if (integerChar !== ',') { - digitCount += 1; - } - } - - return `${result}...`; - } - - return amountText; -} - export function formatAmountMaxPrecision( locale: string, num: number | BigNumber, diff --git a/ui/pages/confirmations/components/simulation-details/simulation-details.stories.tsx b/ui/pages/confirmations/components/simulation-details/simulation-details.stories.tsx index 99dbc7ad0c90..15bfab8a2428 100644 --- a/ui/pages/confirmations/components/simulation-details/simulation-details.stories.tsx +++ b/ui/pages/confirmations/components/simulation-details/simulation-details.stories.tsx @@ -11,6 +11,7 @@ import { } from '@metamask/transaction-controller'; import { NameType } from '@metamask/name-controller'; import { CHAIN_IDS } from '../../../../../shared/constants/network'; +import { mockNetworkState } from '../../../../../test/stub/networks'; const DUMMY_BALANCE_CHANGE = { previousBalance: '0xIGNORED' as Hex, @@ -30,10 +31,7 @@ const storeMock = configureStore({ ...mockState.metamask.preferences, useNativeCurrencyAsPrimaryCurrency: false, }, - providerConfig: { - ...mockState.metamask.providerConfig, - chainId: CHAIN_ID_MOCK, - }, + ...mockNetworkState({ chainId: CHAIN_ID_MOCK }), useTokenDetection: true, tokenList: { [ERC20_TOKEN_1_MOCK]: { @@ -76,22 +74,14 @@ const storeMock = configureStore({ const storeMockPolygon = configureStore({ metamask: { ...mockState.metamask, - providerConfig: { - ...mockState.metamask.providerConfig, - chainId: CHAIN_IDS.POLYGON, - ticker: 'MATIC', - }, + ...mockNetworkState({ chainId: CHAIN_IDS.POLYGON }), }, }); const storeMockArbitrum = configureStore({ metamask: { ...mockState.metamask, - providerConfig: { - ...mockState.metamask.providerConfig, - chainId: CHAIN_IDS.ARBITRUM, - ticker: 'ETH', - }, + ...mockNetworkState({ chainId: CHAIN_IDS.ARBITRUM }), }, }); diff --git a/ui/pages/confirmations/components/simulation-details/simulation-details.tsx b/ui/pages/confirmations/components/simulation-details/simulation-details.tsx index 1a19e9f41adf..79d5c2105a9c 100644 --- a/ui/pages/confirmations/components/simulation-details/simulation-details.tsx +++ b/ui/pages/confirmations/components/simulation-details/simulation-details.tsx @@ -1,28 +1,30 @@ -import React from 'react'; import { SimulationData, SimulationError, SimulationErrorCode, } from '@metamask/transaction-controller'; +import React from 'react'; import { Box, Icon, IconName, + IconSize, Text, } from '../../../../components/component-library'; +import Preloader from '../../../../components/ui/icon/preloader/preloader-icon.component'; +import Tooltip from '../../../../components/ui/tooltip'; import { AlignItems, BorderColor, BorderRadius, Display, FlexDirection, + IconColor, JustifyContent, TextColor, TextVariant, } from '../../../../helpers/constants/design-system'; -import InfoTooltip from '../../../../components/ui/info-tooltip/info-tooltip'; import { useI18nContext } from '../../../../hooks/useI18nContext'; -import Preloader from '../../../../components/ui/icon/preloader/preloader-icon.component'; import { BalanceChangeList } from './balance-change-list'; import { useBalanceChanges } from './useBalanceChanges'; import { useSimulationMetrics } from './useSimulationMetrics'; @@ -111,10 +113,23 @@ const HeaderLayout: React.FC = ({ children }) => { {t('simulationDetailsTitle')} - + containerClassName="info-tooltip__tooltip-container" + tooltipInnerClassName="info-tooltip__tooltip-content" + tooltipArrowClassName="info-tooltip__top-tooltip-arrow" + html={t('simulationDetailsTitleTooltip')} + theme="tippy-tooltip-info" + style={{ display: Display.Flex }} + > + + {children} diff --git a/ui/pages/confirmations/components/transaction-alerts/transaction-alerts.stories.js b/ui/pages/confirmations/components/transaction-alerts/transaction-alerts.stories.js index c74dc5f1b005..ae46a4a83903 100644 --- a/ui/pages/confirmations/components/transaction-alerts/transaction-alerts.stories.js +++ b/ui/pages/confirmations/components/transaction-alerts/transaction-alerts.stories.js @@ -2,12 +2,12 @@ import React from 'react'; import { Provider } from 'react-redux'; import { keccak } from 'ethereumjs-util'; import { cloneDeep } from 'lodash'; -import { NetworkType } from '@metamask/controller-utils'; -import { NetworkStatus } from '@metamask/network-controller'; import { GasFeeContextProvider } from '../../../../contexts/gasFee'; import configureStore from '../../../../store/store'; import testData from '../../../../../.storybook/test-data'; import { getSelectedInternalAccountFromMockState } from '../../../../../test/jest/mocks'; +import { CHAIN_IDS } from '../../../../../shared/constants/network'; +import { mockNetworkState } from '../../../../../test/stub/networks'; import TransactionAlerts from '.'; const mockSelectedInternalAccount = @@ -24,7 +24,7 @@ const customTransaction = ({ userFeeLevel: estimateUsed ? 'low' : 'medium', blockNumber: `${10902987 + i}`, id: 4678200543090545 + i, - chainId: testData?.metamask?.providerConfig?.chainId, + chainId: '0x1', status: 'confirmed', time: 1600654021000, txParams: { @@ -63,17 +63,14 @@ const customStore = ({ networkCongestion: isNetworkBusy ? 1 : 0.1, }, // supportsEIP1559 - selectedNetworkClientId: NetworkType.mainnet, - networksMetadata: { - ...testData?.metamask?.networksMetadata, - [NetworkType.mainnet]: { + ...mockNetworkState({ + chainId: CHAIN_IDS.MAINNET, + metadata: { EIPS: { - ...testData?.metamask?.networksMetadata?.EIPS, 1559: Boolean(supportsEIP1559), }, - status: NetworkStatus.Available, }, - }, + }), // pendingTransactions featureFlags: { ...testData?.metamask?.featureFlags, diff --git a/ui/pages/confirmations/confirm-approve/confirm-approve-content/confirm-approve-content.component.test.js b/ui/pages/confirmations/confirm-approve/confirm-approve-content/confirm-approve-content.component.test.js index 9ebabe25ec9e..916da9c1c58b 100644 --- a/ui/pages/confirmations/confirm-approve/confirm-approve-content/confirm-approve-content.component.test.js +++ b/ui/pages/confirmations/confirm-approve/confirm-approve-content/confirm-approve-content.component.test.js @@ -4,12 +4,13 @@ import { fireEvent } from '@testing-library/react'; import { renderWithProvider } from '../../../../../test/jest/rendering'; import { TokenStandard } from '../../../../../shared/constants/transaction'; import { BlockaidResultType } from '../../../../../shared/constants/security-provider'; +import { mockNetworkState } from '../../../../../test/stub/networks'; import ConfirmApproveContent from '.'; const renderComponent = (props) => { const store = configureMockStore([])({ metamask: { - providerConfig: { chainId: '0x0' }, + ...mockNetworkState({ chainId: '0x0' }), preferences: { useNativeCurrencyAsPrimaryCurrency: true, }, diff --git a/ui/pages/confirmations/confirm-send-ether/__snapshots__/confirm-send-ether.test.js.snap b/ui/pages/confirmations/confirm-send-ether/__snapshots__/confirm-send-ether.test.js.snap index bc7049ab2173..42f9a93c1d4f 100644 --- a/ui/pages/confirmations/confirm-send-ether/__snapshots__/confirm-send-ether.test.js.snap +++ b/ui/pages/confirmations/confirm-send-ether/__snapshots__/confirm-send-ether.test.js.snap @@ -301,7 +301,7 @@ exports[`ConfirmSendEther should render correct information for for confirm send
- g + C
Estimated changes

-
-
-
- - - -
+
+
+
@@ -510,20 +500,11 @@ exports[`ConfirmSendEther should render correct information for for confirm send class="mm-box mm-box--display-flex mm-box--flex-wrap-wrap" >

Unknown processing time

-

- - ~ - - -

+
{ unapprovedTypedMessages, ]); - const { warnings } = useSignatureInsights({ txData }); + const { warnings } = useInsightSnaps(txData.id); const resolvedSecurityAlertResponse = signatureSecurityAlertResponses?.[ txData.securityAlertResponse?.securityAlertId diff --git a/ui/pages/confirmations/confirm-signature-request/index.test.js b/ui/pages/confirmations/confirm-signature-request/index.test.js index a90e23aea1f4..504e2d6a412c 100644 --- a/ui/pages/confirmations/confirm-signature-request/index.test.js +++ b/ui/pages/confirmations/confirm-signature-request/index.test.js @@ -3,6 +3,8 @@ import configureMockStore from 'redux-mock-store'; import { EthAccountType } from '@metamask/keyring-api'; import { renderWithProvider } from '../../../../test/lib/render-helpers'; import { ETH_EOA_METHODS } from '../../../../shared/constants/eth-methods'; +import { CHAIN_IDS } from '../../../../shared/constants/network'; +import { mockNetworkState } from '../../../../test/stub/networks'; import ConfTx from '.'; const mockState = { @@ -25,10 +27,13 @@ const mockState = { }, }, unapprovedTypedMessagesCount: 1, - providerConfig: { chainId: '0x5', type: 'goerli' }, + ...mockNetworkState({ + chainId: CHAIN_IDS.GOERLI, + nickname: 'Goerli test network', + ticker: undefined, + }), currencyRates: {}, keyrings: [], - networkConfigurations: {}, subjectMetadata: {}, accountsByChainId: { '0x5': {}, diff --git a/ui/pages/confirmations/confirm-transaction-base/__snapshots__/confirm-transaction-base.test.js.snap b/ui/pages/confirmations/confirm-transaction-base/__snapshots__/confirm-transaction-base.test.js.snap index 63bf71e0c409..977b1eb3ec5a 100644 --- a/ui/pages/confirmations/confirm-transaction-base/__snapshots__/confirm-transaction-base.test.js.snap +++ b/ui/pages/confirmations/confirm-transaction-base/__snapshots__/confirm-transaction-base.test.js.snap @@ -320,29 +320,19 @@ exports[`Confirm Transaction Base should match snapshot 1`] = ` > Estimated changes

-
-
-
- - - -
+
+
+
diff --git a/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.component.js b/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.component.js index 5330a3685d47..f344a9392455 100644 --- a/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.component.js +++ b/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.component.js @@ -134,6 +134,7 @@ export default class ConfirmTransactionBase extends Component { image: PropTypes.string, type: PropTypes.string, getNextNonce: PropTypes.func, + setNextNonce: PropTypes.func, nextNonce: PropTypes.number, tryReverseResolveAddress: PropTypes.func.isRequired, hideSenderToRecipient: PropTypes.bool, @@ -696,12 +697,14 @@ export default class ConfirmTransactionBase extends Component { history, mostRecentOverviewPage, updateCustomNonce, + setNextNonce, } = this.props; this._removeBeforeUnload(); - updateCustomNonce(''); await cancelTransaction(txData); history.push(mostRecentOverviewPage); + updateCustomNonce(''); + setNextNonce(''); } handleSubmit() { @@ -723,6 +726,7 @@ export default class ConfirmTransactionBase extends Component { history, mostRecentOverviewPage, updateCustomNonce, + setNextNonce, methodData, maxFeePerGas, customTokenAmount, @@ -786,6 +790,9 @@ export default class ConfirmTransactionBase extends Component { sendTransaction(txData, false, loadingIndicatorMessage) .then(() => { + updateCustomNonce(''); + setNextNonce(''); + if (!this._isMounted) { return; } @@ -796,11 +803,12 @@ export default class ConfirmTransactionBase extends Component { }, () => { history.push(mostRecentOverviewPage); - updateCustomNonce(''); }, ); }) .catch((error) => { + updateCustomNonce(''); + setNextNonce(''); if (!this._isMounted) { return; } @@ -808,7 +816,6 @@ export default class ConfirmTransactionBase extends Component { submitting: false, submitError: error.message, }); - updateCustomNonce(''); }); }, ); @@ -822,6 +829,7 @@ export default class ConfirmTransactionBase extends Component { history, mostRecentOverviewPage, updateCustomNonce, + setNextNonce, unapprovedTxCount, accountType, isNotification, @@ -894,6 +902,8 @@ export default class ConfirmTransactionBase extends Component { sendTransaction(txData) .then(() => { + updateCustomNonce(''); + setNextNonce(''); if (txData.custodyStatus) { showCustodianDeepLink({ fromAddress, @@ -912,7 +922,6 @@ export default class ConfirmTransactionBase extends Component { } this.setState({ submitting: false }, () => { history.push(mostRecentOverviewPage); - updateCustomNonce(''); }); }, }); @@ -926,12 +935,14 @@ export default class ConfirmTransactionBase extends Component { }, () => { history.push(mostRecentOverviewPage); - updateCustomNonce(''); }, ); } }) .catch((error) => { + updateCustomNonce(''); + setNextNonce(''); + if (!this._isMounted) { return; } @@ -943,7 +954,6 @@ export default class ConfirmTransactionBase extends Component { submitError: error.message, }); setWaitForConfirmDeepLinkDialog(true); - updateCustomNonce(''); }); }, ); diff --git a/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.container.js b/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.container.js index cc3de6095f2d..0efa6fe9c58b 100644 --- a/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.container.js +++ b/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.container.js @@ -29,6 +29,7 @@ import { updateEditableParams, setSwapsFeatureFlags, fetchSmartTransactionsLiveness, + setNextNonce, } from '../../../store/actions'; import { isBalanceSufficient } from '../send/send.utils'; import { shortenAddress, valuesFor } from '../../../helpers/utils/util'; @@ -429,6 +430,7 @@ export const mapDispatchToProps = (dispatch) => { dispatch(fetchSmartTransactionsLiveness()); }, getNextNonce: () => dispatch(getNextNonce()), + setNextNonce: (val) => dispatch(setNextNonce(val)), setDefaultHomeActiveTabName: (tabName) => dispatch(setDefaultHomeActiveTabName(tabName)), updateTransactionGasFees: (gasFees) => { diff --git a/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.test.js b/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.test.js index fc8bbe116069..393280aee3f1 100644 --- a/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.test.js +++ b/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.test.js @@ -3,8 +3,6 @@ import configureMockStore from 'redux-mock-store'; import thunk from 'redux-thunk'; import { fireEvent } from '@testing-library/react'; -import { NetworkType } from '@metamask/controller-utils'; -import { NetworkStatus } from '@metamask/network-controller'; import { EthAccountType } from '@metamask/keyring-api'; import { TransactionStatus, @@ -16,11 +14,7 @@ import { setBackgroundConnection } from '../../../store/background-connection'; import { INITIAL_SEND_STATE_FOR_EXISTING_DRAFT } from '../../../../test/jest/mocks'; import { GasEstimateTypes } from '../../../../shared/constants/gas'; import { KeyringType } from '../../../../shared/constants/keyring'; -import { - CHAIN_IDS, - GOERLI_DISPLAY_NAME, - NETWORK_TYPES, -} from '../../../../shared/constants/network'; +import { CHAIN_IDS } from '../../../../shared/constants/network'; import { domainInitialState } from '../../../ducks/domains'; import { @@ -29,6 +23,7 @@ import { } from '../../../../shared/constants/security-provider'; import { defaultBuyableChains } from '../../../ducks/ramps/constants'; import { ETH_EOA_METHODS } from '../../../../shared/constants/eth-methods'; +import { mockNetworkState } from '../../../../test/stub/networks'; import ConfirmTransactionBase from './confirm-transaction-base.container'; jest.mock('../components/simulation-details/useSimulationMetrics'); @@ -109,24 +104,15 @@ const baseStore = { accounts: ['0x0'], }, ], - selectedNetworkClientId: NetworkType.mainnet, - networksMetadata: { - [NetworkType.mainnet]: { - EIPS: {}, - status: NetworkStatus.Available, - }, - }, + ...mockNetworkState({ + chainId: CHAIN_IDS.GOERLI, + }), tokens: [], preferences: { useNativeCurrencyAsPrimaryCurrency: false, }, currentCurrency: 'USD', currencyRates: {}, - providerConfig: { - chainId: CHAIN_IDS.GOERLI, - nickname: GOERLI_DISPLAY_NAME, - type: NETWORK_TYPES.GOERLI, - }, featureFlags: { sendHexData: false, }, @@ -284,7 +270,15 @@ const render = async ({ props, state } = {}) => { describe('Confirm Transaction Base', () => { it('should match snapshot', async () => { - const { container } = await render(); + const state = { + ...baseStore, + metamask: { + ...baseStore.metamask, + ...mockNetworkState({ chainId: CHAIN_IDS.GOERLI, ticker: undefined }), + }, + }; + + const { container } = await render({ state }); expect(container).toMatchSnapshot(); }); @@ -355,11 +349,9 @@ describe('Confirm Transaction Base', () => { const state = { metamask: { ...baseStore.metamask, - providerConfig: { - ...baseStore.metamask.providerConfig, - chainId: CHAIN_IDS.OPTIMISM, - }, + ...mockNetworkState({ chainId: CHAIN_IDS.OPTIMISM }), }, + confirmTransaction: { ...baseStore.confirmTransaction, txData: { @@ -426,16 +418,10 @@ describe('Confirm Transaction Base', () => { }, }, gasEstimateType: GasEstimateTypes.feeMarket, - selectedNetworkClientId: NetworkType.mainnet, - networksMetadata: { - ...baseStore.metamask.networksMetadata, - [NetworkType.mainnet]: { - EIPS: { - 1559: true, - }, - status: NetworkStatus.Available, - }, - }, + ...mockNetworkState({ + chainId: CHAIN_IDS.GOERLI, + metadata: { EIPS: { 1559: true } }, + }), customGas: { gasLimit: '0x5208', gasPrice: '0x59682f00', @@ -536,16 +522,10 @@ describe('Confirm Transaction Base', () => { }, }, gasEstimateType: GasEstimateTypes.feeMarket, - selectedNetworkClientId: NetworkType.mainnet, - networksMetadata: { - ...baseStore.metamask.networksMetadata, - [NetworkType.mainnet]: { - EIPS: { - 1559: true, - }, - status: NetworkStatus.Available, - }, - }, + ...mockNetworkState({ + chainId: CHAIN_IDS.GOERLI, + metadata: { EIPS: { 1559: true } }, + }), customGas: { gasLimit: '0x5208', gasPrice: '0x59682f00', @@ -675,16 +655,10 @@ describe('Confirm Transaction Base', () => { }, }, gasEstimateType: GasEstimateTypes.feeMarket, - selectedNetworkClientId: NetworkType.mainnet, - networksMetadata: { - ...baseStore.metamask.networksMetadata, - [NetworkType.mainnet]: { - EIPS: { - 1559: true, - }, - status: NetworkStatus.Available, - }, - }, + ...mockNetworkState({ + chainId: CHAIN_IDS.GOERLI, + metadata: { EIPS: { 1559: true } }, + }), customGas: { gasLimit: '0x5208', gasPrice: '0x59682f00', @@ -1016,13 +990,7 @@ describe('Confirm Transaction Base', () => { txParams, }, ], - providerConfig: { - type: NETWORK_TYPES.SEPOLIA, - ticker: 'ETH', - nickname: 'Sepolia', - rpcUrl: '', - chainId: CHAIN_IDS.SEPOLIA, - }, + ...mockNetworkState({ chainId: CHAIN_IDS.SEPOLIA }), }, confirmTransaction: { ...baseStore.confirmTransaction, diff --git a/ui/pages/confirmations/confirm-transaction/confirm-transaction.component.js b/ui/pages/confirmations/confirm-transaction/confirm-transaction.component.js index 27b1e59fbe7a..cb740dd9a99a 100644 --- a/ui/pages/confirmations/confirm-transaction/confirm-transaction.component.js +++ b/ui/pages/confirmations/confirm-transaction/confirm-transaction.component.js @@ -1,7 +1,10 @@ import React, { useCallback, useEffect, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { Route, Switch, useHistory, useParams } from 'react-router-dom'; -import { ORIGIN_METAMASK } from '../../../../shared/constants/app'; +import { + ENVIRONMENT_TYPE_NOTIFICATION, + ORIGIN_METAMASK, +} from '../../../../shared/constants/app'; import Loading from '../../../components/ui/loading-screen'; import { clearConfirmTransaction, @@ -29,6 +32,7 @@ import { use4ByteResolutionSelector, } from '../../../selectors'; import { + endBackgroundTrace, gasFeeStartPollingByNetworkClientId, gasFeeStopPollingByPollingToken, getContractMethodData, @@ -43,6 +47,9 @@ import ConfirmSignatureRequest from '../confirm-signature-request'; import ConfirmTransactionSwitch from '../confirm-transaction-switch'; import Confirm from '../confirm/confirm'; import useCurrentConfirmation from '../hooks/useCurrentConfirmation'; +import { getEnvironmentType } from '../../../../app/scripts/lib/util'; +import { useAsyncResult } from '../../../hooks/useAsyncResult'; +import { TraceName } from '../../../../shared/lib/trace'; import ConfirmTokenTransactionSwitch from './confirm-token-transaction-switch'; const ConfirmTransaction = () => { @@ -88,6 +95,20 @@ const ConfirmTransaction = () => { ]); const { id, type } = transaction; + + const isNotification = getEnvironmentType() === ENVIRONMENT_TYPE_NOTIFICATION; + + useAsyncResult(async () => { + if (!isNotification) { + return undefined; + } + + return await endBackgroundTrace({ + name: TraceName.NotificationDisplay, + id, + }); + }, [id, isNotification]); + const transactionId = id; const isValidTokenMethod = isTokenMethodAction(type); const isValidTransactionId = diff --git a/ui/pages/confirmations/confirm/__snapshots__/confirm.test.tsx.snap b/ui/pages/confirmations/confirm/__snapshots__/confirm.test.tsx.snap index cc1007342e1a..b3053db314a9 100644 --- a/ui/pages/confirmations/confirm/__snapshots__/confirm.test.tsx.snap +++ b/ui/pages/confirmations/confirm/__snapshots__/confirm.test.tsx.snap @@ -141,6 +141,7 @@ exports[`Confirm matches snapshot for signature - personal sign type 1`] = ` >

{ return ( @@ -35,32 +36,34 @@ const Confirm = () => { syncConfirmPath(); return ( - - {/* This context should be removed once we implement the new edit gas fees popovers */} - - - - -