diff --git a/.circleci/config.yml b/.circleci/config.yml index 83756148a9d7..503b5c4c6ed4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -347,7 +347,7 @@ workflows: jobs: trigger-beta-build: - executor: node-browsers-small + executor: node-browsers-medium-plus steps: - run: *shallow-git-clone - run: sudo corepack enable @@ -377,9 +377,9 @@ jobs: - builds-beta create_release_pull_request: - executor: node-browsers-small + executor: node-browsers-medium steps: - - run: *shallow-git-clone + - checkout - run: sudo corepack enable - attach_workspace: at: . @@ -1516,6 +1516,12 @@ jobs: command: | echo "export PARENT_COMMIT=$(git merge-base origin/HEAD HEAD)" >> $BASH_ENV source $BASH_ENV + - run: + name: Set commit message env var + command: | + commit_title=$(git show -s --format='%s' HEAD) + echo "export SHA1_COMMIT_TITLE=\"$commit_title\"" >> $BASH_ENV + source $BASH_ENV - run: name: build:announce command: ./development/metamaskbot-build-announce.js @@ -1530,9 +1536,15 @@ jobs: - run: name: Publish main release to Sentry command: yarn sentry:publish + - run: + name: Publish main MV2 release to Sentry + command: yarn sentry:publish --dist mv2 - run: name: Publish Flask release to Sentry command: yarn sentry:publish --build-type flask + - run: + name: Publish Flask MV2 release to Sentry + command: yarn sentry:publish --build-type flask --dist mv2 - run: name: Publish MMI release to Sentry command: yarn sentry:publish --build-type mmi diff --git a/.circleci/scripts/release-create-gh-release.sh b/.circleci/scripts/release-create-gh-release.sh index 37a654798b9f..29118f57b756 100755 --- a/.circleci/scripts/release-create-gh-release.sh +++ b/.circleci/scripts/release-create-gh-release.sh @@ -66,7 +66,7 @@ then install_github_cli printf '%s\n' 'Creating GitHub Release' - release_body="$(awk -v version="${tag##v}" -f .circleci/scripts/show-changelog.awk CHANGELOG.md)" + release_body="$(awk -v version="[${tag##v}]" -f .circleci/scripts/show-changelog.awk CHANGELOG.md)" hub release create \ --attach builds/metamask-chrome-*.zip \ --attach builds-mv2/metamask-firefox-*.zip \ diff --git a/.github/workflows/update-attributions.yml b/.github/workflows/update-attributions.yml index ca4dbeadeb13..9e2625e3657d 100644 --- a/.github/workflows/update-attributions.yml +++ b/.github/workflows/update-attributions.yml @@ -146,8 +146,6 @@ jobs: git config --global user.email 'metamaskbot@users.noreply.github.com' git commit -am "Update Attributions" git push - env: - GITHUB_TOKEN: ${{ secrets.LAVAMOAT_UPDATE_TOKEN }} - name: Post comment run: | if [[ $HAS_CHANGES == 'true' ]] diff --git a/.metamaskrc.dist b/.metamaskrc.dist index b28f0160f9fb..1a49ba3c3bd7 100644 --- a/.metamaskrc.dist +++ b/.metamaskrc.dist @@ -25,7 +25,8 @@ BLOCKAID_PUBLIC_KEY= ; Set this to true to enable the snap path for the Firefox WebDriver (Linux) ; FIREFOX_SNAP= -ENABLE_CONFIRMATION_REDESIGN= +; Enable the redesigned confirmations still in development, without needing to toggle the developer setting. +; ENABLE_CONFIRMATION_REDESIGN= ; URL of security alerts API used to validate dApp requests ; SECURITY_ALERTS_API_URL='http://localhost:3000' @@ -34,3 +35,7 @@ ENABLE_CONFIRMATION_REDESIGN= ; Enables the Settings Page - Developer Options ; ENABLE_SETTINGS_PAGE_DEV_OPTIONS=true + +; The endpoint used to submit errors and tracing data to Sentry. +; The below is the `test-metamask` project. +; SENTRY_DSN_DEV=https://f59f3dd640d2429d9d0e2445a87ea8e1@sentry.io/273496 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 new file mode 100644 index 000000000000..8961c8f629e6 --- /dev/null +++ b/.yarn/patches/@metamask-assets-controllers-npm-33.0.0-3e7448c4cd.patch @@ -0,0 +1,58 @@ +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 new file mode 100644 index 000000000000..79a007d20474 --- /dev/null +++ b/.yarn/patches/@metamask-keyring-controller-npm-16.1.0-7043d2dc62.patch @@ -0,0 +1,6158 @@ +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-selected-network-controller-npm-13.0.0-b0f6db473a.patch b/.yarn/patches/@metamask-selected-network-controller-npm-13.0.0-b0f6db473a.patch new file mode 100644 index 000000000000..4fddeda9cc57 --- /dev/null +++ b/.yarn/patches/@metamask-selected-network-controller-npm-13.0.0-b0f6db473a.patch @@ -0,0 +1,1469 @@ +diff --git a/dist/SelectedNetworkController.js b/dist/SelectedNetworkController.js +index ca967cd382ba810dadd7ffa914d5a8aceb6f156a..4b4103f6dd6c13e72956daa9557d623ec2c832b6 100644 +--- a/dist/SelectedNetworkController.js ++++ b/dist/SelectedNetworkController.js +@@ -4,12 +4,12 @@ + + + +-var _chunkOGUVGN6Rjs = require('./chunk-OGUVGN6R.js'); ++var _chunkCECZWJ42js = require('./chunk-CECZWJ42.js'); + + + + + + +-exports.METAMASK_DOMAIN = _chunkOGUVGN6Rjs.METAMASK_DOMAIN; exports.SelectedNetworkController = _chunkOGUVGN6Rjs.SelectedNetworkController; exports.SelectedNetworkControllerActionTypes = _chunkOGUVGN6Rjs.SelectedNetworkControllerActionTypes; exports.SelectedNetworkControllerEventTypes = _chunkOGUVGN6Rjs.SelectedNetworkControllerEventTypes; exports.controllerName = _chunkOGUVGN6Rjs.controllerName; ++exports.METAMASK_DOMAIN = _chunkCECZWJ42js.METAMASK_DOMAIN; exports.SelectedNetworkController = _chunkCECZWJ42js.SelectedNetworkController; exports.SelectedNetworkControllerActionTypes = _chunkCECZWJ42js.SelectedNetworkControllerActionTypes; exports.SelectedNetworkControllerEventTypes = _chunkCECZWJ42js.SelectedNetworkControllerEventTypes; exports.controllerName = _chunkCECZWJ42js.controllerName; + //# sourceMappingURL=SelectedNetworkController.js.map +\ No newline at end of file +diff --git a/dist/SelectedNetworkController.mjs b/dist/SelectedNetworkController.mjs +index 5228bbe7acb3da6abb826f00670a1034c51a0514..d4b1f0cf8d8e6123df56be0b766e285968f8587c 100644 +--- a/dist/SelectedNetworkController.mjs ++++ b/dist/SelectedNetworkController.mjs +@@ -4,7 +4,7 @@ import { + SelectedNetworkControllerActionTypes, + SelectedNetworkControllerEventTypes, + controllerName +-} from "./chunk-S4D42VCM.mjs"; ++} from "./chunk-7DSTEJNI.mjs"; + export { + METAMASK_DOMAIN, + SelectedNetworkController, +diff --git a/dist/SelectedNetworkMiddleware.js b/dist/SelectedNetworkMiddleware.js +index 919f26ef57e8c6af5c63d1e0c8e85c43ec241e4a..0b9e434874a1ead6b596fa933597145ee40362d2 100644 +--- a/dist/SelectedNetworkMiddleware.js ++++ b/dist/SelectedNetworkMiddleware.js +@@ -1,8 +1,8 @@ + "use strict";Object.defineProperty(exports, "__esModule", {value: true}); + +-var _chunk6W2ETVOHjs = require('./chunk-6W2ETVOH.js'); +-require('./chunk-OGUVGN6R.js'); ++var _chunkANSSZMDIjs = require('./chunk-ANSSZMDI.js'); ++require('./chunk-CECZWJ42.js'); + + +-exports.createSelectedNetworkMiddleware = _chunk6W2ETVOHjs.createSelectedNetworkMiddleware; ++exports.createSelectedNetworkMiddleware = _chunkANSSZMDIjs.createSelectedNetworkMiddleware; + //# sourceMappingURL=SelectedNetworkMiddleware.js.map +\ No newline at end of file +diff --git a/dist/SelectedNetworkMiddleware.mjs b/dist/SelectedNetworkMiddleware.mjs +index a9031b4aa2589009c2f23d03f1d23a01044c4178..c3302c19f429473963d663e87340b33d9b2caf98 100644 +--- a/dist/SelectedNetworkMiddleware.mjs ++++ b/dist/SelectedNetworkMiddleware.mjs +@@ -1,7 +1,7 @@ + import { + createSelectedNetworkMiddleware +-} from "./chunk-ZY7ETPVE.mjs"; +-import "./chunk-S4D42VCM.mjs"; ++} from "./chunk-HFN7TKJS.mjs"; ++import "./chunk-7DSTEJNI.mjs"; + export { + createSelectedNetworkMiddleware + }; +diff --git a/dist/chunk-6W2ETVOH.js b/dist/chunk-6W2ETVOH.js +deleted file mode 100644 +index 9714addd232767e296dd378a5cc0d57cafc9b882..0000000000000000000000000000000000000000 +--- a/dist/chunk-6W2ETVOH.js ++++ /dev/null +@@ -1,23 +0,0 @@ +-"use strict";Object.defineProperty(exports, "__esModule", {value: true}); +- +-var _chunkOGUVGN6Rjs = require('./chunk-OGUVGN6R.js'); +- +-// src/SelectedNetworkMiddleware.ts +-var createSelectedNetworkMiddleware = (messenger) => { +- const getNetworkClientIdForDomain = (origin) => messenger.call( +- _chunkOGUVGN6Rjs.SelectedNetworkControllerActionTypes.getNetworkClientIdForDomain, +- origin +- ); +- return (req, _, next) => { +- if (!req.origin) { +- throw new Error("Request object is lacking an 'origin'"); +- } +- req.networkClientId = getNetworkClientIdForDomain(req.origin); +- return next(); +- }; +-}; +- +- +- +-exports.createSelectedNetworkMiddleware = createSelectedNetworkMiddleware; +-//# sourceMappingURL=chunk-6W2ETVOH.js.map +\ No newline at end of file +diff --git a/dist/chunk-6W2ETVOH.js.map b/dist/chunk-6W2ETVOH.js.map +deleted file mode 100644 +index 9fe4c1fd6e9b12bfd9091833618334f2c931098f..0000000000000000000000000000000000000000 +--- a/dist/chunk-6W2ETVOH.js.map ++++ /dev/null +@@ -1 +0,0 @@ +-{"version":3,"sources":["../src/SelectedNetworkMiddleware.ts"],"names":[],"mappings":";;;;;AAYO,IAAM,kCAAkC,CAC7C,cAC2C;AAC3C,QAAM,8BAA8B,CAAC,WACnC,UAAU;AAAA,IACR,qCAAqC;AAAA,IACrC;AAAA,EACF;AAEF,SAAO,CAAC,KAA8C,GAAG,SAAS;AAChE,QAAI,CAAC,IAAI,QAAQ;AACf,YAAM,IAAI,MAAM,uCAAuC;AAAA,IACzD;AAEA,QAAI,kBAAkB,4BAA4B,IAAI,MAAM;AAC5D,WAAO,KAAK;AAAA,EACd;AACF","sourcesContent":["import type { JsonRpcMiddleware } from '@metamask/json-rpc-engine';\nimport type { NetworkClientId } from '@metamask/network-controller';\nimport type { Json, JsonRpcParams, JsonRpcRequest } from '@metamask/utils';\n\nimport type { SelectedNetworkControllerMessenger } from './SelectedNetworkController';\nimport { SelectedNetworkControllerActionTypes } from './SelectedNetworkController';\n\nexport type SelectedNetworkMiddlewareJsonRpcRequest = JsonRpcRequest & {\n networkClientId?: NetworkClientId;\n origin?: string;\n};\n\nexport const createSelectedNetworkMiddleware = (\n messenger: SelectedNetworkControllerMessenger,\n): JsonRpcMiddleware => {\n const getNetworkClientIdForDomain = (origin: string) =>\n messenger.call(\n SelectedNetworkControllerActionTypes.getNetworkClientIdForDomain,\n origin,\n );\n\n return (req: SelectedNetworkMiddlewareJsonRpcRequest, _, next) => {\n if (!req.origin) {\n throw new Error(\"Request object is lacking an 'origin'\");\n }\n\n req.networkClientId = getNetworkClientIdForDomain(req.origin);\n return next();\n };\n};\n"]} +\ No newline at end of file +diff --git a/dist/chunk-7DSTEJNI.mjs b/dist/chunk-7DSTEJNI.mjs +new file mode 100644 +index 0000000000000000000000000000000000000000..1902ab28e3c37d563704840e31fdbc885db5354c +--- /dev/null ++++ b/dist/chunk-7DSTEJNI.mjs +@@ -0,0 +1,284 @@ ++var __accessCheck = (obj, member, msg) => { ++ if (!member.has(obj)) ++ throw TypeError("Cannot " + msg); ++}; ++var __privateGet = (obj, member, getter) => { ++ __accessCheck(obj, member, "read from private field"); ++ return getter ? getter.call(obj) : member.get(obj); ++}; ++var __privateAdd = (obj, member, value) => { ++ if (member.has(obj)) ++ throw TypeError("Cannot add the same private member more than once"); ++ member instanceof WeakSet ? member.add(obj) : member.set(obj, value); ++}; ++var __privateSet = (obj, member, value, setter) => { ++ __accessCheck(obj, member, "write to private field"); ++ setter ? setter.call(obj, value) : member.set(obj, value); ++ return value; ++}; ++var __privateMethod = (obj, member, method) => { ++ __accessCheck(obj, member, "access private method"); ++ return method; ++}; ++ ++// src/SelectedNetworkController.ts ++import { BaseController } from "@metamask/base-controller"; ++import { createEventEmitterProxy } from "@metamask/swappable-obj-proxy"; ++var controllerName = "SelectedNetworkController"; ++var stateMetadata = { ++ domains: { persist: true, anonymous: false } ++}; ++var getDefaultState = () => ({ domains: {} }); ++var snapsPrefixes = ["npm:", "local:"]; ++var METAMASK_DOMAIN = "metamask"; ++var SelectedNetworkControllerActionTypes = { ++ getState: `${controllerName}:getState`, ++ getNetworkClientIdForDomain: `${controllerName}:getNetworkClientIdForDomain`, ++ setNetworkClientIdForDomain: `${controllerName}:setNetworkClientIdForDomain` ++}; ++var SelectedNetworkControllerEventTypes = { ++ stateChange: `${controllerName}:stateChange` ++}; ++var _domainProxyMap, _useRequestQueuePreference, _registerMessageHandlers, registerMessageHandlers_fn, _setNetworkClientIdForDomain, setNetworkClientIdForDomain_fn, _unsetNetworkClientIdForDomain, unsetNetworkClientIdForDomain_fn, _domainHasPermissions, domainHasPermissions_fn, _resetAllPermissionedDomains, resetAllPermissionedDomains_fn; ++var SelectedNetworkController = class extends BaseController { ++ /** ++ * Construct a SelectedNetworkController controller. ++ * ++ * @param options - The controller options. ++ * @param options.messenger - The restricted controller messenger for the EncryptionPublicKey controller. ++ * @param options.state - The controllers initial state. ++ * @param options.useRequestQueuePreference - A boolean indicating whether to use the request queue preference. ++ * @param options.onPreferencesStateChange - A callback that is called when the preference state changes. ++ * @param options.domainProxyMap - A map for storing domain-specific proxies that are held in memory only during use. ++ */ ++ constructor({ ++ messenger, ++ state = getDefaultState(), ++ useRequestQueuePreference, ++ onPreferencesStateChange, ++ domainProxyMap ++ }) { ++ super({ ++ name: controllerName, ++ metadata: stateMetadata, ++ messenger, ++ state ++ }); ++ __privateAdd(this, _registerMessageHandlers); ++ __privateAdd(this, _setNetworkClientIdForDomain); ++ /** ++ * This method is used when a domain is removed from the PermissionsController. ++ * It will remove re-point the network proxy to the globally selected network in the domainProxyMap or, if no globally selected network client is available, delete the proxy. ++ * ++ * @param domain - The domain for which to unset the network client ID. ++ */ ++ __privateAdd(this, _unsetNetworkClientIdForDomain); ++ __privateAdd(this, _domainHasPermissions); ++ // Loop through all domains and for those with permissions it points that domain's proxy ++ // to an unproxied instance of the globally selected network client. ++ // NOT the NetworkController's proxy of the globally selected networkClient ++ __privateAdd(this, _resetAllPermissionedDomains); ++ __privateAdd(this, _domainProxyMap, void 0); ++ __privateAdd(this, _useRequestQueuePreference, void 0); ++ __privateSet(this, _useRequestQueuePreference, useRequestQueuePreference); ++ __privateSet(this, _domainProxyMap, domainProxyMap); ++ __privateMethod(this, _registerMessageHandlers, registerMessageHandlers_fn).call(this); ++ this.messagingSystem.call("PermissionController:getSubjectNames").filter((domain) => this.state.domains[domain] === void 0).forEach( ++ (domain) => this.setNetworkClientIdForDomain( ++ domain, ++ this.messagingSystem.call("NetworkController:getState").selectedNetworkClientId ++ ) ++ ); ++ this.messagingSystem.subscribe( ++ "PermissionController:stateChange", ++ (_, patches) => { ++ patches.forEach(({ op, path }) => { ++ const isChangingSubject = path[0] === "subjects" && path[1] !== void 0; ++ if (isChangingSubject && typeof path[1] === "string") { ++ const domain = path[1]; ++ if (op === "add" && this.state.domains[domain] === void 0) { ++ this.setNetworkClientIdForDomain( ++ domain, ++ this.messagingSystem.call("NetworkController:getState").selectedNetworkClientId ++ ); ++ } else if (op === "remove" && this.state.domains[domain] !== void 0) { ++ __privateMethod(this, _unsetNetworkClientIdForDomain, unsetNetworkClientIdForDomain_fn).call(this, domain); ++ } ++ } ++ }); ++ } ++ ); ++ this.messagingSystem.subscribe( ++ "NetworkController:stateChange", ++ ({ selectedNetworkClientId }, patches) => { ++ patches.forEach(({ op, path }) => { ++ if (op === "remove" && path[0] === "networkConfigurations") { ++ const removedNetworkClientId = path[1]; ++ Object.entries(this.state.domains).forEach( ++ ([domain, networkClientIdForDomain]) => { ++ if (networkClientIdForDomain === removedNetworkClientId) { ++ this.setNetworkClientIdForDomain( ++ domain, ++ selectedNetworkClientId ++ ); ++ } ++ } ++ ); ++ } ++ }); ++ } ++ ); ++ onPreferencesStateChange(({ useRequestQueue }) => { ++ if (__privateGet(this, _useRequestQueuePreference) !== useRequestQueue) { ++ if (!useRequestQueue) { ++ Object.keys(this.state.domains).forEach((domain) => { ++ __privateMethod(this, _unsetNetworkClientIdForDomain, unsetNetworkClientIdForDomain_fn).call(this, domain); ++ }); ++ } else { ++ __privateMethod(this, _resetAllPermissionedDomains, resetAllPermissionedDomains_fn).call(this); ++ } ++ __privateSet(this, _useRequestQueuePreference, useRequestQueue); ++ } ++ }); ++ } ++ setNetworkClientIdForDomain(domain, networkClientId) { ++ if (!__privateGet(this, _useRequestQueuePreference)) { ++ return; ++ } ++ if (domain === METAMASK_DOMAIN) { ++ throw new Error( ++ `NetworkClientId for domain "${METAMASK_DOMAIN}" cannot be set on the SelectedNetworkController` ++ ); ++ } ++ if (snapsPrefixes.some((prefix) => domain.startsWith(prefix))) { ++ return; ++ } ++ if (!__privateMethod(this, _domainHasPermissions, domainHasPermissions_fn).call(this, domain)) { ++ throw new Error( ++ "NetworkClientId for domain cannot be called with a domain that has not yet been granted permissions" ++ ); ++ } ++ __privateMethod(this, _setNetworkClientIdForDomain, setNetworkClientIdForDomain_fn).call(this, domain, networkClientId); ++ } ++ getNetworkClientIdForDomain(domain) { ++ const { selectedNetworkClientId: metamaskSelectedNetworkClientId } = this.messagingSystem.call("NetworkController:getState"); ++ if (!__privateGet(this, _useRequestQueuePreference)) { ++ return metamaskSelectedNetworkClientId; ++ } ++ return this.state.domains[domain] ?? metamaskSelectedNetworkClientId; ++ } ++ /** ++ * Accesses the provider and block tracker for the currently selected network. ++ * ++ * @param domain - the domain for the provider ++ * @returns The proxy and block tracker proxies. ++ */ ++ getProviderAndBlockTracker(domain) { ++ if (domain === METAMASK_DOMAIN || snapsPrefixes.some((prefix) => domain.startsWith(prefix))) { ++ const networkClient = this.messagingSystem.call( ++ "NetworkController:getSelectedNetworkClient" ++ ); ++ if (networkClient === void 0) { ++ throw new Error("Selected network not initialized"); ++ } ++ return networkClient; ++ } ++ let networkProxy = __privateGet(this, _domainProxyMap).get(domain); ++ if (networkProxy === void 0) { ++ let networkClient; ++ if (__privateGet(this, _useRequestQueuePreference) && __privateMethod(this, _domainHasPermissions, domainHasPermissions_fn).call(this, domain)) { ++ const networkClientId = this.getNetworkClientIdForDomain(domain); ++ networkClient = this.messagingSystem.call( ++ "NetworkController:getNetworkClientById", ++ networkClientId ++ ); ++ } else { ++ networkClient = this.messagingSystem.call( ++ "NetworkController:getSelectedNetworkClient" ++ ); ++ if (networkClient === void 0) { ++ throw new Error("Selected network not initialized"); ++ } ++ } ++ networkProxy = { ++ provider: createEventEmitterProxy(networkClient.provider), ++ blockTracker: createEventEmitterProxy(networkClient.blockTracker, { ++ eventFilter: "skipInternal" ++ }) ++ }; ++ __privateGet(this, _domainProxyMap).set(domain, networkProxy); ++ } ++ return networkProxy; ++ } ++}; ++_domainProxyMap = new WeakMap(); ++_useRequestQueuePreference = new WeakMap(); ++_registerMessageHandlers = new WeakSet(); ++registerMessageHandlers_fn = function() { ++ this.messagingSystem.registerActionHandler( ++ SelectedNetworkControllerActionTypes.getNetworkClientIdForDomain, ++ this.getNetworkClientIdForDomain.bind(this) ++ ); ++ this.messagingSystem.registerActionHandler( ++ SelectedNetworkControllerActionTypes.setNetworkClientIdForDomain, ++ this.setNetworkClientIdForDomain.bind(this) ++ ); ++}; ++_setNetworkClientIdForDomain = new WeakSet(); ++setNetworkClientIdForDomain_fn = function(domain, networkClientId) { ++ const networkClient = this.messagingSystem.call( ++ "NetworkController:getNetworkClientById", ++ networkClientId ++ ); ++ const networkProxy = this.getProviderAndBlockTracker(domain); ++ networkProxy.provider.setTarget(networkClient.provider); ++ networkProxy.blockTracker.setTarget(networkClient.blockTracker); ++ this.update((state) => { ++ state.domains[domain] = networkClientId; ++ }); ++}; ++_unsetNetworkClientIdForDomain = new WeakSet(); ++unsetNetworkClientIdForDomain_fn = function(domain) { ++ const globallySelectedNetworkClient = this.messagingSystem.call( ++ "NetworkController:getSelectedNetworkClient" ++ ); ++ const networkProxy = __privateGet(this, _domainProxyMap).get(domain); ++ if (networkProxy && globallySelectedNetworkClient) { ++ networkProxy.provider.setTarget(globallySelectedNetworkClient.provider); ++ networkProxy.blockTracker.setTarget( ++ globallySelectedNetworkClient.blockTracker ++ ); ++ } else if (networkProxy) { ++ __privateGet(this, _domainProxyMap).delete(domain); ++ } ++ this.update((state) => { ++ delete state.domains[domain]; ++ }); ++}; ++_domainHasPermissions = new WeakSet(); ++domainHasPermissions_fn = function(domain) { ++ return this.messagingSystem.call( ++ "PermissionController:hasPermissions", ++ domain ++ ); ++}; ++_resetAllPermissionedDomains = new WeakSet(); ++resetAllPermissionedDomains_fn = function() { ++ __privateGet(this, _domainProxyMap).forEach((_, domain) => { ++ const { selectedNetworkClientId } = this.messagingSystem.call( ++ "NetworkController:getState" ++ ); ++ if (__privateMethod(this, _domainHasPermissions, domainHasPermissions_fn).call(this, domain)) { ++ __privateMethod(this, _setNetworkClientIdForDomain, setNetworkClientIdForDomain_fn).call(this, domain, selectedNetworkClientId); ++ } ++ }); ++}; ++ ++export { ++ controllerName, ++ METAMASK_DOMAIN, ++ SelectedNetworkControllerActionTypes, ++ SelectedNetworkControllerEventTypes, ++ SelectedNetworkController ++}; ++//# sourceMappingURL=chunk-7DSTEJNI.mjs.map +\ No newline at end of file +diff --git a/dist/chunk-7DSTEJNI.mjs.map b/dist/chunk-7DSTEJNI.mjs.map +new file mode 100644 +index 0000000000000000000000000000000000000000..33fde9b65d7c89592b3754333e6c1f62a0d90f63 +--- /dev/null ++++ b/dist/chunk-7DSTEJNI.mjs.map +@@ -0,0 +1 @@ ++{"version":3,"sources":["../src/SelectedNetworkController.ts"],"sourcesContent":["import type { RestrictedControllerMessenger } from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport type {\n BlockTrackerProxy,\n NetworkClientId,\n NetworkControllerGetNetworkClientByIdAction,\n NetworkControllerGetSelectedNetworkClientAction,\n NetworkControllerGetStateAction,\n NetworkControllerStateChangeEvent,\n ProviderProxy,\n} from '@metamask/network-controller';\nimport type {\n PermissionControllerStateChange,\n GetSubjects as PermissionControllerGetSubjectsAction,\n HasPermissions as PermissionControllerHasPermissions,\n} from '@metamask/permission-controller';\nimport { createEventEmitterProxy } from '@metamask/swappable-obj-proxy';\nimport type { Patch } from 'immer';\n\nexport const controllerName = 'SelectedNetworkController';\n\nconst stateMetadata = {\n domains: { persist: true, anonymous: false },\n};\n\nconst getDefaultState = () => ({ domains: {} });\n\n// npm and local are currently the only valid prefixes for snap domains\n// TODO: eventually we maybe want to pull this in from snaps-utils to ensure it stays in sync\n// For now it seems like overkill to add a dependency for this one constant\n// https://github.com/MetaMask/snaps/blob/2beee7803bfe9e540788a3558b546b9f55dc3cb4/packages/snaps-utils/src/types.ts#L120\nconst snapsPrefixes = ['npm:', 'local:'] as const;\n\nexport type Domain = string;\n\nexport const METAMASK_DOMAIN = 'metamask' as const;\n\nexport const SelectedNetworkControllerActionTypes = {\n getState: `${controllerName}:getState` as const,\n getNetworkClientIdForDomain:\n `${controllerName}:getNetworkClientIdForDomain` as const,\n setNetworkClientIdForDomain:\n `${controllerName}:setNetworkClientIdForDomain` as const,\n};\n\nexport const SelectedNetworkControllerEventTypes = {\n stateChange: `${controllerName}:stateChange` as const,\n};\n\nexport type SelectedNetworkControllerState = {\n domains: Record;\n};\n\nexport type SelectedNetworkControllerStateChangeEvent = {\n type: typeof SelectedNetworkControllerEventTypes.stateChange;\n payload: [SelectedNetworkControllerState, Patch[]];\n};\n\nexport type SelectedNetworkControllerGetSelectedNetworkStateAction = {\n type: typeof SelectedNetworkControllerActionTypes.getState;\n handler: () => SelectedNetworkControllerState;\n};\n\nexport type SelectedNetworkControllerGetNetworkClientIdForDomainAction = {\n type: typeof SelectedNetworkControllerActionTypes.getNetworkClientIdForDomain;\n handler: SelectedNetworkController['getNetworkClientIdForDomain'];\n};\n\nexport type SelectedNetworkControllerSetNetworkClientIdForDomainAction = {\n type: typeof SelectedNetworkControllerActionTypes.setNetworkClientIdForDomain;\n handler: SelectedNetworkController['setNetworkClientIdForDomain'];\n};\n\nexport type SelectedNetworkControllerActions =\n | SelectedNetworkControllerGetSelectedNetworkStateAction\n | SelectedNetworkControllerGetNetworkClientIdForDomainAction\n | SelectedNetworkControllerSetNetworkClientIdForDomainAction;\n\nexport type AllowedActions =\n | NetworkControllerGetNetworkClientByIdAction\n | NetworkControllerGetSelectedNetworkClientAction\n | NetworkControllerGetStateAction\n | PermissionControllerHasPermissions\n | PermissionControllerGetSubjectsAction;\n\nexport type SelectedNetworkControllerEvents =\n SelectedNetworkControllerStateChangeEvent;\n\nexport type AllowedEvents =\n | NetworkControllerStateChangeEvent\n | PermissionControllerStateChange;\n\nexport type SelectedNetworkControllerMessenger = RestrictedControllerMessenger<\n typeof controllerName,\n SelectedNetworkControllerActions | AllowedActions,\n SelectedNetworkControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\nexport type SelectedNetworkControllerOptions = {\n state?: SelectedNetworkControllerState;\n messenger: SelectedNetworkControllerMessenger;\n useRequestQueuePreference: boolean;\n onPreferencesStateChange: (\n listener: (preferencesState: { useRequestQueue: boolean }) => void,\n ) => void;\n domainProxyMap: Map;\n};\n\nexport type NetworkProxy = {\n provider: ProviderProxy;\n blockTracker: BlockTrackerProxy;\n};\n\n/**\n * Controller for getting and setting the network for a particular domain.\n */\nexport class SelectedNetworkController extends BaseController<\n typeof controllerName,\n SelectedNetworkControllerState,\n SelectedNetworkControllerMessenger\n> {\n #domainProxyMap: Map;\n\n #useRequestQueuePreference: boolean;\n\n /**\n * Construct a SelectedNetworkController controller.\n *\n * @param options - The controller options.\n * @param options.messenger - The restricted controller messenger for the EncryptionPublicKey controller.\n * @param options.state - The controllers initial state.\n * @param options.useRequestQueuePreference - A boolean indicating whether to use the request queue preference.\n * @param options.onPreferencesStateChange - A callback that is called when the preference state changes.\n * @param options.domainProxyMap - A map for storing domain-specific proxies that are held in memory only during use.\n */\n constructor({\n messenger,\n state = getDefaultState(),\n useRequestQueuePreference,\n onPreferencesStateChange,\n domainProxyMap,\n }: SelectedNetworkControllerOptions) {\n super({\n name: controllerName,\n metadata: stateMetadata,\n messenger,\n state,\n });\n this.#useRequestQueuePreference = useRequestQueuePreference;\n this.#domainProxyMap = domainProxyMap;\n this.#registerMessageHandlers();\n\n // this is fetching all the dapp permissions from the PermissionsController and looking for any domains that are not in domains state in this controller. Then we take any missing domains and add them to state here, setting it with the globally selected networkClientId (fetched from the NetworkController)\n this.messagingSystem\n .call('PermissionController:getSubjectNames')\n .filter((domain) => this.state.domains[domain] === undefined)\n .forEach((domain) =>\n this.setNetworkClientIdForDomain(\n domain,\n this.messagingSystem.call('NetworkController:getState')\n .selectedNetworkClientId,\n ),\n );\n\n this.messagingSystem.subscribe(\n 'PermissionController:stateChange',\n (_, patches) => {\n patches.forEach(({ op, path }) => {\n const isChangingSubject =\n path[0] === 'subjects' && path[1] !== undefined;\n if (isChangingSubject && typeof path[1] === 'string') {\n const domain = path[1];\n if (op === 'add' && this.state.domains[domain] === undefined) {\n this.setNetworkClientIdForDomain(\n domain,\n this.messagingSystem.call('NetworkController:getState')\n .selectedNetworkClientId,\n );\n } else if (\n op === 'remove' &&\n this.state.domains[domain] !== undefined\n ) {\n this.#unsetNetworkClientIdForDomain(domain);\n }\n }\n });\n },\n );\n\n this.messagingSystem.subscribe(\n 'NetworkController:stateChange',\n ({ selectedNetworkClientId }, patches) => {\n patches.forEach(({ op, path }) => {\n // if a network is removed, update the networkClientId for all domains that were using it to the selected network\n if (op === 'remove' && path[0] === 'networkConfigurations') {\n const removedNetworkClientId = path[1] as NetworkClientId;\n Object.entries(this.state.domains).forEach(\n ([domain, networkClientIdForDomain]) => {\n if (networkClientIdForDomain === removedNetworkClientId) {\n this.setNetworkClientIdForDomain(\n domain,\n selectedNetworkClientId,\n );\n }\n },\n );\n }\n });\n },\n );\n\n onPreferencesStateChange(({ useRequestQueue }) => {\n if (this.#useRequestQueuePreference !== useRequestQueue) {\n if (!useRequestQueue) {\n // Loop through all domains and points each domain's proxy\n // to the NetworkController's own proxy of the globally selected networkClient\n Object.keys(this.state.domains).forEach((domain) => {\n this.#unsetNetworkClientIdForDomain(domain);\n });\n } else {\n this.#resetAllPermissionedDomains();\n }\n this.#useRequestQueuePreference = useRequestQueue;\n }\n });\n }\n\n #registerMessageHandlers(): void {\n this.messagingSystem.registerActionHandler(\n SelectedNetworkControllerActionTypes.getNetworkClientIdForDomain,\n this.getNetworkClientIdForDomain.bind(this),\n );\n this.messagingSystem.registerActionHandler(\n SelectedNetworkControllerActionTypes.setNetworkClientIdForDomain,\n this.setNetworkClientIdForDomain.bind(this),\n );\n }\n\n #setNetworkClientIdForDomain(\n domain: Domain,\n networkClientId: NetworkClientId,\n ) {\n const networkClient = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n );\n const networkProxy = this.getProviderAndBlockTracker(domain);\n networkProxy.provider.setTarget(networkClient.provider);\n networkProxy.blockTracker.setTarget(networkClient.blockTracker);\n\n this.update((state) => {\n state.domains[domain] = networkClientId;\n });\n }\n\n /**\n * This method is used when a domain is removed from the PermissionsController.\n * It will remove re-point the network proxy to the globally selected network in the domainProxyMap or, if no globally selected network client is available, delete the proxy.\n *\n * @param domain - The domain for which to unset the network client ID.\n */\n #unsetNetworkClientIdForDomain(domain: Domain) {\n const globallySelectedNetworkClient = this.messagingSystem.call(\n 'NetworkController:getSelectedNetworkClient',\n );\n const networkProxy = this.#domainProxyMap.get(domain);\n if (networkProxy && globallySelectedNetworkClient) {\n networkProxy.provider.setTarget(globallySelectedNetworkClient.provider);\n networkProxy.blockTracker.setTarget(\n globallySelectedNetworkClient.blockTracker,\n );\n } else if (networkProxy) {\n this.#domainProxyMap.delete(domain);\n }\n this.update((state) => {\n delete state.domains[domain];\n });\n }\n\n #domainHasPermissions(domain: Domain): boolean {\n return this.messagingSystem.call(\n 'PermissionController:hasPermissions',\n domain,\n );\n }\n\n // Loop through all domains and for those with permissions it points that domain's proxy\n // to an unproxied instance of the globally selected network client.\n // NOT the NetworkController's proxy of the globally selected networkClient\n #resetAllPermissionedDomains() {\n this.#domainProxyMap.forEach((_: NetworkProxy, domain: string) => {\n const { selectedNetworkClientId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n // can't use public setNetworkClientIdForDomain because it will throw an error\n // rather than simply skip if the domain doesn't have permissions which can happen\n // in this case since proxies are added for each site the user visits\n if (this.#domainHasPermissions(domain)) {\n this.#setNetworkClientIdForDomain(domain, selectedNetworkClientId);\n }\n });\n }\n\n setNetworkClientIdForDomain(\n domain: Domain,\n networkClientId: NetworkClientId,\n ) {\n // Core PR: https://github.com/MetaMask/core/pull/4388\n // Patch Branch: patch-selected-network-controller-13.0.0-setNetworkClient-guard\n if (!this.#useRequestQueuePreference) {\n return;\n }\n if (domain === METAMASK_DOMAIN) {\n throw new Error(\n `NetworkClientId for domain \"${METAMASK_DOMAIN}\" cannot be set on the SelectedNetworkController`,\n );\n }\n\n if (snapsPrefixes.some((prefix) => domain.startsWith(prefix))) {\n return;\n }\n\n if (!this.#domainHasPermissions(domain)) {\n throw new Error(\n 'NetworkClientId for domain cannot be called with a domain that has not yet been granted permissions',\n );\n }\n\n this.#setNetworkClientIdForDomain(domain, networkClientId);\n }\n\n getNetworkClientIdForDomain(domain: Domain): NetworkClientId {\n const { selectedNetworkClientId: metamaskSelectedNetworkClientId } =\n this.messagingSystem.call('NetworkController:getState');\n if (!this.#useRequestQueuePreference) {\n return metamaskSelectedNetworkClientId;\n }\n return this.state.domains[domain] ?? metamaskSelectedNetworkClientId;\n }\n\n /**\n * Accesses the provider and block tracker for the currently selected network.\n *\n * @param domain - the domain for the provider\n * @returns The proxy and block tracker proxies.\n */\n getProviderAndBlockTracker(domain: Domain): NetworkProxy {\n // If the domain is 'metamask' or a snap, return the NetworkController's globally selected network client proxy\n if (\n domain === METAMASK_DOMAIN ||\n snapsPrefixes.some((prefix) => domain.startsWith(prefix))\n ) {\n const networkClient = this.messagingSystem.call(\n 'NetworkController:getSelectedNetworkClient',\n );\n if (networkClient === undefined) {\n throw new Error('Selected network not initialized');\n }\n return networkClient;\n }\n\n let networkProxy = this.#domainProxyMap.get(domain);\n if (networkProxy === undefined) {\n let networkClient;\n if (\n this.#useRequestQueuePreference &&\n this.#domainHasPermissions(domain)\n ) {\n const networkClientId = this.getNetworkClientIdForDomain(domain);\n networkClient = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n );\n } else {\n networkClient = this.messagingSystem.call(\n 'NetworkController:getSelectedNetworkClient',\n );\n if (networkClient === undefined) {\n throw new Error('Selected network not initialized');\n }\n }\n networkProxy = {\n provider: createEventEmitterProxy(networkClient.provider),\n blockTracker: createEventEmitterProxy(networkClient.blockTracker, {\n eventFilter: 'skipInternal',\n }),\n };\n this.#domainProxyMap.set(domain, networkProxy);\n }\n return networkProxy;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AACA,SAAS,sBAAsB;AAe/B,SAAS,+BAA+B;AAGjC,IAAM,iBAAiB;AAE9B,IAAM,gBAAgB;AAAA,EACpB,SAAS,EAAE,SAAS,MAAM,WAAW,MAAM;AAC7C;AAEA,IAAM,kBAAkB,OAAO,EAAE,SAAS,CAAC,EAAE;AAM7C,IAAM,gBAAgB,CAAC,QAAQ,QAAQ;AAIhC,IAAM,kBAAkB;AAExB,IAAM,uCAAuC;AAAA,EAClD,UAAU,GAAG,cAAc;AAAA,EAC3B,6BACE,GAAG,cAAc;AAAA,EACnB,6BACE,GAAG,cAAc;AACrB;AAEO,IAAM,sCAAsC;AAAA,EACjD,aAAa,GAAG,cAAc;AAChC;AA/CA;AAsHO,IAAM,4BAAN,cAAwC,eAI7C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,YAAY;AAAA,IACV;AAAA,IACA,QAAQ,gBAAgB;AAAA,IACxB;AAAA,IACA;AAAA,IACA;AAAA,EACF,GAAqC;AACnC,UAAM;AAAA,MACJ,MAAM;AAAA,MACN,UAAU;AAAA,MACV;AAAA,MACA;AAAA,IACF,CAAC;AAgFH;AAWA;AAuBA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAkBA;AAUA;AAAA;AAAA;AAAA;AAxKA;AAEA;AAyBE,uBAAK,4BAA6B;AAClC,uBAAK,iBAAkB;AACvB,0BAAK,sDAAL;AAGA,SAAK,gBACF,KAAK,sCAAsC,EAC3C,OAAO,CAAC,WAAW,KAAK,MAAM,QAAQ,MAAM,MAAM,MAAS,EAC3D;AAAA,MAAQ,CAAC,WACR,KAAK;AAAA,QACH;AAAA,QACA,KAAK,gBAAgB,KAAK,4BAA4B,EACnD;AAAA,MACL;AAAA,IACF;AAEF,SAAK,gBAAgB;AAAA,MACnB;AAAA,MACA,CAAC,GAAG,YAAY;AACd,gBAAQ,QAAQ,CAAC,EAAE,IAAI,KAAK,MAAM;AAChC,gBAAM,oBACJ,KAAK,CAAC,MAAM,cAAc,KAAK,CAAC,MAAM;AACxC,cAAI,qBAAqB,OAAO,KAAK,CAAC,MAAM,UAAU;AACpD,kBAAM,SAAS,KAAK,CAAC;AACrB,gBAAI,OAAO,SAAS,KAAK,MAAM,QAAQ,MAAM,MAAM,QAAW;AAC5D,mBAAK;AAAA,gBACH;AAAA,gBACA,KAAK,gBAAgB,KAAK,4BAA4B,EACnD;AAAA,cACL;AAAA,YACF,WACE,OAAO,YACP,KAAK,MAAM,QAAQ,MAAM,MAAM,QAC/B;AACA,oCAAK,kEAAL,WAAoC;AAAA,YACtC;AAAA,UACF;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAEA,SAAK,gBAAgB;AAAA,MACnB;AAAA,MACA,CAAC,EAAE,wBAAwB,GAAG,YAAY;AACxC,gBAAQ,QAAQ,CAAC,EAAE,IAAI,KAAK,MAAM;AAEhC,cAAI,OAAO,YAAY,KAAK,CAAC,MAAM,yBAAyB;AAC1D,kBAAM,yBAAyB,KAAK,CAAC;AACrC,mBAAO,QAAQ,KAAK,MAAM,OAAO,EAAE;AAAA,cACjC,CAAC,CAAC,QAAQ,wBAAwB,MAAM;AACtC,oBAAI,6BAA6B,wBAAwB;AACvD,uBAAK;AAAA,oBACH;AAAA,oBACA;AAAA,kBACF;AAAA,gBACF;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAEA,6BAAyB,CAAC,EAAE,gBAAgB,MAAM;AAChD,UAAI,mBAAK,gCAA+B,iBAAiB;AACvD,YAAI,CAAC,iBAAiB;AAGpB,iBAAO,KAAK,KAAK,MAAM,OAAO,EAAE,QAAQ,CAAC,WAAW;AAClD,kCAAK,kEAAL,WAAoC;AAAA,UACtC,CAAC;AAAA,QACH,OAAO;AACL,gCAAK,8DAAL;AAAA,QACF;AACA,2BAAK,4BAA6B;AAAA,MACpC;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EA8EA,4BACE,QACA,iBACA;AAGA,QAAI,CAAC,mBAAK,6BAA4B;AACpC;AAAA,IACF;AACA,QAAI,WAAW,iBAAiB;AAC9B,YAAM,IAAI;AAAA,QACR,+BAA+B,eAAe;AAAA,MAChD;AAAA,IACF;AAEA,QAAI,cAAc,KAAK,CAAC,WAAW,OAAO,WAAW,MAAM,CAAC,GAAG;AAC7D;AAAA,IACF;AAEA,QAAI,CAAC,sBAAK,gDAAL,WAA2B,SAAS;AACvC,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,0BAAK,8DAAL,WAAkC,QAAQ;AAAA,EAC5C;AAAA,EAEA,4BAA4B,QAAiC;AAC3D,UAAM,EAAE,yBAAyB,gCAAgC,IAC/D,KAAK,gBAAgB,KAAK,4BAA4B;AACxD,QAAI,CAAC,mBAAK,6BAA4B;AACpC,aAAO;AAAA,IACT;AACA,WAAO,KAAK,MAAM,QAAQ,MAAM,KAAK;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,2BAA2B,QAA8B;AAEvD,QACE,WAAW,mBACX,cAAc,KAAK,CAAC,WAAW,OAAO,WAAW,MAAM,CAAC,GACxD;AACA,YAAM,gBAAgB,KAAK,gBAAgB;AAAA,QACzC;AAAA,MACF;AACA,UAAI,kBAAkB,QAAW;AAC/B,cAAM,IAAI,MAAM,kCAAkC;AAAA,MACpD;AACA,aAAO;AAAA,IACT;AAEA,QAAI,eAAe,mBAAK,iBAAgB,IAAI,MAAM;AAClD,QAAI,iBAAiB,QAAW;AAC9B,UAAI;AACJ,UACE,mBAAK,+BACL,sBAAK,gDAAL,WAA2B,SAC3B;AACA,cAAM,kBAAkB,KAAK,4BAA4B,MAAM;AAC/D,wBAAgB,KAAK,gBAAgB;AAAA,UACnC;AAAA,UACA;AAAA,QACF;AAAA,MACF,OAAO;AACL,wBAAgB,KAAK,gBAAgB;AAAA,UACnC;AAAA,QACF;AACA,YAAI,kBAAkB,QAAW;AAC/B,gBAAM,IAAI,MAAM,kCAAkC;AAAA,QACpD;AAAA,MACF;AACA,qBAAe;AAAA,QACb,UAAU,wBAAwB,cAAc,QAAQ;AAAA,QACxD,cAAc,wBAAwB,cAAc,cAAc;AAAA,UAChE,aAAa;AAAA,QACf,CAAC;AAAA,MACH;AACA,yBAAK,iBAAgB,IAAI,QAAQ,YAAY;AAAA,IAC/C;AACA,WAAO;AAAA,EACT;AACF;AA9QE;AAEA;AAwGA;AAAA,6BAAwB,WAAS;AAC/B,OAAK,gBAAgB;AAAA,IACnB,qCAAqC;AAAA,IACrC,KAAK,4BAA4B,KAAK,IAAI;AAAA,EAC5C;AACA,OAAK,gBAAgB;AAAA,IACnB,qCAAqC;AAAA,IACrC,KAAK,4BAA4B,KAAK,IAAI;AAAA,EAC5C;AACF;AAEA;AAAA,iCAA4B,SAC1B,QACA,iBACA;AACA,QAAM,gBAAgB,KAAK,gBAAgB;AAAA,IACzC;AAAA,IACA;AAAA,EACF;AACA,QAAM,eAAe,KAAK,2BAA2B,MAAM;AAC3D,eAAa,SAAS,UAAU,cAAc,QAAQ;AACtD,eAAa,aAAa,UAAU,cAAc,YAAY;AAE9D,OAAK,OAAO,CAAC,UAAU;AACrB,UAAM,QAAQ,MAAM,IAAI;AAAA,EAC1B,CAAC;AACH;AAQA;AAAA,mCAA8B,SAAC,QAAgB;AAC7C,QAAM,gCAAgC,KAAK,gBAAgB;AAAA,IACzD;AAAA,EACF;AACA,QAAM,eAAe,mBAAK,iBAAgB,IAAI,MAAM;AACpD,MAAI,gBAAgB,+BAA+B;AACjD,iBAAa,SAAS,UAAU,8BAA8B,QAAQ;AACtE,iBAAa,aAAa;AAAA,MACxB,8BAA8B;AAAA,IAChC;AAAA,EACF,WAAW,cAAc;AACvB,uBAAK,iBAAgB,OAAO,MAAM;AAAA,EACpC;AACA,OAAK,OAAO,CAAC,UAAU;AACrB,WAAO,MAAM,QAAQ,MAAM;AAAA,EAC7B,CAAC;AACH;AAEA;AAAA,0BAAqB,SAAC,QAAyB;AAC7C,SAAO,KAAK,gBAAgB;AAAA,IAC1B;AAAA,IACA;AAAA,EACF;AACF;AAKA;AAAA,iCAA4B,WAAG;AAC7B,qBAAK,iBAAgB,QAAQ,CAAC,GAAiB,WAAmB;AAChE,UAAM,EAAE,wBAAwB,IAAI,KAAK,gBAAgB;AAAA,MACvD;AAAA,IACF;AAIA,QAAI,sBAAK,gDAAL,WAA2B,SAAS;AACtC,4BAAK,8DAAL,WAAkC,QAAQ;AAAA,IAC5C;AAAA,EACF,CAAC;AACH;","names":[]} +\ No newline at end of file +diff --git a/dist/chunk-ANSSZMDI.js b/dist/chunk-ANSSZMDI.js +new file mode 100644 +index 0000000000000000000000000000000000000000..46fa6599a0d673a526cc2d054a47733fc0570e64 +--- /dev/null ++++ b/dist/chunk-ANSSZMDI.js +@@ -0,0 +1,23 @@ ++"use strict";Object.defineProperty(exports, "__esModule", {value: true}); ++ ++var _chunkCECZWJ42js = require('./chunk-CECZWJ42.js'); ++ ++// src/SelectedNetworkMiddleware.ts ++var createSelectedNetworkMiddleware = (messenger) => { ++ const getNetworkClientIdForDomain = (origin) => messenger.call( ++ _chunkCECZWJ42js.SelectedNetworkControllerActionTypes.getNetworkClientIdForDomain, ++ origin ++ ); ++ return (req, _, next) => { ++ if (!req.origin) { ++ throw new Error("Request object is lacking an 'origin'"); ++ } ++ req.networkClientId = getNetworkClientIdForDomain(req.origin); ++ return next(); ++ }; ++}; ++ ++ ++ ++exports.createSelectedNetworkMiddleware = createSelectedNetworkMiddleware; ++//# sourceMappingURL=chunk-ANSSZMDI.js.map +\ No newline at end of file +diff --git a/dist/chunk-ANSSZMDI.js.map b/dist/chunk-ANSSZMDI.js.map +new file mode 100644 +index 0000000000000000000000000000000000000000..9fe4c1fd6e9b12bfd9091833618334f2c931098f +--- /dev/null ++++ b/dist/chunk-ANSSZMDI.js.map +@@ -0,0 +1 @@ ++{"version":3,"sources":["../src/SelectedNetworkMiddleware.ts"],"names":[],"mappings":";;;;;AAYO,IAAM,kCAAkC,CAC7C,cAC2C;AAC3C,QAAM,8BAA8B,CAAC,WACnC,UAAU;AAAA,IACR,qCAAqC;AAAA,IACrC;AAAA,EACF;AAEF,SAAO,CAAC,KAA8C,GAAG,SAAS;AAChE,QAAI,CAAC,IAAI,QAAQ;AACf,YAAM,IAAI,MAAM,uCAAuC;AAAA,IACzD;AAEA,QAAI,kBAAkB,4BAA4B,IAAI,MAAM;AAC5D,WAAO,KAAK;AAAA,EACd;AACF","sourcesContent":["import type { JsonRpcMiddleware } from '@metamask/json-rpc-engine';\nimport type { NetworkClientId } from '@metamask/network-controller';\nimport type { Json, JsonRpcParams, JsonRpcRequest } from '@metamask/utils';\n\nimport type { SelectedNetworkControllerMessenger } from './SelectedNetworkController';\nimport { SelectedNetworkControllerActionTypes } from './SelectedNetworkController';\n\nexport type SelectedNetworkMiddlewareJsonRpcRequest = JsonRpcRequest & {\n networkClientId?: NetworkClientId;\n origin?: string;\n};\n\nexport const createSelectedNetworkMiddleware = (\n messenger: SelectedNetworkControllerMessenger,\n): JsonRpcMiddleware => {\n const getNetworkClientIdForDomain = (origin: string) =>\n messenger.call(\n SelectedNetworkControllerActionTypes.getNetworkClientIdForDomain,\n origin,\n );\n\n return (req: SelectedNetworkMiddlewareJsonRpcRequest, _, next) => {\n if (!req.origin) {\n throw new Error(\"Request object is lacking an 'origin'\");\n }\n\n req.networkClientId = getNetworkClientIdForDomain(req.origin);\n return next();\n };\n};\n"]} +\ No newline at end of file +diff --git a/dist/chunk-CECZWJ42.js b/dist/chunk-CECZWJ42.js +new file mode 100644 +index 0000000000000000000000000000000000000000..3471ce2b3b927414a25b81b864ddad0cdaeeb26a +--- /dev/null ++++ b/dist/chunk-CECZWJ42.js +@@ -0,0 +1,284 @@ ++"use strict";Object.defineProperty(exports, "__esModule", {value: true});var __accessCheck = (obj, member, msg) => { ++ if (!member.has(obj)) ++ throw TypeError("Cannot " + msg); ++}; ++var __privateGet = (obj, member, getter) => { ++ __accessCheck(obj, member, "read from private field"); ++ return getter ? getter.call(obj) : member.get(obj); ++}; ++var __privateAdd = (obj, member, value) => { ++ if (member.has(obj)) ++ throw TypeError("Cannot add the same private member more than once"); ++ member instanceof WeakSet ? member.add(obj) : member.set(obj, value); ++}; ++var __privateSet = (obj, member, value, setter) => { ++ __accessCheck(obj, member, "write to private field"); ++ setter ? setter.call(obj, value) : member.set(obj, value); ++ return value; ++}; ++var __privateMethod = (obj, member, method) => { ++ __accessCheck(obj, member, "access private method"); ++ return method; ++}; ++ ++// src/SelectedNetworkController.ts ++var _basecontroller = require('@metamask/base-controller'); ++var _swappableobjproxy = require('@metamask/swappable-obj-proxy'); ++var controllerName = "SelectedNetworkController"; ++var stateMetadata = { ++ domains: { persist: true, anonymous: false } ++}; ++var getDefaultState = () => ({ domains: {} }); ++var snapsPrefixes = ["npm:", "local:"]; ++var METAMASK_DOMAIN = "metamask"; ++var SelectedNetworkControllerActionTypes = { ++ getState: `${controllerName}:getState`, ++ getNetworkClientIdForDomain: `${controllerName}:getNetworkClientIdForDomain`, ++ setNetworkClientIdForDomain: `${controllerName}:setNetworkClientIdForDomain` ++}; ++var SelectedNetworkControllerEventTypes = { ++ stateChange: `${controllerName}:stateChange` ++}; ++var _domainProxyMap, _useRequestQueuePreference, _registerMessageHandlers, registerMessageHandlers_fn, _setNetworkClientIdForDomain, setNetworkClientIdForDomain_fn, _unsetNetworkClientIdForDomain, unsetNetworkClientIdForDomain_fn, _domainHasPermissions, domainHasPermissions_fn, _resetAllPermissionedDomains, resetAllPermissionedDomains_fn; ++var SelectedNetworkController = class extends _basecontroller.BaseController { ++ /** ++ * Construct a SelectedNetworkController controller. ++ * ++ * @param options - The controller options. ++ * @param options.messenger - The restricted controller messenger for the EncryptionPublicKey controller. ++ * @param options.state - The controllers initial state. ++ * @param options.useRequestQueuePreference - A boolean indicating whether to use the request queue preference. ++ * @param options.onPreferencesStateChange - A callback that is called when the preference state changes. ++ * @param options.domainProxyMap - A map for storing domain-specific proxies that are held in memory only during use. ++ */ ++ constructor({ ++ messenger, ++ state = getDefaultState(), ++ useRequestQueuePreference, ++ onPreferencesStateChange, ++ domainProxyMap ++ }) { ++ super({ ++ name: controllerName, ++ metadata: stateMetadata, ++ messenger, ++ state ++ }); ++ __privateAdd(this, _registerMessageHandlers); ++ __privateAdd(this, _setNetworkClientIdForDomain); ++ /** ++ * This method is used when a domain is removed from the PermissionsController. ++ * It will remove re-point the network proxy to the globally selected network in the domainProxyMap or, if no globally selected network client is available, delete the proxy. ++ * ++ * @param domain - The domain for which to unset the network client ID. ++ */ ++ __privateAdd(this, _unsetNetworkClientIdForDomain); ++ __privateAdd(this, _domainHasPermissions); ++ // Loop through all domains and for those with permissions it points that domain's proxy ++ // to an unproxied instance of the globally selected network client. ++ // NOT the NetworkController's proxy of the globally selected networkClient ++ __privateAdd(this, _resetAllPermissionedDomains); ++ __privateAdd(this, _domainProxyMap, void 0); ++ __privateAdd(this, _useRequestQueuePreference, void 0); ++ __privateSet(this, _useRequestQueuePreference, useRequestQueuePreference); ++ __privateSet(this, _domainProxyMap, domainProxyMap); ++ __privateMethod(this, _registerMessageHandlers, registerMessageHandlers_fn).call(this); ++ this.messagingSystem.call("PermissionController:getSubjectNames").filter((domain) => this.state.domains[domain] === void 0).forEach( ++ (domain) => this.setNetworkClientIdForDomain( ++ domain, ++ this.messagingSystem.call("NetworkController:getState").selectedNetworkClientId ++ ) ++ ); ++ this.messagingSystem.subscribe( ++ "PermissionController:stateChange", ++ (_, patches) => { ++ patches.forEach(({ op, path }) => { ++ const isChangingSubject = path[0] === "subjects" && path[1] !== void 0; ++ if (isChangingSubject && typeof path[1] === "string") { ++ const domain = path[1]; ++ if (op === "add" && this.state.domains[domain] === void 0) { ++ this.setNetworkClientIdForDomain( ++ domain, ++ this.messagingSystem.call("NetworkController:getState").selectedNetworkClientId ++ ); ++ } else if (op === "remove" && this.state.domains[domain] !== void 0) { ++ __privateMethod(this, _unsetNetworkClientIdForDomain, unsetNetworkClientIdForDomain_fn).call(this, domain); ++ } ++ } ++ }); ++ } ++ ); ++ this.messagingSystem.subscribe( ++ "NetworkController:stateChange", ++ ({ selectedNetworkClientId }, patches) => { ++ patches.forEach(({ op, path }) => { ++ if (op === "remove" && path[0] === "networkConfigurations") { ++ const removedNetworkClientId = path[1]; ++ Object.entries(this.state.domains).forEach( ++ ([domain, networkClientIdForDomain]) => { ++ if (networkClientIdForDomain === removedNetworkClientId) { ++ this.setNetworkClientIdForDomain( ++ domain, ++ selectedNetworkClientId ++ ); ++ } ++ } ++ ); ++ } ++ }); ++ } ++ ); ++ onPreferencesStateChange(({ useRequestQueue }) => { ++ if (__privateGet(this, _useRequestQueuePreference) !== useRequestQueue) { ++ if (!useRequestQueue) { ++ Object.keys(this.state.domains).forEach((domain) => { ++ __privateMethod(this, _unsetNetworkClientIdForDomain, unsetNetworkClientIdForDomain_fn).call(this, domain); ++ }); ++ } else { ++ __privateMethod(this, _resetAllPermissionedDomains, resetAllPermissionedDomains_fn).call(this); ++ } ++ __privateSet(this, _useRequestQueuePreference, useRequestQueue); ++ } ++ }); ++ } ++ setNetworkClientIdForDomain(domain, networkClientId) { ++ if (!__privateGet(this, _useRequestQueuePreference)) { ++ return; ++ } ++ if (domain === METAMASK_DOMAIN) { ++ throw new Error( ++ `NetworkClientId for domain "${METAMASK_DOMAIN}" cannot be set on the SelectedNetworkController` ++ ); ++ } ++ if (snapsPrefixes.some((prefix) => domain.startsWith(prefix))) { ++ return; ++ } ++ if (!__privateMethod(this, _domainHasPermissions, domainHasPermissions_fn).call(this, domain)) { ++ throw new Error( ++ "NetworkClientId for domain cannot be called with a domain that has not yet been granted permissions" ++ ); ++ } ++ __privateMethod(this, _setNetworkClientIdForDomain, setNetworkClientIdForDomain_fn).call(this, domain, networkClientId); ++ } ++ getNetworkClientIdForDomain(domain) { ++ const { selectedNetworkClientId: metamaskSelectedNetworkClientId } = this.messagingSystem.call("NetworkController:getState"); ++ if (!__privateGet(this, _useRequestQueuePreference)) { ++ return metamaskSelectedNetworkClientId; ++ } ++ return this.state.domains[domain] ?? metamaskSelectedNetworkClientId; ++ } ++ /** ++ * Accesses the provider and block tracker for the currently selected network. ++ * ++ * @param domain - the domain for the provider ++ * @returns The proxy and block tracker proxies. ++ */ ++ getProviderAndBlockTracker(domain) { ++ if (domain === METAMASK_DOMAIN || snapsPrefixes.some((prefix) => domain.startsWith(prefix))) { ++ const networkClient = this.messagingSystem.call( ++ "NetworkController:getSelectedNetworkClient" ++ ); ++ if (networkClient === void 0) { ++ throw new Error("Selected network not initialized"); ++ } ++ return networkClient; ++ } ++ let networkProxy = __privateGet(this, _domainProxyMap).get(domain); ++ if (networkProxy === void 0) { ++ let networkClient; ++ if (__privateGet(this, _useRequestQueuePreference) && __privateMethod(this, _domainHasPermissions, domainHasPermissions_fn).call(this, domain)) { ++ const networkClientId = this.getNetworkClientIdForDomain(domain); ++ networkClient = this.messagingSystem.call( ++ "NetworkController:getNetworkClientById", ++ networkClientId ++ ); ++ } else { ++ networkClient = this.messagingSystem.call( ++ "NetworkController:getSelectedNetworkClient" ++ ); ++ if (networkClient === void 0) { ++ throw new Error("Selected network not initialized"); ++ } ++ } ++ networkProxy = { ++ provider: _swappableobjproxy.createEventEmitterProxy.call(void 0, networkClient.provider), ++ blockTracker: _swappableobjproxy.createEventEmitterProxy.call(void 0, networkClient.blockTracker, { ++ eventFilter: "skipInternal" ++ }) ++ }; ++ __privateGet(this, _domainProxyMap).set(domain, networkProxy); ++ } ++ return networkProxy; ++ } ++}; ++_domainProxyMap = new WeakMap(); ++_useRequestQueuePreference = new WeakMap(); ++_registerMessageHandlers = new WeakSet(); ++registerMessageHandlers_fn = function() { ++ this.messagingSystem.registerActionHandler( ++ SelectedNetworkControllerActionTypes.getNetworkClientIdForDomain, ++ this.getNetworkClientIdForDomain.bind(this) ++ ); ++ this.messagingSystem.registerActionHandler( ++ SelectedNetworkControllerActionTypes.setNetworkClientIdForDomain, ++ this.setNetworkClientIdForDomain.bind(this) ++ ); ++}; ++_setNetworkClientIdForDomain = new WeakSet(); ++setNetworkClientIdForDomain_fn = function(domain, networkClientId) { ++ const networkClient = this.messagingSystem.call( ++ "NetworkController:getNetworkClientById", ++ networkClientId ++ ); ++ const networkProxy = this.getProviderAndBlockTracker(domain); ++ networkProxy.provider.setTarget(networkClient.provider); ++ networkProxy.blockTracker.setTarget(networkClient.blockTracker); ++ this.update((state) => { ++ state.domains[domain] = networkClientId; ++ }); ++}; ++_unsetNetworkClientIdForDomain = new WeakSet(); ++unsetNetworkClientIdForDomain_fn = function(domain) { ++ const globallySelectedNetworkClient = this.messagingSystem.call( ++ "NetworkController:getSelectedNetworkClient" ++ ); ++ const networkProxy = __privateGet(this, _domainProxyMap).get(domain); ++ if (networkProxy && globallySelectedNetworkClient) { ++ networkProxy.provider.setTarget(globallySelectedNetworkClient.provider); ++ networkProxy.blockTracker.setTarget( ++ globallySelectedNetworkClient.blockTracker ++ ); ++ } else if (networkProxy) { ++ __privateGet(this, _domainProxyMap).delete(domain); ++ } ++ this.update((state) => { ++ delete state.domains[domain]; ++ }); ++}; ++_domainHasPermissions = new WeakSet(); ++domainHasPermissions_fn = function(domain) { ++ return this.messagingSystem.call( ++ "PermissionController:hasPermissions", ++ domain ++ ); ++}; ++_resetAllPermissionedDomains = new WeakSet(); ++resetAllPermissionedDomains_fn = function() { ++ __privateGet(this, _domainProxyMap).forEach((_, domain) => { ++ const { selectedNetworkClientId } = this.messagingSystem.call( ++ "NetworkController:getState" ++ ); ++ if (__privateMethod(this, _domainHasPermissions, domainHasPermissions_fn).call(this, domain)) { ++ __privateMethod(this, _setNetworkClientIdForDomain, setNetworkClientIdForDomain_fn).call(this, domain, selectedNetworkClientId); ++ } ++ }); ++}; ++ ++ ++ ++ ++ ++ ++ ++exports.controllerName = controllerName; exports.METAMASK_DOMAIN = METAMASK_DOMAIN; exports.SelectedNetworkControllerActionTypes = SelectedNetworkControllerActionTypes; exports.SelectedNetworkControllerEventTypes = SelectedNetworkControllerEventTypes; exports.SelectedNetworkController = SelectedNetworkController; ++//# sourceMappingURL=chunk-CECZWJ42.js.map +\ No newline at end of file +diff --git a/dist/chunk-CECZWJ42.js.map b/dist/chunk-CECZWJ42.js.map +new file mode 100644 +index 0000000000000000000000000000000000000000..a2eab942638757819a9cb850057450500d22f9ed +--- /dev/null ++++ b/dist/chunk-CECZWJ42.js.map +@@ -0,0 +1 @@ ++{"version":3,"sources":["../src/SelectedNetworkController.ts"],"names":[],"mappings":"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","sourcesContent":["import type { RestrictedControllerMessenger } from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport type {\n BlockTrackerProxy,\n NetworkClientId,\n NetworkControllerGetNetworkClientByIdAction,\n NetworkControllerGetSelectedNetworkClientAction,\n NetworkControllerGetStateAction,\n NetworkControllerStateChangeEvent,\n ProviderProxy,\n} from '@metamask/network-controller';\nimport type {\n PermissionControllerStateChange,\n GetSubjects as PermissionControllerGetSubjectsAction,\n HasPermissions as PermissionControllerHasPermissions,\n} from '@metamask/permission-controller';\nimport { createEventEmitterProxy } from '@metamask/swappable-obj-proxy';\nimport type { Patch } from 'immer';\n\nexport const controllerName = 'SelectedNetworkController';\n\nconst stateMetadata = {\n domains: { persist: true, anonymous: false },\n};\n\nconst getDefaultState = () => ({ domains: {} });\n\n// npm and local are currently the only valid prefixes for snap domains\n// TODO: eventually we maybe want to pull this in from snaps-utils to ensure it stays in sync\n// For now it seems like overkill to add a dependency for this one constant\n// https://github.com/MetaMask/snaps/blob/2beee7803bfe9e540788a3558b546b9f55dc3cb4/packages/snaps-utils/src/types.ts#L120\nconst snapsPrefixes = ['npm:', 'local:'] as const;\n\nexport type Domain = string;\n\nexport const METAMASK_DOMAIN = 'metamask' as const;\n\nexport const SelectedNetworkControllerActionTypes = {\n getState: `${controllerName}:getState` as const,\n getNetworkClientIdForDomain:\n `${controllerName}:getNetworkClientIdForDomain` as const,\n setNetworkClientIdForDomain:\n `${controllerName}:setNetworkClientIdForDomain` as const,\n};\n\nexport const SelectedNetworkControllerEventTypes = {\n stateChange: `${controllerName}:stateChange` as const,\n};\n\nexport type SelectedNetworkControllerState = {\n domains: Record;\n};\n\nexport type SelectedNetworkControllerStateChangeEvent = {\n type: typeof SelectedNetworkControllerEventTypes.stateChange;\n payload: [SelectedNetworkControllerState, Patch[]];\n};\n\nexport type SelectedNetworkControllerGetSelectedNetworkStateAction = {\n type: typeof SelectedNetworkControllerActionTypes.getState;\n handler: () => SelectedNetworkControllerState;\n};\n\nexport type SelectedNetworkControllerGetNetworkClientIdForDomainAction = {\n type: typeof SelectedNetworkControllerActionTypes.getNetworkClientIdForDomain;\n handler: SelectedNetworkController['getNetworkClientIdForDomain'];\n};\n\nexport type SelectedNetworkControllerSetNetworkClientIdForDomainAction = {\n type: typeof SelectedNetworkControllerActionTypes.setNetworkClientIdForDomain;\n handler: SelectedNetworkController['setNetworkClientIdForDomain'];\n};\n\nexport type SelectedNetworkControllerActions =\n | SelectedNetworkControllerGetSelectedNetworkStateAction\n | SelectedNetworkControllerGetNetworkClientIdForDomainAction\n | SelectedNetworkControllerSetNetworkClientIdForDomainAction;\n\nexport type AllowedActions =\n | NetworkControllerGetNetworkClientByIdAction\n | NetworkControllerGetSelectedNetworkClientAction\n | NetworkControllerGetStateAction\n | PermissionControllerHasPermissions\n | PermissionControllerGetSubjectsAction;\n\nexport type SelectedNetworkControllerEvents =\n SelectedNetworkControllerStateChangeEvent;\n\nexport type AllowedEvents =\n | NetworkControllerStateChangeEvent\n | PermissionControllerStateChange;\n\nexport type SelectedNetworkControllerMessenger = RestrictedControllerMessenger<\n typeof controllerName,\n SelectedNetworkControllerActions | AllowedActions,\n SelectedNetworkControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\nexport type SelectedNetworkControllerOptions = {\n state?: SelectedNetworkControllerState;\n messenger: SelectedNetworkControllerMessenger;\n useRequestQueuePreference: boolean;\n onPreferencesStateChange: (\n listener: (preferencesState: { useRequestQueue: boolean }) => void,\n ) => void;\n domainProxyMap: Map;\n};\n\nexport type NetworkProxy = {\n provider: ProviderProxy;\n blockTracker: BlockTrackerProxy;\n};\n\n/**\n * Controller for getting and setting the network for a particular domain.\n */\nexport class SelectedNetworkController extends BaseController<\n typeof controllerName,\n SelectedNetworkControllerState,\n SelectedNetworkControllerMessenger\n> {\n #domainProxyMap: Map;\n\n #useRequestQueuePreference: boolean;\n\n /**\n * Construct a SelectedNetworkController controller.\n *\n * @param options - The controller options.\n * @param options.messenger - The restricted controller messenger for the EncryptionPublicKey controller.\n * @param options.state - The controllers initial state.\n * @param options.useRequestQueuePreference - A boolean indicating whether to use the request queue preference.\n * @param options.onPreferencesStateChange - A callback that is called when the preference state changes.\n * @param options.domainProxyMap - A map for storing domain-specific proxies that are held in memory only during use.\n */\n constructor({\n messenger,\n state = getDefaultState(),\n useRequestQueuePreference,\n onPreferencesStateChange,\n domainProxyMap,\n }: SelectedNetworkControllerOptions) {\n super({\n name: controllerName,\n metadata: stateMetadata,\n messenger,\n state,\n });\n this.#useRequestQueuePreference = useRequestQueuePreference;\n this.#domainProxyMap = domainProxyMap;\n this.#registerMessageHandlers();\n\n // this is fetching all the dapp permissions from the PermissionsController and looking for any domains that are not in domains state in this controller. Then we take any missing domains and add them to state here, setting it with the globally selected networkClientId (fetched from the NetworkController)\n this.messagingSystem\n .call('PermissionController:getSubjectNames')\n .filter((domain) => this.state.domains[domain] === undefined)\n .forEach((domain) =>\n this.setNetworkClientIdForDomain(\n domain,\n this.messagingSystem.call('NetworkController:getState')\n .selectedNetworkClientId,\n ),\n );\n\n this.messagingSystem.subscribe(\n 'PermissionController:stateChange',\n (_, patches) => {\n patches.forEach(({ op, path }) => {\n const isChangingSubject =\n path[0] === 'subjects' && path[1] !== undefined;\n if (isChangingSubject && typeof path[1] === 'string') {\n const domain = path[1];\n if (op === 'add' && this.state.domains[domain] === undefined) {\n this.setNetworkClientIdForDomain(\n domain,\n this.messagingSystem.call('NetworkController:getState')\n .selectedNetworkClientId,\n );\n } else if (\n op === 'remove' &&\n this.state.domains[domain] !== undefined\n ) {\n this.#unsetNetworkClientIdForDomain(domain);\n }\n }\n });\n },\n );\n\n this.messagingSystem.subscribe(\n 'NetworkController:stateChange',\n ({ selectedNetworkClientId }, patches) => {\n patches.forEach(({ op, path }) => {\n // if a network is removed, update the networkClientId for all domains that were using it to the selected network\n if (op === 'remove' && path[0] === 'networkConfigurations') {\n const removedNetworkClientId = path[1] as NetworkClientId;\n Object.entries(this.state.domains).forEach(\n ([domain, networkClientIdForDomain]) => {\n if (networkClientIdForDomain === removedNetworkClientId) {\n this.setNetworkClientIdForDomain(\n domain,\n selectedNetworkClientId,\n );\n }\n },\n );\n }\n });\n },\n );\n\n onPreferencesStateChange(({ useRequestQueue }) => {\n if (this.#useRequestQueuePreference !== useRequestQueue) {\n if (!useRequestQueue) {\n // Loop through all domains and points each domain's proxy\n // to the NetworkController's own proxy of the globally selected networkClient\n Object.keys(this.state.domains).forEach((domain) => {\n this.#unsetNetworkClientIdForDomain(domain);\n });\n } else {\n this.#resetAllPermissionedDomains();\n }\n this.#useRequestQueuePreference = useRequestQueue;\n }\n });\n }\n\n #registerMessageHandlers(): void {\n this.messagingSystem.registerActionHandler(\n SelectedNetworkControllerActionTypes.getNetworkClientIdForDomain,\n this.getNetworkClientIdForDomain.bind(this),\n );\n this.messagingSystem.registerActionHandler(\n SelectedNetworkControllerActionTypes.setNetworkClientIdForDomain,\n this.setNetworkClientIdForDomain.bind(this),\n );\n }\n\n #setNetworkClientIdForDomain(\n domain: Domain,\n networkClientId: NetworkClientId,\n ) {\n const networkClient = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n );\n const networkProxy = this.getProviderAndBlockTracker(domain);\n networkProxy.provider.setTarget(networkClient.provider);\n networkProxy.blockTracker.setTarget(networkClient.blockTracker);\n\n this.update((state) => {\n state.domains[domain] = networkClientId;\n });\n }\n\n /**\n * This method is used when a domain is removed from the PermissionsController.\n * It will remove re-point the network proxy to the globally selected network in the domainProxyMap or, if no globally selected network client is available, delete the proxy.\n *\n * @param domain - The domain for which to unset the network client ID.\n */\n #unsetNetworkClientIdForDomain(domain: Domain) {\n const globallySelectedNetworkClient = this.messagingSystem.call(\n 'NetworkController:getSelectedNetworkClient',\n );\n const networkProxy = this.#domainProxyMap.get(domain);\n if (networkProxy && globallySelectedNetworkClient) {\n networkProxy.provider.setTarget(globallySelectedNetworkClient.provider);\n networkProxy.blockTracker.setTarget(\n globallySelectedNetworkClient.blockTracker,\n );\n } else if (networkProxy) {\n this.#domainProxyMap.delete(domain);\n }\n this.update((state) => {\n delete state.domains[domain];\n });\n }\n\n #domainHasPermissions(domain: Domain): boolean {\n return this.messagingSystem.call(\n 'PermissionController:hasPermissions',\n domain,\n );\n }\n\n // Loop through all domains and for those with permissions it points that domain's proxy\n // to an unproxied instance of the globally selected network client.\n // NOT the NetworkController's proxy of the globally selected networkClient\n #resetAllPermissionedDomains() {\n this.#domainProxyMap.forEach((_: NetworkProxy, domain: string) => {\n const { selectedNetworkClientId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n // can't use public setNetworkClientIdForDomain because it will throw an error\n // rather than simply skip if the domain doesn't have permissions which can happen\n // in this case since proxies are added for each site the user visits\n if (this.#domainHasPermissions(domain)) {\n this.#setNetworkClientIdForDomain(domain, selectedNetworkClientId);\n }\n });\n }\n\n setNetworkClientIdForDomain(\n domain: Domain,\n networkClientId: NetworkClientId,\n ) {\n // Core PR: https://github.com/MetaMask/core/pull/4388\n // Patch Branch: patch-selected-network-controller-13.0.0-setNetworkClient-guard\n if (!this.#useRequestQueuePreference) {\n return;\n }\n if (domain === METAMASK_DOMAIN) {\n throw new Error(\n `NetworkClientId for domain \"${METAMASK_DOMAIN}\" cannot be set on the SelectedNetworkController`,\n );\n }\n\n if (snapsPrefixes.some((prefix) => domain.startsWith(prefix))) {\n return;\n }\n\n if (!this.#domainHasPermissions(domain)) {\n throw new Error(\n 'NetworkClientId for domain cannot be called with a domain that has not yet been granted permissions',\n );\n }\n\n this.#setNetworkClientIdForDomain(domain, networkClientId);\n }\n\n getNetworkClientIdForDomain(domain: Domain): NetworkClientId {\n const { selectedNetworkClientId: metamaskSelectedNetworkClientId } =\n this.messagingSystem.call('NetworkController:getState');\n if (!this.#useRequestQueuePreference) {\n return metamaskSelectedNetworkClientId;\n }\n return this.state.domains[domain] ?? metamaskSelectedNetworkClientId;\n }\n\n /**\n * Accesses the provider and block tracker for the currently selected network.\n *\n * @param domain - the domain for the provider\n * @returns The proxy and block tracker proxies.\n */\n getProviderAndBlockTracker(domain: Domain): NetworkProxy {\n // If the domain is 'metamask' or a snap, return the NetworkController's globally selected network client proxy\n if (\n domain === METAMASK_DOMAIN ||\n snapsPrefixes.some((prefix) => domain.startsWith(prefix))\n ) {\n const networkClient = this.messagingSystem.call(\n 'NetworkController:getSelectedNetworkClient',\n );\n if (networkClient === undefined) {\n throw new Error('Selected network not initialized');\n }\n return networkClient;\n }\n\n let networkProxy = this.#domainProxyMap.get(domain);\n if (networkProxy === undefined) {\n let networkClient;\n if (\n this.#useRequestQueuePreference &&\n this.#domainHasPermissions(domain)\n ) {\n const networkClientId = this.getNetworkClientIdForDomain(domain);\n networkClient = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n );\n } else {\n networkClient = this.messagingSystem.call(\n 'NetworkController:getSelectedNetworkClient',\n );\n if (networkClient === undefined) {\n throw new Error('Selected network not initialized');\n }\n }\n networkProxy = {\n provider: createEventEmitterProxy(networkClient.provider),\n blockTracker: createEventEmitterProxy(networkClient.blockTracker, {\n eventFilter: 'skipInternal',\n }),\n };\n this.#domainProxyMap.set(domain, networkProxy);\n }\n return networkProxy;\n }\n}\n"]} +\ No newline at end of file +diff --git a/dist/chunk-HFN7TKJS.mjs b/dist/chunk-HFN7TKJS.mjs +new file mode 100644 +index 0000000000000000000000000000000000000000..6f4a4ec6b18571939d2b6c10ddb8ab4e5345389c +--- /dev/null ++++ b/dist/chunk-HFN7TKJS.mjs +@@ -0,0 +1,23 @@ ++import { ++ SelectedNetworkControllerActionTypes ++} from "./chunk-7DSTEJNI.mjs"; ++ ++// src/SelectedNetworkMiddleware.ts ++var createSelectedNetworkMiddleware = (messenger) => { ++ const getNetworkClientIdForDomain = (origin) => messenger.call( ++ SelectedNetworkControllerActionTypes.getNetworkClientIdForDomain, ++ origin ++ ); ++ return (req, _, next) => { ++ if (!req.origin) { ++ throw new Error("Request object is lacking an 'origin'"); ++ } ++ req.networkClientId = getNetworkClientIdForDomain(req.origin); ++ return next(); ++ }; ++}; ++ ++export { ++ createSelectedNetworkMiddleware ++}; ++//# sourceMappingURL=chunk-HFN7TKJS.mjs.map +\ No newline at end of file +diff --git a/dist/chunk-HFN7TKJS.mjs.map b/dist/chunk-HFN7TKJS.mjs.map +new file mode 100644 +index 0000000000000000000000000000000000000000..fbcfd73d57f291b4f129caa47ef0c9614987cc30 +--- /dev/null ++++ b/dist/chunk-HFN7TKJS.mjs.map +@@ -0,0 +1 @@ ++{"version":3,"sources":["../src/SelectedNetworkMiddleware.ts"],"sourcesContent":["import type { JsonRpcMiddleware } from '@metamask/json-rpc-engine';\nimport type { NetworkClientId } from '@metamask/network-controller';\nimport type { Json, JsonRpcParams, JsonRpcRequest } from '@metamask/utils';\n\nimport type { SelectedNetworkControllerMessenger } from './SelectedNetworkController';\nimport { SelectedNetworkControllerActionTypes } from './SelectedNetworkController';\n\nexport type SelectedNetworkMiddlewareJsonRpcRequest = JsonRpcRequest & {\n networkClientId?: NetworkClientId;\n origin?: string;\n};\n\nexport const createSelectedNetworkMiddleware = (\n messenger: SelectedNetworkControllerMessenger,\n): JsonRpcMiddleware => {\n const getNetworkClientIdForDomain = (origin: string) =>\n messenger.call(\n SelectedNetworkControllerActionTypes.getNetworkClientIdForDomain,\n origin,\n );\n\n return (req: SelectedNetworkMiddlewareJsonRpcRequest, _, next) => {\n if (!req.origin) {\n throw new Error(\"Request object is lacking an 'origin'\");\n }\n\n req.networkClientId = getNetworkClientIdForDomain(req.origin);\n return next();\n };\n};\n"],"mappings":";;;;;AAYO,IAAM,kCAAkC,CAC7C,cAC2C;AAC3C,QAAM,8BAA8B,CAAC,WACnC,UAAU;AAAA,IACR,qCAAqC;AAAA,IACrC;AAAA,EACF;AAEF,SAAO,CAAC,KAA8C,GAAG,SAAS;AAChE,QAAI,CAAC,IAAI,QAAQ;AACf,YAAM,IAAI,MAAM,uCAAuC;AAAA,IACzD;AAEA,QAAI,kBAAkB,4BAA4B,IAAI,MAAM;AAC5D,WAAO,KAAK;AAAA,EACd;AACF;","names":[]} +\ No newline at end of file +diff --git a/dist/chunk-OGUVGN6R.js b/dist/chunk-OGUVGN6R.js +deleted file mode 100644 +index 1fb2670b25d9230aa3b0d1d496223ccee8f48263..0000000000000000000000000000000000000000 +--- a/dist/chunk-OGUVGN6R.js ++++ /dev/null +@@ -1,281 +0,0 @@ +-"use strict";Object.defineProperty(exports, "__esModule", {value: true});var __accessCheck = (obj, member, msg) => { +- if (!member.has(obj)) +- throw TypeError("Cannot " + msg); +-}; +-var __privateGet = (obj, member, getter) => { +- __accessCheck(obj, member, "read from private field"); +- return getter ? getter.call(obj) : member.get(obj); +-}; +-var __privateAdd = (obj, member, value) => { +- if (member.has(obj)) +- throw TypeError("Cannot add the same private member more than once"); +- member instanceof WeakSet ? member.add(obj) : member.set(obj, value); +-}; +-var __privateSet = (obj, member, value, setter) => { +- __accessCheck(obj, member, "write to private field"); +- setter ? setter.call(obj, value) : member.set(obj, value); +- return value; +-}; +-var __privateMethod = (obj, member, method) => { +- __accessCheck(obj, member, "access private method"); +- return method; +-}; +- +-// src/SelectedNetworkController.ts +-var _basecontroller = require('@metamask/base-controller'); +-var _swappableobjproxy = require('@metamask/swappable-obj-proxy'); +-var controllerName = "SelectedNetworkController"; +-var stateMetadata = { +- domains: { persist: true, anonymous: false } +-}; +-var getDefaultState = () => ({ domains: {} }); +-var snapsPrefixes = ["npm:", "local:"]; +-var METAMASK_DOMAIN = "metamask"; +-var SelectedNetworkControllerActionTypes = { +- getState: `${controllerName}:getState`, +- getNetworkClientIdForDomain: `${controllerName}:getNetworkClientIdForDomain`, +- setNetworkClientIdForDomain: `${controllerName}:setNetworkClientIdForDomain` +-}; +-var SelectedNetworkControllerEventTypes = { +- stateChange: `${controllerName}:stateChange` +-}; +-var _domainProxyMap, _useRequestQueuePreference, _registerMessageHandlers, registerMessageHandlers_fn, _setNetworkClientIdForDomain, setNetworkClientIdForDomain_fn, _unsetNetworkClientIdForDomain, unsetNetworkClientIdForDomain_fn, _domainHasPermissions, domainHasPermissions_fn, _resetAllPermissionedDomains, resetAllPermissionedDomains_fn; +-var SelectedNetworkController = class extends _basecontroller.BaseController { +- /** +- * Construct a SelectedNetworkController controller. +- * +- * @param options - The controller options. +- * @param options.messenger - The restricted controller messenger for the EncryptionPublicKey controller. +- * @param options.state - The controllers initial state. +- * @param options.useRequestQueuePreference - A boolean indicating whether to use the request queue preference. +- * @param options.onPreferencesStateChange - A callback that is called when the preference state changes. +- * @param options.domainProxyMap - A map for storing domain-specific proxies that are held in memory only during use. +- */ +- constructor({ +- messenger, +- state = getDefaultState(), +- useRequestQueuePreference, +- onPreferencesStateChange, +- domainProxyMap +- }) { +- super({ +- name: controllerName, +- metadata: stateMetadata, +- messenger, +- state +- }); +- __privateAdd(this, _registerMessageHandlers); +- __privateAdd(this, _setNetworkClientIdForDomain); +- /** +- * This method is used when a domain is removed from the PermissionsController. +- * It will remove re-point the network proxy to the globally selected network in the domainProxyMap or, if no globally selected network client is available, delete the proxy. +- * +- * @param domain - The domain for which to unset the network client ID. +- */ +- __privateAdd(this, _unsetNetworkClientIdForDomain); +- __privateAdd(this, _domainHasPermissions); +- // Loop through all domains and for those with permissions it points that domain's proxy +- // to an unproxied instance of the globally selected network client. +- // NOT the NetworkController's proxy of the globally selected networkClient +- __privateAdd(this, _resetAllPermissionedDomains); +- __privateAdd(this, _domainProxyMap, void 0); +- __privateAdd(this, _useRequestQueuePreference, void 0); +- __privateSet(this, _useRequestQueuePreference, useRequestQueuePreference); +- __privateSet(this, _domainProxyMap, domainProxyMap); +- __privateMethod(this, _registerMessageHandlers, registerMessageHandlers_fn).call(this); +- this.messagingSystem.call("PermissionController:getSubjectNames").filter((domain) => this.state.domains[domain] === void 0).forEach( +- (domain) => this.setNetworkClientIdForDomain( +- domain, +- this.messagingSystem.call("NetworkController:getState").selectedNetworkClientId +- ) +- ); +- this.messagingSystem.subscribe( +- "PermissionController:stateChange", +- (_, patches) => { +- patches.forEach(({ op, path }) => { +- const isChangingSubject = path[0] === "subjects" && path[1] !== void 0; +- if (isChangingSubject && typeof path[1] === "string") { +- const domain = path[1]; +- if (op === "add" && this.state.domains[domain] === void 0) { +- this.setNetworkClientIdForDomain( +- domain, +- this.messagingSystem.call("NetworkController:getState").selectedNetworkClientId +- ); +- } else if (op === "remove" && this.state.domains[domain] !== void 0) { +- __privateMethod(this, _unsetNetworkClientIdForDomain, unsetNetworkClientIdForDomain_fn).call(this, domain); +- } +- } +- }); +- } +- ); +- this.messagingSystem.subscribe( +- "NetworkController:stateChange", +- ({ selectedNetworkClientId }, patches) => { +- patches.forEach(({ op, path }) => { +- if (op === "remove" && path[0] === "networkConfigurations") { +- const removedNetworkClientId = path[1]; +- Object.entries(this.state.domains).forEach( +- ([domain, networkClientIdForDomain]) => { +- if (networkClientIdForDomain === removedNetworkClientId) { +- this.setNetworkClientIdForDomain( +- domain, +- selectedNetworkClientId +- ); +- } +- } +- ); +- } +- }); +- } +- ); +- onPreferencesStateChange(({ useRequestQueue }) => { +- if (__privateGet(this, _useRequestQueuePreference) !== useRequestQueue) { +- if (!useRequestQueue) { +- Object.keys(this.state.domains).forEach((domain) => { +- __privateMethod(this, _unsetNetworkClientIdForDomain, unsetNetworkClientIdForDomain_fn).call(this, domain); +- }); +- } else { +- __privateMethod(this, _resetAllPermissionedDomains, resetAllPermissionedDomains_fn).call(this); +- } +- __privateSet(this, _useRequestQueuePreference, useRequestQueue); +- } +- }); +- } +- setNetworkClientIdForDomain(domain, networkClientId) { +- if (domain === METAMASK_DOMAIN) { +- throw new Error( +- `NetworkClientId for domain "${METAMASK_DOMAIN}" cannot be set on the SelectedNetworkController` +- ); +- } +- if (snapsPrefixes.some((prefix) => domain.startsWith(prefix))) { +- return; +- } +- if (!__privateMethod(this, _domainHasPermissions, domainHasPermissions_fn).call(this, domain)) { +- throw new Error( +- "NetworkClientId for domain cannot be called with a domain that has not yet been granted permissions" +- ); +- } +- __privateMethod(this, _setNetworkClientIdForDomain, setNetworkClientIdForDomain_fn).call(this, domain, networkClientId); +- } +- getNetworkClientIdForDomain(domain) { +- const { selectedNetworkClientId: metamaskSelectedNetworkClientId } = this.messagingSystem.call("NetworkController:getState"); +- if (!__privateGet(this, _useRequestQueuePreference)) { +- return metamaskSelectedNetworkClientId; +- } +- return this.state.domains[domain] ?? metamaskSelectedNetworkClientId; +- } +- /** +- * Accesses the provider and block tracker for the currently selected network. +- * +- * @param domain - the domain for the provider +- * @returns The proxy and block tracker proxies. +- */ +- getProviderAndBlockTracker(domain) { +- if (domain === METAMASK_DOMAIN || snapsPrefixes.some((prefix) => domain.startsWith(prefix))) { +- const networkClient = this.messagingSystem.call( +- "NetworkController:getSelectedNetworkClient" +- ); +- if (networkClient === void 0) { +- throw new Error("Selected network not initialized"); +- } +- return networkClient; +- } +- let networkProxy = __privateGet(this, _domainProxyMap).get(domain); +- if (networkProxy === void 0) { +- let networkClient; +- if (__privateGet(this, _useRequestQueuePreference) && __privateMethod(this, _domainHasPermissions, domainHasPermissions_fn).call(this, domain)) { +- const networkClientId = this.getNetworkClientIdForDomain(domain); +- networkClient = this.messagingSystem.call( +- "NetworkController:getNetworkClientById", +- networkClientId +- ); +- } else { +- networkClient = this.messagingSystem.call( +- "NetworkController:getSelectedNetworkClient" +- ); +- if (networkClient === void 0) { +- throw new Error("Selected network not initialized"); +- } +- } +- networkProxy = { +- provider: _swappableobjproxy.createEventEmitterProxy.call(void 0, networkClient.provider), +- blockTracker: _swappableobjproxy.createEventEmitterProxy.call(void 0, networkClient.blockTracker, { +- eventFilter: "skipInternal" +- }) +- }; +- __privateGet(this, _domainProxyMap).set(domain, networkProxy); +- } +- return networkProxy; +- } +-}; +-_domainProxyMap = new WeakMap(); +-_useRequestQueuePreference = new WeakMap(); +-_registerMessageHandlers = new WeakSet(); +-registerMessageHandlers_fn = function() { +- this.messagingSystem.registerActionHandler( +- SelectedNetworkControllerActionTypes.getNetworkClientIdForDomain, +- this.getNetworkClientIdForDomain.bind(this) +- ); +- this.messagingSystem.registerActionHandler( +- SelectedNetworkControllerActionTypes.setNetworkClientIdForDomain, +- this.setNetworkClientIdForDomain.bind(this) +- ); +-}; +-_setNetworkClientIdForDomain = new WeakSet(); +-setNetworkClientIdForDomain_fn = function(domain, networkClientId) { +- const networkClient = this.messagingSystem.call( +- "NetworkController:getNetworkClientById", +- networkClientId +- ); +- const networkProxy = this.getProviderAndBlockTracker(domain); +- networkProxy.provider.setTarget(networkClient.provider); +- networkProxy.blockTracker.setTarget(networkClient.blockTracker); +- this.update((state) => { +- state.domains[domain] = networkClientId; +- }); +-}; +-_unsetNetworkClientIdForDomain = new WeakSet(); +-unsetNetworkClientIdForDomain_fn = function(domain) { +- const globallySelectedNetworkClient = this.messagingSystem.call( +- "NetworkController:getSelectedNetworkClient" +- ); +- const networkProxy = __privateGet(this, _domainProxyMap).get(domain); +- if (networkProxy && globallySelectedNetworkClient) { +- networkProxy.provider.setTarget(globallySelectedNetworkClient.provider); +- networkProxy.blockTracker.setTarget( +- globallySelectedNetworkClient.blockTracker +- ); +- } else if (networkProxy) { +- __privateGet(this, _domainProxyMap).delete(domain); +- } +- this.update((state) => { +- delete state.domains[domain]; +- }); +-}; +-_domainHasPermissions = new WeakSet(); +-domainHasPermissions_fn = function(domain) { +- return this.messagingSystem.call( +- "PermissionController:hasPermissions", +- domain +- ); +-}; +-_resetAllPermissionedDomains = new WeakSet(); +-resetAllPermissionedDomains_fn = function() { +- __privateGet(this, _domainProxyMap).forEach((_, domain) => { +- const { selectedNetworkClientId } = this.messagingSystem.call( +- "NetworkController:getState" +- ); +- if (__privateMethod(this, _domainHasPermissions, domainHasPermissions_fn).call(this, domain)) { +- __privateMethod(this, _setNetworkClientIdForDomain, setNetworkClientIdForDomain_fn).call(this, domain, selectedNetworkClientId); +- } +- }); +-}; +- +- +- +- +- +- +- +-exports.controllerName = controllerName; exports.METAMASK_DOMAIN = METAMASK_DOMAIN; exports.SelectedNetworkControllerActionTypes = SelectedNetworkControllerActionTypes; exports.SelectedNetworkControllerEventTypes = SelectedNetworkControllerEventTypes; exports.SelectedNetworkController = SelectedNetworkController; +-//# sourceMappingURL=chunk-OGUVGN6R.js.map +\ No newline at end of file +diff --git a/dist/chunk-OGUVGN6R.js.map b/dist/chunk-OGUVGN6R.js.map +deleted file mode 100644 +index 4e38e02bd55874905ac6ea9c5874f01f102a7ff7..0000000000000000000000000000000000000000 +--- a/dist/chunk-OGUVGN6R.js.map ++++ /dev/null +@@ -1 +0,0 @@ +-{"version":3,"sources":["../src/SelectedNetworkController.ts"],"names":[],"mappings":"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","sourcesContent":["import type { RestrictedControllerMessenger } from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport type {\n BlockTrackerProxy,\n NetworkClientId,\n NetworkControllerGetNetworkClientByIdAction,\n NetworkControllerGetSelectedNetworkClientAction,\n NetworkControllerGetStateAction,\n NetworkControllerStateChangeEvent,\n ProviderProxy,\n} from '@metamask/network-controller';\nimport type {\n PermissionControllerStateChange,\n GetSubjects as PermissionControllerGetSubjectsAction,\n HasPermissions as PermissionControllerHasPermissions,\n} from '@metamask/permission-controller';\nimport { createEventEmitterProxy } from '@metamask/swappable-obj-proxy';\nimport type { Patch } from 'immer';\n\nexport const controllerName = 'SelectedNetworkController';\n\nconst stateMetadata = {\n domains: { persist: true, anonymous: false },\n};\n\nconst getDefaultState = () => ({ domains: {} });\n\n// npm and local are currently the only valid prefixes for snap domains\n// TODO: eventually we maybe want to pull this in from snaps-utils to ensure it stays in sync\n// For now it seems like overkill to add a dependency for this one constant\n// https://github.com/MetaMask/snaps/blob/2beee7803bfe9e540788a3558b546b9f55dc3cb4/packages/snaps-utils/src/types.ts#L120\nconst snapsPrefixes = ['npm:', 'local:'] as const;\n\nexport type Domain = string;\n\nexport const METAMASK_DOMAIN = 'metamask' as const;\n\nexport const SelectedNetworkControllerActionTypes = {\n getState: `${controllerName}:getState` as const,\n getNetworkClientIdForDomain:\n `${controllerName}:getNetworkClientIdForDomain` as const,\n setNetworkClientIdForDomain:\n `${controllerName}:setNetworkClientIdForDomain` as const,\n};\n\nexport const SelectedNetworkControllerEventTypes = {\n stateChange: `${controllerName}:stateChange` as const,\n};\n\nexport type SelectedNetworkControllerState = {\n domains: Record;\n};\n\nexport type SelectedNetworkControllerStateChangeEvent = {\n type: typeof SelectedNetworkControllerEventTypes.stateChange;\n payload: [SelectedNetworkControllerState, Patch[]];\n};\n\nexport type SelectedNetworkControllerGetSelectedNetworkStateAction = {\n type: typeof SelectedNetworkControllerActionTypes.getState;\n handler: () => SelectedNetworkControllerState;\n};\n\nexport type SelectedNetworkControllerGetNetworkClientIdForDomainAction = {\n type: typeof SelectedNetworkControllerActionTypes.getNetworkClientIdForDomain;\n handler: SelectedNetworkController['getNetworkClientIdForDomain'];\n};\n\nexport type SelectedNetworkControllerSetNetworkClientIdForDomainAction = {\n type: typeof SelectedNetworkControllerActionTypes.setNetworkClientIdForDomain;\n handler: SelectedNetworkController['setNetworkClientIdForDomain'];\n};\n\nexport type SelectedNetworkControllerActions =\n | SelectedNetworkControllerGetSelectedNetworkStateAction\n | SelectedNetworkControllerGetNetworkClientIdForDomainAction\n | SelectedNetworkControllerSetNetworkClientIdForDomainAction;\n\nexport type AllowedActions =\n | NetworkControllerGetNetworkClientByIdAction\n | NetworkControllerGetSelectedNetworkClientAction\n | NetworkControllerGetStateAction\n | PermissionControllerHasPermissions\n | PermissionControllerGetSubjectsAction;\n\nexport type SelectedNetworkControllerEvents =\n SelectedNetworkControllerStateChangeEvent;\n\nexport type AllowedEvents =\n | NetworkControllerStateChangeEvent\n | PermissionControllerStateChange;\n\nexport type SelectedNetworkControllerMessenger = RestrictedControllerMessenger<\n typeof controllerName,\n SelectedNetworkControllerActions | AllowedActions,\n SelectedNetworkControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\nexport type SelectedNetworkControllerOptions = {\n state?: SelectedNetworkControllerState;\n messenger: SelectedNetworkControllerMessenger;\n useRequestQueuePreference: boolean;\n onPreferencesStateChange: (\n listener: (preferencesState: { useRequestQueue: boolean }) => void,\n ) => void;\n domainProxyMap: Map;\n};\n\nexport type NetworkProxy = {\n provider: ProviderProxy;\n blockTracker: BlockTrackerProxy;\n};\n\n/**\n * Controller for getting and setting the network for a particular domain.\n */\nexport class SelectedNetworkController extends BaseController<\n typeof controllerName,\n SelectedNetworkControllerState,\n SelectedNetworkControllerMessenger\n> {\n #domainProxyMap: Map;\n\n #useRequestQueuePreference: boolean;\n\n /**\n * Construct a SelectedNetworkController controller.\n *\n * @param options - The controller options.\n * @param options.messenger - The restricted controller messenger for the EncryptionPublicKey controller.\n * @param options.state - The controllers initial state.\n * @param options.useRequestQueuePreference - A boolean indicating whether to use the request queue preference.\n * @param options.onPreferencesStateChange - A callback that is called when the preference state changes.\n * @param options.domainProxyMap - A map for storing domain-specific proxies that are held in memory only during use.\n */\n constructor({\n messenger,\n state = getDefaultState(),\n useRequestQueuePreference,\n onPreferencesStateChange,\n domainProxyMap,\n }: SelectedNetworkControllerOptions) {\n super({\n name: controllerName,\n metadata: stateMetadata,\n messenger,\n state,\n });\n this.#useRequestQueuePreference = useRequestQueuePreference;\n this.#domainProxyMap = domainProxyMap;\n this.#registerMessageHandlers();\n\n // this is fetching all the dapp permissions from the PermissionsController and looking for any domains that are not in domains state in this controller. Then we take any missing domains and add them to state here, setting it with the globally selected networkClientId (fetched from the NetworkController)\n this.messagingSystem\n .call('PermissionController:getSubjectNames')\n .filter((domain) => this.state.domains[domain] === undefined)\n .forEach((domain) =>\n this.setNetworkClientIdForDomain(\n domain,\n this.messagingSystem.call('NetworkController:getState')\n .selectedNetworkClientId,\n ),\n );\n\n this.messagingSystem.subscribe(\n 'PermissionController:stateChange',\n (_, patches) => {\n patches.forEach(({ op, path }) => {\n const isChangingSubject =\n path[0] === 'subjects' && path[1] !== undefined;\n if (isChangingSubject && typeof path[1] === 'string') {\n const domain = path[1];\n if (op === 'add' && this.state.domains[domain] === undefined) {\n this.setNetworkClientIdForDomain(\n domain,\n this.messagingSystem.call('NetworkController:getState')\n .selectedNetworkClientId,\n );\n } else if (\n op === 'remove' &&\n this.state.domains[domain] !== undefined\n ) {\n this.#unsetNetworkClientIdForDomain(domain);\n }\n }\n });\n },\n );\n\n this.messagingSystem.subscribe(\n 'NetworkController:stateChange',\n ({ selectedNetworkClientId }, patches) => {\n patches.forEach(({ op, path }) => {\n // if a network is removed, update the networkClientId for all domains that were using it to the selected network\n if (op === 'remove' && path[0] === 'networkConfigurations') {\n const removedNetworkClientId = path[1] as NetworkClientId;\n Object.entries(this.state.domains).forEach(\n ([domain, networkClientIdForDomain]) => {\n if (networkClientIdForDomain === removedNetworkClientId) {\n this.setNetworkClientIdForDomain(\n domain,\n selectedNetworkClientId,\n );\n }\n },\n );\n }\n });\n },\n );\n\n onPreferencesStateChange(({ useRequestQueue }) => {\n if (this.#useRequestQueuePreference !== useRequestQueue) {\n if (!useRequestQueue) {\n // Loop through all domains and points each domain's proxy\n // to the NetworkController's own proxy of the globally selected networkClient\n Object.keys(this.state.domains).forEach((domain) => {\n this.#unsetNetworkClientIdForDomain(domain);\n });\n } else {\n this.#resetAllPermissionedDomains();\n }\n this.#useRequestQueuePreference = useRequestQueue;\n }\n });\n }\n\n #registerMessageHandlers(): void {\n this.messagingSystem.registerActionHandler(\n SelectedNetworkControllerActionTypes.getNetworkClientIdForDomain,\n this.getNetworkClientIdForDomain.bind(this),\n );\n this.messagingSystem.registerActionHandler(\n SelectedNetworkControllerActionTypes.setNetworkClientIdForDomain,\n this.setNetworkClientIdForDomain.bind(this),\n );\n }\n\n #setNetworkClientIdForDomain(\n domain: Domain,\n networkClientId: NetworkClientId,\n ) {\n const networkClient = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n );\n const networkProxy = this.getProviderAndBlockTracker(domain);\n networkProxy.provider.setTarget(networkClient.provider);\n networkProxy.blockTracker.setTarget(networkClient.blockTracker);\n\n this.update((state) => {\n state.domains[domain] = networkClientId;\n });\n }\n\n /**\n * This method is used when a domain is removed from the PermissionsController.\n * It will remove re-point the network proxy to the globally selected network in the domainProxyMap or, if no globally selected network client is available, delete the proxy.\n *\n * @param domain - The domain for which to unset the network client ID.\n */\n #unsetNetworkClientIdForDomain(domain: Domain) {\n const globallySelectedNetworkClient = this.messagingSystem.call(\n 'NetworkController:getSelectedNetworkClient',\n );\n const networkProxy = this.#domainProxyMap.get(domain);\n if (networkProxy && globallySelectedNetworkClient) {\n networkProxy.provider.setTarget(globallySelectedNetworkClient.provider);\n networkProxy.blockTracker.setTarget(\n globallySelectedNetworkClient.blockTracker,\n );\n } else if (networkProxy) {\n this.#domainProxyMap.delete(domain);\n }\n this.update((state) => {\n delete state.domains[domain];\n });\n }\n\n #domainHasPermissions(domain: Domain): boolean {\n return this.messagingSystem.call(\n 'PermissionController:hasPermissions',\n domain,\n );\n }\n\n // Loop through all domains and for those with permissions it points that domain's proxy\n // to an unproxied instance of the globally selected network client.\n // NOT the NetworkController's proxy of the globally selected networkClient\n #resetAllPermissionedDomains() {\n this.#domainProxyMap.forEach((_: NetworkProxy, domain: string) => {\n const { selectedNetworkClientId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n // can't use public setNetworkClientIdForDomain because it will throw an error\n // rather than simply skip if the domain doesn't have permissions which can happen\n // in this case since proxies are added for each site the user visits\n if (this.#domainHasPermissions(domain)) {\n this.#setNetworkClientIdForDomain(domain, selectedNetworkClientId);\n }\n });\n }\n\n setNetworkClientIdForDomain(\n domain: Domain,\n networkClientId: NetworkClientId,\n ) {\n if (domain === METAMASK_DOMAIN) {\n throw new Error(\n `NetworkClientId for domain \"${METAMASK_DOMAIN}\" cannot be set on the SelectedNetworkController`,\n );\n }\n\n if (snapsPrefixes.some((prefix) => domain.startsWith(prefix))) {\n return;\n }\n\n if (!this.#domainHasPermissions(domain)) {\n throw new Error(\n 'NetworkClientId for domain cannot be called with a domain that has not yet been granted permissions',\n );\n }\n\n this.#setNetworkClientIdForDomain(domain, networkClientId);\n }\n\n getNetworkClientIdForDomain(domain: Domain): NetworkClientId {\n const { selectedNetworkClientId: metamaskSelectedNetworkClientId } =\n this.messagingSystem.call('NetworkController:getState');\n if (!this.#useRequestQueuePreference) {\n return metamaskSelectedNetworkClientId;\n }\n return this.state.domains[domain] ?? metamaskSelectedNetworkClientId;\n }\n\n /**\n * Accesses the provider and block tracker for the currently selected network.\n *\n * @param domain - the domain for the provider\n * @returns The proxy and block tracker proxies.\n */\n getProviderAndBlockTracker(domain: Domain): NetworkProxy {\n // If the domain is 'metamask' or a snap, return the NetworkController's globally selected network client proxy\n if (\n domain === METAMASK_DOMAIN ||\n snapsPrefixes.some((prefix) => domain.startsWith(prefix))\n ) {\n const networkClient = this.messagingSystem.call(\n 'NetworkController:getSelectedNetworkClient',\n );\n if (networkClient === undefined) {\n throw new Error('Selected network not initialized');\n }\n return networkClient;\n }\n\n let networkProxy = this.#domainProxyMap.get(domain);\n if (networkProxy === undefined) {\n let networkClient;\n if (\n this.#useRequestQueuePreference &&\n this.#domainHasPermissions(domain)\n ) {\n const networkClientId = this.getNetworkClientIdForDomain(domain);\n networkClient = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n );\n } else {\n networkClient = this.messagingSystem.call(\n 'NetworkController:getSelectedNetworkClient',\n );\n if (networkClient === undefined) {\n throw new Error('Selected network not initialized');\n }\n }\n networkProxy = {\n provider: createEventEmitterProxy(networkClient.provider),\n blockTracker: createEventEmitterProxy(networkClient.blockTracker, {\n eventFilter: 'skipInternal',\n }),\n };\n this.#domainProxyMap.set(domain, networkProxy);\n }\n return networkProxy;\n }\n}\n"]} +\ No newline at end of file +diff --git a/dist/chunk-S4D42VCM.mjs b/dist/chunk-S4D42VCM.mjs +deleted file mode 100644 +index a2a93432c8799383bef3bbe2cf3adf1ac3bda043..0000000000000000000000000000000000000000 +--- a/dist/chunk-S4D42VCM.mjs ++++ /dev/null +@@ -1,281 +0,0 @@ +-var __accessCheck = (obj, member, msg) => { +- if (!member.has(obj)) +- throw TypeError("Cannot " + msg); +-}; +-var __privateGet = (obj, member, getter) => { +- __accessCheck(obj, member, "read from private field"); +- return getter ? getter.call(obj) : member.get(obj); +-}; +-var __privateAdd = (obj, member, value) => { +- if (member.has(obj)) +- throw TypeError("Cannot add the same private member more than once"); +- member instanceof WeakSet ? member.add(obj) : member.set(obj, value); +-}; +-var __privateSet = (obj, member, value, setter) => { +- __accessCheck(obj, member, "write to private field"); +- setter ? setter.call(obj, value) : member.set(obj, value); +- return value; +-}; +-var __privateMethod = (obj, member, method) => { +- __accessCheck(obj, member, "access private method"); +- return method; +-}; +- +-// src/SelectedNetworkController.ts +-import { BaseController } from "@metamask/base-controller"; +-import { createEventEmitterProxy } from "@metamask/swappable-obj-proxy"; +-var controllerName = "SelectedNetworkController"; +-var stateMetadata = { +- domains: { persist: true, anonymous: false } +-}; +-var getDefaultState = () => ({ domains: {} }); +-var snapsPrefixes = ["npm:", "local:"]; +-var METAMASK_DOMAIN = "metamask"; +-var SelectedNetworkControllerActionTypes = { +- getState: `${controllerName}:getState`, +- getNetworkClientIdForDomain: `${controllerName}:getNetworkClientIdForDomain`, +- setNetworkClientIdForDomain: `${controllerName}:setNetworkClientIdForDomain` +-}; +-var SelectedNetworkControllerEventTypes = { +- stateChange: `${controllerName}:stateChange` +-}; +-var _domainProxyMap, _useRequestQueuePreference, _registerMessageHandlers, registerMessageHandlers_fn, _setNetworkClientIdForDomain, setNetworkClientIdForDomain_fn, _unsetNetworkClientIdForDomain, unsetNetworkClientIdForDomain_fn, _domainHasPermissions, domainHasPermissions_fn, _resetAllPermissionedDomains, resetAllPermissionedDomains_fn; +-var SelectedNetworkController = class extends BaseController { +- /** +- * Construct a SelectedNetworkController controller. +- * +- * @param options - The controller options. +- * @param options.messenger - The restricted controller messenger for the EncryptionPublicKey controller. +- * @param options.state - The controllers initial state. +- * @param options.useRequestQueuePreference - A boolean indicating whether to use the request queue preference. +- * @param options.onPreferencesStateChange - A callback that is called when the preference state changes. +- * @param options.domainProxyMap - A map for storing domain-specific proxies that are held in memory only during use. +- */ +- constructor({ +- messenger, +- state = getDefaultState(), +- useRequestQueuePreference, +- onPreferencesStateChange, +- domainProxyMap +- }) { +- super({ +- name: controllerName, +- metadata: stateMetadata, +- messenger, +- state +- }); +- __privateAdd(this, _registerMessageHandlers); +- __privateAdd(this, _setNetworkClientIdForDomain); +- /** +- * This method is used when a domain is removed from the PermissionsController. +- * It will remove re-point the network proxy to the globally selected network in the domainProxyMap or, if no globally selected network client is available, delete the proxy. +- * +- * @param domain - The domain for which to unset the network client ID. +- */ +- __privateAdd(this, _unsetNetworkClientIdForDomain); +- __privateAdd(this, _domainHasPermissions); +- // Loop through all domains and for those with permissions it points that domain's proxy +- // to an unproxied instance of the globally selected network client. +- // NOT the NetworkController's proxy of the globally selected networkClient +- __privateAdd(this, _resetAllPermissionedDomains); +- __privateAdd(this, _domainProxyMap, void 0); +- __privateAdd(this, _useRequestQueuePreference, void 0); +- __privateSet(this, _useRequestQueuePreference, useRequestQueuePreference); +- __privateSet(this, _domainProxyMap, domainProxyMap); +- __privateMethod(this, _registerMessageHandlers, registerMessageHandlers_fn).call(this); +- this.messagingSystem.call("PermissionController:getSubjectNames").filter((domain) => this.state.domains[domain] === void 0).forEach( +- (domain) => this.setNetworkClientIdForDomain( +- domain, +- this.messagingSystem.call("NetworkController:getState").selectedNetworkClientId +- ) +- ); +- this.messagingSystem.subscribe( +- "PermissionController:stateChange", +- (_, patches) => { +- patches.forEach(({ op, path }) => { +- const isChangingSubject = path[0] === "subjects" && path[1] !== void 0; +- if (isChangingSubject && typeof path[1] === "string") { +- const domain = path[1]; +- if (op === "add" && this.state.domains[domain] === void 0) { +- this.setNetworkClientIdForDomain( +- domain, +- this.messagingSystem.call("NetworkController:getState").selectedNetworkClientId +- ); +- } else if (op === "remove" && this.state.domains[domain] !== void 0) { +- __privateMethod(this, _unsetNetworkClientIdForDomain, unsetNetworkClientIdForDomain_fn).call(this, domain); +- } +- } +- }); +- } +- ); +- this.messagingSystem.subscribe( +- "NetworkController:stateChange", +- ({ selectedNetworkClientId }, patches) => { +- patches.forEach(({ op, path }) => { +- if (op === "remove" && path[0] === "networkConfigurations") { +- const removedNetworkClientId = path[1]; +- Object.entries(this.state.domains).forEach( +- ([domain, networkClientIdForDomain]) => { +- if (networkClientIdForDomain === removedNetworkClientId) { +- this.setNetworkClientIdForDomain( +- domain, +- selectedNetworkClientId +- ); +- } +- } +- ); +- } +- }); +- } +- ); +- onPreferencesStateChange(({ useRequestQueue }) => { +- if (__privateGet(this, _useRequestQueuePreference) !== useRequestQueue) { +- if (!useRequestQueue) { +- Object.keys(this.state.domains).forEach((domain) => { +- __privateMethod(this, _unsetNetworkClientIdForDomain, unsetNetworkClientIdForDomain_fn).call(this, domain); +- }); +- } else { +- __privateMethod(this, _resetAllPermissionedDomains, resetAllPermissionedDomains_fn).call(this); +- } +- __privateSet(this, _useRequestQueuePreference, useRequestQueue); +- } +- }); +- } +- setNetworkClientIdForDomain(domain, networkClientId) { +- if (domain === METAMASK_DOMAIN) { +- throw new Error( +- `NetworkClientId for domain "${METAMASK_DOMAIN}" cannot be set on the SelectedNetworkController` +- ); +- } +- if (snapsPrefixes.some((prefix) => domain.startsWith(prefix))) { +- return; +- } +- if (!__privateMethod(this, _domainHasPermissions, domainHasPermissions_fn).call(this, domain)) { +- throw new Error( +- "NetworkClientId for domain cannot be called with a domain that has not yet been granted permissions" +- ); +- } +- __privateMethod(this, _setNetworkClientIdForDomain, setNetworkClientIdForDomain_fn).call(this, domain, networkClientId); +- } +- getNetworkClientIdForDomain(domain) { +- const { selectedNetworkClientId: metamaskSelectedNetworkClientId } = this.messagingSystem.call("NetworkController:getState"); +- if (!__privateGet(this, _useRequestQueuePreference)) { +- return metamaskSelectedNetworkClientId; +- } +- return this.state.domains[domain] ?? metamaskSelectedNetworkClientId; +- } +- /** +- * Accesses the provider and block tracker for the currently selected network. +- * +- * @param domain - the domain for the provider +- * @returns The proxy and block tracker proxies. +- */ +- getProviderAndBlockTracker(domain) { +- if (domain === METAMASK_DOMAIN || snapsPrefixes.some((prefix) => domain.startsWith(prefix))) { +- const networkClient = this.messagingSystem.call( +- "NetworkController:getSelectedNetworkClient" +- ); +- if (networkClient === void 0) { +- throw new Error("Selected network not initialized"); +- } +- return networkClient; +- } +- let networkProxy = __privateGet(this, _domainProxyMap).get(domain); +- if (networkProxy === void 0) { +- let networkClient; +- if (__privateGet(this, _useRequestQueuePreference) && __privateMethod(this, _domainHasPermissions, domainHasPermissions_fn).call(this, domain)) { +- const networkClientId = this.getNetworkClientIdForDomain(domain); +- networkClient = this.messagingSystem.call( +- "NetworkController:getNetworkClientById", +- networkClientId +- ); +- } else { +- networkClient = this.messagingSystem.call( +- "NetworkController:getSelectedNetworkClient" +- ); +- if (networkClient === void 0) { +- throw new Error("Selected network not initialized"); +- } +- } +- networkProxy = { +- provider: createEventEmitterProxy(networkClient.provider), +- blockTracker: createEventEmitterProxy(networkClient.blockTracker, { +- eventFilter: "skipInternal" +- }) +- }; +- __privateGet(this, _domainProxyMap).set(domain, networkProxy); +- } +- return networkProxy; +- } +-}; +-_domainProxyMap = new WeakMap(); +-_useRequestQueuePreference = new WeakMap(); +-_registerMessageHandlers = new WeakSet(); +-registerMessageHandlers_fn = function() { +- this.messagingSystem.registerActionHandler( +- SelectedNetworkControllerActionTypes.getNetworkClientIdForDomain, +- this.getNetworkClientIdForDomain.bind(this) +- ); +- this.messagingSystem.registerActionHandler( +- SelectedNetworkControllerActionTypes.setNetworkClientIdForDomain, +- this.setNetworkClientIdForDomain.bind(this) +- ); +-}; +-_setNetworkClientIdForDomain = new WeakSet(); +-setNetworkClientIdForDomain_fn = function(domain, networkClientId) { +- const networkClient = this.messagingSystem.call( +- "NetworkController:getNetworkClientById", +- networkClientId +- ); +- const networkProxy = this.getProviderAndBlockTracker(domain); +- networkProxy.provider.setTarget(networkClient.provider); +- networkProxy.blockTracker.setTarget(networkClient.blockTracker); +- this.update((state) => { +- state.domains[domain] = networkClientId; +- }); +-}; +-_unsetNetworkClientIdForDomain = new WeakSet(); +-unsetNetworkClientIdForDomain_fn = function(domain) { +- const globallySelectedNetworkClient = this.messagingSystem.call( +- "NetworkController:getSelectedNetworkClient" +- ); +- const networkProxy = __privateGet(this, _domainProxyMap).get(domain); +- if (networkProxy && globallySelectedNetworkClient) { +- networkProxy.provider.setTarget(globallySelectedNetworkClient.provider); +- networkProxy.blockTracker.setTarget( +- globallySelectedNetworkClient.blockTracker +- ); +- } else if (networkProxy) { +- __privateGet(this, _domainProxyMap).delete(domain); +- } +- this.update((state) => { +- delete state.domains[domain]; +- }); +-}; +-_domainHasPermissions = new WeakSet(); +-domainHasPermissions_fn = function(domain) { +- return this.messagingSystem.call( +- "PermissionController:hasPermissions", +- domain +- ); +-}; +-_resetAllPermissionedDomains = new WeakSet(); +-resetAllPermissionedDomains_fn = function() { +- __privateGet(this, _domainProxyMap).forEach((_, domain) => { +- const { selectedNetworkClientId } = this.messagingSystem.call( +- "NetworkController:getState" +- ); +- if (__privateMethod(this, _domainHasPermissions, domainHasPermissions_fn).call(this, domain)) { +- __privateMethod(this, _setNetworkClientIdForDomain, setNetworkClientIdForDomain_fn).call(this, domain, selectedNetworkClientId); +- } +- }); +-}; +- +-export { +- controllerName, +- METAMASK_DOMAIN, +- SelectedNetworkControllerActionTypes, +- SelectedNetworkControllerEventTypes, +- SelectedNetworkController +-}; +-//# sourceMappingURL=chunk-S4D42VCM.mjs.map +\ No newline at end of file +diff --git a/dist/chunk-S4D42VCM.mjs.map b/dist/chunk-S4D42VCM.mjs.map +deleted file mode 100644 +index b05eba0cbdeb5af4729d92e7b8de695c2d2a75e3..0000000000000000000000000000000000000000 +--- a/dist/chunk-S4D42VCM.mjs.map ++++ /dev/null +@@ -1 +0,0 @@ +-{"version":3,"sources":["../src/SelectedNetworkController.ts"],"sourcesContent":["import type { RestrictedControllerMessenger } from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport type {\n BlockTrackerProxy,\n NetworkClientId,\n NetworkControllerGetNetworkClientByIdAction,\n NetworkControllerGetSelectedNetworkClientAction,\n NetworkControllerGetStateAction,\n NetworkControllerStateChangeEvent,\n ProviderProxy,\n} from '@metamask/network-controller';\nimport type {\n PermissionControllerStateChange,\n GetSubjects as PermissionControllerGetSubjectsAction,\n HasPermissions as PermissionControllerHasPermissions,\n} from '@metamask/permission-controller';\nimport { createEventEmitterProxy } from '@metamask/swappable-obj-proxy';\nimport type { Patch } from 'immer';\n\nexport const controllerName = 'SelectedNetworkController';\n\nconst stateMetadata = {\n domains: { persist: true, anonymous: false },\n};\n\nconst getDefaultState = () => ({ domains: {} });\n\n// npm and local are currently the only valid prefixes for snap domains\n// TODO: eventually we maybe want to pull this in from snaps-utils to ensure it stays in sync\n// For now it seems like overkill to add a dependency for this one constant\n// https://github.com/MetaMask/snaps/blob/2beee7803bfe9e540788a3558b546b9f55dc3cb4/packages/snaps-utils/src/types.ts#L120\nconst snapsPrefixes = ['npm:', 'local:'] as const;\n\nexport type Domain = string;\n\nexport const METAMASK_DOMAIN = 'metamask' as const;\n\nexport const SelectedNetworkControllerActionTypes = {\n getState: `${controllerName}:getState` as const,\n getNetworkClientIdForDomain:\n `${controllerName}:getNetworkClientIdForDomain` as const,\n setNetworkClientIdForDomain:\n `${controllerName}:setNetworkClientIdForDomain` as const,\n};\n\nexport const SelectedNetworkControllerEventTypes = {\n stateChange: `${controllerName}:stateChange` as const,\n};\n\nexport type SelectedNetworkControllerState = {\n domains: Record;\n};\n\nexport type SelectedNetworkControllerStateChangeEvent = {\n type: typeof SelectedNetworkControllerEventTypes.stateChange;\n payload: [SelectedNetworkControllerState, Patch[]];\n};\n\nexport type SelectedNetworkControllerGetSelectedNetworkStateAction = {\n type: typeof SelectedNetworkControllerActionTypes.getState;\n handler: () => SelectedNetworkControllerState;\n};\n\nexport type SelectedNetworkControllerGetNetworkClientIdForDomainAction = {\n type: typeof SelectedNetworkControllerActionTypes.getNetworkClientIdForDomain;\n handler: SelectedNetworkController['getNetworkClientIdForDomain'];\n};\n\nexport type SelectedNetworkControllerSetNetworkClientIdForDomainAction = {\n type: typeof SelectedNetworkControllerActionTypes.setNetworkClientIdForDomain;\n handler: SelectedNetworkController['setNetworkClientIdForDomain'];\n};\n\nexport type SelectedNetworkControllerActions =\n | SelectedNetworkControllerGetSelectedNetworkStateAction\n | SelectedNetworkControllerGetNetworkClientIdForDomainAction\n | SelectedNetworkControllerSetNetworkClientIdForDomainAction;\n\nexport type AllowedActions =\n | NetworkControllerGetNetworkClientByIdAction\n | NetworkControllerGetSelectedNetworkClientAction\n | NetworkControllerGetStateAction\n | PermissionControllerHasPermissions\n | PermissionControllerGetSubjectsAction;\n\nexport type SelectedNetworkControllerEvents =\n SelectedNetworkControllerStateChangeEvent;\n\nexport type AllowedEvents =\n | NetworkControllerStateChangeEvent\n | PermissionControllerStateChange;\n\nexport type SelectedNetworkControllerMessenger = RestrictedControllerMessenger<\n typeof controllerName,\n SelectedNetworkControllerActions | AllowedActions,\n SelectedNetworkControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\nexport type SelectedNetworkControllerOptions = {\n state?: SelectedNetworkControllerState;\n messenger: SelectedNetworkControllerMessenger;\n useRequestQueuePreference: boolean;\n onPreferencesStateChange: (\n listener: (preferencesState: { useRequestQueue: boolean }) => void,\n ) => void;\n domainProxyMap: Map;\n};\n\nexport type NetworkProxy = {\n provider: ProviderProxy;\n blockTracker: BlockTrackerProxy;\n};\n\n/**\n * Controller for getting and setting the network for a particular domain.\n */\nexport class SelectedNetworkController extends BaseController<\n typeof controllerName,\n SelectedNetworkControllerState,\n SelectedNetworkControllerMessenger\n> {\n #domainProxyMap: Map;\n\n #useRequestQueuePreference: boolean;\n\n /**\n * Construct a SelectedNetworkController controller.\n *\n * @param options - The controller options.\n * @param options.messenger - The restricted controller messenger for the EncryptionPublicKey controller.\n * @param options.state - The controllers initial state.\n * @param options.useRequestQueuePreference - A boolean indicating whether to use the request queue preference.\n * @param options.onPreferencesStateChange - A callback that is called when the preference state changes.\n * @param options.domainProxyMap - A map for storing domain-specific proxies that are held in memory only during use.\n */\n constructor({\n messenger,\n state = getDefaultState(),\n useRequestQueuePreference,\n onPreferencesStateChange,\n domainProxyMap,\n }: SelectedNetworkControllerOptions) {\n super({\n name: controllerName,\n metadata: stateMetadata,\n messenger,\n state,\n });\n this.#useRequestQueuePreference = useRequestQueuePreference;\n this.#domainProxyMap = domainProxyMap;\n this.#registerMessageHandlers();\n\n // this is fetching all the dapp permissions from the PermissionsController and looking for any domains that are not in domains state in this controller. Then we take any missing domains and add them to state here, setting it with the globally selected networkClientId (fetched from the NetworkController)\n this.messagingSystem\n .call('PermissionController:getSubjectNames')\n .filter((domain) => this.state.domains[domain] === undefined)\n .forEach((domain) =>\n this.setNetworkClientIdForDomain(\n domain,\n this.messagingSystem.call('NetworkController:getState')\n .selectedNetworkClientId,\n ),\n );\n\n this.messagingSystem.subscribe(\n 'PermissionController:stateChange',\n (_, patches) => {\n patches.forEach(({ op, path }) => {\n const isChangingSubject =\n path[0] === 'subjects' && path[1] !== undefined;\n if (isChangingSubject && typeof path[1] === 'string') {\n const domain = path[1];\n if (op === 'add' && this.state.domains[domain] === undefined) {\n this.setNetworkClientIdForDomain(\n domain,\n this.messagingSystem.call('NetworkController:getState')\n .selectedNetworkClientId,\n );\n } else if (\n op === 'remove' &&\n this.state.domains[domain] !== undefined\n ) {\n this.#unsetNetworkClientIdForDomain(domain);\n }\n }\n });\n },\n );\n\n this.messagingSystem.subscribe(\n 'NetworkController:stateChange',\n ({ selectedNetworkClientId }, patches) => {\n patches.forEach(({ op, path }) => {\n // if a network is removed, update the networkClientId for all domains that were using it to the selected network\n if (op === 'remove' && path[0] === 'networkConfigurations') {\n const removedNetworkClientId = path[1] as NetworkClientId;\n Object.entries(this.state.domains).forEach(\n ([domain, networkClientIdForDomain]) => {\n if (networkClientIdForDomain === removedNetworkClientId) {\n this.setNetworkClientIdForDomain(\n domain,\n selectedNetworkClientId,\n );\n }\n },\n );\n }\n });\n },\n );\n\n onPreferencesStateChange(({ useRequestQueue }) => {\n if (this.#useRequestQueuePreference !== useRequestQueue) {\n if (!useRequestQueue) {\n // Loop through all domains and points each domain's proxy\n // to the NetworkController's own proxy of the globally selected networkClient\n Object.keys(this.state.domains).forEach((domain) => {\n this.#unsetNetworkClientIdForDomain(domain);\n });\n } else {\n this.#resetAllPermissionedDomains();\n }\n this.#useRequestQueuePreference = useRequestQueue;\n }\n });\n }\n\n #registerMessageHandlers(): void {\n this.messagingSystem.registerActionHandler(\n SelectedNetworkControllerActionTypes.getNetworkClientIdForDomain,\n this.getNetworkClientIdForDomain.bind(this),\n );\n this.messagingSystem.registerActionHandler(\n SelectedNetworkControllerActionTypes.setNetworkClientIdForDomain,\n this.setNetworkClientIdForDomain.bind(this),\n );\n }\n\n #setNetworkClientIdForDomain(\n domain: Domain,\n networkClientId: NetworkClientId,\n ) {\n const networkClient = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n );\n const networkProxy = this.getProviderAndBlockTracker(domain);\n networkProxy.provider.setTarget(networkClient.provider);\n networkProxy.blockTracker.setTarget(networkClient.blockTracker);\n\n this.update((state) => {\n state.domains[domain] = networkClientId;\n });\n }\n\n /**\n * This method is used when a domain is removed from the PermissionsController.\n * It will remove re-point the network proxy to the globally selected network in the domainProxyMap or, if no globally selected network client is available, delete the proxy.\n *\n * @param domain - The domain for which to unset the network client ID.\n */\n #unsetNetworkClientIdForDomain(domain: Domain) {\n const globallySelectedNetworkClient = this.messagingSystem.call(\n 'NetworkController:getSelectedNetworkClient',\n );\n const networkProxy = this.#domainProxyMap.get(domain);\n if (networkProxy && globallySelectedNetworkClient) {\n networkProxy.provider.setTarget(globallySelectedNetworkClient.provider);\n networkProxy.blockTracker.setTarget(\n globallySelectedNetworkClient.blockTracker,\n );\n } else if (networkProxy) {\n this.#domainProxyMap.delete(domain);\n }\n this.update((state) => {\n delete state.domains[domain];\n });\n }\n\n #domainHasPermissions(domain: Domain): boolean {\n return this.messagingSystem.call(\n 'PermissionController:hasPermissions',\n domain,\n );\n }\n\n // Loop through all domains and for those with permissions it points that domain's proxy\n // to an unproxied instance of the globally selected network client.\n // NOT the NetworkController's proxy of the globally selected networkClient\n #resetAllPermissionedDomains() {\n this.#domainProxyMap.forEach((_: NetworkProxy, domain: string) => {\n const { selectedNetworkClientId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n // can't use public setNetworkClientIdForDomain because it will throw an error\n // rather than simply skip if the domain doesn't have permissions which can happen\n // in this case since proxies are added for each site the user visits\n if (this.#domainHasPermissions(domain)) {\n this.#setNetworkClientIdForDomain(domain, selectedNetworkClientId);\n }\n });\n }\n\n setNetworkClientIdForDomain(\n domain: Domain,\n networkClientId: NetworkClientId,\n ) {\n if (domain === METAMASK_DOMAIN) {\n throw new Error(\n `NetworkClientId for domain \"${METAMASK_DOMAIN}\" cannot be set on the SelectedNetworkController`,\n );\n }\n\n if (snapsPrefixes.some((prefix) => domain.startsWith(prefix))) {\n return;\n }\n\n if (!this.#domainHasPermissions(domain)) {\n throw new Error(\n 'NetworkClientId for domain cannot be called with a domain that has not yet been granted permissions',\n );\n }\n\n this.#setNetworkClientIdForDomain(domain, networkClientId);\n }\n\n getNetworkClientIdForDomain(domain: Domain): NetworkClientId {\n const { selectedNetworkClientId: metamaskSelectedNetworkClientId } =\n this.messagingSystem.call('NetworkController:getState');\n if (!this.#useRequestQueuePreference) {\n return metamaskSelectedNetworkClientId;\n }\n return this.state.domains[domain] ?? metamaskSelectedNetworkClientId;\n }\n\n /**\n * Accesses the provider and block tracker for the currently selected network.\n *\n * @param domain - the domain for the provider\n * @returns The proxy and block tracker proxies.\n */\n getProviderAndBlockTracker(domain: Domain): NetworkProxy {\n // If the domain is 'metamask' or a snap, return the NetworkController's globally selected network client proxy\n if (\n domain === METAMASK_DOMAIN ||\n snapsPrefixes.some((prefix) => domain.startsWith(prefix))\n ) {\n const networkClient = this.messagingSystem.call(\n 'NetworkController:getSelectedNetworkClient',\n );\n if (networkClient === undefined) {\n throw new Error('Selected network not initialized');\n }\n return networkClient;\n }\n\n let networkProxy = this.#domainProxyMap.get(domain);\n if (networkProxy === undefined) {\n let networkClient;\n if (\n this.#useRequestQueuePreference &&\n this.#domainHasPermissions(domain)\n ) {\n const networkClientId = this.getNetworkClientIdForDomain(domain);\n networkClient = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n );\n } else {\n networkClient = this.messagingSystem.call(\n 'NetworkController:getSelectedNetworkClient',\n );\n if (networkClient === undefined) {\n throw new Error('Selected network not initialized');\n }\n }\n networkProxy = {\n provider: createEventEmitterProxy(networkClient.provider),\n blockTracker: createEventEmitterProxy(networkClient.blockTracker, {\n eventFilter: 'skipInternal',\n }),\n };\n this.#domainProxyMap.set(domain, networkProxy);\n }\n return networkProxy;\n }\n}\n"],"mappings":"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","names":[]} +\ No newline at end of file +diff --git a/dist/chunk-ZY7ETPVE.mjs b/dist/chunk-ZY7ETPVE.mjs +deleted file mode 100644 +index 50a21cfa6988a3e32a4c1a20f9113eaec7bf99ac..0000000000000000000000000000000000000000 +--- a/dist/chunk-ZY7ETPVE.mjs ++++ /dev/null +@@ -1,23 +0,0 @@ +-import { +- SelectedNetworkControllerActionTypes +-} from "./chunk-S4D42VCM.mjs"; +- +-// src/SelectedNetworkMiddleware.ts +-var createSelectedNetworkMiddleware = (messenger) => { +- const getNetworkClientIdForDomain = (origin) => messenger.call( +- SelectedNetworkControllerActionTypes.getNetworkClientIdForDomain, +- origin +- ); +- return (req, _, next) => { +- if (!req.origin) { +- throw new Error("Request object is lacking an 'origin'"); +- } +- req.networkClientId = getNetworkClientIdForDomain(req.origin); +- return next(); +- }; +-}; +- +-export { +- createSelectedNetworkMiddleware +-}; +-//# sourceMappingURL=chunk-ZY7ETPVE.mjs.map +\ No newline at end of file +diff --git a/dist/chunk-ZY7ETPVE.mjs.map b/dist/chunk-ZY7ETPVE.mjs.map +deleted file mode 100644 +index fbcfd73d57f291b4f129caa47ef0c9614987cc30..0000000000000000000000000000000000000000 +--- a/dist/chunk-ZY7ETPVE.mjs.map ++++ /dev/null +@@ -1 +0,0 @@ +-{"version":3,"sources":["../src/SelectedNetworkMiddleware.ts"],"sourcesContent":["import type { JsonRpcMiddleware } from '@metamask/json-rpc-engine';\nimport type { NetworkClientId } from '@metamask/network-controller';\nimport type { Json, JsonRpcParams, JsonRpcRequest } from '@metamask/utils';\n\nimport type { SelectedNetworkControllerMessenger } from './SelectedNetworkController';\nimport { SelectedNetworkControllerActionTypes } from './SelectedNetworkController';\n\nexport type SelectedNetworkMiddlewareJsonRpcRequest = JsonRpcRequest & {\n networkClientId?: NetworkClientId;\n origin?: string;\n};\n\nexport const createSelectedNetworkMiddleware = (\n messenger: SelectedNetworkControllerMessenger,\n): JsonRpcMiddleware => {\n const getNetworkClientIdForDomain = (origin: string) =>\n messenger.call(\n SelectedNetworkControllerActionTypes.getNetworkClientIdForDomain,\n origin,\n );\n\n return (req: SelectedNetworkMiddlewareJsonRpcRequest, _, next) => {\n if (!req.origin) {\n throw new Error(\"Request object is lacking an 'origin'\");\n }\n\n req.networkClientId = getNetworkClientIdForDomain(req.origin);\n return next();\n };\n};\n"],"mappings":";;;;;AAYO,IAAM,kCAAkC,CAC7C,cAC2C;AAC3C,QAAM,8BAA8B,CAAC,WACnC,UAAU;AAAA,IACR,qCAAqC;AAAA,IACrC;AAAA,EACF;AAEF,SAAO,CAAC,KAA8C,GAAG,SAAS;AAChE,QAAI,CAAC,IAAI,QAAQ;AACf,YAAM,IAAI,MAAM,uCAAuC;AAAA,IACzD;AAEA,QAAI,kBAAkB,4BAA4B,IAAI,MAAM;AAC5D,WAAO,KAAK;AAAA,EACd;AACF;","names":[]} +\ No newline at end of file +diff --git a/dist/index.js b/dist/index.js +index 4c9b1798d88466e11abbcf1c0616f2617d73ab5a..cd71df24b3f6424f640c62ce8a7938fcf15ae480 100644 +--- a/dist/index.js ++++ b/dist/index.js +@@ -1,17 +1,17 @@ + "use strict";Object.defineProperty(exports, "__esModule", {value: true}); + +-var _chunk6W2ETVOHjs = require('./chunk-6W2ETVOH.js'); ++var _chunkANSSZMDIjs = require('./chunk-ANSSZMDI.js'); + + + + + +-var _chunkOGUVGN6Rjs = require('./chunk-OGUVGN6R.js'); ++var _chunkCECZWJ42js = require('./chunk-CECZWJ42.js'); + + + + + + +-exports.METAMASK_DOMAIN = _chunkOGUVGN6Rjs.METAMASK_DOMAIN; exports.SelectedNetworkController = _chunkOGUVGN6Rjs.SelectedNetworkController; exports.SelectedNetworkControllerActionTypes = _chunkOGUVGN6Rjs.SelectedNetworkControllerActionTypes; exports.SelectedNetworkControllerEventTypes = _chunkOGUVGN6Rjs.SelectedNetworkControllerEventTypes; exports.createSelectedNetworkMiddleware = _chunk6W2ETVOHjs.createSelectedNetworkMiddleware; ++exports.METAMASK_DOMAIN = _chunkCECZWJ42js.METAMASK_DOMAIN; exports.SelectedNetworkController = _chunkCECZWJ42js.SelectedNetworkController; exports.SelectedNetworkControllerActionTypes = _chunkCECZWJ42js.SelectedNetworkControllerActionTypes; exports.SelectedNetworkControllerEventTypes = _chunkCECZWJ42js.SelectedNetworkControllerEventTypes; exports.createSelectedNetworkMiddleware = _chunkANSSZMDIjs.createSelectedNetworkMiddleware; + //# sourceMappingURL=index.js.map +\ No newline at end of file +diff --git a/dist/index.mjs b/dist/index.mjs +index 8780cadd8a535b745960ebde8d7aa49989451ef6..0c7b103ca9cb6a604bf5f831bccef3002abd7c5f 100644 +--- a/dist/index.mjs ++++ b/dist/index.mjs +@@ -1,12 +1,12 @@ + import { + createSelectedNetworkMiddleware +-} from "./chunk-ZY7ETPVE.mjs"; ++} from "./chunk-HFN7TKJS.mjs"; + import { + METAMASK_DOMAIN, + SelectedNetworkController, + SelectedNetworkControllerActionTypes, + SelectedNetworkControllerEventTypes +-} from "./chunk-S4D42VCM.mjs"; ++} from "./chunk-7DSTEJNI.mjs"; + export { + METAMASK_DOMAIN, + SelectedNetworkController, +diff --git a/dist/tsconfig.build.tsbuildinfo b/dist/tsconfig.build.tsbuildinfo +index 0f3a7a7e18a90ebf82da9962818a7e552c892d9c..8a2c9ef72e1d3a4302d4bda2c3e634414225fd17 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","../../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/@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/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/@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","../../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","../../../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","../../../node_modules/@metamask/safe-event-emitter/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","../../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/eth-block-tracker/dist/blocktracker.d.ts","../../../node_modules/eth-block-tracker/dist/pollingblocktracker.d.ts","../../../node_modules/eth-block-tracker/dist/subscribeblocktracker.d.ts","../../../node_modules/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","../../../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","../../approval-controller/dist/types/approvalcontroller.d.ts","../../approval-controller/dist/types/errors.d.ts","../../approval-controller/dist/types/index.d.ts","../../permission-controller/dist/types/permission-middleware.d.ts","../../permission-controller/dist/types/subjectmetadatacontroller.d.ts","../../permission-controller/dist/types/permissioncontroller.d.ts","../../permission-controller/dist/types/permission.d.ts","../../permission-controller/dist/types/caveat.d.ts","../../permission-controller/dist/types/errors.d.ts","../../permission-controller/dist/types/utils.d.ts","../../permission-controller/dist/types/rpc-methods/getpermissions.d.ts","../../permission-controller/dist/types/rpc-methods/requestpermissions.d.ts","../../permission-controller/dist/types/rpc-methods/revokepermissions.d.ts","../../permission-controller/dist/types/rpc-methods/index.d.ts","../../permission-controller/dist/types/index.d.ts","../src/selectednetworkcontroller.ts","../src/selectednetworkmiddleware.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/readable-stream/node_modules/safe-buffer/index.d.ts","../../../node_modules/@types/readable-stream/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","75b22c74010ba649de1a1676a4c4b8b5bb4294fecd05089e2094429b16d7840c","e475453e7140e95542332943d3052fe4c7430ad1efce42b3e9157f1fee8cbc5f","ebfdf904255ce746c9d30117c2edef355fb19bf7650478d2405f39f0e4f302e6","f3f63b48addb8e2ea9d20bb671c3c306413b3daa39996d0ae52f63d8e32158e1","a50599c08934a62f11657bdbe0dc929ab66da1b1f09974408fd9a33ec1bb8060","5a20e7d6c630b91be15e9b837853173829d00273197481dc8d3e94df61105a71","8d478048d71cc16f806d4b71b252ecb67c7444ccf4f4b09b29a312712184f859","b4000a0a525fa921e896cbdb32ae802c9684f0fd371b5fc69e7310f7918cc2c3","9df4662ca3dbc2522bc115833ee04faa1afbb4e249a85ef4a0a09c621346bd08","b25d9065cf1c1f537a140bbc508e953ed2262f77134574c432d206ff36f4bdbf","1b103313097041aa9cd705a682c652f08613cb5cf8663321061c0902f845e81c","68ccec8662818911d8a12b8ed028bc5729fb4f1d34793c4701265ba60bc73cf4","5f85b8b79dc4d36af672c035b2beb71545de63a5d60bccbeee64c260941672ab","b3d48529ae61dc27d0bfbfa2cb3e0dff8189644bd155bdf5df1e8e14669f7043","40fe4b689225816b31fe5794c0fbf3534568819709e40295ead998a2bc1ab237","f65b5e33b9ad545a1eebbd6afe857314725ad42aaf069913e33f928ab3e4990a","fb6f2a87beb7fb1f4c2b762d0c76a9459fc91f557231569b0ee21399e22aa13d","31c858dc85996fac4b7fa944e1016d5c72f514930a72357ab5001097bf6511c7","3de30a871b3340be8b679c52aa12f90dd1c8c60874517be58968fdbcc4d79445","6fd985bd31eaf77542625306fb0404d32bff978990f0a06428e5f0b9a3b58109","34693fb4a5e771e11668219221344dd1bd7d8b77ed005a1c1d965fb559be8406","7394959e5a741b185456e1ef5d64599c36c60a323207450991e7a42e08911419",{"version":"e44ea2d6b7b853f6c81482416db43dafc11944561b810e469ae423085511ce7e","affectsGlobalScope":true},"f51b4042a3ac86f1f707500a9768f88d0b0c1fc3f3e45a73333283dea720cdc6",{"version":"a7289d79eb84a59d2475b4d0136b4404be3cfdd17c3ea46b9194add1d645df01","affectsGlobalScope":true},"0bb26fa2a90ee890eed57ee812c71fa84d3d07850163ec4a204de86412cc57c1","132ca47da601c60141dd6f10bd08c70d0620177e5638439df2464ec3945b6d98",{"version":"55d2bbae076fed7269c3e16faeb32f988f558427b7a1c3bf04aa7551ab86ae90","affectsGlobalScope":true},"a40826e8476694e90da94aa008283a7de50d1dafd37beada623863f1901cb7fb","cf83847c9264dcd592b6c89c1542925b899b277228687f3638614e3fa784cf76","3a41ebe7f089d50f447466b35b6cabb8b584c0994fc9809d0cd0a4ebc41e1239","7693b90b3075deaccafd5efb467bf9f2b747a3075be888652ef73e64396d8628","0c42d6cba77d9ad1cf45256ccb8489aa502fe2dbee1ec9048a29d49f5d532e73","2cf89c17245db65d175d4ef699cd68187516f9b3ae5c572fc0b9ad60f35dc223","5f20d20b7607174caf1a6da9141aeb9f2142159ae2410ca30c7a0fccd1d19c99",{"version":"a34d65f61ec5aac5b53502c8b0bd4e00d217bccb95bf94d449e2571baa11fb8c","affectsGlobalScope":true},"8d42e5af5fb0a96a77e135ce84cc60636c9bad39d9dba043a4efe9d1bdeb3cc3","56fcc451e9065eb121c9cc4c1b9994a816306f3b0b3b1fce7ad59f0ac97a9999","8a6f12b74d3e6c4f5e1b918cb8e64ae16bc6756cf0d48bcc28a28e1bf26ca0cd","c3759b5bc5cc40f5988d86a497741a80fa91258629ae50a2b3735e774cd377cc","bf268a0aea37ad4ae3b7a9b58559190b6fc01ea16a31e35cd05817a0a60f895a","45dd82fb5aea9b12b2a90b427b28f3a014e8b2ee9b74087a5ab882841cb5fbc5",{"version":"d7dad6db394a3d9f7b49755e4b610fbf8ed6eb0c9810ae5f1a119f6b5d76de45","affectsGlobalScope":true},"48b2f9302651eb31acd5be69bb4e6b35797a7fcd6b77391d10a4ccadf7dc3609","0c8c917ef15498c827bd494a0ef365e9f76deb211f8acbb86932e20489310788","dd67d2b5e4e8a182a38de8e69fb736945eaa4588e0909c14e01a14bd3cc1fd1e",{"version":"9cdc2c6144b03822c9842505d09945bcf813b86827fdb260dd7586b63abc19bf","affectsGlobalScope":true},{"version":"2923dee3c897f03e91b54a210cdbefea7290562f0ac4b948667d4c9ee844b79e","affectsGlobalScope":true},"79169698d09a2be54b14f3bcad2575b414bf3525063fde0a1e4fcd5d6efd380e","051d939bcf77caa3cef3282708ab3a6fdfb741a7366e1d74a9e7603b67417ec3","0be79b3ff0f16b6c2f9bc8c4cc7097ea417d8d67f8267f7e1eec8e32b548c2ff","1c61ffa3a71b77363b30d19832c269ef62fba787f5610cac7254728d3b69ab2e","a234d62ae81d012ebf23898a45672edf3e5c93ecf5a438a42b96c08dd68cde43","269929a24b2816343a178008ac9ae9248304d92a8ba8e233055e0ed6dbe6ef71","09ed02a725db002693236b6dfc49b2c6eb5557be1421d7fbe4f07cfe38211d92","09d801ff4a303d4976d4b9cb94af3a9097c4a70345e662d176975872d2998e51","c8558b01389b5f7610ac293aa612ccea2ae64d83af43b49f8142f190be1f414c","c40fdf7b2e18df49ce0568e37f0292c12807a0748be79e272745e7216bed2606",{"version":"b10b426c56e220b5093bf8a2446ee47af47263b7b1a03f4b18e42326b231b111","affectsGlobalScope":true},"4e228e78c1e9b0a75c70588d59288f63a6258e8b1fe4a67b0c53fe03461421d9","b4635ef36bee17e1304337d591c3b6b461ecdbc1876d0effbe6a581e62201fe5","205d50c24359ead003dc537b9b65d2a64208dfdffe368f403cf9e0357831db9e","1265fddcd0c68be9d2a3b29805d0280484c961264dd95e0b675f7bd91f777e78",{"version":"e4507242542bd499238f693d88b2d32e22177cc508854625f87bcc9bc3fa1256","affectsGlobalScope":true},{"version":"d942354e4966a98d3a92d1b1af0b4ac06f33af3f88116743e2c304c027ca26ef","affectsGlobalScope":true},"39f0808e5be3cb38674726c21fe2eb453c55e48a901679b4ce30fef85549b892","6afd66a7432ef100027ea110449e874196381e019e30eda7e7d8ca390366b7a8","befb8a9a78ac99d8fbc3ed392810489a7b90760c7a58934e8f1c8538f581cff3","e670bdf01540d35c170fae68edfd2f288eff909936780c379d6a9103b787b22c","867f95abf1df444aab146b19847391fc2f922a55f6a970a27ed8226766cee29f",{"version":"ab9b9a36e5284fd8d3bf2f7d5fcbc60052f25f27e4d20954782099282c60d23e","affectsGlobalScope":true},"88003d9ab15507806f41b120be6d407c1afe566c2f6689ebe3a034dd5ec0c8dc","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","980d21b0081cbf81774083b1e3a46f4bbdcd2b68858df0f66d7fad9c82bc34bc","6a9c5127096b35264eb7cd21b2417bfc1d42cceca9ba4ce2bb0c3410b7816042","93b7325b49dfbf613d940ed0e471216657b2d77459dac34f1b5b1678f08f884c","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","5d697a4b315cc5bb3042ae869abffd10c3b0d7b182cda0e4c45d8819937e5796","563fa27fdaec8f195b84f71a7af0ef48d30d5cc830575db86da86a63a470c8e6","6ee58aa536dabb19b09bc036f1abe83feb51e13d63b23d30b2d0631a2de99b8f","8aceb205dcc6f814ad99635baf1e40b6e01d06d3fe27b72fd766c6d0b8c0c600","299567f84bfedd1468dca2755a829cb19e607a6811673788807dc8921e211bc9","795d9fb85aad92221504db74dd179b506bd189bba0c104426f7e7bb8a66ffee5","1311bc194e0a69fe61031e852c1c0b439e2a2a3d1d5e2d8ff795499b9f283459","4b7ce19369d7e7fae76720c2c6c7f671bf3fa0f7093edb864f1ac358ca7c456c","c972ef44deca1fa8fab465915ffa00f82e126aacf3dfc8979c03b1b066ce5bb6","30285a1011c6d6b52f3ba3abb0a984be8148c05cdefb8eb6eb562335a3991f35","8e7adb22c0adecf7464861fc58ae3fc617b41ffbd70c97aa8493dc0966a82273","755f3cd1d9c1b564cff090e3b0e29200ae55690a91b87cb9e7a64c2dbeb314d3","d6bb7e0a6877b7856c183bff13d09dd9ae599ea43c6f6b33d3d5f72a830ed460","f1b51ae93c762d7c43f559933cd4842dd870367e8d92e90704ffa685dd5b29a3","3f450762fd7c34ed545e738abccb0af6a703572a10521643cf8fc88e3724c99c","fcc8beef29f39f09b1d9c9f99c42f9fed605ab1c28d2a630185f732b9ba53763","d6e6620a30d582182acc3f0a992a0c311adc589f111096aea11ab83fc09a5ccc","6213b8f686f56beab22b59a0f468590fd3a4c5fa931236a017efeca91d7c9584","c451cec9a588b1f105a5ea2c6063d4fca112b9d70105cacdadda0e1ef67e9379","cb047832dc68f5a2c41c62c5e95ddcacbae3a8b034d40cd15319a8cb7f25104a","980336ccdfc3c08f3c3b201aa6662e6016e20f15847f8465b68f3e8e67b4665c","5a3493939995f46ff3d9073cd534fb8961c3bf4e08c71db27066ff03d906dea8","bb5a2ac327605ebebf831c469b05bd34a33a6a46ee8c1edd9f3310aad32cf6a1","bf5d041f2440b4a9391e2b5eb3b8d94cbf1e3b8ff4703b6539d4e65e758c8f37","8516469eb90e723b0eb03df1be098f7e6a4709f6f48fd4532868d20a0a934f6e","d60e9ab369a72d234aac49adbe2900d8ef1408a6ea4db552cf2a48c9d8d6a1bc","0ebb4698803f01e2e7df6acce572fff068f4a20c47221721dafd70a27e372831","a12eaa942232703a8a8477a2f240ad5a2c26c595012ea8f128224e77984099c4","4070c2f1c3434fcf84886e04d30d82cd650ee443e53b82b404b144175cf8741e","2cea9689efa8591732096235abe7f084fc29c92badd5b0897a5e876b77e71887","4ed4e504126014fee13aaef5e3fc140f2ff7031ff3a8b5386717905820ea2d09","8129a34006218a6f3cdc81bbd438d5429eb18b08b4338a26977ac3b4df129d75","30d2170e1a718b5035611af55e3618b4ba8f42f0749bb52ee593da6082c4e2ce","98ef38666d88ec9699a722053e07ede65d3042f693fe7ff8c786e53dbb6fd43b","a3b8b6be7620897d1e481e8650c980a210a138fceb6e710eaf95fd9dd0dfe94a","12c89d0e32758c120a569045f21cf5b77244f86792611ced8de7f86b37e77781","9f9e5bae412fa5909fae636d6733aee27a108cc2ed5b13980611016336774d3c","662fe197bba64bd3f17ee118058cd2d0d2dbe33d7c0c865fd6365d90bfc44e1e","030519c351f800551cac2658038804969ca4584d2c0175a710602ac234ca1340","0278a6939ca83cd040b08ff8c5fc7838b6693ddc52f22526bf158e6b10e0246c","c2d6206e5ba4fd3063b01218c2b3b997afc1cfbeb49fcee991fa8595842ce53d","29c188a2c660f99f1b4835022e011c4268d7af989d4b7dda33c0a69ca1a777f8","1ed0bf138e87912d741e28333b58cbf814ae863783b3b404d2454cbabb9c5fc0","3452ee7d8ef0b1bbd47b2a56924a1dc3c79dc84a19d212e9dc496f92e4943aa0","99510e20e3d4816e283e59e8f0f31f603b2f026648240ffdb1ca9f24be678419","037d1fbeb96dc35600814be14d0fbf31acf35f1d7b443ea33514937de69c2bf2","64d1859b7dd9f419ba08e064c2b16b1a5edde0316d6c2bb1833c9381d4dffc3d","69b0f96ca137c0dba05f321a159141ad36f79cfba2fffcc29d131e280275e6f2","7bb64cf513a2b1cf7a94f81ca201f3d76a9b17af556f0cfc4e2707443e6caa66","a9add2a29da4cd0b617ae89f196b3f2172a031aeb086922cdf097236eef8b008","99afac3e6e683ee3111e499f9919953e9489cb39cad74363717aa3805e91db51","c49f92b83968f4ee0b6026396a9b6e2d6fee8b660d08a90efb03355ce3433a7a","4fdc6afe4d7ef6aeb32ac0818d47e99f98a31d0696abc4cb2af489c78ac1ba1d","d73d5a0e854037d43781b2d5d33f4b95ee509e0ddede677aade79fbee6a97cdc","35d14e1ae04be300828b1a1614316b9312a009cfd5e29fa56f94c2a9f60b12df","d160fe745f9c3b72d7b9036fdb2b6b500a520d43e36bb842c927b6fe59ea2c23",{"version":"4a415bf08be658b3f7ab2c2754a077e36a32e08aabb73aad26de47a45c0fa81e","signature":"3a4f9e7087c566703447928d15c234bd0bcc63a2a50b6ec39ed37c9bc0342310"},{"version":"506bcad28e45e13c23c2c25c9691e0dc42d8755a0e24b9a48586f51b5bebae8f","signature":"3a12771c76c5ed979438dc0e390224bd3d8661bcbea18114f77c4c6d9de5b8b2"},{"version":"6fbdb35bc3b9cfb225c48108b5bacb5c0d67bbcb677cf13b912e15a852551023","signature":"ee06131adf64c6cd201de36563174cff0e160b81438be186a7bd85e6c2b54fa7"},"a5aaeca001d2f69093d04aac4db321e4c338fd9b20cbc4f0b0af3dc6ae0f235b","cc957354aa3c94c9961ebf46282cfde1e81d107fc5785a61f62c67f1dd3ac2eb","8041cfce439ff29d339742389de04c136e3029d6b1817f07b2d7fcbfb7534990","93de1c6dab503f053efe8d304cb522bb3a89feab8c98f307a674a4fae04773e9","29a46d003ca3c721e6405f00dee7e3de91b14e09701eba5d887bf76fb2d47d38","069bebfee29864e3955378107e243508b163e77ab10de6a5ee03ae06939f0bb9","9990f9e566bc3c2c6e38df81294fb756e7f5b7b0e5bb17ab75384e190548b4b6",{"version":"64d4b35c5456adf258d2cf56c341e203a073253f229ef3208fc0d5020253b241","affectsGlobalScope":true},"ee7d8894904b465b072be0d2e4b45cf6b887cdba16a467645c4e200982ece7ea","f3d8c757e148ad968f0d98697987db363070abada5f503da3c06aefd9d4248c1","df95e00612c1faa5e0e7ef0dba589b18665bbeb3221db2b6cee1fe4d0e61921f","afe73051ff6a03a9565cbd8ebb0e956ee3df5e913ad5c1ded64218aabfa3dcb5","8b06ac3faeacb8484d84ddb44571d8f410697f98d7bfa86c0fda60373a9f5215","7eb06594824ada538b1d8b48c3925a83e7db792f47a081a62cf3e5c4e23cf0ee","f5638f7c2f12a9a1a57b5c41b3c1ea7db3876c003bab68e6a57afd6bcc169af0","0d14fa22c41fdc7277e6f71473b20ebc07f40f00e38875142335d5b63cdfc9d2","d8aab31ba8e618cc3eea10b0945de81cb93b7e8150a013a482332263b9305322","462bccdf75fcafc1ae8c30400c9425e1a4681db5d605d1a0edb4f990a54d8094","5923d8facbac6ecf7c84739a5c701a57af94a6f6648d6229a6c768cf28f0f8cb","7adecb2c3238794c378d336a8182d4c3dd2c4fa6fa1785e2797a3db550edea62","dc12dc0e5aa06f4e1a7692149b78f89116af823b9e1f1e4eae140cd3e0e674e6","1bfc6565b90c8771615cd8cfcf9b36efc0275e5e83ac7d9181307e96eb495161","8a8a96898906f065f296665e411f51010b51372fa260d5373bf9f64356703190","7f82ef88bdb67d9a850dd1c7cd2d690f33e0f0acd208e3c9eba086f3670d4f73",{"version":"ccfd8774cd9b929f63ff7dcf657977eb0652e3547f1fcac1b3a1dc5db22d4d58","affectsGlobalScope":true},"d92dc90fecd2552db74d8dc3c6fb4db9145b2aa0efe2c127236ba035969068d4","96d14f21b7652903852eef49379d04dbda28c16ed36468f8c9fa08f7c14c9538","675e702f2032766a91eeadee64f51014c64688525da99dccd8178f0c599f13a8","458111fc89d11d2151277c822dfdc1a28fa5b6b2493cf942e37d4cd0a6ee5f22","19c816167e076e7c24f074389c6cf3ed87bdbb917d1ea439ca281f9d26db2439","187119ff4f9553676a884e296089e131e8cc01691c546273b1d0089c3533ce42","febf0b2de54781102b00f61653b21377390a048fbf5262718c91860d11ff34a6","98f9d826db9cd99d27a01a59ee5f22863df00ccf1aaf43e1d7db80ebf716f7c3","0aaef8cded245bf5036a7a40b65622dd6c4da71f7a35343112edbe112b348a1e","00baffbe8a2f2e4875367479489b5d43b5fc1429ecb4a4cc98cfc3009095f52a","dcd91d3b697cb650b95db5471189b99815af5db2a1cd28760f91e0b12ede8ed5","3c92b6dfd43cc1c2485d9eba5ff0b74a19bb8725b692773ef1d66dac48cda4bd","3cf0d343c2276842a5b617f22ba82af6322c7cfe8bb52238ffc0c491a3c21019","df996e25faa505f85aeb294d15ebe61b399cf1d1e49959cdfaf2cc0815c203f9",{"version":"f2eff8704452659641164876c1ef0df4174659ce7311b0665798ea3f556fa9ad","affectsGlobalScope":true},"8841e2aa774b89bd23302dede20663306dc1b9902431ac64b24be8b8d0e3f649","2b8264b2fefd7367e0f20e2c04eed5d3038831fe00f5efbc110ff0131aab899b","a73a445c1e0a6d0f8b48e8eb22dc9d647896783a7f8991cbbc31c0d94bf1f5a2","d88a5e779faf033be3d52142a04fbe1cb96009868e3bbdd296b2bc6c59e06c0e","cd1d2f103b79002cd94b85a640a103f094227a2c4c53bc8af1fdbf4e13d9729e","5e379df3d61561c2ed7789b5995b9ba2143bbba21a905e2381e16efe7d1fa424","f07a137bbe2de7a122c37bfea00e761975fb264c49f18003d398d71b3fb35a5f","3dce33e7eb25594863b8e615f14a45ab98190d85953436750644212d8a18c066","2b93035328f7778d200252681c1d86285d501ed424825a18f81e4c3028aa51d9","2ac9c8332c5f8510b8bdd571f8271e0f39b0577714d5e95c1e79a12b2616f069","42c21aa963e7b86fa00801d96e88b36803188018d5ad91db2a9101bccd40b3ff","d31eb848cdebb4c55b4893b335a7c0cca95ad66dee13cbb7d0893810c0a9c301","b9f96255e1048ed2ea33ec553122716f0e57fc1c3ad778e9aa15f5b46547bd23","7a9e0a564fee396cacf706523b5aeed96e04c6b871a8bebefad78499fbffc5bc","906c751ef5822ec0dadcea2f0e9db64a33fb4ee926cc9f7efa38afe5d5371b2a","5387c049e9702f2d2d7ece1a74836a14b47fbebe9bbeb19f94c580a37c855351","c68391fb9efad5d99ff332c65b1606248c4e4a9f1dd9a087204242b56c7126d6","e9cf02252d3a0ced987d24845dcb1f11c1be5541f17e5daa44c6de2d18138d0c","e8b02b879754d85f48489294f99147aeccc352c760d95a6fe2b6e49cd400b2fe","9f6908ab3d8a86c68b86e38578afc7095114e66b2fc36a2a96e9252aac3998e0","0eedb2344442b143ddcd788f87096961cd8572b64f10b4afc3356aa0460171c6","71405cc70f183d029cc5018375f6c35117ffdaf11846c35ebf85ee3956b1b2a6","c68baff4d8ba346130e9753cefe2e487a16731bf17e05fdacc81e8c9a26aae9d","2cd15528d8bb5d0453aa339b4b52e0696e8b07e790c153831c642c3dea5ac8af","479d622e66283ffa9883fbc33e441f7fc928b2277ff30aacbec7b7761b4e9579","ade307876dc5ca267ca308d09e737b611505e015c535863f22420a11fffc1c54","f8cdefa3e0dee639eccbe9794b46f90291e5fd3989fcba60d2f08fde56179fb9","86c5a62f99aac7053976e317dbe9acb2eaf903aaf3d2e5bb1cafe5c2df7b37a8","2b300954ce01a8343866f737656e13243e86e5baef51bd0631b21dcef1f6e954","a2d409a9ffd872d6b9d78ead00baa116bbc73cfa959fce9a2f29d3227876b2a1","b288936f560cd71f4a6002953290de9ff8dfbfbf37f5a9391be5c83322324898","61178a781ef82e0ff54f9430397e71e8f365fc1e3725e0e5346f2de7b0d50dfa","6a6ccb37feb3aad32d9be026a3337db195979cd5727a616fc0f557e974101a54","c649ea79205c029a02272ef55b7ab14ada0903db26144d2205021f24727ac7a3","38e2b02897c6357bbcff729ef84c736727b45cc152abe95a7567caccdfad2a1d","d6610ea7e0b1a7686dba062a1e5544dd7d34140f4545305b7c6afaebfb348341","3dee35db743bdba2c8d19aece7ac049bde6fa587e195d86547c882784e6ba34c","b15e55c5fa977c2f25ca0b1db52cfa2d1fd4bf0baf90a8b90d4a7678ca462ff1","f41d30972724714763a2698ae949fbc463afb203b5fa7c4ad7e4de0871129a17","843dd7b6a7c6269fd43827303f5cbe65c1fecabc30b4670a50d5a15d57daeeb9","f06d8b8567ee9fd799bf7f806efe93b67683ef24f4dea5b23ef12edff4434d9d","6017384f697ff38bc3ef6a546df5b230c3c31329db84cbfe686c83bec011e2b2","e1a5b30d9248549ca0c0bb1d653bafae20c64c4aa5928cc4cd3017b55c2177b0","a593632d5878f17295bd53e1c77f27bf4c15212822f764a2bfc1702f4b413fa0","a868a534ba1c2ca9060b8a13b0ffbbbf78b4be7b0ff80d8c75b02773f7192c29","da7545aba8f54a50fde23e2ede00158dc8112560d934cee58098dfb03aae9b9d","34baf65cfee92f110d6653322e2120c2d368ee64b3c7981dff08ed105c4f19b0","a1a261624efb3a00ff346b13580f70f3463b8cdcc58b60f5793ff11785d52cab","f83b320cceccfc48457a818d18fc9a006ab18d0bdd727aa2c2e73dc1b4a45e98","9d92b037978bb9525bc4b673ebddd443277542e010c0aef019c03a170ccdaa73","b0d10e46cfe3f6c476b69af02eaa38e4ccc7430221ce3109ae84bb9fb8282298","fab58e600970e66547644a44bc9918e3223aa2cbd9e8763cec004b2cfb48827e","70e9a18da08294f75bf23e46c7d69e67634c0765d355887b9b41f0d959e1426e","ed44ba6b95f08b758748be7902e0cc54178b1337c56d0e2469c77b03f63ac73b"],"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":[[120,247],[120],[91,120,127,128,129,144],[120,128,129,145,146],[120,127,128],[120,127,144,147,150],[120,127,147,150,151],[120,148,149,150,152,153],[120,127,150],[120,127,144,147,148,149,152],[120,127,135],[120,127],[91,120,127],[80,120,127],[120,131,132,133,134,135,136,137,138,139,140,141,142,143],[120,127,133,134],[120,127,133,135],[120,166,224],[120,224,225],[120,224,225,226,227],[120,166],[120,198],[120,198,199,200],[64,120],[67,120],[64,67,120],[65,66,67,68,69,70,71,72,73,74,75,120,155,158,159,160,161,162,163,164,165],[58,64,65,120],[67,73,75,120,154],[120,157],[67,68,120],[64,120,161],[120,193,194],[120,247,248,249,250,251],[120,247,249],[120,156],[120,254,255,256],[92,120,127],[120,259],[120,260],[120,271],[120,265,270],[120,274,276,277,278,279,280,281,282,283,284,285,286],[120,274,275,277,278,279,280,281,282,283,284,285,286],[120,275,276,277,278,279,280,281,282,283,284,285,286],[120,274,275,276,278,279,280,281,282,283,284,285,286],[120,274,275,276,277,279,280,281,282,283,284,285,286],[120,274,275,276,277,278,280,281,282,283,284,285,286],[120,274,275,276,277,278,279,281,282,283,284,285,286],[120,274,275,276,277,278,279,280,282,283,284,285,286],[120,274,275,276,277,278,279,280,281,283,284,285,286],[120,274,275,276,277,278,279,280,281,282,284,285,286],[120,274,275,276,277,278,279,280,281,282,283,285,286],[120,274,275,276,277,278,279,280,281,282,283,284,286],[120,274,275,276,277,278,279,280,281,282,283,284,285],[76,120],[79,120],[80,85,111,120],[81,91,92,99,108,119,120],[81,82,91,99,120],[83,120],[84,85,92,100,120],[85,108,116,120],[86,88,91,99,120],[87,120],[88,89,120],[90,91,120],[91,120],[91,92,93,108,119,120],[91,92,93,108,120],[91,94,99,108,119,120],[91,92,94,95,99,108,116,119,120],[94,96,108,116,119,120],[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,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126],[91,97,120],[98,119,120,124],[88,91,99,108,120],[100,120],[101,120],[79,102,120],[103,118,120,124],[104,120],[105,120],[91,106,120],[106,107,120,122],[80,91,108,109,110,120],[80,108,110,120],[108,109,120],[111,120],[112,120],[91,114,115,120],[114,115,120],[85,99,116,120],[117,120],[99,118,120],[80,94,105,119,120],[85,120],[108,120,121],[120,122],[120,123],[80,85,91,93,102,108,119,120,122,124],[108,120,125],[120,127,292],[120,295,334],[120,295,319,334],[120,334],[120,295],[120,295,320,334],[120,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333],[120,320,334],[120,335],[120,339],[120,203],[120,215,216,217],[120,203,214,215],[120,178],[120,178,179,180,181,182],[120,167,168,169,170,171,172,173,174,175,176,177],[120,263,266],[120,263,266,267,268],[120,265],[120,262,269],[120,264],[57,59,60,61,62,63,120],[57,58,120],[59,120],[58,59,120],[57,59,120],[120,166,187,228],[120,229,230],[120,166,183,184,185],[120,184],[56,120,184,185,186],[120,185],[120,188],[120,188,189,192,196],[120,195],[120,166,190,191],[120,211,212,213],[120,210,211],[120,166,210,211],[120,166,203,210],[120,166,204],[120,204,205,206,207,208,209],[120,166,203],[120,219],[120,202,219,221,222],[120,166,187,190,197,201,202,219,220],[120,166,197,214,218],[120,166,235],[120,166,228,235],[120,233,234,235,236,237,238,242],[120,166,210,243],[120,166,187,197,233,234,236],[120,166,187,197,231,232,233,235,236],[120,234,235,238],[120,239,240,241,243],[120,235,238],[120,166,235,238],[120,166,187,234],[120,166,210,235,236],[120,244,245],[120,183,187,201,223,243],[120,166,210,223,244],[244,245],[183,187,223,243],[166,210,223,244]],"referencedMap":[[249,1],[247,2],[145,3],[128,2],[147,4],[129,5],[146,2],[151,6],[152,7],[148,7],[154,8],[149,7],[153,9],[150,10],[136,11],[133,12],[140,13],[134,11],[131,14],[139,2],[144,15],[141,2],[142,2],[143,2],[138,12],[135,16],[132,2],[137,17],[190,2],[225,18],[227,2],[226,19],[228,20],[224,21],[203,13],[199,22],[200,22],[201,23],[198,2],[65,24],[66,24],[68,25],[69,24],[70,24],[71,26],[72,2],[73,2],[74,2],[67,24],[166,27],[75,28],[155,29],[158,30],[159,2],[160,2],[161,2],[162,2],[163,2],[164,31],[165,32],[193,2],[195,33],[194,2],[252,34],[248,1],[250,35],[251,1],[191,12],[157,36],[253,2],[254,2],[257,37],[255,2],[258,38],[259,2],[260,39],[261,40],[272,41],[271,42],[256,2],[273,2],[275,43],[276,44],[274,45],[277,46],[278,47],[279,48],[280,49],[281,50],[282,51],[283,52],[284,53],[285,54],[286,55],[287,2],[156,2],[76,56],[77,56],[79,57],[80,58],[81,59],[82,60],[83,61],[84,62],[85,63],[86,64],[87,65],[88,66],[89,66],[90,67],[91,68],[92,69],[93,70],[78,2],[126,2],[94,71],[95,72],[96,73],[127,74],[97,75],[98,76],[99,77],[100,78],[101,79],[102,80],[103,81],[104,82],[105,83],[106,84],[107,85],[108,86],[110,87],[109,88],[111,89],[112,90],[113,2],[114,91],[115,92],[116,93],[117,94],[118,95],[119,96],[120,97],[121,98],[122,99],[123,100],[124,101],[125,102],[288,2],[289,12],[290,2],[291,2],[293,103],[292,2],[294,12],[319,104],[320,105],[295,106],[298,106],[317,104],[318,104],[308,104],[307,107],[305,104],[300,104],[313,104],[311,104],[315,104],[299,104],[312,104],[316,104],[301,104],[302,104],[314,104],[296,104],[303,104],[304,104],[306,104],[310,104],[321,108],[309,104],[297,104],[334,109],[333,2],[328,108],[330,110],[329,108],[322,108],[323,108],[325,108],[327,108],[331,110],[332,110],[324,110],[326,110],[336,111],[335,2],[337,2],[338,2],[339,2],[340,112],[130,2],[262,2],[215,113],[218,114],[216,115],[217,115],[177,2],[174,116],[176,116],[175,116],[173,116],[183,117],[178,118],[182,2],[179,2],[181,2],[180,2],[169,116],[170,116],[171,116],[167,2],[168,2],[172,116],[263,2],[267,119],[269,120],[268,119],[266,121],[270,122],[265,123],[264,2],[57,2],[64,124],[59,125],[60,126],[61,126],[62,127],[63,127],[58,128],[8,2],[10,2],[9,2],[2,2],[11,2],[12,2],[13,2],[14,2],[15,2],[16,2],[17,2],[18,2],[3,2],[4,2],[22,2],[19,2],[20,2],[21,2],[23,2],[24,2],[25,2],[5,2],[26,2],[27,2],[28,2],[29,2],[6,2],[33,2],[30,2],[31,2],[32,2],[34,2],[7,2],[35,2],[40,2],[41,2],[36,2],[37,2],[38,2],[39,2],[1,2],[42,2],[229,129],[230,2],[231,130],[56,2],[186,131],[185,132],[187,133],[184,134],[189,135],[197,136],[196,137],[188,2],[192,138],[214,139],[212,140],[213,141],[211,142],[205,143],[206,143],[207,2],[208,143],[210,144],[204,145],[209,143],[202,2],[220,146],[222,146],[223,147],[221,148],[219,149],[236,150],[237,151],[243,152],[232,153],[235,154],[234,155],[239,156],[242,157],[240,158],[241,159],[233,160],[238,161],[246,162],[244,163],[245,164],[47,2],[48,2],[49,2],[50,2],[51,2],[52,2],[43,2],[53,2],[54,2],[55,2],[44,2],[45,2],[46,2]],"exportedModulesMap":[[249,1],[247,2],[145,3],[128,2],[147,4],[129,5],[146,2],[151,6],[152,7],[148,7],[154,8],[149,7],[153,9],[150,10],[136,11],[133,12],[140,13],[134,11],[131,14],[139,2],[144,15],[141,2],[142,2],[143,2],[138,12],[135,16],[132,2],[137,17],[190,2],[225,18],[227,2],[226,19],[228,20],[224,21],[203,13],[199,22],[200,22],[201,23],[198,2],[65,24],[66,24],[68,25],[69,24],[70,24],[71,26],[72,2],[73,2],[74,2],[67,24],[166,27],[75,28],[155,29],[158,30],[159,2],[160,2],[161,2],[162,2],[163,2],[164,31],[165,32],[193,2],[195,33],[194,2],[252,34],[248,1],[250,35],[251,1],[191,12],[157,36],[253,2],[254,2],[257,37],[255,2],[258,38],[259,2],[260,39],[261,40],[272,41],[271,42],[256,2],[273,2],[275,43],[276,44],[274,45],[277,46],[278,47],[279,48],[280,49],[281,50],[282,51],[283,52],[284,53],[285,54],[286,55],[287,2],[156,2],[76,56],[77,56],[79,57],[80,58],[81,59],[82,60],[83,61],[84,62],[85,63],[86,64],[87,65],[88,66],[89,66],[90,67],[91,68],[92,69],[93,70],[78,2],[126,2],[94,71],[95,72],[96,73],[127,74],[97,75],[98,76],[99,77],[100,78],[101,79],[102,80],[103,81],[104,82],[105,83],[106,84],[107,85],[108,86],[110,87],[109,88],[111,89],[112,90],[113,2],[114,91],[115,92],[116,93],[117,94],[118,95],[119,96],[120,97],[121,98],[122,99],[123,100],[124,101],[125,102],[288,2],[289,12],[290,2],[291,2],[293,103],[292,2],[294,12],[319,104],[320,105],[295,106],[298,106],[317,104],[318,104],[308,104],[307,107],[305,104],[300,104],[313,104],[311,104],[315,104],[299,104],[312,104],[316,104],[301,104],[302,104],[314,104],[296,104],[303,104],[304,104],[306,104],[310,104],[321,108],[309,104],[297,104],[334,109],[333,2],[328,108],[330,110],[329,108],[322,108],[323,108],[325,108],[327,108],[331,110],[332,110],[324,110],[326,110],[336,111],[335,2],[337,2],[338,2],[339,2],[340,112],[130,2],[262,2],[215,113],[218,114],[216,115],[217,115],[177,2],[174,116],[176,116],[175,116],[173,116],[183,117],[178,118],[182,2],[179,2],[181,2],[180,2],[169,116],[170,116],[171,116],[167,2],[168,2],[172,116],[263,2],[267,119],[269,120],[268,119],[266,121],[270,122],[265,123],[264,2],[57,2],[64,124],[59,125],[60,126],[61,126],[62,127],[63,127],[58,128],[8,2],[10,2],[9,2],[2,2],[11,2],[12,2],[13,2],[14,2],[15,2],[16,2],[17,2],[18,2],[3,2],[4,2],[22,2],[19,2],[20,2],[21,2],[23,2],[24,2],[25,2],[5,2],[26,2],[27,2],[28,2],[29,2],[6,2],[33,2],[30,2],[31,2],[32,2],[34,2],[7,2],[35,2],[40,2],[41,2],[36,2],[37,2],[38,2],[39,2],[1,2],[42,2],[229,129],[230,2],[231,130],[56,2],[186,131],[185,132],[187,133],[184,134],[189,135],[197,136],[196,137],[188,2],[192,138],[214,139],[212,140],[213,141],[211,142],[205,143],[206,143],[207,2],[208,143],[210,144],[204,145],[209,143],[202,2],[220,146],[222,146],[223,147],[221,148],[219,149],[236,150],[237,151],[243,152],[232,153],[235,154],[234,155],[239,156],[242,157],[240,158],[241,159],[233,160],[238,161],[246,165],[244,166],[245,167],[47,2],[48,2],[49,2],[50,2],[51,2],[52,2],[43,2],[53,2],[54,2],[55,2],[44,2],[45,2],[46,2]],"semanticDiagnosticsPerFile":[249,247,145,128,147,129,146,151,152,148,154,149,153,150,136,133,140,134,131,139,144,141,142,143,138,135,132,137,190,225,227,226,228,224,203,199,200,201,198,65,66,68,69,70,71,72,73,74,67,166,75,155,158,159,160,161,162,163,164,165,193,195,194,252,248,250,251,191,157,253,254,257,255,258,259,260,261,272,271,256,273,275,276,274,277,278,279,280,281,282,283,284,285,286,287,156,76,77,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,78,126,94,95,96,127,97,98,99,100,101,102,103,104,105,106,107,108,110,109,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,288,289,290,291,293,292,294,319,320,295,298,317,318,308,307,305,300,313,311,315,299,312,316,301,302,314,296,303,304,306,310,321,309,297,334,333,328,330,329,322,323,325,327,331,332,324,326,336,335,337,338,339,340,130,262,215,218,216,217,177,174,176,175,173,183,178,182,179,181,180,169,170,171,167,168,172,263,267,269,268,266,270,265,264,57,64,59,60,61,62,63,58,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,229,230,231,56,186,185,187,184,189,197,196,188,192,214,212,213,211,205,206,207,208,210,204,209,202,220,222,223,221,219,236,237,243,232,235,234,239,242,240,241,233,238,246,244,245,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","../../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/@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/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/@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","../../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","../../../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","../../../node_modules/@metamask/safe-event-emitter/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","../../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/eth-block-tracker/dist/BlockTracker.d.ts","../../../node_modules/eth-block-tracker/dist/PollingBlockTracker.d.ts","../../../node_modules/eth-block-tracker/dist/SubscribeBlockTracker.d.ts","../../../node_modules/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","../../../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","../../approval-controller/dist/types/ApprovalController.d.ts","../../approval-controller/dist/types/errors.d.ts","../../approval-controller/dist/types/index.d.ts","../../permission-controller/dist/types/permission-middleware.d.ts","../../permission-controller/dist/types/SubjectMetadataController.d.ts","../../permission-controller/dist/types/PermissionController.d.ts","../../permission-controller/dist/types/Permission.d.ts","../../permission-controller/dist/types/Caveat.d.ts","../../permission-controller/dist/types/errors.d.ts","../../permission-controller/dist/types/utils.d.ts","../../permission-controller/dist/types/rpc-methods/getPermissions.d.ts","../../permission-controller/dist/types/rpc-methods/requestPermissions.d.ts","../../permission-controller/dist/types/rpc-methods/revokePermissions.d.ts","../../permission-controller/dist/types/rpc-methods/index.d.ts","../../permission-controller/dist/types/index.d.ts","../src/SelectedNetworkController.ts","../src/SelectedNetworkMiddleware.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/readable-stream/node_modules/safe-buffer/index.d.ts","../../../node_modules/@types/readable-stream/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","75b22c74010ba649de1a1676a4c4b8b5bb4294fecd05089e2094429b16d7840c","e475453e7140e95542332943d3052fe4c7430ad1efce42b3e9157f1fee8cbc5f","ebfdf904255ce746c9d30117c2edef355fb19bf7650478d2405f39f0e4f302e6","f3f63b48addb8e2ea9d20bb671c3c306413b3daa39996d0ae52f63d8e32158e1","a50599c08934a62f11657bdbe0dc929ab66da1b1f09974408fd9a33ec1bb8060","5a20e7d6c630b91be15e9b837853173829d00273197481dc8d3e94df61105a71","8d478048d71cc16f806d4b71b252ecb67c7444ccf4f4b09b29a312712184f859","b4000a0a525fa921e896cbdb32ae802c9684f0fd371b5fc69e7310f7918cc2c3","9df4662ca3dbc2522bc115833ee04faa1afbb4e249a85ef4a0a09c621346bd08","b25d9065cf1c1f537a140bbc508e953ed2262f77134574c432d206ff36f4bdbf","1b103313097041aa9cd705a682c652f08613cb5cf8663321061c0902f845e81c","68ccec8662818911d8a12b8ed028bc5729fb4f1d34793c4701265ba60bc73cf4","5f85b8b79dc4d36af672c035b2beb71545de63a5d60bccbeee64c260941672ab","b3d48529ae61dc27d0bfbfa2cb3e0dff8189644bd155bdf5df1e8e14669f7043","40fe4b689225816b31fe5794c0fbf3534568819709e40295ead998a2bc1ab237","f65b5e33b9ad545a1eebbd6afe857314725ad42aaf069913e33f928ab3e4990a","fb6f2a87beb7fb1f4c2b762d0c76a9459fc91f557231569b0ee21399e22aa13d","31c858dc85996fac4b7fa944e1016d5c72f514930a72357ab5001097bf6511c7","3de30a871b3340be8b679c52aa12f90dd1c8c60874517be58968fdbcc4d79445","6fd985bd31eaf77542625306fb0404d32bff978990f0a06428e5f0b9a3b58109","34693fb4a5e771e11668219221344dd1bd7d8b77ed005a1c1d965fb559be8406","7394959e5a741b185456e1ef5d64599c36c60a323207450991e7a42e08911419",{"version":"e44ea2d6b7b853f6c81482416db43dafc11944561b810e469ae423085511ce7e","affectsGlobalScope":true},"f51b4042a3ac86f1f707500a9768f88d0b0c1fc3f3e45a73333283dea720cdc6",{"version":"a7289d79eb84a59d2475b4d0136b4404be3cfdd17c3ea46b9194add1d645df01","affectsGlobalScope":true},"0bb26fa2a90ee890eed57ee812c71fa84d3d07850163ec4a204de86412cc57c1","132ca47da601c60141dd6f10bd08c70d0620177e5638439df2464ec3945b6d98",{"version":"55d2bbae076fed7269c3e16faeb32f988f558427b7a1c3bf04aa7551ab86ae90","affectsGlobalScope":true},"a40826e8476694e90da94aa008283a7de50d1dafd37beada623863f1901cb7fb","cf83847c9264dcd592b6c89c1542925b899b277228687f3638614e3fa784cf76","3a41ebe7f089d50f447466b35b6cabb8b584c0994fc9809d0cd0a4ebc41e1239","7693b90b3075deaccafd5efb467bf9f2b747a3075be888652ef73e64396d8628","0c42d6cba77d9ad1cf45256ccb8489aa502fe2dbee1ec9048a29d49f5d532e73","2cf89c17245db65d175d4ef699cd68187516f9b3ae5c572fc0b9ad60f35dc223","5f20d20b7607174caf1a6da9141aeb9f2142159ae2410ca30c7a0fccd1d19c99",{"version":"a34d65f61ec5aac5b53502c8b0bd4e00d217bccb95bf94d449e2571baa11fb8c","affectsGlobalScope":true},"8d42e5af5fb0a96a77e135ce84cc60636c9bad39d9dba043a4efe9d1bdeb3cc3","56fcc451e9065eb121c9cc4c1b9994a816306f3b0b3b1fce7ad59f0ac97a9999","8a6f12b74d3e6c4f5e1b918cb8e64ae16bc6756cf0d48bcc28a28e1bf26ca0cd","c3759b5bc5cc40f5988d86a497741a80fa91258629ae50a2b3735e774cd377cc","bf268a0aea37ad4ae3b7a9b58559190b6fc01ea16a31e35cd05817a0a60f895a","45dd82fb5aea9b12b2a90b427b28f3a014e8b2ee9b74087a5ab882841cb5fbc5",{"version":"d7dad6db394a3d9f7b49755e4b610fbf8ed6eb0c9810ae5f1a119f6b5d76de45","affectsGlobalScope":true},"48b2f9302651eb31acd5be69bb4e6b35797a7fcd6b77391d10a4ccadf7dc3609","0c8c917ef15498c827bd494a0ef365e9f76deb211f8acbb86932e20489310788","dd67d2b5e4e8a182a38de8e69fb736945eaa4588e0909c14e01a14bd3cc1fd1e",{"version":"9cdc2c6144b03822c9842505d09945bcf813b86827fdb260dd7586b63abc19bf","affectsGlobalScope":true},{"version":"2923dee3c897f03e91b54a210cdbefea7290562f0ac4b948667d4c9ee844b79e","affectsGlobalScope":true},"79169698d09a2be54b14f3bcad2575b414bf3525063fde0a1e4fcd5d6efd380e","051d939bcf77caa3cef3282708ab3a6fdfb741a7366e1d74a9e7603b67417ec3","0be79b3ff0f16b6c2f9bc8c4cc7097ea417d8d67f8267f7e1eec8e32b548c2ff","1c61ffa3a71b77363b30d19832c269ef62fba787f5610cac7254728d3b69ab2e","a234d62ae81d012ebf23898a45672edf3e5c93ecf5a438a42b96c08dd68cde43","269929a24b2816343a178008ac9ae9248304d92a8ba8e233055e0ed6dbe6ef71","09ed02a725db002693236b6dfc49b2c6eb5557be1421d7fbe4f07cfe38211d92","09d801ff4a303d4976d4b9cb94af3a9097c4a70345e662d176975872d2998e51","c8558b01389b5f7610ac293aa612ccea2ae64d83af43b49f8142f190be1f414c","c40fdf7b2e18df49ce0568e37f0292c12807a0748be79e272745e7216bed2606",{"version":"b10b426c56e220b5093bf8a2446ee47af47263b7b1a03f4b18e42326b231b111","affectsGlobalScope":true},"4e228e78c1e9b0a75c70588d59288f63a6258e8b1fe4a67b0c53fe03461421d9","b4635ef36bee17e1304337d591c3b6b461ecdbc1876d0effbe6a581e62201fe5","205d50c24359ead003dc537b9b65d2a64208dfdffe368f403cf9e0357831db9e","1265fddcd0c68be9d2a3b29805d0280484c961264dd95e0b675f7bd91f777e78",{"version":"e4507242542bd499238f693d88b2d32e22177cc508854625f87bcc9bc3fa1256","affectsGlobalScope":true},{"version":"d942354e4966a98d3a92d1b1af0b4ac06f33af3f88116743e2c304c027ca26ef","affectsGlobalScope":true},"39f0808e5be3cb38674726c21fe2eb453c55e48a901679b4ce30fef85549b892","6afd66a7432ef100027ea110449e874196381e019e30eda7e7d8ca390366b7a8","befb8a9a78ac99d8fbc3ed392810489a7b90760c7a58934e8f1c8538f581cff3","e670bdf01540d35c170fae68edfd2f288eff909936780c379d6a9103b787b22c","867f95abf1df444aab146b19847391fc2f922a55f6a970a27ed8226766cee29f",{"version":"ab9b9a36e5284fd8d3bf2f7d5fcbc60052f25f27e4d20954782099282c60d23e","affectsGlobalScope":true},"88003d9ab15507806f41b120be6d407c1afe566c2f6689ebe3a034dd5ec0c8dc","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","980d21b0081cbf81774083b1e3a46f4bbdcd2b68858df0f66d7fad9c82bc34bc","6a9c5127096b35264eb7cd21b2417bfc1d42cceca9ba4ce2bb0c3410b7816042","93b7325b49dfbf613d940ed0e471216657b2d77459dac34f1b5b1678f08f884c","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","5d697a4b315cc5bb3042ae869abffd10c3b0d7b182cda0e4c45d8819937e5796","563fa27fdaec8f195b84f71a7af0ef48d30d5cc830575db86da86a63a470c8e6","6ee58aa536dabb19b09bc036f1abe83feb51e13d63b23d30b2d0631a2de99b8f","8aceb205dcc6f814ad99635baf1e40b6e01d06d3fe27b72fd766c6d0b8c0c600","299567f84bfedd1468dca2755a829cb19e607a6811673788807dc8921e211bc9","795d9fb85aad92221504db74dd179b506bd189bba0c104426f7e7bb8a66ffee5","1311bc194e0a69fe61031e852c1c0b439e2a2a3d1d5e2d8ff795499b9f283459","4b7ce19369d7e7fae76720c2c6c7f671bf3fa0f7093edb864f1ac358ca7c456c","c972ef44deca1fa8fab465915ffa00f82e126aacf3dfc8979c03b1b066ce5bb6","30285a1011c6d6b52f3ba3abb0a984be8148c05cdefb8eb6eb562335a3991f35","8e7adb22c0adecf7464861fc58ae3fc617b41ffbd70c97aa8493dc0966a82273","755f3cd1d9c1b564cff090e3b0e29200ae55690a91b87cb9e7a64c2dbeb314d3","d6bb7e0a6877b7856c183bff13d09dd9ae599ea43c6f6b33d3d5f72a830ed460","f1b51ae93c762d7c43f559933cd4842dd870367e8d92e90704ffa685dd5b29a3","3f450762fd7c34ed545e738abccb0af6a703572a10521643cf8fc88e3724c99c","fcc8beef29f39f09b1d9c9f99c42f9fed605ab1c28d2a630185f732b9ba53763","d6e6620a30d582182acc3f0a992a0c311adc589f111096aea11ab83fc09a5ccc","6213b8f686f56beab22b59a0f468590fd3a4c5fa931236a017efeca91d7c9584","c451cec9a588b1f105a5ea2c6063d4fca112b9d70105cacdadda0e1ef67e9379","cb047832dc68f5a2c41c62c5e95ddcacbae3a8b034d40cd15319a8cb7f25104a","980336ccdfc3c08f3c3b201aa6662e6016e20f15847f8465b68f3e8e67b4665c","5a3493939995f46ff3d9073cd534fb8961c3bf4e08c71db27066ff03d906dea8","bb5a2ac327605ebebf831c469b05bd34a33a6a46ee8c1edd9f3310aad32cf6a1","bf5d041f2440b4a9391e2b5eb3b8d94cbf1e3b8ff4703b6539d4e65e758c8f37","8516469eb90e723b0eb03df1be098f7e6a4709f6f48fd4532868d20a0a934f6e","d60e9ab369a72d234aac49adbe2900d8ef1408a6ea4db552cf2a48c9d8d6a1bc","0ebb4698803f01e2e7df6acce572fff068f4a20c47221721dafd70a27e372831","a12eaa942232703a8a8477a2f240ad5a2c26c595012ea8f128224e77984099c4","4070c2f1c3434fcf84886e04d30d82cd650ee443e53b82b404b144175cf8741e","2cea9689efa8591732096235abe7f084fc29c92badd5b0897a5e876b77e71887","4ed4e504126014fee13aaef5e3fc140f2ff7031ff3a8b5386717905820ea2d09","8129a34006218a6f3cdc81bbd438d5429eb18b08b4338a26977ac3b4df129d75","30d2170e1a718b5035611af55e3618b4ba8f42f0749bb52ee593da6082c4e2ce","98ef38666d88ec9699a722053e07ede65d3042f693fe7ff8c786e53dbb6fd43b","a3b8b6be7620897d1e481e8650c980a210a138fceb6e710eaf95fd9dd0dfe94a","12c89d0e32758c120a569045f21cf5b77244f86792611ced8de7f86b37e77781","9f9e5bae412fa5909fae636d6733aee27a108cc2ed5b13980611016336774d3c","662fe197bba64bd3f17ee118058cd2d0d2dbe33d7c0c865fd6365d90bfc44e1e","030519c351f800551cac2658038804969ca4584d2c0175a710602ac234ca1340","0278a6939ca83cd040b08ff8c5fc7838b6693ddc52f22526bf158e6b10e0246c","c2d6206e5ba4fd3063b01218c2b3b997afc1cfbeb49fcee991fa8595842ce53d","29c188a2c660f99f1b4835022e011c4268d7af989d4b7dda33c0a69ca1a777f8","1ed0bf138e87912d741e28333b58cbf814ae863783b3b404d2454cbabb9c5fc0","3452ee7d8ef0b1bbd47b2a56924a1dc3c79dc84a19d212e9dc496f92e4943aa0","99510e20e3d4816e283e59e8f0f31f603b2f026648240ffdb1ca9f24be678419","037d1fbeb96dc35600814be14d0fbf31acf35f1d7b443ea33514937de69c2bf2","64d1859b7dd9f419ba08e064c2b16b1a5edde0316d6c2bb1833c9381d4dffc3d","69b0f96ca137c0dba05f321a159141ad36f79cfba2fffcc29d131e280275e6f2","7bb64cf513a2b1cf7a94f81ca201f3d76a9b17af556f0cfc4e2707443e6caa66","a9add2a29da4cd0b617ae89f196b3f2172a031aeb086922cdf097236eef8b008","99afac3e6e683ee3111e499f9919953e9489cb39cad74363717aa3805e91db51","c49f92b83968f4ee0b6026396a9b6e2d6fee8b660d08a90efb03355ce3433a7a","4fdc6afe4d7ef6aeb32ac0818d47e99f98a31d0696abc4cb2af489c78ac1ba1d","d73d5a0e854037d43781b2d5d33f4b95ee509e0ddede677aade79fbee6a97cdc","35d14e1ae04be300828b1a1614316b9312a009cfd5e29fa56f94c2a9f60b12df","d160fe745f9c3b72d7b9036fdb2b6b500a520d43e36bb842c927b6fe59ea2c23",{"version":"4a6571cadd05e8087a34f7ca44f63a8f61aa5fcca650f0a577febc4877b3c34f","signature":"3a4f9e7087c566703447928d15c234bd0bcc63a2a50b6ec39ed37c9bc0342310"},{"version":"506bcad28e45e13c23c2c25c9691e0dc42d8755a0e24b9a48586f51b5bebae8f","signature":"3a12771c76c5ed979438dc0e390224bd3d8661bcbea18114f77c4c6d9de5b8b2"},{"version":"6fbdb35bc3b9cfb225c48108b5bacb5c0d67bbcb677cf13b912e15a852551023","signature":"ee06131adf64c6cd201de36563174cff0e160b81438be186a7bd85e6c2b54fa7"},"a5aaeca001d2f69093d04aac4db321e4c338fd9b20cbc4f0b0af3dc6ae0f235b","cc957354aa3c94c9961ebf46282cfde1e81d107fc5785a61f62c67f1dd3ac2eb","8041cfce439ff29d339742389de04c136e3029d6b1817f07b2d7fcbfb7534990","93de1c6dab503f053efe8d304cb522bb3a89feab8c98f307a674a4fae04773e9","29a46d003ca3c721e6405f00dee7e3de91b14e09701eba5d887bf76fb2d47d38","069bebfee29864e3955378107e243508b163e77ab10de6a5ee03ae06939f0bb9","9990f9e566bc3c2c6e38df81294fb756e7f5b7b0e5bb17ab75384e190548b4b6",{"version":"64d4b35c5456adf258d2cf56c341e203a073253f229ef3208fc0d5020253b241","affectsGlobalScope":true},"ee7d8894904b465b072be0d2e4b45cf6b887cdba16a467645c4e200982ece7ea","f3d8c757e148ad968f0d98697987db363070abada5f503da3c06aefd9d4248c1","df95e00612c1faa5e0e7ef0dba589b18665bbeb3221db2b6cee1fe4d0e61921f","afe73051ff6a03a9565cbd8ebb0e956ee3df5e913ad5c1ded64218aabfa3dcb5","8b06ac3faeacb8484d84ddb44571d8f410697f98d7bfa86c0fda60373a9f5215","7eb06594824ada538b1d8b48c3925a83e7db792f47a081a62cf3e5c4e23cf0ee","f5638f7c2f12a9a1a57b5c41b3c1ea7db3876c003bab68e6a57afd6bcc169af0","0d14fa22c41fdc7277e6f71473b20ebc07f40f00e38875142335d5b63cdfc9d2","d8aab31ba8e618cc3eea10b0945de81cb93b7e8150a013a482332263b9305322","462bccdf75fcafc1ae8c30400c9425e1a4681db5d605d1a0edb4f990a54d8094","5923d8facbac6ecf7c84739a5c701a57af94a6f6648d6229a6c768cf28f0f8cb","7adecb2c3238794c378d336a8182d4c3dd2c4fa6fa1785e2797a3db550edea62","dc12dc0e5aa06f4e1a7692149b78f89116af823b9e1f1e4eae140cd3e0e674e6","1bfc6565b90c8771615cd8cfcf9b36efc0275e5e83ac7d9181307e96eb495161","8a8a96898906f065f296665e411f51010b51372fa260d5373bf9f64356703190","7f82ef88bdb67d9a850dd1c7cd2d690f33e0f0acd208e3c9eba086f3670d4f73",{"version":"ccfd8774cd9b929f63ff7dcf657977eb0652e3547f1fcac1b3a1dc5db22d4d58","affectsGlobalScope":true},"d92dc90fecd2552db74d8dc3c6fb4db9145b2aa0efe2c127236ba035969068d4","96d14f21b7652903852eef49379d04dbda28c16ed36468f8c9fa08f7c14c9538","675e702f2032766a91eeadee64f51014c64688525da99dccd8178f0c599f13a8","458111fc89d11d2151277c822dfdc1a28fa5b6b2493cf942e37d4cd0a6ee5f22","19c816167e076e7c24f074389c6cf3ed87bdbb917d1ea439ca281f9d26db2439","187119ff4f9553676a884e296089e131e8cc01691c546273b1d0089c3533ce42","febf0b2de54781102b00f61653b21377390a048fbf5262718c91860d11ff34a6","98f9d826db9cd99d27a01a59ee5f22863df00ccf1aaf43e1d7db80ebf716f7c3","0aaef8cded245bf5036a7a40b65622dd6c4da71f7a35343112edbe112b348a1e","00baffbe8a2f2e4875367479489b5d43b5fc1429ecb4a4cc98cfc3009095f52a","dcd91d3b697cb650b95db5471189b99815af5db2a1cd28760f91e0b12ede8ed5","3c92b6dfd43cc1c2485d9eba5ff0b74a19bb8725b692773ef1d66dac48cda4bd","3cf0d343c2276842a5b617f22ba82af6322c7cfe8bb52238ffc0c491a3c21019","df996e25faa505f85aeb294d15ebe61b399cf1d1e49959cdfaf2cc0815c203f9",{"version":"f2eff8704452659641164876c1ef0df4174659ce7311b0665798ea3f556fa9ad","affectsGlobalScope":true},"8841e2aa774b89bd23302dede20663306dc1b9902431ac64b24be8b8d0e3f649","2b8264b2fefd7367e0f20e2c04eed5d3038831fe00f5efbc110ff0131aab899b","a73a445c1e0a6d0f8b48e8eb22dc9d647896783a7f8991cbbc31c0d94bf1f5a2","d88a5e779faf033be3d52142a04fbe1cb96009868e3bbdd296b2bc6c59e06c0e","cd1d2f103b79002cd94b85a640a103f094227a2c4c53bc8af1fdbf4e13d9729e","5e379df3d61561c2ed7789b5995b9ba2143bbba21a905e2381e16efe7d1fa424","f07a137bbe2de7a122c37bfea00e761975fb264c49f18003d398d71b3fb35a5f","3dce33e7eb25594863b8e615f14a45ab98190d85953436750644212d8a18c066","2b93035328f7778d200252681c1d86285d501ed424825a18f81e4c3028aa51d9","2ac9c8332c5f8510b8bdd571f8271e0f39b0577714d5e95c1e79a12b2616f069","42c21aa963e7b86fa00801d96e88b36803188018d5ad91db2a9101bccd40b3ff","d31eb848cdebb4c55b4893b335a7c0cca95ad66dee13cbb7d0893810c0a9c301","b9f96255e1048ed2ea33ec553122716f0e57fc1c3ad778e9aa15f5b46547bd23","7a9e0a564fee396cacf706523b5aeed96e04c6b871a8bebefad78499fbffc5bc","906c751ef5822ec0dadcea2f0e9db64a33fb4ee926cc9f7efa38afe5d5371b2a","5387c049e9702f2d2d7ece1a74836a14b47fbebe9bbeb19f94c580a37c855351","c68391fb9efad5d99ff332c65b1606248c4e4a9f1dd9a087204242b56c7126d6","e9cf02252d3a0ced987d24845dcb1f11c1be5541f17e5daa44c6de2d18138d0c","e8b02b879754d85f48489294f99147aeccc352c760d95a6fe2b6e49cd400b2fe","9f6908ab3d8a86c68b86e38578afc7095114e66b2fc36a2a96e9252aac3998e0","0eedb2344442b143ddcd788f87096961cd8572b64f10b4afc3356aa0460171c6","71405cc70f183d029cc5018375f6c35117ffdaf11846c35ebf85ee3956b1b2a6","c68baff4d8ba346130e9753cefe2e487a16731bf17e05fdacc81e8c9a26aae9d","2cd15528d8bb5d0453aa339b4b52e0696e8b07e790c153831c642c3dea5ac8af","479d622e66283ffa9883fbc33e441f7fc928b2277ff30aacbec7b7761b4e9579","ade307876dc5ca267ca308d09e737b611505e015c535863f22420a11fffc1c54","f8cdefa3e0dee639eccbe9794b46f90291e5fd3989fcba60d2f08fde56179fb9","86c5a62f99aac7053976e317dbe9acb2eaf903aaf3d2e5bb1cafe5c2df7b37a8","2b300954ce01a8343866f737656e13243e86e5baef51bd0631b21dcef1f6e954","a2d409a9ffd872d6b9d78ead00baa116bbc73cfa959fce9a2f29d3227876b2a1","b288936f560cd71f4a6002953290de9ff8dfbfbf37f5a9391be5c83322324898","61178a781ef82e0ff54f9430397e71e8f365fc1e3725e0e5346f2de7b0d50dfa","6a6ccb37feb3aad32d9be026a3337db195979cd5727a616fc0f557e974101a54","c649ea79205c029a02272ef55b7ab14ada0903db26144d2205021f24727ac7a3","38e2b02897c6357bbcff729ef84c736727b45cc152abe95a7567caccdfad2a1d","d6610ea7e0b1a7686dba062a1e5544dd7d34140f4545305b7c6afaebfb348341","3dee35db743bdba2c8d19aece7ac049bde6fa587e195d86547c882784e6ba34c","b15e55c5fa977c2f25ca0b1db52cfa2d1fd4bf0baf90a8b90d4a7678ca462ff1","f41d30972724714763a2698ae949fbc463afb203b5fa7c4ad7e4de0871129a17","843dd7b6a7c6269fd43827303f5cbe65c1fecabc30b4670a50d5a15d57daeeb9","f06d8b8567ee9fd799bf7f806efe93b67683ef24f4dea5b23ef12edff4434d9d","6017384f697ff38bc3ef6a546df5b230c3c31329db84cbfe686c83bec011e2b2","e1a5b30d9248549ca0c0bb1d653bafae20c64c4aa5928cc4cd3017b55c2177b0","a593632d5878f17295bd53e1c77f27bf4c15212822f764a2bfc1702f4b413fa0","a868a534ba1c2ca9060b8a13b0ffbbbf78b4be7b0ff80d8c75b02773f7192c29","da7545aba8f54a50fde23e2ede00158dc8112560d934cee58098dfb03aae9b9d","34baf65cfee92f110d6653322e2120c2d368ee64b3c7981dff08ed105c4f19b0","a1a261624efb3a00ff346b13580f70f3463b8cdcc58b60f5793ff11785d52cab","f83b320cceccfc48457a818d18fc9a006ab18d0bdd727aa2c2e73dc1b4a45e98","9d92b037978bb9525bc4b673ebddd443277542e010c0aef019c03a170ccdaa73","b0d10e46cfe3f6c476b69af02eaa38e4ccc7430221ce3109ae84bb9fb8282298","fab58e600970e66547644a44bc9918e3223aa2cbd9e8763cec004b2cfb48827e","70e9a18da08294f75bf23e46c7d69e67634c0765d355887b9b41f0d959e1426e","ed44ba6b95f08b758748be7902e0cc54178b1337c56d0e2469c77b03f63ac73b"],"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":[[120,247],[120],[91,120,127,128,129,144],[120,128,129,145,146],[120,127,128],[120,127,144,147,150],[120,127,147,150,151],[120,148,149,150,152,153],[120,127,150],[120,127,144,147,148,149,152],[120,127,135],[120,127],[91,120,127],[80,120,127],[120,131,132,133,134,135,136,137,138,139,140,141,142,143],[120,127,133,134],[120,127,133,135],[120,166,224],[120,224,225],[120,224,225,226,227],[120,166],[120,198],[120,198,199,200],[64,120],[67,120],[64,67,120],[65,66,67,68,69,70,71,72,73,74,75,120,155,158,159,160,161,162,163,164,165],[58,64,65,120],[67,73,75,120,154],[120,157],[67,68,120],[64,120,161],[120,193,194],[120,247,248,249,250,251],[120,247,249],[120,156],[120,254,255,256],[92,120,127],[120,259],[120,260],[120,271],[120,265,270],[120,274,276,277,278,279,280,281,282,283,284,285,286],[120,274,275,277,278,279,280,281,282,283,284,285,286],[120,275,276,277,278,279,280,281,282,283,284,285,286],[120,274,275,276,278,279,280,281,282,283,284,285,286],[120,274,275,276,277,279,280,281,282,283,284,285,286],[120,274,275,276,277,278,280,281,282,283,284,285,286],[120,274,275,276,277,278,279,281,282,283,284,285,286],[120,274,275,276,277,278,279,280,282,283,284,285,286],[120,274,275,276,277,278,279,280,281,283,284,285,286],[120,274,275,276,277,278,279,280,281,282,284,285,286],[120,274,275,276,277,278,279,280,281,282,283,285,286],[120,274,275,276,277,278,279,280,281,282,283,284,286],[120,274,275,276,277,278,279,280,281,282,283,284,285],[76,120],[79,120],[80,85,111,120],[81,91,92,99,108,119,120],[81,82,91,99,120],[83,120],[84,85,92,100,120],[85,108,116,120],[86,88,91,99,120],[87,120],[88,89,120],[90,91,120],[91,120],[91,92,93,108,119,120],[91,92,93,108,120],[91,94,99,108,119,120],[91,92,94,95,99,108,116,119,120],[94,96,108,116,119,120],[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,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126],[91,97,120],[98,119,120,124],[88,91,99,108,120],[100,120],[101,120],[79,102,120],[103,118,120,124],[104,120],[105,120],[91,106,120],[106,107,120,122],[80,91,108,109,110,120],[80,108,110,120],[108,109,120],[111,120],[112,120],[91,114,115,120],[114,115,120],[85,99,116,120],[117,120],[99,118,120],[80,94,105,119,120],[85,120],[108,120,121],[120,122],[120,123],[80,85,91,93,102,108,119,120,122,124],[108,120,125],[120,127,292],[120,295,334],[120,295,319,334],[120,334],[120,295],[120,295,320,334],[120,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333],[120,320,334],[120,335],[120,339],[120,203],[120,203,214,215],[120,215,216,217],[120,178],[120,178,179,180,181,182],[120,167,168,169,170,171,172,173,174,175,176,177],[120,263,266],[120,263,266,267,268],[120,265],[120,262,269],[120,264],[57,59,60,61,62,63,120],[57,58,120],[59,120],[58,59,120],[57,59,120],[120,166,187,228],[120,229,230],[120,166,183,184,185],[120,184],[120,185],[56,120,184,185,186],[120,188],[120,188,189,192,196],[120,195],[120,166,190,191],[120,211,212,213],[120,210,211],[120,166,210,211],[120,166,203,210],[120,166,203],[120,166,204],[120,204,205,206,207,208,209],[120,166,187,190,197,201,202,219,220],[120,219],[120,202,219,221,222],[120,166,197,214,218],[120,166,235],[120,166,187,197,233,234,236],[120,166,187,197,231,232,233,235,236],[120,166,187,234],[120,166,228,235],[120,233,234,235,236,237,238,242],[120,166,210,243],[120,234,235,238],[120,239,240,241,243],[120,235,238],[120,166,235,238],[120,166,210,235,236],[120,183,187,201,223,243],[120,166,210,223,244],[120,244,245],[183,187,223,243],[166,210,223,244],[244,245]],"referencedMap":[[249,1],[247,2],[145,3],[128,2],[147,4],[129,5],[146,2],[151,6],[152,7],[148,7],[154,8],[149,7],[153,9],[150,10],[136,11],[133,12],[140,13],[134,11],[131,14],[139,2],[144,15],[141,2],[142,2],[143,2],[138,12],[135,16],[132,2],[137,17],[190,2],[225,18],[227,2],[226,19],[228,20],[224,21],[203,13],[199,22],[200,22],[201,23],[198,2],[65,24],[66,24],[68,25],[69,24],[70,24],[71,26],[72,2],[73,2],[74,2],[67,24],[166,27],[75,28],[155,29],[158,30],[159,2],[160,2],[161,2],[162,2],[163,2],[164,31],[165,32],[193,2],[195,33],[194,2],[252,34],[248,1],[250,35],[251,1],[191,12],[157,36],[253,2],[254,2],[257,37],[255,2],[258,38],[259,2],[260,39],[261,40],[272,41],[271,42],[256,2],[273,2],[275,43],[276,44],[274,45],[277,46],[278,47],[279,48],[280,49],[281,50],[282,51],[283,52],[284,53],[285,54],[286,55],[287,2],[156,2],[76,56],[77,56],[79,57],[80,58],[81,59],[82,60],[83,61],[84,62],[85,63],[86,64],[87,65],[88,66],[89,66],[90,67],[91,68],[92,69],[93,70],[78,2],[126,2],[94,71],[95,72],[96,73],[127,74],[97,75],[98,76],[99,77],[100,78],[101,79],[102,80],[103,81],[104,82],[105,83],[106,84],[107,85],[108,86],[110,87],[109,88],[111,89],[112,90],[113,2],[114,91],[115,92],[116,93],[117,94],[118,95],[119,96],[120,97],[121,98],[122,99],[123,100],[124,101],[125,102],[288,2],[289,12],[290,2],[291,2],[293,103],[292,2],[294,12],[319,104],[320,105],[295,106],[298,106],[317,104],[318,104],[308,104],[307,107],[305,104],[300,104],[313,104],[311,104],[315,104],[299,104],[312,104],[316,104],[301,104],[302,104],[314,104],[296,104],[303,104],[304,104],[306,104],[310,104],[321,108],[309,104],[297,104],[334,109],[333,2],[328,108],[330,110],[329,108],[322,108],[323,108],[325,108],[327,108],[331,110],[332,110],[324,110],[326,110],[336,111],[335,2],[337,2],[338,2],[339,2],[340,112],[130,2],[262,2],[215,113],[216,114],[217,114],[218,115],[177,2],[174,116],[176,116],[175,116],[173,116],[183,117],[178,118],[182,2],[179,2],[181,2],[180,2],[169,116],[170,116],[171,116],[167,2],[168,2],[172,116],[263,2],[267,119],[269,120],[268,119],[266,121],[270,122],[265,123],[264,2],[57,2],[64,124],[59,125],[60,126],[61,126],[62,127],[63,127],[58,128],[8,2],[10,2],[9,2],[2,2],[11,2],[12,2],[13,2],[14,2],[15,2],[16,2],[17,2],[18,2],[3,2],[4,2],[22,2],[19,2],[20,2],[21,2],[23,2],[24,2],[25,2],[5,2],[26,2],[27,2],[28,2],[29,2],[6,2],[33,2],[30,2],[31,2],[32,2],[34,2],[7,2],[35,2],[40,2],[41,2],[36,2],[37,2],[38,2],[39,2],[1,2],[42,2],[229,129],[230,2],[231,130],[56,2],[186,131],[185,132],[184,133],[187,134],[189,135],[197,136],[196,137],[188,2],[192,138],[214,139],[212,140],[213,141],[211,142],[204,143],[205,144],[206,144],[207,2],[208,144],[210,145],[209,144],[221,146],[202,2],[220,147],[222,147],[223,148],[219,149],[236,150],[235,151],[234,152],[233,153],[237,154],[243,155],[232,156],[239,157],[242,158],[240,159],[241,160],[238,161],[244,162],[245,163],[246,164],[47,2],[48,2],[49,2],[50,2],[51,2],[52,2],[43,2],[53,2],[54,2],[55,2],[44,2],[45,2],[46,2]],"exportedModulesMap":[[249,1],[247,2],[145,3],[128,2],[147,4],[129,5],[146,2],[151,6],[152,7],[148,7],[154,8],[149,7],[153,9],[150,10],[136,11],[133,12],[140,13],[134,11],[131,14],[139,2],[144,15],[141,2],[142,2],[143,2],[138,12],[135,16],[132,2],[137,17],[190,2],[225,18],[227,2],[226,19],[228,20],[224,21],[203,13],[199,22],[200,22],[201,23],[198,2],[65,24],[66,24],[68,25],[69,24],[70,24],[71,26],[72,2],[73,2],[74,2],[67,24],[166,27],[75,28],[155,29],[158,30],[159,2],[160,2],[161,2],[162,2],[163,2],[164,31],[165,32],[193,2],[195,33],[194,2],[252,34],[248,1],[250,35],[251,1],[191,12],[157,36],[253,2],[254,2],[257,37],[255,2],[258,38],[259,2],[260,39],[261,40],[272,41],[271,42],[256,2],[273,2],[275,43],[276,44],[274,45],[277,46],[278,47],[279,48],[280,49],[281,50],[282,51],[283,52],[284,53],[285,54],[286,55],[287,2],[156,2],[76,56],[77,56],[79,57],[80,58],[81,59],[82,60],[83,61],[84,62],[85,63],[86,64],[87,65],[88,66],[89,66],[90,67],[91,68],[92,69],[93,70],[78,2],[126,2],[94,71],[95,72],[96,73],[127,74],[97,75],[98,76],[99,77],[100,78],[101,79],[102,80],[103,81],[104,82],[105,83],[106,84],[107,85],[108,86],[110,87],[109,88],[111,89],[112,90],[113,2],[114,91],[115,92],[116,93],[117,94],[118,95],[119,96],[120,97],[121,98],[122,99],[123,100],[124,101],[125,102],[288,2],[289,12],[290,2],[291,2],[293,103],[292,2],[294,12],[319,104],[320,105],[295,106],[298,106],[317,104],[318,104],[308,104],[307,107],[305,104],[300,104],[313,104],[311,104],[315,104],[299,104],[312,104],[316,104],[301,104],[302,104],[314,104],[296,104],[303,104],[304,104],[306,104],[310,104],[321,108],[309,104],[297,104],[334,109],[333,2],[328,108],[330,110],[329,108],[322,108],[323,108],[325,108],[327,108],[331,110],[332,110],[324,110],[326,110],[336,111],[335,2],[337,2],[338,2],[339,2],[340,112],[130,2],[262,2],[215,113],[216,114],[217,114],[218,115],[177,2],[174,116],[176,116],[175,116],[173,116],[183,117],[178,118],[182,2],[179,2],[181,2],[180,2],[169,116],[170,116],[171,116],[167,2],[168,2],[172,116],[263,2],[267,119],[269,120],[268,119],[266,121],[270,122],[265,123],[264,2],[57,2],[64,124],[59,125],[60,126],[61,126],[62,127],[63,127],[58,128],[8,2],[10,2],[9,2],[2,2],[11,2],[12,2],[13,2],[14,2],[15,2],[16,2],[17,2],[18,2],[3,2],[4,2],[22,2],[19,2],[20,2],[21,2],[23,2],[24,2],[25,2],[5,2],[26,2],[27,2],[28,2],[29,2],[6,2],[33,2],[30,2],[31,2],[32,2],[34,2],[7,2],[35,2],[40,2],[41,2],[36,2],[37,2],[38,2],[39,2],[1,2],[42,2],[229,129],[230,2],[231,130],[56,2],[186,131],[185,132],[184,133],[187,134],[189,135],[197,136],[196,137],[188,2],[192,138],[214,139],[212,140],[213,141],[211,142],[204,143],[205,144],[206,144],[207,2],[208,144],[210,145],[209,144],[221,146],[202,2],[220,147],[222,147],[223,148],[219,149],[236,150],[235,151],[234,152],[233,153],[237,154],[243,155],[232,156],[239,157],[242,158],[240,159],[241,160],[238,161],[244,165],[245,166],[246,167],[47,2],[48,2],[49,2],[50,2],[51,2],[52,2],[43,2],[53,2],[54,2],[55,2],[44,2],[45,2],[46,2]],"semanticDiagnosticsPerFile":[249,247,145,128,147,129,146,151,152,148,154,149,153,150,136,133,140,134,131,139,144,141,142,143,138,135,132,137,190,225,227,226,228,224,203,199,200,201,198,65,66,68,69,70,71,72,73,74,67,166,75,155,158,159,160,161,162,163,164,165,193,195,194,252,248,250,251,191,157,253,254,257,255,258,259,260,261,272,271,256,273,275,276,274,277,278,279,280,281,282,283,284,285,286,287,156,76,77,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,78,126,94,95,96,127,97,98,99,100,101,102,103,104,105,106,107,108,110,109,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,288,289,290,291,293,292,294,319,320,295,298,317,318,308,307,305,300,313,311,315,299,312,316,301,302,314,296,303,304,306,310,321,309,297,334,333,328,330,329,322,323,325,327,331,332,324,326,336,335,337,338,339,340,130,262,215,216,217,218,177,174,176,175,173,183,178,182,179,181,180,169,170,171,167,168,172,263,267,269,268,266,270,265,264,57,64,59,60,61,62,63,58,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,229,230,231,56,186,185,184,187,189,197,196,188,192,214,212,213,211,204,205,206,207,208,210,209,221,202,220,222,223,219,236,235,234,233,237,243,232,239,242,240,241,238,244,245,246,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/SelectedNetworkController.d.ts.map b/dist/types/SelectedNetworkController.d.ts.map +index f9de27de6d312eeb12e583354bd0df73b08598a4..eb0dcc3cd8563769606ae8c6748d2ad1bd5665c1 100644 +--- a/dist/types/SelectedNetworkController.d.ts.map ++++ b/dist/types/SelectedNetworkController.d.ts.map +@@ -1 +1 @@ +-{"version":3,"file":"SelectedNetworkController.d.ts","sourceRoot":"","sources":["../../src/SelectedNetworkController.ts"],"names":[],"mappings":"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"} +\ No newline at end of file ++{"version":3,"file":"SelectedNetworkController.d.ts","sourceRoot":"","sources":["../../src/SelectedNetworkController.ts"],"names":[],"mappings":"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"} +\ No newline at end of file diff --git a/.yarn/patches/@metamask-snaps-utils-npm-7.7.0-2cc1f044af.patch b/.yarn/patches/@metamask-snaps-utils-npm-7.7.0-2cc1f044af.patch new file mode 100644 index 000000000000..82ddce260b99 --- /dev/null +++ b/.yarn/patches/@metamask-snaps-utils-npm-7.7.0-2cc1f044af.patch @@ -0,0 +1,30 @@ +diff --git a/dist/chunk-37VHIRUJ.js b/dist/chunk-37VHIRUJ.js +index a909a4ef20305665a07db5c25b4a9ff7eb0a447e..98dd75bf33a9716dc6cca96a38d184645f6ec033 100644 +--- a/dist/chunk-37VHIRUJ.js ++++ b/dist/chunk-37VHIRUJ.js +@@ -53,8 +53,8 @@ function assertIsKeyringOrigins(value, ErrorWrapper) { + } + function createOriginRegExp(matcher) { + const escaped = matcher.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&"); +- const regex = escaped.replace(/\*/gu, ".*"); +- return RegExp(regex, "u"); ++ const regex = escaped.replace(/\\\*/gu, '.*'); ++ return RegExp(`${regex}$`, 'u'); + } + function checkAllowedOrigin(matcher, origin) { + if (matcher === "*" || matcher === origin) { +diff --git a/dist/chunk-K2OTEZZZ.mjs b/dist/chunk-K2OTEZZZ.mjs +index 15be5da7563a5bdf464d7e9c28ed6f04863e378a..7f38bf328e71c1feb2b8850ba050ce9e55801668 100644 +--- a/dist/chunk-K2OTEZZZ.mjs ++++ b/dist/chunk-K2OTEZZZ.mjs +@@ -53,8 +53,8 @@ function assertIsKeyringOrigins(value, ErrorWrapper) { + } + function createOriginRegExp(matcher) { + const escaped = matcher.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&"); +- const regex = escaped.replace(/\*/gu, ".*"); +- return RegExp(regex, "u"); ++ const regex = escaped.replace(/\\\*/gu, '.*'); ++ return RegExp(`${regex}$`, 'u'); + } + function checkAllowedOrigin(matcher, origin) { + if (matcher === "*" || matcher === origin) { diff --git a/.yarn/patches/@metamask-transaction-controller-npm-32.0.0-e23c2c3443.patch b/.yarn/patches/@metamask-transaction-controller-npm-32.0.0-e23c2c3443.patch new file mode 100644 index 000000000000..eb2684b678b7 Binary files /dev/null and b/.yarn/patches/@metamask-transaction-controller-npm-32.0.0-e23c2c3443.patch differ diff --git a/CHANGELOG.md b/CHANGELOG.md index 277509e21eb3..213138e17664 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,217 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [12.2.0] +## [12.1.0] +### Added +- Launched a feature displaying the percentage increase or decrease for tokens within the UI ([#24223](https://github.com/MetaMask/metamask-extension/pull/24223)) +- Added a search feature to easily filter through the popular network list ([#25170](https://github.com/MetaMask/metamask-extension/pull/25170)) +- Launched a new asset details page ([#24522](https://github.com/MetaMask/metamask-extension/pull/24522)) +- Implemented a loader on the confirmation page during Blockaid validation ([#25477](https://github.com/MetaMask/metamask-extension/pull/25477)) +- Introduced an advanced details component for transactions ([#24833](https://github.com/MetaMask/metamask-extension/pull/24833)) +- Enhanced permit signatures with a simulation section ([#24862](https://github.com/MetaMask/metamask-extension/pull/24862)) +- Introduced dynamic support for identifying 'buyable' networks through the Ramps API ([#24041](https://github.com/MetaMask/metamask-extension/pull/24041)) +- Introduced a new notification counter over the Fox icon to display unread notifications ([#25093](https://github.com/MetaMask/metamask-extension/pull/25093)) +- Improved the display of large and small token values on the permit signature page ([#25720](https://github.com/MetaMask/metamask-extension/pull/25720)) +- Replaced "Approve spend limit" with "Spending cap" in the Permit signature redesign ([#25618](https://github.com/MetaMask/metamask-extension/pull/25618)) +- Display ellipsis to Permit simulation fiat values ([#26001](https://github.com/MetaMask/metamask-extension/pull/26001)) +- Display ellipsis for Permit values with over 15 characters ([#26458](https://github.com/MetaMask/metamask-extension/pull/26458)) + +### Changed +- Introduced the ability to edit custom networks directly from the modal ([#25272](https://github.com/MetaMask/metamask-extension/pull/25272)) +- Launched an initial UI for editing multiple RPC URLs in the network form ([#25219](https://github.com/MetaMask/metamask-extension/pull/25219)) +- Added paymaster address display for certain transactions in the contract interaction confirmation ([#25396](https://github.com/MetaMask/metamask-extension/pull/25396)) +- Updated the text for permit signatures ([#24975](https://github.com/MetaMask/metamask-extension/pull/24975)) + +### Fixed +- Updated all help center URLs from the old Zendesk format to the current support.metamask.io domain ([#24286](https://github.com/MetaMask/metamask-extension/pull/24286)) +- Improved NFT ownership updates in MetaMask by parsing logs from contract interaction transactions ([#25380](https://github.com/MetaMask/metamask-extension/pull/25380)) +- Corrected the display of the delete network icon to only appear on mouseover in the network modal ([#25547](https://github.com/MetaMask/metamask-extension/pull/25547)) +- Fixed a UI issue where a non-zero percentage change was displayed next to a $0.00 account balance ([#25550](https://github.com/MetaMask/metamask-extension/pull/25550)) +- Improved the display of large token amounts in the wallet ([#25464](https://github.com/MetaMask/metamask-extension/pull/25464)) +- Resolved an issue where the Linea mainnet ticker was incorrectly displayed as undefined ([#25436](https://github.com/MetaMask/metamask-extension/pull/25436)) +- Deprecated Mumbai and Linea Goerli test networks ([#24994](https://github.com/MetaMask/metamask-extension/pull/24994)) +- Fixed an issue where STX was incorrectly used for Swap+Send approvals ([#25510](https://github.com/MetaMask/metamask-extension/pull/25510)) +- Addressed and resolved all identified bugs in the Swap+Send feature ([#25100](https://github.com/MetaMask/metamask-extension/pull/25100)) +- Enhanced network indicator consistency on the confirmation page by aligning it with the network selection dropdown ([#25518](https://github.com/MetaMask/metamask-extension/pull/25518)) +- Implemented a setting to allow users to opt-out of displaying fiat values for testnet transactions ([#25167](https://github.com/MetaMask/metamask-extension/pull/25167)) +- Adjusted styling to properly handle long NFT IDs in transaction simulations ([#25252](https://github.com/MetaMask/metamask-extension/pull/25252)) +- Enhanced the deadline field formatting on the permit signature page ([#25321](https://github.com/MetaMask/metamask-extension/pull/25321)) +- Refined the design of the permit signature confirmation page ([#25383](https://github.com/MetaMask/metamask-extension/pull/25383)) +- Updated the text on redesigned Sign-In With Ethereum (SIWE) signature pages ([#25381](https://github.com/MetaMask/metamask-extension/pull/25381)) +- Adjusted signature pages to only display simulations when the user has enabled this preference ([#25186](https://github.com/MetaMask/metamask-extension/pull/25186)) +- Refined the alerts on typed sign data pages based on feedback ([#25163](https://github.com/MetaMask/metamask-extension/pull/25163)) +- Corrected the display of USD amounts in swap notifications ([#25444](https://github.com/MetaMask/metamask-extension/pull/25444)) +- Refined notifications UI to only show tabs when relevant ([#25350](https://github.com/MetaMask/metamask-extension/pull/25350)) +- Resolved an issue to ensure push notifications are properly received and not delivered as silent notifications ([#25340](https://github.com/MetaMask/metamask-extension/pull/25340)) +- Fixed a bug to ensure native currency symbols are correctly displayed in notifications ([#25364](https://github.com/MetaMask/metamask-extension/pull/25364)) +- Adjusted the notifications-tag-counter to have a minimum width ([#25322](https://github.com/MetaMask/metamask-extension/pull/25322)) +- Resolved an issue where incorrect type assertions led to a crash when splitting an undefined value in notification detail network fees ([#25315](https://chat.consensys.io/chat/jgI56K8PwrDp4d4EERs4l)) +- Excluded SNAP notifications from 'Mark all as read' server updates ([#25099](https://github.com/MetaMask/metamask-extension/pull/25099)) +- Resolved an issue by ensuring max gas values for swaps are rounded to integers ([#25488](https://github.com/MetaMask/metamask-extension/pull/25488)) +- Updated the outdated browser warning to display immediately for new installations ([#25366](https://github.com/MetaMask/metamask-extension/pull/25366)) +- Enhanced the Send flow by displaying a network badge next to the chosen asset ([#25470](https://github.com/MetaMask/metamask-extension/pull/25470)) +- Fixed various display issues in the asset picker modal and balance display ([#25601](https://github.com/MetaMask/metamask-extension/pull/25601)) +- Corrected the display of decimal places for token values on permit pages ([#25718](https://github.com/MetaMask/metamask-extension/pull/25718)) +- Added ellipsis for long permit values and fixed errors with very large numbers ([#25741](https://github.com/MetaMask/metamask-extension/pull/25741)) +- Fixed an issue where the connected account was missing on the connection page ([#25500](https://github.com/MetaMask/metamask-extension/pull/25500)) +- Removed the NFT autodetection modal when the feature is disabled in settings ([#25993](https://github.com/MetaMask/metamask-extension/pull/25993)) +- Fixed the Blockaid report URL on redesigned pages ([#25702](https://github.com/MetaMask/metamask-extension/pull/25702)) +- Fixed the "Show conversion in testnets" option for Bitcoin and Ethereum test assets ([#26224](https://github.com/MetaMask/metamask-extension/pull/26224)) +- Enabled the Save button on the Add Contact page when a valid address or ENS name is entered ([#26456](https://github.com/MetaMask/metamask-extension/pull/26456)) +- Fixed fallback conversion rates for token market data to ensure accurate market cap ([#26460](https://github.com/MetaMask/metamask-extension/pull/26460)) +- Resolved the issue of selecting the correct network when multiple networks have the same chain ID ([#25805](https://github.com/MetaMask/metamask-extension/pull/25805)) +- Updated alignment and padding for permit simulations ([#26186](https://github.com/MetaMask/metamask-extension/pull/26186)) +- Updated the pending transactions badge to display a number instead of three dots ([#26116](https://github.com/MetaMask/metamask-extension/pull/26116)) +- Adjusted spacing in the send asset picker for proper vertical alignment with other dropdowns ([#25576](https://github.com/MetaMask/metamask-extension/pull/25576)) +- Fixed decimal display for Permit values and added a reusable component for displaying token units ([#26105](https://github.com/MetaMask/metamask-extension/pull/26105)) +- Fixed precision loss for very large values in signature simulations ([#25968](https://github.com/MetaMask/metamask-extension/pull/25968)) +- Included decimals in fiat calculations for Permit simulations and added tooltips for shortened values ([#26523](https://github.com/MetaMask/metamask-extension/pull/26523)) +- Fixed the missing deadline timer on the swaps status screen ([#26544](https://github.com/MetaMask/metamask-extension/pull/26544)) +- Fixed an issue where account names were out of sync in the account list during the connect account flow ([#26542](https://github.com/MetaMask/metamask-extension/pull/26542)) + +## [12.0.6] +### Changed +- Improve error diagnostics ([#26482](https://github.com/MetaMask/metamask-extension/pull/26482)) + +### Fixed +- Fix errors in error diagnistics on Firefox ([#26467](https://github.com/MetaMask/metamask-extension/pull/26467)) +- Prevent pending transactions from causing excessive disk space usage ([#26485](https://github.com/MetaMask/metamask-extension/pull/26485)) + - This is a repeat of the fix included in v12.0.1; it wasn't working for all users, but it should now. +- Prevent UI crash while signature confirmation is closing ([#26248](https://github.com/MetaMask/metamask-extension/pull/26248)) + +## [12.0.5] +### Fixed +- Prevent network selection per dapp from breaking if the rpc url of the currently selected network is edited ([#26453](https://github.com/MetaMask/metamask-extension/pull/26453)) +- Fix the display of prices and balances of some tokens on networks other than ethereum mainnet ([#26450](https://github.com/MetaMask/metamask-extension/pull/26450)) + +## [12.0.4] +### Fixed +- Further prevented data persistence errors related to networks ([#26432](https://github.com/MetaMask/metamask-extension/pull/26432)) + +## [12.0.3] + +## [12.0.2] +### Fixed +- Prevented data persistence errors related to networks, and phishing protection ([#26383](https://github.com/MetaMask/metamask-extension/pull/26383), [#26396](https://github.com/MetaMask/metamask-extension/pull/26396)) +- Fix token state migration errors ([#26397](https://github.com/MetaMask/metamask-extension/pull/26397)) + +## [12.0.1] +### Removed +- Disabled the "Restore Backup" feature ([#26325](https://github.com/MetaMask/metamask-extension/pull/26325)) + - This was linked to reports of state corruption. It has been disabled at least until we can ensure it is safe to use. + +### Fixed +- Fixed corrupted state for some users, which could prevent further data updates from persisting between sessions ([#26325](https://github.com/MetaMask/metamask-extension/pull/26325), [#26280](https://github.com/MetaMask/metamask-extension/pull/26280), [#26265](https://github.com/MetaMask/metamask-extension/pull/26265), [#26282](https://github.com/MetaMask/metamask-extension/pull/26282), [#26298](https://github.com/MetaMask/metamask-extension/pull/26298), [#26302](https://github.com/MetaMask/metamask-extension/pull/26302), [#26308](https://github.com/MetaMask/metamask-extension/pull/26308), [#26310](https://github.com/MetaMask/metamask-extension/pull/26310)) +- Prevent crashes / error screens when taking some token actions if the "Show balance and token price checker" setting is off ([#26264](https://github.com/MetaMask/metamask-extension/pull/26264)) +- Ensure error messages are correctly shown to the user inline on the send screen, if an error occurs when attempting to create the transaction ([#26253](https://github.com/MetaMask/metamask-extension/pull/26253)) +- Fix error that could prevent the send screen from loading ([#26295](https://github.com/MetaMask/metamask-extension/pull/26295)) +- Fix crash that could occur when selected network is missing from network configuration ([#26327](https://github.com/MetaMask/metamask-extension/pull/26327)) +- Prevent pending transactions from causing excessive disk space usage ([#26236](https://github.com/MetaMask/metamask-extension/pull/26236)) +- Fix excessive disk space usage for some users that have had stuck pending transactions in the past ([#26291](https://github.com/MetaMask/metamask-extension/pull/26291)) + +## [12.0.0] +### Added +- Allow users to 'Select networks for each site' ([#24274](https://github.com/MetaMask/metamask-extension/pull/24274)) +- Introduced new features in MetaMask including Profile Syncing for privacy-preserving data sharing across clients and customizable on-chain activity notifications ([#24482](https://github.com/MetaMask/metamask-extension/pull/24482)) +- Implemented Blockaid validation support for the OPBNB network ([#24361](https://github.com/MetaMask/metamask-extension/pull/24361)) +- Implemented the Swap+Send feature, allowing users to swap assets immediately before sending ([#23703](https://github.com/MetaMask/metamask-extension/pull/23703)) +- Introduced a modal prompting existing users to enable automatic token detection ([#24281](https://github.com/MetaMask/metamask-extension/pull/24281)) +- Added the `wallet_switchEthereumChain` permission, to let dApps switch Ethereum chains with user's initial permission ([#24415](https://github.com/MetaMask/metamask-extension/pull/24415)) +- Introduced QR code support for adding OneKey Pro hardware wallets ([#24492](https://github.com/MetaMask/metamask-extension/pull/24492)) + +### Changed +- Updated the 'Help us improve MetaMask' onboarding text ([#24050](https://github.com/MetaMask/metamask-extension/pull/24050)) +- Redesign of the Send flow UI ([#22457](https://github.com/MetaMask/metamask-extension/pull/22457)) +- Enhanced the UI to display pre-approved connections during the permission confirmation step for installing or updating a Snap ([#24403](https://github.com/MetaMask/metamask-extension/pull/24403)) +- Updated the information section for SIWE signature types ([#24811](https://github.com/MetaMask/metamask-extension/pull/24811)) +- Updated the gas UI styles in Transaction Details ([#24241](https://github.com/MetaMask/metamask-extension/pull/24241)) +- Improved the positioning of the warning icon on gas details ([#24999](https://github.com/MetaMask/metamask-extension/pull/24999)) +- Disabled specific gas warnings and errors for transactions on the Mantle network ([#24860](https://github.com/MetaMask/metamask-extension/pull/24860)) +- Redesigns of the personal and typed (EIP-712) signature screens, now available for opt-in ([#24466](https://github.com/MetaMask/metamask-extension/pull/24466)) +- Updated the information section for permit signature types to provide clearer details ([#24808](https://github.com/MetaMask/metamask-extension/pull/24808)) +- Introduced a simulation section for Sign-In With Ethereum (SIWE) personal signature requests ([#24865](https://github.com/MetaMask/metamask-extension/pull/24865)) +- Support transaction specific gas fee estimates, allowing for more precise gas estimates in some scenarios ([#24222](https://github.com/MetaMask/metamask-extension/pull/24222)) +- Updated contract names for MetaMask Pooled Staking and MetaMask Third Party Staking features ([#25077](https://github.com/MetaMask/metamask-extension/pull/25077)) +- Redesigned Snap UI buttons as inline-style text buttons ([#23610](https://github.com/MetaMask/metamask-extension/pull/23610)) +- Removed the account type pill from the wallet overview screen ([#24717](https://github.com/MetaMask/metamask-extension/pull/24717)) +- Updated the Snaps interface to conceal Snaps that are currently installing ([#24215](https://github.com/MetaMask/metamask-extension/pull/24215)) +- Adjusted the validation for network symbols to allow single character symbols when adding a network ([#24872](https://github.com/MetaMask/metamask-extension/pull/24872)) +- Corrected Aurora deprecation message to guide users on migrating from Infura to mainnet.aurora.dev for continued support ([#24534](https://github.com/MetaMask/metamask-extension/pull/24534)) +- Implemented a fallback for currency formatting in cases of unknown currencies ([#24563](https://github.com/MetaMask/metamask-extension/pull/24563)) +- Updated the SnapAvatar's background and border colors to enhance visual appeal ([#24346](https://github.com/MetaMask/metamask-extension/pull/24346)) + +### Fixed +- Fixed an issue where adding a new HD account incorrectly retrieved addresses from all keyrings ([#24199](https://github.com/MetaMask/metamask-extension/pull/24199)) +- Fixed an issue where changing accounts during an NFT transaction incorrectly retained the NFT asset selection ([#24398](https://github.com/MetaMask/metamask-extension/pull/24398)) +- Enhanced network verification to handle special cases like chain ID collisions with different symbols ([#23802](https://github.com/MetaMask/metamask-extension/pull/23802)) +- Resolved an issue where users were unable to manually edit gas fees in the advanced gas fee editing modal ([#24387](https://github.com/MetaMask/metamask-extension/pull/24387)) +- Fixed an issue where swaps initiated by DApps were causing errors in MetaMask ([#24382](https://github.com/MetaMask/metamask-extension/pull/24382)) +- Corrected the display mismatch of maximum gas for transactions on the Optimism network ([#24272](https://github.com/MetaMask/metamask-extension/pull/24272)) +- Resolved an issue causing duplicate display of Layer 1 and Layer 2 fees for transactions on Layer 2 networks ([#24264](https://github.com/MetaMask/metamask-extension/pull/24264)) +- Improved the layout of the confirmation page in extended view ([#24211](https://github.com/MetaMask/metamask-extension/pull/24211)) +- Improved support for custom token approval transactions, allowing for extra data like attribution tags ([#23527](https://github.com/MetaMask/metamask-extension/pull/23527)) +- Adjusted the Connect Account Toast to honor user preferences ([#24474](https://github.com/MetaMask/metamask-extension/pull/24474)) +- Fixed a display issue by removing the outdated account alert ([#24475](https://github.com/MetaMask/metamask-extension/pull/24475)) +- Resolved a double-click issue and console errors during ERC20 token import process ([#24620](https://github.com/MetaMask/metamask-extension/pull/24620)) +- Optimized handling of prerender issues in MetaMask for certain Chromium versions ([#24317](https://github.com/MetaMask/metamask-extension/pull/24317)) +- Enhanced MetaMask's browser support by updating code for better compatibility with older versions ([#24907](https://github.com/MetaMask/metamask-extension/pull/24907)) +- Fixed a UI inconsistency in the Transaction Insight tab for Snaps ([#24849](https://github.com/MetaMask/metamask-extension/pull/24849)) +- Resolved error handling issues for user rejections on chain switch and add requests ([#25339](https://github.com/MetaMask/metamask-extension/pull/25339)) +- Adjusted UI to truncate long NFT IDs with ellipses ([#25479](https://github.com/MetaMask/metamask-extension/pull/25479)) +- Fixed a bug to handle cases of undefined transaction data ([#25506](https://github.com/MetaMask/metamask-extension/pull/25506)) +- Fixed a bug that caused empty nonce fields for new transactions on different networks when a Smart Transaction was pending ([#25629](https://github.com/MetaMask/metamask-extension/pull/25629)) +- Fixed an issue ensuring MetaMask UI remains responsive when switching to a dApp tab on a down network ([#25425](https://github.com/MetaMask/metamask-extension/pull/25425)) + +## [11.16.16] +### Changed +- Update Blockaid feature for latest security enhancements ([#26167](https://github.com/MetaMask/metamask-extension/pull/26167)) + +## [11.16.15] +### Changed +- Updates MMI packages, support for a custodian has been removed ([#25502](https://github.com/MetaMask/metamask-extension/pull/25502)) + +### Fixed +- Fix bug preventing connection of Trezor hardware wallets on firefox ([#25487](https://github.com/MetaMask/metamask-extension/pull/25487)) + +## [11.16.14] +### Fixed +- Fix bug that could result in failure of some swap transactions ([#25488](https://github.com/MetaMask/metamask-extension/pull/25488)) + +## [11.16.13] +### Changed +- Update Blockaid feature for latest security protections ([#25442](https://github.com/MetaMask/metamask-extension/pull/25442)) + +## [11.16.12] +### Added +- Add a marketing data collection consent toggle across Onboarding, Wallet Toast, and Settings, empowering users to control their participation in marketing analytics ([#24605](https://github.com/MetaMask/metamask-extension/pull/24605)) + +## [11.16.11] +### Fixed +- Ensure network requests for phishing lists are not sent if the Basic Functionality Toggle is off ([#25306](https://github.com/MetaMask/metamask-extension/pull/25306)) + +## [11.16.10] +### Fixed +- Capture Segment errors during initialization ([#25253](https://github.com/MetaMask/metamask-extension/pull/25253)) + +## [11.16.9] +### Fixed +- Fix an issue where Snaps would be unable to decrypt older state ([#25172](https://github.com/MetaMask/metamask-extension/pull/25172)) + +## [11.16.8] +### Changed +- Prepare for increasing the minimum Chromium version supported by MetaMask ([#25142](https://github.com/MetaMask/metamask-extension/pull/25142)) + - Users on Chromium versions lower than 109 will now see a warning that they need to update their browser. + Assuming their Chromium browser does not support the Offscreen Document api, the warning will include a note + that Snaps and Hardware wallets do not work on their current browser version. +- [MMI] Updates MMI packages to latest versions ([#24581](https://github.com/MetaMask/metamask-extension/pull/24581)) + +### Fixed +- Fix bug that could cause users to be locked out of MetaMask if they previously emptied the "Auto-lock timer" advanced setting +input field and save it in that empty state. ([#25109](https://github.com/MetaMask/metamask-extension/pull/25109)) +- Fix bug that can prevent updates to network data in connected dapps after manually switching the network in the wallet ui ([#25127](https://github.com/MetaMask/metamask-extension/pull/25127)) +- [MMI] Fixed an issue that prevented MMI transactions from being sent correctly ([#24947](https://github.com/MetaMask/metamask-extension/pull/24947)) +- [MMI] Better support for MMI Portfolio Dashboard with an update to the allow list ([#24992](https://github.com/MetaMask/metamask-extension/pull/24992)) + ## [11.16.7] ### Fixed - Fix bug that breaks dapps that expect users to switch chains manually([#25046]https://github.com/MetaMask/metamask-extension/pull/25046) @@ -4788,7 +4999,24 @@ Update styles and spacing on the critical error page ([#20350](https://github.c [Unreleased]: https://github.com/MetaMask/metamask-extension/compare/v12.2.0...HEAD -[12.2.0]: https://github.com/MetaMask/metamask-extension/compare/v11.16.7...v12.2.0 +[12.2.0]: https://github.com/MetaMask/metamask-extension/compare/v12.1.0...v12.2.0 +[12.1.0]: https://github.com/MetaMask/metamask-extension/compare/v12.0.6...v12.1.0 +[12.0.6]: https://github.com/MetaMask/metamask-extension/compare/v12.0.5...v12.0.6 +[12.0.5]: https://github.com/MetaMask/metamask-extension/compare/v12.0.4...v12.0.5 +[12.0.4]: https://github.com/MetaMask/metamask-extension/compare/v12.0.3...v12.0.4 +[12.0.3]: https://github.com/MetaMask/metamask-extension/compare/v12.0.2...v12.0.3 +[12.0.2]: https://github.com/MetaMask/metamask-extension/compare/v12.0.1...v12.0.2 +[12.0.1]: https://github.com/MetaMask/metamask-extension/compare/v12.0.0...v12.0.1 +[12.0.0]: https://github.com/MetaMask/metamask-extension/compare/v11.16.16...v12.0.0 +[11.16.16]: https://github.com/MetaMask/metamask-extension/compare/v11.16.15...v11.16.16 +[11.16.15]: https://github.com/MetaMask/metamask-extension/compare/v11.16.14...v11.16.15 +[11.16.14]: https://github.com/MetaMask/metamask-extension/compare/v11.16.13...v11.16.14 +[11.16.13]: https://github.com/MetaMask/metamask-extension/compare/v11.16.12...v11.16.13 +[11.16.12]: https://github.com/MetaMask/metamask-extension/compare/v11.16.11...v11.16.12 +[11.16.11]: https://github.com/MetaMask/metamask-extension/compare/v11.16.10...v11.16.11 +[11.16.10]: https://github.com/MetaMask/metamask-extension/compare/v11.16.9...v11.16.10 +[11.16.9]: https://github.com/MetaMask/metamask-extension/compare/v11.16.8...v11.16.9 +[11.16.8]: https://github.com/MetaMask/metamask-extension/compare/v11.16.7...v11.16.8 [11.16.7]: https://github.com/MetaMask/metamask-extension/compare/v11.16.6...v11.16.7 [11.16.6]: https://github.com/MetaMask/metamask-extension/compare/v11.16.5...v11.16.6 [11.16.5]: https://github.com/MetaMask/metamask-extension/compare/v11.16.4...v11.16.5 diff --git a/app/_locales/de/messages.json b/app/_locales/de/messages.json index 7566d8c1872d..450b51ca04f3 100644 --- a/app/_locales/de/messages.json +++ b/app/_locales/de/messages.json @@ -1263,9 +1263,6 @@ "data": { "message": "Daten" }, - "dataBackupSeemsCorrupt": { - "message": "Ihre Daten konnten nicht wiederhergestellt werden. Die Datei scheint beschädigt zu sein." - }, "dataHex": { "message": "Hexadezimal" }, @@ -1555,6 +1552,9 @@ "message": "$1 aktivieren", "description": "$1 is a token symbol, e.g. ETH" }, + "enableTokenAutoDetection": { + "message": "Automatische Token-Erkennung aktivieren" + }, "enabled": { "message": "Aktiviert" }, @@ -2498,9 +2498,6 @@ "message": "Stellen Sie sicher, dass niemand zuschaut.", "description": "Warning to users to be care while creating and saving their new Secret Recovery Phrase" }, - "manageInSettings": { - "message": "In Einstellungen verwalten" - }, "max": { "message": "Max." }, @@ -2931,6 +2928,9 @@ "noSnaps": { "message": "Keine Snaps installiert" }, + "noThanks": { + "message": "Nein, danke!" + }, "noTransactions": { "message": "Keine Transaktionen" }, @@ -3100,7 +3100,7 @@ }, "notificationTransactionSuccessView": { "message": "Auf $1 ansehen", - "description": "Additional content in browser notification that appears when a transaction is confirmed and has a block explorer URL" + "description": "Additional content in a notification that appears when a transaction is confirmed and has a block explorer URL." }, "notifications": { "message": "Benachrichtigungen" @@ -4024,12 +4024,6 @@ "restore": { "message": "Wiederherstellen" }, - "restoreFailed": { - "message": "Wiederherstellung Ihrer Daten aus der bereitgestellten Datei nicht möglich" - }, - "restoreSuccessful": { - "message": "Ihre Daten wurden erfolgreich wiederhergestellt." - }, "restoreUserData": { "message": "Benutzerdaten wiederherstellen" }, @@ -5869,7 +5863,7 @@ "message": "NFTs automatisch erkennen" }, "useNftDetectionDescriptionText": { - "message": "Lassen Sie MetaMask NFTs, die Sie besitzen, anhand von Drittanbieterdiensten (wie OpenSea) hinzufügen. Durch die automatische Erkennung von NFTs werden Ihre IP- und Kontoadresse für diese Dienste offengelegt. Durch die Aktivierung dieser Funktion können eventuell Ihre IP- und Ethereum-Adresse miteinander in Verbindung gebracht und gefälschte, von Betrügern per Airdropping abgesetzte NFTs anzeigt werden. Es ist möglich, Tokens manuell hinzufügen, um dieses Risiko zu vermeiden." + "message": "Lassen Sie MetaMask NFTs, die Sie besitzen, anhand von Drittanbieterdiensten hinzufügen. Durch die automatische Erkennung von NFTs werden Ihre IP- und Kontoadresse für diese Dienste offengelegt. Durch die Aktivierung dieser Funktion können eventuell Ihre IP- und Ethereum-Adresse miteinander in Verbindung gebracht und gefälschte, von Betrügern per Airdropping abgesetzte NFTs anzeigt werden. Es ist möglich, Tokens manuell hinzufügen, um dieses Risiko zu vermeiden." }, "usePhishingDetection": { "message": "Phishing-Erkennung verwenden" diff --git a/app/_locales/el/messages.json b/app/_locales/el/messages.json index 5ca813ae6c72..8c80b6bf6094 100644 --- a/app/_locales/el/messages.json +++ b/app/_locales/el/messages.json @@ -275,7 +275,7 @@ "description": "$1 is Learn more link" }, "addNewAccount": { - "message": "Προσθήκη νέου λογαριασμού" + "message": "Προσθήκη νέου λογαριασμού Ethereum" }, "addNewToken": { "message": "Προσθήκη νέου token" @@ -1263,9 +1263,6 @@ "data": { "message": "Δεδομένα" }, - "dataBackupSeemsCorrupt": { - "message": "Δεν μπορείτε να επαναφέρετε τα δεδομένα σας. Το αρχείο φαίνεται να είναι κατεστραμμένο." - }, "dataHex": { "message": "Δεκαεξαδικός" }, @@ -1555,6 +1552,9 @@ "message": "ενεργοποίηση $1", "description": "$1 is a token symbol, e.g. ETH" }, + "enableTokenAutoDetection": { + "message": "Ενεργοποίηση της αυτόματης ανίχνευσης των tokens" + }, "enabled": { "message": "Ενεργοποιημένο" }, @@ -2498,9 +2498,6 @@ "message": "Βεβαιωθείτε ότι κανείς δεν κοιτάει", "description": "Warning to users to be care while creating and saving their new Secret Recovery Phrase" }, - "manageInSettings": { - "message": "Διαχείριση των ρυθμίσεων" - }, "max": { "message": "Μέγ." }, @@ -2931,6 +2928,9 @@ "noSnaps": { "message": "Δεν έχετε εγκαταστήσει κανένα snap." }, + "noThanks": { + "message": "Όχι, ευχαριστώ" + }, "noTransactions": { "message": "Δεν έχετε καμιά συναλλαγή" }, @@ -3100,7 +3100,7 @@ }, "notificationTransactionSuccessView": { "message": "Προβολή σε $1", - "description": "Additional content in browser notification that appears when a transaction is confirmed and has a block explorer URL" + "description": "Additional content in a notification that appears when a transaction is confirmed and has a block explorer URL." }, "notifications": { "message": "Ειδοποιήσεις" @@ -4024,12 +4024,6 @@ "restore": { "message": "Επαναφορά" }, - "restoreFailed": { - "message": "Δεν είναι δυνατή η επαναφορά των δεδομένων σας από το αρχείο που δόθηκε" - }, - "restoreSuccessful": { - "message": "Επιτυχής επαναφορά των δεδομένων σας" - }, "restoreUserData": { "message": "Επαναφορά δεδομένων χρήστη" }, @@ -5869,7 +5863,7 @@ "message": "Αυτόματη ανίχνευση των NFT" }, "useNftDetectionDescriptionText": { - "message": "Επιτρέψτε στο MetaMask να προσθέτει NFT που σας ανήκουν χρησιμοποιώντας υπηρεσίες τρίτων (όπως το OpenSea). Ο αυτόματος εντοπισμός NFT εκθέτει τη διεύθυνση IP και τον λογαριασμό σας σε αυτές τις υπηρεσίες. Η ενεργοποίηση αυτής της λειτουργίας θα μπορούσε να συσχετίσει τη διεύθυνση IP σας με τη διεύθυνση Ethereum σας και να εμφανίσει ψεύτικα NFT που διοχετεύουν οι απατεώνες. Μπορείτε να προσθέσετε tokens χειροκίνητα για να αποφύγετε αυτόν τον κίνδυνο." + "message": "Επιτρέψτε στο MetaMask να προσθέτει NFT που σας ανήκουν χρησιμοποιώντας υπηρεσίες τρίτων. Ο αυτόματος εντοπισμός NFT εκθέτει τη διεύθυνση IP και την διεύθυνση του λογαριασμού σας σε αυτές τις υπηρεσίες. Η ενεργοποίηση αυτής της λειτουργίας θα μπορούσε να συσχετίσει τη διεύθυνση IP σας με τη διεύθυνση Ethereum σας και να εμφανίσει ψεύτικα NFT τακτικής airdrop που διοχετεύουν οι απατεώνες. Μπορείτε να προσθέσετε tokens μη αυτόματα για να αποφύγετε αυτόν τον κίνδυνο." }, "usePhishingDetection": { "message": "Χρήση ανίχνευσης phishing" diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index a00a938f3882..b753de1d494d 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -1430,9 +1430,6 @@ "data": { "message": "Data" }, - "dataBackupSeemsCorrupt": { - "message": "Can not restore your data. The file appears to be corrupt." - }, "dataCollectionForMarketing": { "message": "Data collection for marketing" }, @@ -1545,6 +1542,24 @@ "developerOptionsResetStatesOnboarding": { "message": "Resets various states related to onboarding and redirects to the \"Secure Your Wallet\" onboarding page." }, + "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." + }, "developerOptionsServiceWorkerKeepAlive": { "message": "Results in a timestamp being continuously saved to session.storage" }, @@ -2737,9 +2752,6 @@ "message": "Make sure nobody is looking", "description": "Warning to users to be care while creating and saving their new Secret Recovery Phrase" }, - "manageInSettings": { - "message": "Manage in settings" - }, "marketCap": { "message": "Market cap" }, @@ -3201,6 +3213,9 @@ "noSnaps": { "message": "You don't have any snaps installed." }, + "noThanks": { + "message": "No thanks" + }, "noTransactions": { "message": "You have no transactions" }, @@ -3373,7 +3388,7 @@ }, "notificationTransactionSuccessView": { "message": "View on $1", - "description": "Additional content in browser notification that appears when a transaction is confirmed and has a block explorer URL" + "description": "Additional content in a notification that appears when a transaction is confirmed and has a block explorer URL." }, "notifications": { "message": "Notifications" @@ -3544,6 +3559,9 @@ "onboardingMetametricsNeverSellDataEmphasis": { "message": "Optional:" }, + "onboardingMetametricsPrivacyDescription": { + "message": "Learn how we protect your privacy while collecting usage data for your profile." + }, "onboardingMetametricsTitle": { "message": "Help us improve MetaMask" }, @@ -4354,12 +4372,6 @@ "restore": { "message": "Restore" }, - "restoreFailed": { - "message": "Can not restore your data from the file provided" - }, - "restoreSuccessful": { - "message": "Your data has been restored successfully" - }, "restoreUserData": { "message": "Restore user data" }, @@ -6155,7 +6167,7 @@ "message": "Stay in the loop on what's happening in your wallet with notifications." }, "turnOnMetamaskNotificationsMessagePrivacyBold": { - "message": "Settings > Notifications." + "message": "notifications settings." }, "turnOnMetamaskNotificationsMessagePrivacyLink": { "message": "Learn how we protect your privacy while using this feature." @@ -6164,7 +6176,7 @@ "message": "To use wallet notifications, we use a profile to sync some settings across your devices. $1" }, "turnOnMetamaskNotificationsMessageThird": { - "message": "You can turn off notifications at any time in $1" + "message": "You can turn off notifications at any time in the $1" }, "turnOnTokenDetection": { "message": "Turn on enhanced token detection" @@ -6267,7 +6279,7 @@ "message": "Autodetect NFTs" }, "useNftDetectionDescriptionText": { - "message": "Let MetaMask add NFTs you own using third-party services (like OpenSea). Autodetecting NFTs exposes your IP and account address to these services. Enabling this feature could associate your IP address with your Ethereum address and display fake NFTs airdropped by scammers. You can add tokens manually to avoid this risk." + "message": "Let MetaMask add NFTs you own using third-party services. Autodetecting NFTs exposes your IP and account address to these services. Enabling this feature could associate your IP address with your Ethereum address and display fake NFTs airdropped by scammers. You can add tokens manually to avoid this risk." }, "usePhishingDetection": { "message": "Use phishing detection" diff --git a/app/_locales/en_GB/messages.json b/app/_locales/en_GB/messages.json new file mode 100644 index 000000000000..1787f059bea9 --- /dev/null +++ b/app/_locales/en_GB/messages.json @@ -0,0 +1,6589 @@ +{ + "QRHardwareInvalidTransactionTitle": { + "message": "Error" + }, + "QRHardwareMismatchedSignId": { + "message": "Incongruent transaction data. Please check the transaction details." + }, + "QRHardwarePubkeyAccountOutOfRange": { + "message": "No more accounts. If you would like to access another account unlisted below, please reconnect your hardware wallet and select it." + }, + "QRHardwareScanInstructions": { + "message": "Place the QR code in front of your camera. The screen is blurred, but it will not affect the reading." + }, + "QRHardwareSignRequestCancel": { + "message": "Reject" + }, + "QRHardwareSignRequestDescription": { + "message": "After you’ve signed with your wallet, click on 'Get Signature' to receive the signature" + }, + "QRHardwareSignRequestGetSignature": { + "message": "Get signature" + }, + "QRHardwareSignRequestSubtitle": { + "message": "Scan the QR code with your wallet" + }, + "QRHardwareSignRequestTitle": { + "message": "Request signature" + }, + "QRHardwareUnknownQRCodeTitle": { + "message": "Error" + }, + "QRHardwareUnknownWalletQRCode": { + "message": "Invalid QR code. Please scan the sync QR code of the hardware wallet." + }, + "QRHardwareWalletImporterTitle": { + "message": "Scan QR code" + }, + "QRHardwareWalletSteps1Description": { + "message": "You can choose from a list of official QR-code supporting partners below." + }, + "QRHardwareWalletSteps1Title": { + "message": "Connect your QR hardware wallet" + }, + "QRHardwareWalletSteps2Description": { + "message": "Ngrave (coming soon)" + }, + "SIWEAddressInvalid": { + "message": "The address in the sign-in request does not match the address of the account you are using to sign in." + }, + "SIWEDomainInvalidText": { + "message": "The site you're attempting to sign into doesn't match the domain in the request. Proceed with caution." + }, + "SIWEDomainInvalidTitle": { + "message": "Deceptive site request." + }, + "SIWEDomainWarningBody": { + "message": "The website ($1) is asking you to sign in to the wrong domain. This may be a phishing attack.", + "description": "$1 represents the website domain" + }, + "SIWEDomainWarningLabel": { + "message": "Unsafe" + }, + "SIWELabelChainID": { + "message": "Chain ID:" + }, + "SIWELabelExpirationTime": { + "message": "Expires At:" + }, + "SIWELabelIssuedAt": { + "message": "Issued At:" + }, + "SIWELabelMessage": { + "message": "Message:" + }, + "SIWELabelNonce": { + "message": "Nonce:" + }, + "SIWELabelNotBefore": { + "message": "Not Before:" + }, + "SIWELabelRequestID": { + "message": "Request ID:" + }, + "SIWELabelResources": { + "message": "Resources: $1", + "description": "$1 represents the number of resources" + }, + "SIWELabelURI": { + "message": "URI:" + }, + "SIWELabelVersion": { + "message": "Version:" + }, + "SIWESiteRequestSubtitle": { + "message": "This site is requesting to sign in with" + }, + "SIWESiteRequestTitle": { + "message": "Sign-in request" + }, + "SIWEWarningSubtitle": { + "message": "To confirm you understand, check:" + }, + "SIWEWarningTitle": { + "message": "Are you sure?" + }, + "about": { + "message": "About" + }, + "accept": { + "message": "Accept" + }, + "acceptTermsOfUse": { + "message": "I have read and agree to the $1", + "description": "$1 is the `terms` message" + }, + "accessAndSpendNoticeNFT": { + "message": "$1 may access and spend this asset", + "description": "$1 is the url of the site requesting ability to spend" + }, + "accessYourWalletWithSRP": { + "message": "Access your wallet with your Secret Recovery Phrase" + }, + "accessYourWalletWithSRPDescription": { + "message": "MetaMask cannot recover your password. We will use your Secret Recovery Phrase to validate your ownership, restore your wallet and set up a new password. First, enter the Secret Recovery Phrase that you were given when you created your wallet. $1", + "description": "$1 is the words 'Learn More' from key 'learnMore', separated here so that it can be added as a link" + }, + "accessingYourCamera": { + "message": "Accessing your camera..." + }, + "account": { + "message": "Account" + }, + "accountActivity": { + "message": "Account activity" + }, + "accountActivityText": { + "message": "Select the accounts you want to be notified about:" + }, + "accountDetails": { + "message": "Account details" + }, + "accountIdenticon": { + "message": "Account identicon" + }, + "accountIsntConnectedToastText": { + "message": "$1 isn't connected to $2" + }, + "accountName": { + "message": "Account name" + }, + "accountNameDuplicate": { + "message": "This account name already exists", + "description": "This is an error message shown when the user enters a new account name that matches an existing account name" + }, + "accountNameReserved": { + "message": "This account name is reserved", + "description": "This is an error message shown when the user enters a new account name that is reserved for future use" + }, + "accountOptions": { + "message": "Account options" + }, + "accountSelectionRequired": { + "message": "You need to select an account!" + }, + "accountTypeNotSupported": { + "message": "Account type not supported" + }, + "accounts": { + "message": "Accounts" + }, + "accountsConnected": { + "message": "Accounts connected" + }, + "active": { + "message": "Active" + }, + "activity": { + "message": "Activity" + }, + "activityLog": { + "message": "Activity log" + }, + "add": { + "message": "Add" + }, + "addANetwork": { + "message": "Add a network" + }, + "addANetworkManually": { + "message": "Add a network manually" + }, + "addANickname": { + "message": "Add a nickname" + }, + "addAccount": { + "message": "Add account" + }, + "addAccountToMetaMask": { + "message": "Add account to MetaMask" + }, + "addAcquiredTokens": { + "message": "Add the tokens you've acquired using MetaMask" + }, + "addAlias": { + "message": "Add alias" + }, + "addBlockExplorer": { + "message": "Add a block explorer" + }, + "addContact": { + "message": "Add contact" + }, + "addCustomNetwork": { + "message": "Add custom network" + }, + "addEthereumChainConfirmationDescription": { + "message": "This will allow this network to be used within MetaMask." + }, + "addEthereumChainConfirmationRisks": { + "message": "MetaMask does not verify custom networks." + }, + "addEthereumChainConfirmationRisksLearnMore": { + "message": "Learn about $1.", + "description": "$1 is a link with text that is provided by the 'addEthereumChainConfirmationRisksLearnMoreLink' key" + }, + "addEthereumChainConfirmationRisksLearnMoreLink": { + "message": "scams and network security risks", + "description": "Link text for the 'addEthereumChainConfirmationRisksLearnMore' translation key" + }, + "addEthereumChainConfirmationTitle": { + "message": "Allow this site to add a network?" + }, + "addEthereumChainWarningModalHeader": { + "message": "Only add this RPC provider if you’re sure you can trust it. $1", + "description": "$1 is addEthereumChainWarningModalHeaderPartTwo passed separately so that it can be bolded" + }, + "addEthereumChainWarningModalHeaderPartTwo": { + "message": "Malicious providers may lie about the state of the blockchain and record your network activity." + }, + "addEthereumChainWarningModalListHeader": { + "message": "It's important that your provider is reliable, as it has the power to:" + }, + "addEthereumChainWarningModalListPointOne": { + "message": "See your accounts and IP address, and associate them together" + }, + "addEthereumChainWarningModalListPointThree": { + "message": "Show account balances and other on-chain states" + }, + "addEthereumChainWarningModalListPointTwo": { + "message": "Broadcast your transactions" + }, + "addEthereumChainWarningModalTitle": { + "message": "You are adding a new RPC provider for Ethereum Mainnet" + }, + "addFriendsAndAddresses": { + "message": "Add friends and addresses you trust" + }, + "addFromAListOfPopularNetworks": { + "message": "Add from a list of popular networks or add a network manually. Only interact with the entities you trust." + }, + "addHardwareWallet": { + "message": "Add hardware wallet" + }, + "addIPFSGateway": { + "message": "Add your preferred IPFS gateway" + }, + "addImportAccount": { + "message": "Add account or hardware wallet" + }, + "addMemo": { + "message": "Add memo" + }, + "addMoreNetworks": { + "message": "add more networks manually" + }, + "addNetwork": { + "message": "Add network" + }, + "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" + }, + "addNewAccount": { + "message": "Add a new Ethereum account" + }, + "addNewBitcoinAccount": { + "message": "Add a new Bitcoin account (Beta)" + }, + "addNewBitcoinTestnetAccount": { + "message": "Add a new Bitcoin account (Testnet)" + }, + "addNewToken": { + "message": "Add new token" + }, + "addNft": { + "message": "Add NFT" + }, + "addNfts": { + "message": "Add NFTs" + }, + "addRpcUrl": { + "message": "Add RPC URL" + }, + "addSnapAccountToggle": { + "message": "Enable \"Add account Snap (Beta)\"" + }, + "addSnapAccountsDescription": { + "message": "Turning on this feature will give you the option to add the new Beta account Snaps right from your account list. If you install an account Snap, remember that it is a third-party service." + }, + "addSuggestedNFTs": { + "message": "Add suggested NFTs" + }, + "addSuggestedTokens": { + "message": "Add suggested tokens" + }, + "addToken": { + "message": "Add token" + }, + "addTokenByContractAddress": { + "message": "Can’t find a token? You can manually add any token by pasting its address. Token contract addresses can be found on $1", + "description": "$1 is a blockchain explorer for a specific network, e.g. Etherscan for Ethereum" + }, + "addUrl": { + "message": "Add URL" + }, + "addingAccount": { + "message": "Adding account" + }, + "addingCustomNetwork": { + "message": "Adding Network" + }, + "addingTokens": { + "message": "Adding tokens" + }, + "additionalNetworks": { + "message": "Additional networks" + }, + "additionalRpcUrl": { + "message": "Additional RPC URL" + }, + "address": { + "message": "Address" + }, + "addressCopied": { + "message": "Address copied!" + }, + "advanced": { + "message": "Advanced" + }, + "advancedBaseGasFeeToolTip": { + "message": "When your transaction gets included in the block, any difference between your max base fee and the actual base fee will be refunded. Total amount is calculated as max base fee (in GWEI) * gas limit." + }, + "advancedConfiguration": { + "message": "Advanced configuration" + }, + "advancedDetailsDataDesc": { + "message": "Data" + }, + "advancedDetailsHexDesc": { + "message": "Hex" + }, + "advancedDetailsNonceDesc": { + "message": "Nonce" + }, + "advancedDetailsNonceTooltip": { + "message": "This is the transaction number of an account. Nonce for the first transaction is 0 and it increases in sequential order." + }, + "advancedGasFeeDefaultOptIn": { + "message": "Save these values as my default for the $1 network.", + "description": "$1 is the current network name." + }, + "advancedGasFeeModalTitle": { + "message": "Advanced gas fee" + }, + "advancedGasPriceTitle": { + "message": "Gas price" + }, + "advancedPriorityFeeToolTip": { + "message": "Priority fee (aka “miner tip”) goes directly to miners and incentivizes them to prioritize your transaction." + }, + "agreeTermsOfUse": { + "message": "I agree to MetaMask's $1", + "description": "$1 is the `terms` link" + }, + "airgapVault": { + "message": "AirGap Vault" + }, + "alert": { + "message": "Alert" + }, + "alertActionBuy": { + "message": "Buy ETH" + }, + "alertActionUpdateGas": { + "message": "Update gas limit" + }, + "alertActionUpdateGasFee": { + "message": "Update fee" + }, + "alertActionUpdateGasFeeLevel": { + "message": "Update gas options" + }, + "alertBannerMultipleAlertsDescription": { + "message": "If you approve this request, a third party known for scams might take all your assets." + }, + "alertBannerMultipleAlertsTitle": { + "message": "Multiple alerts!" + }, + "alertDisableTooltip": { + "message": "This can be changed in \"Settings > Alerts\"" + }, + "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." + }, + "alertMessageGasFeeLow": { + "message": "When choosing a low fee, expect slower transactions and longer wait times. For faster transactions, choose Market or Aggressive fee options." + }, + "alertMessageGasTooLow": { + "message": "To continue with this transaction, you’ll need to increase the gas limit to 21000 or higher." + }, + "alertMessageInsufficientBalance": { + "message": "You do not have enough ETH in your account to pay for transaction fees." + }, + "alertMessageNetworkBusy": { + "message": "Gas prices are high and estimates are less accurate." + }, + "alertMessageNoGasPrice": { + "message": "We can’t move forward with this transaction until you manually update the fee." + }, + "alertMessagePendingTransactions": { + "message": "This transaction won’t go through until a previous transaction is complete. Learn how to cancel or speed up a transaction." + }, + "alertMessageSignInDomainMismatch": { + "message": "The site making the request is not the site you’re signing into. This could be an attempt to steal your login credentials." + }, + "alertMessageSignInWrongAccount": { + "message": "This site is asking you to sign in using the wrong account." + }, + "alertMessageSigningOrSubmitting": { + "message": "This transaction will only go through once your previous transaction is complete." + }, + "alertModalAcknowledge": { + "message": "I have acknowledged the risk and still want to proceed" + }, + "alertModalDetails": { + "message": "Alert Details" + }, + "alertModalReviewAllAlerts": { + "message": "Review all alerts" + }, + "alertReasonGasEstimateFailed": { + "message": "Inaccurate fee" + }, + "alertReasonGasFeeLow": { + "message": "Slow speed" + }, + "alertReasonGasTooLow": { + "message": "Low gas limit" + }, + "alertReasonInsufficientBalance": { + "message": "Insufficient funds" + }, + "alertReasonNetworkBusy": { + "message": "Network is busy" + }, + "alertReasonNoGasPrice": { + "message": "Fee estimate unavailable" + }, + "alertReasonPendingTransactions": { + "message": "Pending transaction" + }, + "alertReasonSignIn": { + "message": "Suspicious sign-in request" + }, + "alertReasonWrongAccount": { + "message": "Wrong account" + }, + "alertSettingsUnconnectedAccount": { + "message": "Browsing a website with an unconnected account selected" + }, + "alertSettingsUnconnectedAccountDescription": { + "message": "This alert is shown in the popup when you are browsing a connected web3 site, but the currently selected account is not connected." + }, + "alertSettingsWeb3ShimUsage": { + "message": "When a website tries to use the removed window.web3 API" + }, + "alertSettingsWeb3ShimUsageDescription": { + "message": "This alert is shown in the popup when you are browsing a site that tries to use the removed window.web3 API, and may be broken as a result." + }, + "alerts": { + "message": "Alerts" + }, + "all": { + "message": "All" + }, + "allCustodianAccountsConnectedSubtitle": { + "message": "You have either already connected all your custodian accounts or don’t have any account to connect to MetaMask Institutional." + }, + "allCustodianAccountsConnectedTitle": { + "message": "No accounts available to connect" + }, + "allOfYour": { + "message": "All of your $1", + "description": "$1 is the symbol or name of the token that the user is approving spending" + }, + "allPermissions": { + "message": "All Permissions" + }, + "allTimeHigh": { + "message": "All time high" + }, + "allTimeLow": { + "message": "All time low" + }, + "allYourNFTsOf": { + "message": "All of your NFTs from $1", + "description": "$1 is a link to contract on the block explorer when we're not able to retrieve a erc721 or erc1155 name" + }, + "allow": { + "message": "Allow" + }, + "allowMetaMaskToDetectNFTs": { + "message": "Allow MetaMask to detect and display your NFTs with autodetection. You’ll be able to:" + }, + "allowMetaMaskToDetectTokens": { + "message": "Allow MetaMask to detect and display your tokens with autodetection. You’ll be able to:" + }, + "allowMmiToConnectToCustodian": { + "message": "This will allow MMI to connect to $1 to import your accounts." + }, + "allowNotifications": { + "message": "Allow notifications" + }, + "allowSpendToken": { + "message": "Give permission to access your $1?", + "description": "$1 is the symbol of the token that are requesting to spend" + }, + "allowWithdrawAndSpend": { + "message": "Allow $1 to withdraw and spend up to the following amount:", + "description": "The url of the site that requested permission to 'withdraw and spend'" + }, + "amount": { + "message": "Amount" + }, + "amountReceived": { + "message": "Amount Received" + }, + "amountSent": { + "message": "Amount Sent" + }, + "andForListItems": { + "message": "$1, and $2", + "description": "$1 is the first item, $2 is the last item in a list of items. Used in Snap Install Warning modal." + }, + "andForTwoItems": { + "message": "$1 and $2", + "description": "$1 is the first item, $2 is the second item. Used in Snap Install Warning modal." + }, + "announcements": { + "message": "Announcements" + }, + "appDescription": { + "message": "An Ethereum Wallet in your Browser", + "description": "The description of the application" + }, + "appName": { + "message": "MetaMask", + "description": "The name of the application" + }, + "appNameBeta": { + "message": "MetaMask Beta", + "description": "The name of the application (Beta)" + }, + "appNameFlask": { + "message": "MetaMask Flask", + "description": "The name of the application (Flask)" + }, + "appNameMmi": { + "message": "MetaMask Institutional", + "description": "The name of the application (MMI)" + }, + "approve": { + "message": "Approve spend limit" + }, + "approveAllTokensTitle": { + "message": "Allow access to and transfer of all your $1?", + "description": "$1 is the symbol of the token for which the user is granting approval" + }, + "approveAllTokensTitleWithoutSymbol": { + "message": "Allow access to and transfer all of your NFTs from $1?", + "description": "$1 a link to contract on the block explorer when we're not able to retrieve a erc721 or erc1155 name" + }, + "approveButtonText": { + "message": "Approve" + }, + "approveIncreaseAllowance": { + "message": "Increase $1 spending cap", + "description": "The token symbol that is being approved" + }, + "approveSpendingCap": { + "message": "Approve $1 spending cap", + "description": "The token symbol that is being approved" + }, + "approveTokenDescription": { + "message": "This allows a third party to access and transfer the following NFTs without further notice until you revoke its access." + }, + "approveTokenDescriptionWithoutSymbol": { + "message": "This allows a third party to access and transfer all of your NFTs from $1 without further notice until you revoke its access.", + "description": "$1 is a link to contract on the block explorer when we're not able to retrieve a erc721 or erc1155 name" + }, + "approveTokenTitle": { + "message": "Allow access to and transfer of your $1?", + "description": "$1 is the symbol of the token for which the user is granting approval" + }, + "approved": { + "message": "Approved" + }, + "approvedAsset": { + "message": "Approved asset" + }, + "approvedOn": { + "message": "Approved on $1", + "description": "$1 is the approval date for a permission" + }, + "approvedOnForAccounts": { + "message": "Approved on $1 for $2", + "description": "$1 is the approval date for a permission. $2 is the AvatarGroup component displaying account images." + }, + "areYouSure": { + "message": "Are you sure?" + }, + "asset": { + "message": "Asset" + }, + "assetOptions": { + "message": "Asset options" + }, + "attemptSendingAssets": { + "message": "You may lose your assets if you try to send them from another network. Transfer funds safely between networks by using a bridge." + }, + "attemptSendingAssetsWithPortfolio": { + "message": "You may lose your assets if you try to send them from another network. Transfer funds safely between networks by using a bridge, like $1" + }, + "attemptToCancelSwapForFree": { + "message": "Attempt to cancel swap for free" + }, + "attributes": { + "message": "Attributes" + }, + "attributions": { + "message": "Attributions" + }, + "auroraRpcDeprecationMessage": { + "message": "The Infura RPC URL is no longer supporting Aurora." + }, + "authorizedPermissions": { + "message": "You have authorized the following permissions" + }, + "autoDetectTokens": { + "message": "Autodetect tokens" + }, + "autoDetectTokensDescription": { + "message": "We use third-party APIs to detect and display new tokens sent to your wallet. Turn off if you don’t want the app to automatically pull data from those services. $1", + "description": "$1 is a link to a support article" + }, + "autoLockTimeLimit": { + "message": "Auto-lock timer (minutes)" + }, + "autoLockTimeLimitDescription": { + "message": "Set the idle time in minutes before MetaMask will become locked." + }, + "average": { + "message": "Average" + }, + "awaitingApproval": { + "message": "Awaiting approval..." + }, + "back": { + "message": "Back" + }, + "backup": { + "message": "Back up" + }, + "backupApprovalInfo": { + "message": "This secret code is required to recover your wallet in case you lose your device, forget your password, have to re-install MetaMask, or want to access your wallet on another device." + }, + "backupApprovalNotice": { + "message": "Back up your Secret Recovery Phrase to keep your wallet and funds secure." + }, + "backupKeyringSnapReminder": { + "message": "Be sure you can access any accounts created by this Snap on your own before removing it" + }, + "backupNow": { + "message": "Back up now" + }, + "backupUserData": { + "message": "Back up your data" + }, + "backupUserDataDescription": { + "message": "You can back up data like your contacts and preferences." + }, + "balance": { + "message": "Balance" + }, + "balanceOutdated": { + "message": "Balance may be outdated" + }, + "baseFee": { + "message": "Base fee" + }, + "basic": { + "message": "Basic" + }, + "basicConfigurationBannerCTA": { + "message": "Turn on basic functionality" + }, + "basicConfigurationBannerTitle": { + "message": "Basic functionality is off" + }, + "basicConfigurationDescription": { + "message": "MetaMask offers basic features like token details and gas settings through internet services. When you use internet services, your IP address is shared, in this case with MetaMask. This is just like when you visit any website. MetaMask uses this data temporarily and never sells your data. You can use a VPN or turn off these services, but it may affect your MetaMask experience. To learn more read our $1.", + "description": "$1 is to be replaced by the message for privacyMsg, and will link to https://consensys.io/privacy-policy" + }, + "basicConfigurationLabel": { + "message": "Basic functionality" + }, + "basicConfigurationModalCheckbox": { + "message": "I understand and want to continue" + }, + "basicConfigurationModalDisclaimerOff": { + "message": "This means you won't fully optimize your time on MetaMask. Basic features (like token details, optimal gas settings, and others) won't be available to you." + }, + "basicConfigurationModalDisclaimerOn": { + "message": "To optimize your time on MetaMask, you’ll need to turn on this feature. Basic functions (like token details, optimal gas settings, and others) are important to the web3 experience." + }, + "basicConfigurationModalHeadingOff": { + "message": "Turn off basic functionality" + }, + "basicConfigurationModalHeadingOn": { + "message": "Turn on basic functionality" + }, + "beCareful": { + "message": "Be careful" + }, + "beta": { + "message": "Beta" + }, + "betaHeaderText": { + "message": "This is a beta version. Please report bugs $1", + "description": "$1 represents the word 'here' in a hyperlink" + }, + "betaMetamaskInstitutionalVersion": { + "message": "MetaMask Institutional Beta Version" + }, + "betaMetamaskVersion": { + "message": "MetaMask Beta Version" + }, + "betaTerms": { + "message": "Beta Terms of use" + }, + "betaWalletCreationSuccessReminder1": { + "message": "MetaMask Beta can’t recover your Secret Recovery Phrase." + }, + "betaWalletCreationSuccessReminder2": { + "message": "MetaMask Beta will never ask you for your Secret Recovery Phrase." + }, + "billionAbbreviation": { + "message": "B", + "description": "Shortened form of 'billion'" + }, + "bitcoinActivityNotSupported": { + "message": "Bitcoin activity is not supported" + }, + "bitcoinSupportSectionTitle": { + "message": "Bitcoin" + }, + "bitcoinSupportToggleDescription": { + "message": "Turning on this feature will give you the option to add a Bitcoin Account to your MetaMask Extension derived from your existing Secret Recovery Phrase. This is an experimental Beta feature, so you should use it at your own risk. To give us feedback on this new Bitcoin experience, please fill out this $1.", + "description": "$1 is the link to a product feedback form" + }, + "bitcoinSupportToggleTitle": { + "message": "Enable \"Add a new Bitcoin account (Beta)\"" + }, + "bitcoinTestnetSupportToggleDescription": { + "message": "Turning on this feature will give you the option to add a Bitcoin Account for the test network." + }, + "bitcoinTestnetSupportToggleTitle": { + "message": "Enable \"Add a new Bitcoin account (Testnet)\"" + }, + "blockExplorerAccountAction": { + "message": "Account", + "description": "This is used with viewOnEtherscan and viewInExplorer e.g View Account in Explorer" + }, + "blockExplorerAssetAction": { + "message": "Asset", + "description": "This is used with viewOnEtherscan and viewInExplorer e.g View Asset in Explorer" + }, + "blockExplorerSwapAction": { + "message": "Swap", + "description": "This is used with viewOnEtherscan e.g View Swap on Etherscan" + }, + "blockExplorerUrl": { + "message": "Block explorer URL" + }, + "blockExplorerUrlDefinition": { + "message": "The URL used as the block explorer for this network." + }, + "blockExplorerView": { + "message": "View account at $1", + "description": "$1 replaced by URL for custom block explorer" + }, + "blockaid": { + "message": "Blockaid" + }, + "blockaidAlertInfo": { + "message": "We don't recommend proceeding with this request." + }, + "blockaidDescriptionApproveFarming": { + "message": "If you approve this request, a third party known for scams might take all your assets." + }, + "blockaidDescriptionBlurFarming": { + "message": "If you approve this request, someone can steal your assets listed on Blur." + }, + "blockaidDescriptionErrored": { + "message": "Because of an error, we couldn't check for security alerts. Only continue if you trust every address involved." + }, + "blockaidDescriptionMaliciousDomain": { + "message": "You're interacting with a malicious domain. If you approve this request, you might lose your assets." + }, + "blockaidDescriptionMightLoseAssets": { + "message": "If you approve this request, you might lose your assets." + }, + "blockaidDescriptionSeaportFarming": { + "message": "If you approve this request, someone can steal your assets listed on OpenSea." + }, + "blockaidDescriptionTransferFarming": { + "message": "If you approve this request, a third party known for scams will take all your assets." + }, + "blockaidDescriptionWarning": { + "message": "This could be a deceptive request. Only continue if you trust every address involved." + }, + "blockaidMessage": { + "message": "Privacy preserving - no data is shared with third parties. Available on Arbitrum, Avalanche, BNB chain, Ethereum Mainnet, Linea, Optimism, Polygon, Base and Sepolia." + }, + "blockaidTitleDeceptive": { + "message": "This is a deceptive request" + }, + "blockaidTitleMayNotBeSafe": { + "message": "Be careful" + }, + "blockaidTitleSuspicious": { + "message": "This is a suspicious request" + }, + "blockies": { + "message": "Blockies" + }, + "boughtFor": { + "message": "Bought for" + }, + "bridge": { + "message": "Bridge" + }, + "bridgeDontSend": { + "message": "Bridge, don't send" + }, + "browserNotSupported": { + "message": "Your browser is not supported..." + }, + "buildContactList": { + "message": "Build your contact list" + }, + "builtAroundTheWorld": { + "message": "MetaMask is designed and built around the world." + }, + "busy": { + "message": "Busy" + }, + "buyAndSell": { + "message": "Buy & Sell" + }, + "buyAsset": { + "message": "Buy $1", + "description": "$1 is the ticker symbol of a an asset the user is being prompted to purchase" + }, + "buyMoreAsset": { + "message": "Buy more $1", + "description": "$1 is the ticker symbol of a an asset the user is being prompted to purchase" + }, + "buyNow": { + "message": "Buy Now" + }, + "buyToken": { + "message": "Buy $1", + "description": "$1 is the token symbol" + }, + "bytes": { + "message": "Bytes" + }, + "canToggleInSettings": { + "message": "You can re-enable this notification in Settings > Alerts." + }, + "cancel": { + "message": "Cancel" + }, + "cancelPopoverTitle": { + "message": "Cancel transaction" + }, + "cancelSpeedUp": { + "message": "cancel or speed up a transaction." + }, + "cancelSpeedUpLabel": { + "message": "This gas fee will $1 the original.", + "description": "$1 is text 'replace' in bold" + }, + "cancelSpeedUpTransactionTooltip": { + "message": "To $1 a transaction the gas fee must be increased by at least 10% for it to be recognized by the network.", + "description": "$1 is string 'cancel' or 'speed up'" + }, + "cancelled": { + "message": "Cancelled" + }, + "chainId": { + "message": "Chain ID" + }, + "chainIdDefinition": { + "message": "The chain ID used to sign transactions for this network." + }, + "chainIdExistsErrorMsg": { + "message": "This Chain ID is currently used by the $1 network." + }, + "chainListReturnedDifferentTickerSymbol": { + "message": "This token symbol doesn't match the network name or chain ID entered. Many popular tokens use similar symbols, which scammers can use to trick you into sending them a more valuable token in return. Verify everything before you continue." + }, + "chooseYourNetwork": { + "message": "Choose your network" + }, + "chooseYourNetworkDescription": { + "message": "We use Infura as our remote procedure call (RPC) provider to offer the most reliable and private access to Ethereum data we can. You can choose your own RPC, but remember that any RPC will receive your IP address and Ethereum wallet to make transactions. Read our $1 to learn more about how Infura handles data.", + "description": "$1 is a link to the privacy policy" + }, + "chromeRequiredForHardwareWallets": { + "message": "You need to use MetaMask on Google Chrome in order to connect to your Hardware Wallet." + }, + "circulatingSupply": { + "message": "Circulating supply" + }, + "clear": { + "message": "Clear" + }, + "clearActivity": { + "message": "Clear activity and nonce data" + }, + "clearActivityButton": { + "message": "Clear activity tab data" + }, + "clearActivityDescription": { + "message": "This resets the account's nonce and erases data from the activity tab in your wallet. Only the current account and network will be affected. Your balances and incoming transactions won't change." + }, + "click": { + "message": "Click" + }, + "clickToConnectLedgerViaWebHID": { + "message": "Click here to connect your Ledger via WebHID", + "description": "Text that can be clicked to open a browser popup for connecting the ledger device via webhid" + }, + "clickToManuallyAdd": { + "message": "You can always add tokens manually." + }, + "close": { + "message": "Close" + }, + "closeExtension": { + "message": "Close extension" + }, + "closeWindowAnytime": { + "message": "You may close this window anytime." + }, + "coingecko": { + "message": "CoinGecko" + }, + "collectionName": { + "message": "Collection name" + }, + "comboNoOptions": { + "message": "No options found", + "description": "Default text shown in the combo field dropdown if no options." + }, + "configureSnapPopupDescription": { + "message": "You're now leaving MetaMask to configure this snap." + }, + "configureSnapPopupInstallDescription": { + "message": "You're now leaving MetaMask to install this snap." + }, + "configureSnapPopupInstallTitle": { + "message": "Install snap" + }, + "configureSnapPopupLink": { + "message": "Click this link to continue:" + }, + "configureSnapPopupTitle": { + "message": "Configure snap" + }, + "confirm": { + "message": "Confirm" + }, + "confirmAlertModalAcknowledgeMultiple": { + "message": "I have acknowledged the alerts and still want to proceed" + }, + "confirmAlertModalAcknowledgeSingle": { + "message": "I have acknowledged the alert and still want to proceed" + }, + "confirmAlertModalDetails": { + "message": "If you sign in, a third party known for scams might take all your assets. Please review the alerts before you proceed." + }, + "confirmAlertModalTitle": { + "message": "Your assets may be at risk" + }, + "confirmConnectCustodianRedirect": { + "message": "We will redirect you to $1 upon clicking continue." + }, + "confirmConnectCustodianText": { + "message": "To connect your accounts log into your $1 account and click on the 'connect to MMI' button." + }, + "confirmConnectionTitle": { + "message": "Confirm connection to $1" + }, + "confirmDeletion": { + "message": "Confirm deletion" + }, + "confirmFieldPaymaster": { + "message": "Fee paid by" + }, + "confirmFieldTooltipPaymaster": { + "message": "The fee for this transaction will be paid by the paymaster smart contract." + }, + "confirmPassword": { + "message": "Confirm password" + }, + "confirmRecoveryPhrase": { + "message": "Confirm Secret Recovery Phrase" + }, + "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." + }, + "confirmTitleDescSIWESignature": { + "message": "A site wants you to sign in to prove you own this account." + }, + "confirmTitleDescSignature": { + "message": "Only confirm this message if you approve the content and trust the requesting site." + }, + "confirmTitlePermitSignature": { + "message": "Spending cap request" + }, + "confirmTitleSIWESignature": { + "message": "Sign-in request" + }, + "confirmTitleSignature": { + "message": "Signature request" + }, + "confirmTitleTransaction": { + "message": "Transaction request" + }, + "confirmed": { + "message": "Confirmed" + }, + "confusableUnicode": { + "message": "'$1' is similar to '$2'." + }, + "confusableZeroWidthUnicode": { + "message": "Zero-width character found." + }, + "confusingEnsDomain": { + "message": "We have detected a confusable character in the ENS name. Check the ENS name to avoid a potential scam." + }, + "connect": { + "message": "Connect" + }, + "connectAccount": { + "message": "Connect account" + }, + "connectAccountOrCreate": { + "message": "Connect account or create new" + }, + "connectAccounts": { + "message": "Connect accounts" + }, + "connectCustodialAccountMenu": { + "message": "Connect Custodial Account" + }, + "connectCustodialAccountMsg": { + "message": "Please choose the custodian you want to connect in order to add or refresh a token." + }, + "connectCustodialAccountTitle": { + "message": "Custodial Accounts" + }, + "connectCustodianAccounts": { + "message": "Connect $1 accounts" + }, + "connectManually": { + "message": "Manually connect to current site" + }, + "connectMoreAccounts": { + "message": "Connect more accounts" + }, + "connectSnap": { + "message": "Connect $1", + "description": "$1 is the snap for which a connection is being requested." + }, + "connectWithMetaMask": { + "message": "Connect with MetaMask" + }, + "connectedAccounts": { + "message": "Connected accounts" + }, + "connectedAccountsDescriptionPlural": { + "message": "You have $1 accounts connected to this site.", + "description": "$1 is the number of accounts" + }, + "connectedAccountsDescriptionSingular": { + "message": "You have 1 account connected to this site." + }, + "connectedAccountsEmptyDescription": { + "message": "MetaMask is not connected to this site. To connect to a web3 site, find and click the connect button." + }, + "connectedAccountsListTooltip": { + "message": "$1 can see the account balance, address, activity, and suggest transactions to approve for connected accounts.", + "description": "$1 is the origin name" + }, + "connectedAccountsToast": { + "message": "Connected accounts updated" + }, + "connectedSites": { + "message": "Connected sites" + }, + "connectedSitesDescription": { + "message": "$1 is connected to these sites. They can view your account address.", + "description": "$1 is the account name" + }, + "connectedSitesEmptyDescription": { + "message": "$1 is not connected to any sites.", + "description": "$1 is the account name" + }, + "connectedSnapAndNoAccountDescription": { + "message": "MetaMask is connected to this site, but no accounts are connected yet" + }, + "connectedWith": { + "message": "Connected with" + }, + "connecting": { + "message": "Connecting" + }, + "connectingTo": { + "message": "Connecting to $1" + }, + "connectingToDeprecatedNetwork": { + "message": "'$1' is being phased out and may not work. Try another network." + }, + "connectingToGoerli": { + "message": "Connecting to Goerli test network" + }, + "connectingToLineaGoerli": { + "message": "Connecting to Linea Goerli test network" + }, + "connectingToLineaMainnet": { + "message": "Connecting to Linea Mainnet" + }, + "connectingToLineaSepolia": { + "message": "Connecting to Linea Sepolia test network" + }, + "connectingToMainnet": { + "message": "Connecting to Ethereum Mainnet" + }, + "connectingToSepolia": { + "message": "Connecting to Sepolia test network" + }, + "connectionFailed": { + "message": "Connection failed" + }, + "connectionFailedDescription": { + "message": "Fetching of $1 failed, check your network and try again.", + "description": "$1 is the name of the snap being fetched." + }, + "connectionRequest": { + "message": "Connection request" + }, + "contactUs": { + "message": "Contact us" + }, + "contacts": { + "message": "Contacts" + }, + "contentFromSnap": { + "message": "Content from $1", + "description": "$1 represents the name of the snap" + }, + "continue": { + "message": "Continue" + }, + "continueMmiOnboarding": { + "message": "Continue MetaMask Institutional onboarding" + }, + "continueToWallet": { + "message": "Continue to wallet" + }, + "contract": { + "message": "Contract" + }, + "contractAddress": { + "message": "Contract address" + }, + "contractAddressError": { + "message": "You are sending tokens to the token's contract address. This may result in the loss of these tokens." + }, + "contractDeployment": { + "message": "Contract deployment" + }, + "contractDescription": { + "message": "To protect yourself against scammers, take a moment to verify third-party details." + }, + "contractInteraction": { + "message": "Contract interaction" + }, + "contractNFT": { + "message": "NFT contract" + }, + "contractRequestingAccess": { + "message": "Third party requesting access" + }, + "contractRequestingSignature": { + "message": "Third party requesting signature" + }, + "contractRequestingSpendingCap": { + "message": "Third party requesting spending cap" + }, + "contractTitle": { + "message": "Third-party details" + }, + "contractToken": { + "message": "Token contract" + }, + "convertTokenToNFTDescription": { + "message": "We've detected that this asset is an NFT. MetaMask now has full native support for NFTs. Would you like to remove it from your token list and add it as an NFT?" + }, + "convertTokenToNFTExistDescription": { + "message": "We’ve detected that this asset has been added as an NFT. Would you like to remove it from your token list?" + }, + "coolWallet": { + "message": "CoolWallet" + }, + "copiedExclamation": { + "message": "Copied." + }, + "copyAddress": { + "message": "Copy address to clipboard" + }, + "copyPrivateKey": { + "message": "Copy private key" + }, + "copyRawTransactionData": { + "message": "Copy raw transaction data" + }, + "copyToClipboard": { + "message": "Copy to clipboard" + }, + "copyTransactionId": { + "message": "Copy transaction ID" + }, + "create": { + "message": "Create" + }, + "createNewWallet": { + "message": "Create a new wallet" + }, + "createPassword": { + "message": "Create password" + }, + "createSnapAccountDescription": { + "message": "$1 wants to add a new account to MetaMask." + }, + "createSnapAccountTitle": { + "message": "Create account" + }, + "creatorAddress": { + "message": "Creator address" + }, + "crossChainSwapsLink": { + "message": "Swap across networks with MetaMask Portfolio" + }, + "cryptoCompare": { + "message": "CryptoCompare" + }, + "currencyConversion": { + "message": "Currency conversion" + }, + "currencyRateCheckToggle": { + "message": "Show balance and token price checker" + }, + "currencyRateCheckToggleDescription": { + "message": "We use $1 and $2 APIs to display your balance and token price. $3", + "description": "$1 represents Coingecko, $2 represents CryptoCompare and $3 represents Privacy Policy" + }, + "currencySymbol": { + "message": "Currency symbol" + }, + "currencySymbolDefinition": { + "message": "The ticker symbol displayed for this network’s currency." + }, + "currentAccountNotConnected": { + "message": "Your current account is not connected" + }, + "currentExtension": { + "message": "Current extension page" + }, + "currentLanguage": { + "message": "Current language" + }, + "currentRpcUrlDeprecated": { + "message": "The current rpc url for this network has been deprecated." + }, + "currentTitle": { + "message": "Current:" + }, + "currentlyUnavailable": { + "message": "Unavailable on this network" + }, + "curveHighGasEstimate": { + "message": "Aggressive gas estimate graph" + }, + "curveLowGasEstimate": { + "message": "Low gas estimate graph" + }, + "curveMediumGasEstimate": { + "message": "Market gas estimate graph" + }, + "custodian": { + "message": "Custodian" + }, + "custodianAccountAddedDesc": { + "message": "You can now use your accounts in MetaMask Institutional." + }, + "custodianAccountAddedTitle": { + "message": "Selected $1 accounts have been added." + }, + "custodianQRCodeScan": { + "message": "Scan QR code with your $1 mobile app" + }, + "custodianQRCodeScanDescription": { + "message": "Or log into your $1 account and click on the 'Connect to MMI' button" + }, + "custodianReplaceRefreshTokenChangedFailed": { + "message": "Please go to $1 and click the 'Connect to MMI' button within their user interface to connect your accounts to MMI again." + }, + "custodianReplaceRefreshTokenChangedSubtitle": { + "message": "You can now use your custodian accounts in MetaMask Institutional." + }, + "custodianReplaceRefreshTokenChangedTitle": { + "message": "Your custodian token has been refreshed" + }, + "custodianReplaceRefreshTokenSubtitle": { + "message": "This is will replace the custodian token for the following address:" + }, + "custodianReplaceRefreshTokenTitle": { + "message": "Replace custodian token" + }, + "custodyDeeplinkDescription": { + "message": "Approve the transaction in the $1 app. Once all required custody approvals have been performed the transaction will complete. Check your $1 app for status." + }, + "custodyRefreshTokenModalDescription": { + "message": "Please go to $1 and click the 'Connect to MMI' button within their user interface to connect your accounts to MMI again." + }, + "custodyRefreshTokenModalDescription1": { + "message": "Your custodian issues a token that authenticates the MetaMask Institutional extension, allowing you to connect your accounts." + }, + "custodyRefreshTokenModalDescription2": { + "message": "This token expires after a certain period for security reasons. This requires you to reconnect to MMI." + }, + "custodyRefreshTokenModalSubtitle": { + "message": "Why am I seeing this?" + }, + "custodyRefreshTokenModalTitle": { + "message": "Your custodian session has expired" + }, + "custodySessionExpired": { + "message": "Custodian session expired." + }, + "custodyWrongChain": { + "message": "This account is not set up for use with $1" + }, + "custom": { + "message": "Advanced" + }, + "customContentSearch": { + "message": "Search for a previously added network" + }, + "customGasSettingToolTipMessage": { + "message": "Use $1 to customize the gas price. This can be confusing if you aren’t familiar. Interact at your own risk.", + "description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold font-weight" + }, + "customSpendLimit": { + "message": "Custom spend limit" + }, + "customSpendingCap": { + "message": "Custom spending cap" + }, + "customToken": { + "message": "Custom token" + }, + "customTokenWarningInNonTokenDetectionNetwork": { + "message": "Token detection is not available on this network yet. Please import token manually and make sure you trust it. Learn about $1" + }, + "customTokenWarningInTokenDetectionNetwork": { + "message": "Anyone can create a token, including creating fake versions of existing tokens. Learn about $1" + }, + "customTokenWarningInTokenDetectionNetworkWithTDOFF": { + "message": "Make sure you trust a token before you import it. Learn how to avoid $1. You can also enable token detection $2." + }, + "customerSupport": { + "message": "customer support" + }, + "customizeYourNotifications": { + "message": "Customize your notifications" + }, + "customizeYourNotificationsText": { + "message": "Turn on the types of notifications you want to receive:" + }, + "dappRequestedSpendingCap": { + "message": "Site requested spending cap" + }, + "dappSuggested": { + "message": "Site suggested" + }, + "dappSuggestedGasSettingToolTipMessage": { + "message": "$1 has suggested this price.", + "description": "$1 is url for the dapp that has suggested gas settings" + }, + "dappSuggestedHigh": { + "message": "Site suggested" + }, + "dappSuggestedHighShortLabel": { + "message": "Site (high)" + }, + "dappSuggestedShortLabel": { + "message": "Site" + }, + "dappSuggestedTooltip": { + "message": "$1 has recommended this price.", + "description": "$1 represents the Dapp's origin" + }, + "darkTheme": { + "message": "Dark" + }, + "data": { + "message": "Data" + }, + "dataCollectionForMarketing": { + "message": "Data collection for marketing" + }, + "dataCollectionForMarketingDescription": { + "message": "We'll use MetaMetrics to learn how you interact with our marketing communications. We may share relevant news (like product features and other materials)." + }, + "dataCollectionWarningPopoverButton": { + "message": "Okay" + }, + "dataCollectionWarningPopoverDescription": { + "message": "You turned off data collection for our marketing purposes. This only applies to this device. If you use MetaMask on other devices, make sure to opt out there as well." + }, + "dataHex": { + "message": "Hex" + }, + "dataUnavailable": { + "message": "data unavailable" + }, + "dateCreated": { + "message": "Date created" + }, + "dcent": { + "message": "D'Cent" + }, + "decimal": { + "message": "Token decimal" + }, + "decimalsMustZerotoTen": { + "message": "Decimals must be at least 0, and not over 36." + }, + "decrypt": { + "message": "Decrypt" + }, + "decryptCopy": { + "message": "Copy encrypted message" + }, + "decryptInlineError": { + "message": "This message cannot be decrypted due to error: $1", + "description": "$1 is error message" + }, + "decryptMessageNotice": { + "message": "$1 would like to read this message to complete your action", + "description": "$1 is the web3 site name" + }, + "decryptMetamask": { + "message": "Decrypt message" + }, + "decryptRequest": { + "message": "Decrypt request" + }, + "defaultRpcUrl": { + "message": "Default RPC URL" + }, + "delete": { + "message": "Delete" + }, + "deleteContact": { + "message": "Delete contact" + }, + "deleteNetwork": { + "message": "Delete network?" + }, + "deleteNetworkIntro": { + "message": "If you delete this network, you will need to add it again to view your assets in this network" + }, + "deleteNetworkTitle": { + "message": "Delete $1 network?", + "description": "$1 represents the name of the network" + }, + "deleteRpcUrl": { + "message": "Delete RPC URL" + }, + "deposit": { + "message": "Deposit" + }, + "deprecatedGoerliNtwrkMsg": { + "message": "Because of updates to the Ethereum system, the Goerli test network will be phased out soon." + }, + "deprecatedNetwork": { + "message": "This network is deprecated" + }, + "deprecatedNetworkButtonMsg": { + "message": "Got it" + }, + "deprecatedNetworkDescription": { + "message": "The network you're trying to connect to is no longer supported by Metamask. $1" + }, + "description": { + "message": "Description" + }, + "descriptionFromSnap": { + "message": "Description from $1", + "description": "$1 represents the name of the snap" + }, + "details": { + "message": "Details" + }, + "developerOptions": { + "message": "Developer Options" + }, + "developerOptionsEnableConfirmationsRedesignDescription": { + "message": "Enables or disables the confirmations redesign feature currently in development" + }, + "developerOptionsEnableConfirmationsRedesignTitle": { + "message": "Confirmations Redesign" + }, + "developerOptionsNetworkMenuRedesignDescription": { + "message": "Toggles the new design of the Networks menu" + }, + "developerOptionsNetworkMenuRedesignTitle": { + "message": "Network Menu Redesign" + }, + "developerOptionsResetStatesAnnouncementsDescription": { + "message": "Resets isShown boolean to false for all announcements. Announcements are the notifications shown in the What's New popup modal." + }, + "developerOptionsResetStatesOnboarding": { + "message": "Resets various states related to onboarding and redirects to the \"Secure Your Wallet\" onboarding page." + }, + "developerOptionsServiceWorkerKeepAlive": { + "message": "Results in a timestamp being continuously saved to session.storage" + }, + "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" + }, + "disconnect": { + "message": "Disconnect" + }, + "disconnectAllAccounts": { + "message": "Disconnect all accounts" + }, + "disconnectAllAccountsConfirmationDescription": { + "message": "Are you sure you want to disconnect? You may lose site functionality." + }, + "disconnectAllAccountsText": { + "message": "accounts" + }, + "disconnectAllSnapsText": { + "message": "Snaps" + }, + "disconnectAllText": { + "message": "If you disconnect your $1 from $2, you'll need to reconnect to use them again.", + "description": "$1 will map to `disconnectAllAccountsText` or `disconnectAllSnapsText`, $2 represents the website hostname" + }, + "disconnectAllTitle": { + "message": "Disconnect all $1", + "description": "$1 will map to `disconnectAllAccountsText` or `disconnectAllSnapsText`" + }, + "disconnectPrompt": { + "message": "Disconnect $1" + }, + "disconnectThisAccount": { + "message": "Disconnect this account" + }, + "disconnectedAllAccountsToast": { + "message": "All accounts disconnected from $1", + "description": "$1 is name of the dapp`" + }, + "disconnectedSingleAccountToast": { + "message": "$1 disconnected from $2", + "description": "$1 is name of the name and $2 represents the dapp name`" + }, + "discoverSnaps": { + "message": "Discover Snaps", + "description": "Text that links to the Snaps website. Displayed in a banner on Snaps list page in settings." + }, + "dismiss": { + "message": "Dismiss" + }, + "dismissReminderDescriptionField": { + "message": "Turn this on to dismiss the Secret Recovery Phrase backup reminder message. We highly recommend that you back up your Secret Recovery Phrase to avoid loss of funds" + }, + "dismissReminderField": { + "message": "Dismiss Secret Recovery Phrase backup reminder" + }, + "displayNftMedia": { + "message": "Display NFT media" + }, + "displayNftMediaDescription": { + "message": "Displaying NFT media and data exposes your IP address to OpenSea or other third parties. This can allow attackers to associate your IP address with your Ethereum address. NFT autodetection relies on this setting, and won't be available when this is turned off." + }, + "diveStraightIntoUsingYourNFTs": { + "message": "Dive straight into using your NFTs" + }, + "diveStraightIntoUsingYourTokens": { + "message": "Dive straight into using your tokens" + }, + "doNotShare": { + "message": "Do not share this with anyone" + }, + "domain": { + "message": "Domain" + }, + "domainNotSupportedOnNetwork": { + "message": "Network does not support domain lookup" + }, + "done": { + "message": "Done" + }, + "dontShowThisAgain": { + "message": "Don't show this again" + }, + "downArrow": { + "message": "down arrow" + }, + "downloadGoogleChrome": { + "message": "Download Google Chrome" + }, + "downloadNow": { + "message": "Download Now" + }, + "downloadStateLogs": { + "message": "Download state logs" + }, + "dragAndDropBanner": { + "message": "You can drag networks to reorder them. " + }, + "dropped": { + "message": "Dropped" + }, + "edit": { + "message": "Edit" + }, + "editANickname": { + "message": "Edit nickname" + }, + "editAddressNickname": { + "message": "Edit address nickname" + }, + "editCancellationGasFeeModalTitle": { + "message": "Edit cancellation gas fee" + }, + "editContact": { + "message": "Edit contact" + }, + "editGasFeeModalTitle": { + "message": "Edit gas fee" + }, + "editGasLimitOutOfBounds": { + "message": "Gas limit must be at least $1" + }, + "editGasLimitOutOfBoundsV2": { + "message": "Gas limit must be greater than $1 and less than $2", + "description": "$1 is the minimum limit for gas and $2 is the maximum limit" + }, + "editGasLimitTooltip": { + "message": "Gas limit is the maximum units of gas you are willing to use. Units of gas are a multiplier to “Max priority fee” and “Max fee”." + }, + "editGasMaxBaseFeeGWEIImbalance": { + "message": "Max base fee cannot be lower than priority fee" + }, + "editGasMaxBaseFeeHigh": { + "message": "Max base fee is higher than necessary" + }, + "editGasMaxBaseFeeLow": { + "message": "Max base fee is low for current network conditions" + }, + "editGasMaxFeeHigh": { + "message": "Max fee is higher than necessary" + }, + "editGasMaxFeeLow": { + "message": "Max fee too low for network conditions" + }, + "editGasMaxFeePriorityImbalance": { + "message": "Max fee cannot be lower than max priority fee" + }, + "editGasMaxPriorityFeeBelowMinimum": { + "message": "Max priority fee must be greater than 0 GWEI" + }, + "editGasMaxPriorityFeeBelowMinimumV2": { + "message": "Priority fee must be greater than 0." + }, + "editGasMaxPriorityFeeHigh": { + "message": "Max priority fee is higher than necessary. You may pay more than needed." + }, + "editGasMaxPriorityFeeHighV2": { + "message": "Priority fee is higher than necessary. You may pay more than needed" + }, + "editGasMaxPriorityFeeLow": { + "message": "Max priority fee is low for current network conditions" + }, + "editGasMaxPriorityFeeLowV2": { + "message": "Priority fee is low for current network conditions" + }, + "editGasPriceTooLow": { + "message": "Gas price must be greater than 0" + }, + "editGasPriceTooltip": { + "message": "This network requires a “Gas price” field when submitting a transaction. Gas price is the amount you will pay pay per unit of gas." + }, + "editGasSubTextAmountLabel": { + "message": "Max amount:", + "description": "This is meant to be used as the $1 substitution editGasSubTextAmount" + }, + "editGasSubTextFeeLabel": { + "message": "Max fee:" + }, + "editGasTitle": { + "message": "Edit priority" + }, + "editGasTooLow": { + "message": "Unknown processing time" + }, + "editNetworkLink": { + "message": "edit the original network" + }, + "editNonceField": { + "message": "Edit nonce" + }, + "editNonceMessage": { + "message": "This is an advanced feature, use cautiously." + }, + "editPermission": { + "message": "Edit permission" + }, + "editSpeedUpEditGasFeeModalTitle": { + "message": "Edit speed up gas fee" + }, + "effortlesslyNavigateYourDigitalAssets": { + "message": "Effortlessly navigate your digital assets" + }, + "enable": { + "message": "Enable" + }, + "enableAutoDetect": { + "message": " Enable autodetect" + }, + "enableFromSettings": { + "message": " Enable it from Settings." + }, + "enableNftAutoDetection": { + "message": "Enable NFT autodetection" + }, + "enableSnap": { + "message": "Enable" + }, + "enableToken": { + "message": "enable $1", + "description": "$1 is a token symbol, e.g. ETH" + }, + "enableTokenAutoDetection": { + "message": "Enable token autodetection" + }, + "enable_auto_detection_toggle_automatically": { + "message": "Users with the Basic Functionality toggle on will have this automatically turned on in the Metamask Extension v12.3.0" + }, + "enabled": { + "message": "Enabled" + }, + "enabledNetworks": { + "message": "Enabled networks" + }, + "encryptionPublicKeyNotice": { + "message": "$1 would like your public encryption key. By consenting, this site will be able to compose encrypted messages to you.", + "description": "$1 is the web3 site name" + }, + "encryptionPublicKeyRequest": { + "message": "Request encryption public key" + }, + "endpointReturnedDifferentChainId": { + "message": "The RPC URL you have entered returned a different chain ID ($1). Please update the Chain ID to match the RPC URL of the network you are trying to add.", + "description": "$1 is the return value of eth_chainId from an RPC endpoint" + }, + "enhancedTokenDetectionAlertMessage": { + "message": "Enhanced token detection is currently available on $1. $2" + }, + "ensDomainsSettingDescriptionIntroduction": { + "message": "MetaMask lets you see ENS domains right in your browser's address bar. Here's how it works:" + }, + "ensDomainsSettingDescriptionOutroduction": { + "message": "Keep in mind that using this feature exposes your IP address to IPFS third-party services." + }, + "ensDomainsSettingDescriptionPart1": { + "message": "MetaMask checks with Ethereum's ENS contract to find the code connected to the ENS name." + }, + "ensDomainsSettingDescriptionPart2": { + "message": "If the code links to IPFS, you can see the content associated with it (usually a website)." + }, + "ensDomainsSettingTitle": { + "message": "Show ENS domains in address bar" + }, + "ensIllegalCharacter": { + "message": "Illegal character for ENS." + }, + "ensRegistrationError": { + "message": "Error in ENS name registration" + }, + "ensUnknownError": { + "message": "ENS lookup failed." + }, + "enterANumber": { + "message": "Enter a number" + }, + "enterCustodianToken": { + "message": "Enter your $1 token or add a new token" + }, + "enterMaxSpendLimit": { + "message": "Enter max spend limit" + }, + "enterOptionalPassword": { + "message": "Enter optional password" + }, + "enterPasswordContinue": { + "message": "Enter password to continue" + }, + "enterTokenNameOrAddress": { + "message": "Enter token name or paste address" + }, + "enterYourPassword": { + "message": "Enter your password" + }, + "errorCode": { + "message": "Code: $1", + "description": "Displayed error code for debugging purposes. $1 is the error code" + }, + "errorDetails": { + "message": "Error details", + "description": "Title for collapsible section that displays error details for debugging purposes" + }, + "errorGettingSafeChainList": { + "message": "Error while getting safe chain list, please continue with caution." + }, + "errorMessage": { + "message": "Message: $1", + "description": "Displayed error message for debugging purposes. $1 is the error message" + }, + "errorName": { + "message": "Code: $1", + "description": "Displayed error name for debugging purposes. $1 is the error name" + }, + "errorPageMessage": { + "message": "Try again by reloading the page, or contact support $1.", + "description": "Message displayed on generic error page in the fullscreen or notification UI, $1 is a clickable link with text defined by the 'here' key. The link will open to a form where users can file support tickets." + }, + "errorPagePopupMessage": { + "message": "Try again by closing and reopening the popup, or contact support $1.", + "description": "Message displayed on generic error page in the popup UI, $1 is a clickable link with text defined by the 'here' key. The link will open to a form where users can file support tickets." + }, + "errorPageTitle": { + "message": "MetaMask encountered an error", + "description": "Title of generic error page" + }, + "errorStack": { + "message": "Stack:", + "description": "Title for error stack, which is displayed for debugging purposes" + }, + "errorWhileConnectingToRPC": { + "message": "Error while connecting to the custom network." + }, + "errorWithSnap": { + "message": "Error with $1", + "description": "$1 represents the name of the snap" + }, + "estimatedFee": { + "message": "Estimated fee" + }, + "estimatedFeeTooltip": { + "message": "Amount paid to process the transaction on network." + }, + "ethGasPriceFetchWarning": { + "message": "Backup gas price is provided as the main gas estimation service is unavailable right now." + }, + "ethereumProviderAccess": { + "message": "Grant Ethereum provider access to $1", + "description": "The parameter is the name of the requesting origin" + }, + "ethereumPublicAddress": { + "message": "Ethereum public address" + }, + "etherscan": { + "message": "Etherscan" + }, + "etherscanView": { + "message": "View account on Etherscan" + }, + "etherscanViewOn": { + "message": "View on Etherscan" + }, + "existingChainId": { + "message": "The information you have entered is associated with an existing chain ID." + }, + "existingRequestsBannerAlertDesc": { + "message": "To view and confirm your most recent request, you'll need to approve or reject existing requests first." + }, + "existingRpcUrl": { + "message": "This URL is associated with another chain ID." + }, + "expandView": { + "message": "Expand view" + }, + "experimental": { + "message": "Experimental" + }, + "extendWalletWithSnaps": { + "message": "Explore community-built Snaps to customize your web3 experience", + "description": "Banner description displayed on Snaps list page in Settings when less than 6 Snaps is installed." + }, + "extensionInsallCompleteDescription": { + "message": "Return to the MetaMask Institutional product onboarding to connect your custodial or self-custodial accounts." + }, + "extensionInsallCompleteTitle": { + "message": "Extension install complete" + }, + "externalExtension": { + "message": "External extension" + }, + "externalNameSourcesSetting": { + "message": "Proposed nicknames" + }, + "externalNameSourcesSettingDescription": { + "message": "We’ll fetch proposed nicknames for addresses you interact with from third-party sources like Etherscan, Infura, and Lens Protocol. These sources will be able to see those addresses and your IP address. Your account address won’t be exposed to third parties." + }, + "failed": { + "message": "Failed" + }, + "failedToFetchChainId": { + "message": "Could not fetch chain ID. Is your RPC URL correct?" + }, + "failedToFetchTickerSymbolData": { + "message": "Ticker symbol verification data is currently unavailable, make sure that the symbol you have entered is correct. It will impact the conversion rates that you see for this network" + }, + "failureMessage": { + "message": "Something went wrong, and we were unable to complete the action" + }, + "fast": { + "message": "Fast" + }, + "feeAssociatedRequest": { + "message": "A fee is associated with this request." + }, + "feeDetails": { + "message": "Fee details" + }, + "fiat": { + "message": "Fiat", + "description": "Exchange type" + }, + "fileImportFail": { + "message": "File import not working? Click here!", + "description": "Helps user import their account from a JSON file" + }, + "findTheRightChainId": { + "message": "Find the right one on:" + }, + "flaskWelcomeUninstall": { + "message": "you should uninstall this extension", + "description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded." + }, + "flaskWelcomeWarning1": { + "message": "Flask is for developers to experiment with new unstable APIs. Unless you are a developer or beta tester, $1.", + "description": "This is a warning shown on the Flask Welcome screen, intended to encourage non-developers not to proceed any further. $1 is the bolded message 'flaskWelcomeUninstall'" + }, + "flaskWelcomeWarning2": { + "message": "We do not guarantee the safety or stability of this extension. The new APIs offered by Flask are not hardened against phishing attacks, meaning that any site or snap that requires Flask might be a malicious attempt to steal your assets.", + "description": "This explains the risks of using MetaMask Flask" + }, + "flaskWelcomeWarning3": { + "message": "All Flask APIs are experimental. They may be changed or removed without notice, or they might stay on Flask indefinitely without ever being migrated to stable MetaMask. Use them at your own risk.", + "description": "This message warns developers about unstable Flask APIs" + }, + "flaskWelcomeWarning4": { + "message": "Make sure to disable your regular MetaMask extension when using Flask.", + "description": "This message calls to pay attention about multiple versions of MetaMask running on the same site (Flask + Prod)" + }, + "flaskWelcomeWarningAcceptButton": { + "message": "I accept the risks", + "description": "this text is shown on a button, which the user presses to confirm they understand the risks of using Flask" + }, + "floatAmountToken": { + "message": "Token amount must be an integer" + }, + "followUsOnTwitter": { + "message": "Follow us on Twitter" + }, + "forbiddenIpfsGateway": { + "message": "Forbidden IPFS Gateway: Please specify a CID gateway" + }, + "forgetDevice": { + "message": "Forget this device" + }, + "forgotPassword": { + "message": "Forgot password?" + }, + "form": { + "message": "form" + }, + "from": { + "message": "From" + }, + "fromAddress": { + "message": "From: $1", + "description": "$1 is the address to include in the From label. It is typically shortened first using shortenAddress" + }, + "fromTokenLists": { + "message": "From token lists: $1" + }, + "function": { + "message": "Function: $1" + }, + "functionApprove": { + "message": "Function: Approve" + }, + "functionSetApprovalForAll": { + "message": "Function: SetApprovalForAll" + }, + "functionType": { + "message": "Function type" + }, + "fundYourWallet": { + "message": "Fund your wallet" + }, + "fundYourWalletDescription": { + "message": "Get started by adding some $1 to your wallet.", + "description": "$1 is the token symbol" + }, + "gas": { + "message": "Gas" + }, + "gasDisplayAcknowledgeDappButtonText": { + "message": "Edit suggested gas fee" + }, + "gasDisplayDappWarning": { + "message": "This gas fee has been suggested by $1. Overriding this may cause a problem with your transaction. Please reach out to $1 if you have questions.", + "description": "$1 represents the Dapp's origin" + }, + "gasIsETH": { + "message": "Gas is $1 " + }, + "gasLimit": { + "message": "Gas limit" + }, + "gasLimitInfoTooltipContent": { + "message": "Gas limit is the maximum amount of units of gas you are willing to spend." + }, + "gasLimitRecommended": { + "message": "Recommended gas limit is $1. If the gas limit is less than that, it may fail." + }, + "gasLimitTooLow": { + "message": "Gas limit must be at least 21000" + }, + "gasLimitTooLowWithDynamicFee": { + "message": "Gas limit must be at least $1", + "description": "$1 is the custom gas limit, in decimal." + }, + "gasLimitV2": { + "message": "Gas limit" + }, + "gasOption": { + "message": "Gas option" + }, + "gasPrice": { + "message": "Gas price (GWEI)" + }, + "gasPriceExcessive": { + "message": "Your gas fee is set unnecessarily high. Consider lowering the amount." + }, + "gasPriceExcessiveInput": { + "message": "Gas price is excessive" + }, + "gasPriceExtremelyLow": { + "message": "Gas price extremely low" + }, + "gasPriceFetchFailed": { + "message": "Gas price estimation failed due to network error." + }, + "gasPriceInfoTooltipContent": { + "message": "Gas price specifies the amount of Ether you are willing to pay for each unit of gas." + }, + "gasTimingHoursShort": { + "message": "$1 hrs", + "description": "$1 represents a number of hours" + }, + "gasTimingLow": { + "message": "Slow" + }, + "gasTimingMinutesShort": { + "message": "$1 min", + "description": "$1 represents a number of minutes" + }, + "gasTimingSecondsShort": { + "message": "$1 sec", + "description": "$1 represents a number of seconds" + }, + "gasUsed": { + "message": "Gas used" + }, + "general": { + "message": "General" + }, + "generalCameraError": { + "message": "We couldn't access your camera. Please give it another try." + }, + "generalCameraErrorTitle": { + "message": "Something went wrong...." + }, + "genericExplorerView": { + "message": "View account on $1" + }, + "getStartedWithNFTs": { + "message": "Get $1 to buy NFTs", + "description": "$1 is the token symbol" + }, + "getStartedWithNFTsDescription": { + "message": "Get started with NFTs by adding some $1 to your wallet.", + "description": "$1 is the token symbol" + }, + "goBack": { + "message": "Go back" + }, + "goToSite": { + "message": "Go to site" + }, + "goerli": { + "message": "Goerli test network" + }, + "gotIt": { + "message": "Got it" + }, + "grantedToWithColon": { + "message": "Granted to:" + }, + "gwei": { + "message": "GWEI" + }, + "hardware": { + "message": "Hardware" + }, + "hardwareWalletConnected": { + "message": "Hardware wallet connected" + }, + "hardwareWalletLegacyDescription": { + "message": "(legacy)", + "description": "Text representing the MEW path" + }, + "hardwareWalletSupportLinkConversion": { + "message": "click here" + }, + "hardwareWallets": { + "message": "Connect a hardware wallet" + }, + "hardwareWalletsInfo": { + "message": "Hardware wallet integrations use API calls to external servers, which can see your IP address and the smart contract addresses you interact with." + }, + "hardwareWalletsMsg": { + "message": "Select a hardware wallet you would like to use with MetaMask." + }, + "here": { + "message": "here", + "description": "as in -click here- for more information (goes with troubleTokenBalances)" + }, + "hexData": { + "message": "Hex data" + }, + "hiddenAccounts": { + "message": "Hidden accounts" + }, + "hide": { + "message": "Hide" + }, + "hideAccount": { + "message": "Hide account" + }, + "hideFullTransactionDetails": { + "message": "Hide full transaction details" + }, + "hideSeedPhrase": { + "message": "Hide seed phrase" + }, + "hideSentitiveInfo": { + "message": "Hide sensitive information" + }, + "hideToken": { + "message": "Hide token" + }, + "hideTokenPrompt": { + "message": "Hide token?" + }, + "hideTokenSymbol": { + "message": "Hide $1", + "description": "$1 is the symbol for a token (e.g. 'DAI')" + }, + "hideZeroBalanceTokens": { + "message": "Hide tokens without balance" + }, + "high": { + "message": "Aggressive" + }, + "highGasSettingToolTipMessage": { + "message": "High probability, even in volatile markets. Use $1 to cover surges in network traffic due to things like popular NFT drops.", + "description": "$1 is key 'high' (text: 'Aggressive') separated here so that it can be passed in with bold font-weight" + }, + "highLowercase": { + "message": "high" + }, + "highestCurrentBid": { + "message": "Highest current bid" + }, + "highestFloorPrice": { + "message": "Highest floor price" + }, + "history": { + "message": "History" + }, + "holdToRevealContent1": { + "message": "Your Secret Recovery Phrase provides $1", + "description": "$1 is a bolded text with the message from 'holdToRevealContent2'" + }, + "holdToRevealContent2": { + "message": "full access to your wallet and funds.", + "description": "Is the bolded text in 'holdToRevealContent1'" + }, + "holdToRevealContent3": { + "message": "Do not share this with anyone. $1 $2", + "description": "$1 is a message from 'holdToRevealContent4' and $2 is a text link with the message from 'holdToRevealContent5'" + }, + "holdToRevealContent4": { + "message": "MetaMask Support will not request this,", + "description": "Part of 'holdToRevealContent3'" + }, + "holdToRevealContent5": { + "message": "but phishers might.", + "description": "The text link in 'holdToRevealContent3'" + }, + "holdToRevealContentPrivateKey1": { + "message": "Your Private Key provides $1", + "description": "$1 is a bolded text with the message from 'holdToRevealContentPrivateKey2'" + }, + "holdToRevealContentPrivateKey2": { + "message": "full access to your wallet and funds.", + "description": "Is the bolded text in 'holdToRevealContentPrivateKey2'" + }, + "holdToRevealLockedLabel": { + "message": "hold to reveal circle locked" + }, + "holdToRevealPrivateKey": { + "message": "Hold to reveal Private Key" + }, + "holdToRevealPrivateKeyTitle": { + "message": "Keep your private key safe" + }, + "holdToRevealSRP": { + "message": "Hold to reveal SRP" + }, + "holdToRevealSRPTitle": { + "message": "Keep your SRP safe" + }, + "holdToRevealUnlockedLabel": { + "message": "hold to reveal circle unlocked" + }, + "id": { + "message": "ID" + }, + "ignoreAll": { + "message": "Ignore all" + }, + "ignoreTokenWarning": { + "message": "If you hide tokens, they will not be shown in your wallet. However, you can still add them by searching for them." + }, + "imToken": { + "message": "imToken" + }, + "immediateAccessToYourNFTs": { + "message": "Immediately access your NFTs" + }, + "immediateAccessToYourTokens": { + "message": "Immediate access to your tokens" + }, + "import": { + "message": "Import", + "description": "Button to import an account from a selected file" + }, + "importAccount": { + "message": "Import account" + }, + "importAccountError": { + "message": "Error importing account." + }, + "importAccountErrorIsSRP": { + "message": "You have entered a Secret Recovery Phrase (or mnemonic). To import an account here, you have to enter a private key, which is a hexadecimal string of length 64." + }, + "importAccountErrorNotAValidPrivateKey": { + "message": "This is not a valid private key. You have entered a hexadecimal string, but it must be 64 characters long." + }, + "importAccountErrorNotHexadecimal": { + "message": "This is not a valid private key. You must enter a hexadecimal string of length 64." + }, + "importAccountJsonLoading1": { + "message": "Expect this JSON import to take a few minutes and freeze MetaMask." + }, + "importAccountJsonLoading2": { + "message": "We apologize, and we will make it faster in the future." + }, + "importAccountMsg": { + "message": "Imported accounts won’t be associated with your MetaMask Secret Recovery Phrase. Learn more about imported accounts" + }, + "importMyWallet": { + "message": "Import my wallet" + }, + "importNFT": { + "message": "Import NFT" + }, + "importNFTAddressToolTip": { + "message": "On OpenSea, for example, on the NFT's page under Details, there is a blue hyperlinked value labeled 'Contract Address'. If you click on this, it will take you to the contract's address on Etherscan; at the top-left of that page, there should be an icon labeled 'Contract', and to the right, a long string of letters and numbers. This is the address of the contract that created your NFT. Click on the 'copy' icon to the right of the address, and you'll have it on your clipboard." + }, + "importNFTPage": { + "message": "Import NFT page" + }, + "importNFTTokenIdToolTip": { + "message": "An NFT's ID is a unique identifier since no two NFTs are alike. Again, on OpenSea this number is under 'Details'. Make a note of it, or copy it onto your clipboard." + }, + "importSelectedTokens": { + "message": "Import selected tokens?" + }, + "importSelectedTokensDescription": { + "message": "Only the tokens you've selected will appear in your wallet. You can always import hidden tokens later by searching for them." + }, + "importTokenQuestion": { + "message": "Import token?" + }, + "importTokenWarning": { + "message": "Anyone can create a token with any name, including fake versions of existing tokens. Add and trade at your own risk!" + }, + "importTokensCamelCase": { + "message": "Import tokens" + }, + "importTokensError": { + "message": "We could not import the tokens. Please try again later." + }, + "importWithCount": { + "message": "Import $1", + "description": "$1 will the number of detected tokens that are selected for importing, if all of them are selected then $1 will be all" + }, + "imported": { + "message": "Imported", + "description": "status showing that an account has been fully loaded into the keyring" + }, + "inYourSettings": { + "message": "in your Settings" + }, + "infuraBlockedNotification": { + "message": "MetaMask is unable to connect to the blockchain host. Review possible reasons $1.", + "description": "$1 is a clickable link with with text defined by the 'here' key" + }, + "initialTransactionConfirmed": { + "message": "Your initial transaction was confirmed by the network. Click OK to go back." + }, + "inputLogicEmptyState": { + "message": "Only enter a number that you're comfortable with the third party spending now or in the future. You can always increase the spending cap later." + }, + "inputLogicEqualOrSmallerNumber": { + "message": "This allows the third party to spend $1 from your current balance.", + "description": "$1 is the current token balance in the account and the name of the current token" + }, + "inputLogicHigherNumber": { + "message": "This allows the third party to spend all your token balance until it reaches the cap or you revoke the spending cap. If this is not intended, consider setting a lower spending cap." + }, + "insightWarning": { + "message": "warning" + }, + "insightWarningCheckboxMessage": { + "message": "$1 the request by $2", + "description": "$1 is the action i.e. sign, confirm. $2 is the origin making the request." + }, + "insightWarningContentPlural": { + "message": "Review $1 before $2. Once made, the $3 is irreversible.", + "description": "$1 the 'insightWarnings' message (2 warnings) representing warnings, $2 is the action (i.e. signing) and $3 is the result (i.e. signature, transaction)" + }, + "insightWarningContentSingular": { + "message": "Review $1 before $2. Once made, the $3 is irreversible.", + "description": "$1 is the 'insightWarning' message (1 warning), $2 is the action (i.e. signing) and $3 is the result (i.e. signature, transaction)" + }, + "insightWarningHeader": { + "message": "This request may be risky" + }, + "insightWarnings": { + "message": "warnings" + }, + "insightsFromSnap": { + "message": "Insights from $1", + "description": "$1 represents the name of the snap" + }, + "install": { + "message": "Install" + }, + "installExtension": { + "message": "Install extension" + }, + "installExtensionDescription": { + "message": "The institution-compliant version of the world's leading web3 wallet, MetaMask." + }, + "installOrigin": { + "message": "Install origin" + }, + "installRequest": { + "message": "Add to MetaMask" + }, + "installedOn": { + "message": "Installed on $1", + "description": "$1 is the date when the snap has been installed" + }, + "insufficientBalance": { + "message": "Insufficient balance." + }, + "insufficientCurrencyBuyOrDeposit": { + "message": "You do not have enough $1 in your account to pay for transaction fees on $2 network. $3 or deposit from another account.", + "description": "$1 is the native currency of the network, $2 is the name of the current network, $3 is the key 'buy' + the ticker symbol of the native currency of the chain wrapped in a button" + }, + "insufficientCurrencyBuyOrReceive": { + "message": "You do not have enough $1 in your account to pay for transaction fees on $2 network. $3 or $4 from another account.", + "description": "$1 is the native currency of the network, $2 is the name of the current network, $3 is the key 'buy' + the ticker symbol of the native currency of the chain wrapped in a button, $4 is the key 'deposit' button" + }, + "insufficientCurrencyDeposit": { + "message": "You do not have enough $1 in your account to pay for transaction fees on $2 network. Deposit $1 from another account.", + "description": "$1 is the native currency of the network, $2 is the name of the current network" + }, + "insufficientFunds": { + "message": "Insufficient funds." + }, + "insufficientFundsForGas": { + "message": "Insufficient funds for gas" + }, + "insufficientTokens": { + "message": "Insufficient tokens." + }, + "interactingWith": { + "message": "Interacting with" + }, + "interactingWithTransactionDescription": { + "message": "This is the contract you're interacting with. Protect yourself from scammers by verifying the details." + }, + "invalidAddress": { + "message": "Invalid address" + }, + "invalidAddressRecipient": { + "message": "Recipient address is invalid" + }, + "invalidAddressRecipientNotEthNetwork": { + "message": "Not ETH network, set to lowercase" + }, + "invalidAssetType": { + "message": "This asset is an NFT and needs to be re-added on the Import NFTs page found under the NFTs tab" + }, + "invalidBlockExplorerURL": { + "message": "Invalid block explorer URL" + }, + "invalidChainIdTooBig": { + "message": "Invalid chain ID. The chain ID is too big." + }, + "invalidCustomNetworkAlertContent1": { + "message": "The chain ID for custom network '$1' has to be re-entered.", + "description": "$1 is the name/identifier of the network." + }, + "invalidCustomNetworkAlertContent2": { + "message": "To protect you from malicious or faulty network providers, chain IDs are now required for all custom networks." + }, + "invalidCustomNetworkAlertContent3": { + "message": "Go to Settings > Network and enter the chain ID. You can find the chain IDs of most popular networks on $1.", + "description": "$1 is a link to https://chainid.network" + }, + "invalidCustomNetworkAlertTitle": { + "message": "Invalid custom network" + }, + "invalidHexNumber": { + "message": "Invalid hexadecimal number." + }, + "invalidHexNumberLeadingZeros": { + "message": "Invalid hexadecimal number. Remove any leading zeros." + }, + "invalidIpfsGateway": { + "message": "Invalid IPFS Gateway: The value must be a valid URL" + }, + "invalidNumber": { + "message": "Invalid number. Enter a decimal or '0x'-prefixed hexadecimal number." + }, + "invalidNumberLeadingZeros": { + "message": "Invalid number. Remove any leading zeros." + }, + "invalidRPC": { + "message": "Invalid RPC URL" + }, + "invalidSeedPhrase": { + "message": "Invalid Secret Recovery Phrase" + }, + "invalidSeedPhraseCaseSensitive": { + "message": "Invalid input! Secret Recovery Phrase is case sensitive." + }, + "ipfsGateway": { + "message": "IPFS gateway" + }, + "ipfsGatewayDescription": { + "message": "MetaMask uses third-party services to show images of your NFTs stored on IPFS, display information related to ENS addresses entered in your browser's address bar, and fetch icons for different tokens. Your IP address may be exposed to these services when you’re using them." + }, + "ipfsToggleModalDescriptionOne": { + "message": "We use third-party services to show images of your NFTs stored on IPFS, display information related to ENS addresses entered in your browser's address bar, and fetch icons for different tokens. Your IP address may be exposed to these services when you’re using them." + }, + "ipfsToggleModalDescriptionTwo": { + "message": "Selecting Confirm turns on IPFS resolution. You can turn it off in $1 at any time.", + "description": "$1 is the method to turn off ipfs" + }, + "ipfsToggleModalSettings": { + "message": "Settings > Security and privacy" + }, + "isSigningOrSubmitting": { + "message": "A previous transaction is still being signed or submitted" + }, + "jazzAndBlockies": { + "message": "Jazzicons and Blockies are two different styles of unique icons that help you identify an account at a glance." + }, + "jazzicons": { + "message": "Jazzicons" + }, + "jsDeliver": { + "message": "jsDeliver" + }, + "jsonFile": { + "message": "JSON File", + "description": "format for importing an account" + }, + "keyringAccountName": { + "message": "Account name" + }, + "keyringAccountPublicAddress": { + "message": "Public Address" + }, + "keyringSnapRemovalResult1": { + "message": "$1 $2removed", + "description": "Displays the result after removal of a keyring snap. $1 is the snap name, $2 is whether it is successful or not" + }, + "keyringSnapRemovalResultNotSuccessful": { + "message": "not ", + "description": "Displays the `not` word in $2." + }, + "keyringSnapRemoveConfirmation": { + "message": "Type $1 to confirm you want to remove this snap:", + "description": "Asks user to input the name nap prior to deleting the snap. $1 is the snap name" + }, + "keystone": { + "message": "Keystone" + }, + "knownAddressRecipient": { + "message": "Known contract address." + }, + "knownTokenWarning": { + "message": "This action will edit tokens that are already listed in your wallet, which can be used to phish you. Only approve if you are certain that you mean to change what these tokens represent. Learn more about $1" + }, + "l1Fee": { + "message": "L1 fee" + }, + "l1FeeTooltip": { + "message": "L1 gas fee" + }, + "l2Fee": { + "message": "L2 fee" + }, + "l2FeeTooltip": { + "message": "L2 gas fee" + }, + "lastConnected": { + "message": "Last connected" + }, + "lastSold": { + "message": "Last sold" + }, + "lavaDomeCopyWarning": { + "message": "For your safety, selecting this text is not available right now." + }, + "layer1Fees": { + "message": "Layer 1 fees" + }, + "layer2Fees": { + "message": "Layer 2 fees" + }, + "learnCancelSpeeedup": { + "message": "Learn how to $1", + "description": "$1 is link to cancel or speed up transactions" + }, + "learnMore": { + "message": "learn more" + }, + "learnMoreAboutGas": { + "message": "Want to $1 about gas?", + "description": "$1 will be replaced by the learnMore translation key" + }, + "learnMoreKeystone": { + "message": "Learn More" + }, + "learnMoreUpperCase": { + "message": "Learn more" + }, + "learnMoreUpperCaseWithDot": { + "message": "Learn more." + }, + "learnScamRisk": { + "message": "scams and security risks." + }, + "learnToBridge": { + "message": "Learn to bridge" + }, + "leaveMetaMask": { + "message": "Leave MetaMask?" + }, + "leaveMetaMaskDesc": { + "message": "You're about to visit a site outside of MetaMask. Double-check the URL before continuing." + }, + "ledgerAccountRestriction": { + "message": "You need to make use your last account before you can add a new one." + }, + "ledgerConnectionInstructionCloseOtherApps": { + "message": "Close any other software connected to your device and then click here to refresh." + }, + "ledgerConnectionInstructionHeader": { + "message": "Prior to clicking confirm:" + }, + "ledgerConnectionInstructionStepFour": { + "message": "Enable \"smart contract data\" or \"blind signing\" on your Ledger device." + }, + "ledgerConnectionInstructionStepThree": { + "message": "Be sure your Ledger is plugged in and to select the Ethereum app." + }, + "ledgerDeviceOpenFailureMessage": { + "message": "The Ledger device failed to open. Your Ledger might be connected to other software. Please close Ledger Live or other applications connected to your Ledger device, and try to connect again." + }, + "ledgerErrorConnectionIssue": { + "message": "Reconnect your ledger, open the ETH app and try again." + }, + "ledgerErrorDevicedLocked": { + "message": "Your Ledger is locked. Unlock it then try again." + }, + "ledgerErrorEthAppNotOpen": { + "message": "To solve the issue, open the ETH application on your device and retry." + }, + "ledgerErrorTransactionDataNotPadded": { + "message": "Ethereum transaction's input data isn't sufficiently padded." + }, + "ledgerLiveApp": { + "message": "Ledger Live App" + }, + "ledgerLocked": { + "message": "Cannot connect to Ledger device. Please make sure your device is unlocked and Ethereum app is opened." + }, + "ledgerTimeout": { + "message": "Ledger Live is taking too long to respond or connection timeout. Make sure Ledger Live app is opened and your device is unlocked." + }, + "ledgerWebHIDNotConnectedErrorMessage": { + "message": "The ledger device was not connected. If you wish to connect your Ledger, please click 'Continue' again and approve HID connection", + "description": "An error message shown to the user during the hardware connect flow." + }, + "levelArrow": { + "message": "level arrow" + }, + "lightTheme": { + "message": "Light" + }, + "likeToImportToken": { + "message": "Would you like to import this token?" + }, + "likeToImportTokens": { + "message": "Would you like to import these tokens?" + }, + "lineaGoerli": { + "message": "Linea Goerli test network" + }, + "lineaMainnet": { + "message": "Linea Mainnet" + }, + "lineaSepolia": { + "message": "Linea Sepolia test network" + }, + "link": { + "message": "Link" + }, + "links": { + "message": "Links" + }, + "loadMore": { + "message": "Load more" + }, + "loading": { + "message": "Loading..." + }, + "loadingScreenHardwareWalletMessage": { + "message": "Please complete the transaction on the hardware wallet." + }, + "loadingScreenSnapMessage": { + "message": "Please complete the transaction on the Snap." + }, + "loadingTokens": { + "message": "Loading tokens..." + }, + "localhost": { + "message": "Localhost 8545" + }, + "lock": { + "message": "Lock" + }, + "lockMetaMask": { + "message": "Lock MetaMask" + }, + "lockTimeInvalid": { + "message": "Lock time must be a number between 0 and 10080" + }, + "logo": { + "message": "$1 logo", + "description": "$1 is the name of the ticker" + }, + "low": { + "message": "Low" + }, + "lowGasSettingToolTipMessage": { + "message": "Use $1 to wait for a cheaper price. Time estimates are much less accurate as prices are somewhat unpredictable.", + "description": "$1 is key 'low' separated here so that it can be passed in with bold font-weight" + }, + "lowLowercase": { + "message": "low" + }, + "lowPriorityMessage": { + "message": "Future transactions will queue after this one." + }, + "mainnet": { + "message": "Ethereum Mainnet" + }, + "mainnetToken": { + "message": "This address matches a known Ethereum Mainnet token address. Recheck the contract address and network for the token you are trying to add." + }, + "makeAnotherSwap": { + "message": "Create a new swap" + }, + "makeSureNoOneWatching": { + "message": "Make sure nobody is looking", + "description": "Warning to users to be care while creating and saving their new Secret Recovery Phrase" + }, + "marketCap": { + "message": "Market cap" + }, + "marketDetails": { + "message": "Market details" + }, + "max": { + "message": "Max" + }, + "maxBaseFee": { + "message": "Max base fee" + }, + "maxFee": { + "message": "Max fee" + }, + "maxFeeTooltip": { + "message": "A maximum fee provided to pay for the transaction." + }, + "maxPriorityFee": { + "message": "Max priority fee" + }, + "medium": { + "message": "Market" + }, + "mediumGasSettingToolTipMessage": { + "message": "Use $1 for fast processing at current market price.", + "description": "$1 is key 'medium' (text: 'Market') separated here so that it can be passed in with bold font-weight" + }, + "memo": { + "message": "memo" + }, + "message": { + "message": "Message" + }, + "metaMaskConnectStatusParagraphOne": { + "message": "You now have more control over your account connections in MetaMask." + }, + "metaMaskConnectStatusParagraphThree": { + "message": "Click it to manage your connected accounts." + }, + "metaMaskConnectStatusParagraphTwo": { + "message": "The connection status button shows if the website you’re visiting is connected to your currently selected account." + }, + "metadataModalSourceTooltip": { + "message": "$1 is hosted on npm and $2 is this Snap’s unique identifier.", + "description": "$1 is the snap name and $2 is the snap NPM id." + }, + "metamaskInstitutionalVersion": { + "message": "MetaMask Institutional Version" + }, + "metamaskNotificationsAreOff": { + "message": "Wallet notifications are currently not active." + }, + "metamaskPortfolio": { + "message": "MetaMask Portfolio." + }, + "metamaskSwapsOfflineDescription": { + "message": "MetaMask Swaps is undergoing maintenance. Please check back later." + }, + "metamaskVersion": { + "message": "MetaMask Version" + }, + "methodData": { + "message": "Method" + }, + "methodDataTransactionDesc": { + "message": "Function executed based on decoded input data." + }, + "methodNotSupported": { + "message": "Not supported with this account." + }, + "metrics": { + "message": "Metrics" + }, + "millionAbbreviation": { + "message": "M", + "description": "Shortened form of 'million'" + }, + "mismatchAccount": { + "message": "Your selected account ($1) is different than the account trying to sign ($2)" + }, + "mismatchedChainLinkText": { + "message": "verify the network details", + "description": "Serves as link text for the 'mismatchedChain' key. This text will be embedded inside the translation for that key." + }, + "mismatchedChainRecommendation": { + "message": "We recommend that you $1 before proceeding.", + "description": "$1 is a clickable link with text defined by the 'mismatchedChainLinkText' key. The link will open to instructions for users to validate custom network details." + }, + "mismatchedNetworkName": { + "message": "According to our record the network name may not correctly match this chain ID." + }, + "mismatchedNetworkSymbol": { + "message": "The submitted currency symbol does not match what we expect for this chain ID." + }, + "mismatchedRpcChainId": { + "message": "Chain ID returned by the custom network does not match the submitted chain ID." + }, + "mismatchedRpcUrl": { + "message": "According to our records the submitted RPC URL value does not match a known provider for this chain ID." + }, + "missingSetting": { + "message": "Can't find a setting?" + }, + "missingSettingRequest": { + "message": "Request here" + }, + "mmiBuiltAroundTheWorld": { + "message": "MetaMask Institutional is designed and built around the world." + }, + "mmiNewNFTDetectedInNFTsTabMessage": { + "message": "Let MetaMask Institutional automatically detect and display NFTs in your wallet." + }, + "mmiPasswordSetupDetails": { + "message": "This password will unlock your MetaMask Institutional extension only." + }, + "more": { + "message": "more" + }, + "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." + }, + "mustSelectOne": { + "message": "Must select at least 1 token." + }, + "name": { + "message": "Name" + }, + "nameAddressLabel": { + "message": "Address", + "description": "Label above address field in name component modal." + }, + "nameInstructionsNew": { + "message": "If you know this address, give it a nickname to recognize it in the future.", + "description": "Instruction text in name component modal when value is not recognised." + }, + "nameInstructionsRecognized": { + "message": "This address has a default nickname, but you can edit it or explore other suggestions.", + "description": "Instruction text in name component modal when value is recognized but not saved." + }, + "nameInstructionsSaved": { + "message": "You've added a nickname for this address before. You can edit or view other suggested nicknames.", + "description": "Instruction text in name component modal when value is saved." + }, + "nameLabel": { + "message": "Nickname", + "description": "Label above name input field in name component modal." + }, + "nameModalMaybeProposedName": { + "message": "Maybe: $1", + "description": "$1 is the proposed name" + }, + "nameModalTitleNew": { + "message": "Unknown address", + "description": "Title of the modal created by the name component when value is not recognised." + }, + "nameModalTitleRecognized": { + "message": "Recognized address", + "description": "Title of the modal created by the name component when value is recognized but not saved." + }, + "nameModalTitleSaved": { + "message": "Saved address", + "description": "Title of the modal created by the name component when value is saved." + }, + "nameProviderProposedBy": { + "message": "Proposed by $1", + "description": "$1 is the name of the provider" + }, + "nameProvider_ens": { + "message": "Ethereum Name Service (ENS)" + }, + "nameProvider_etherscan": { + "message": "Etherscan" + }, + "nameProvider_lens": { + "message": "Lens Protocol" + }, + "nameProvider_token": { + "message": "MetaMask" + }, + "nameSetPlaceholder": { + "message": "Choose a nickname...", + "description": "Placeholder text for name input field in name component modal." + }, + "nativePermissionRequestDescription": { + "message": "Do you want this site to do the following?", + "description": "Description below header used on Permission Connect screen for native permissions." + }, + "nativeToken": { + "message": "The native token on this network is $1. It is the token used for gas fees. ", + "description": "$1 represents the name of the native token on the current network" + }, + "nativeTokenScamWarningConversion": { + "message": "Edit network details" + }, + "nativeTokenScamWarningDescription": { + "message": "This network doesn't match its associated chain ID or name. Many popular tokens use the name $1, making it a target for scams. Scammers may trick you into sending them more valuable currency in return. Verify everything before you continue.", + "description": "$1 represents the currency name" + }, + "nativeTokenScamWarningTitle": { + "message": "This is a potential scam" + }, + "needHelp": { + "message": "Need help? Contact $1", + "description": "$1 represents `needHelpLinkText`, the text which goes in the help link" + }, + "needHelpFeedback": { + "message": "Share your feedback" + }, + "needHelpLinkText": { + "message": "MetaMask support" + }, + "needHelpSubmitTicket": { + "message": "Submit a ticket" + }, + "needImportFile": { + "message": "You must select a file to import.", + "description": "User is important an account and needs to add a file to continue" + }, + "negativeETH": { + "message": "Can not send negative amounts of ETH." + }, + "negativeOrZeroAmountToken": { + "message": "Cannot send negative or zero amounts of asset." + }, + "network": { + "message": "Network:" + }, + "networkAddedSuccessfully": { + "message": "Network added successfully!" + }, + "networkDetails": { + "message": "Network details" + }, + "networkIsBusy": { + "message": "Network is busy. Gas prices are high and estimates are less accurate." + }, + "networkMenu": { + "message": "Network Menu" + }, + "networkMenuHeading": { + "message": "Select a network" + }, + "networkName": { + "message": "Network name" + }, + "networkNameArbitrum": { + "message": "Arbitrum" + }, + "networkNameAvalanche": { + "message": "Avalanche" + }, + "networkNameBSC": { + "message": "BSC" + }, + "networkNameBase": { + "message": "Base" + }, + "networkNameBitcoin": { + "message": "Bitcoin" + }, + "networkNameDefinition": { + "message": "The name associated with this network." + }, + "networkNameEthereum": { + "message": "Ethereum" + }, + "networkNameGoerli": { + "message": "Goerli" + }, + "networkNameLinea": { + "message": "Linea" + }, + "networkNameOpMainnet": { + "message": "OP Mainnet" + }, + "networkNamePolygon": { + "message": "Polygon" + }, + "networkNameTestnet": { + "message": "Testnet" + }, + "networkNameZkSyncEra": { + "message": "zkSync Era" + }, + "networkOptions": { + "message": "Network options" + }, + "networkProvider": { + "message": "Network provider" + }, + "networkSettingsChainIdDescription": { + "message": "The chain ID is used for signing transactions. It must match the chain ID returned by the network. You can enter a decimal or '0x'-prefixed hexadecimal number, but we will display the number in decimal." + }, + "networkStatus": { + "message": "Network status" + }, + "networkStatusBaseFeeTooltip": { + "message": "The base fee is set by the network and changes every 13-14 seconds. Our $1 and $2 options account for sudden increases.", + "description": "$1 and $2 are bold text for Medium and Aggressive respectively." + }, + "networkStatusPriorityFeeTooltip": { + "message": "Range of priority fees (aka “miner tip”). This goes to miners and incentivizes them to prioritize your transaction." + }, + "networkStatusStabilityFeeTooltip": { + "message": "Gas fees are $1 relative to the past 72 hours.", + "description": "$1 is networks stability value - stable, low, high" + }, + "networkSwitchConnectionError": { + "message": "We can't connect to $1", + "description": "$1 represents the network name" + }, + "networkURL": { + "message": "Network URL" + }, + "networkURLDefinition": { + "message": "The URL used to access this network." + }, + "networks": { + "message": "Networks" + }, + "nevermind": { + "message": "Nevermind" + }, + "new": { + "message": "New!" + }, + "newAccount": { + "message": "New account" + }, + "newAccountNumberName": { + "message": "Account $1", + "description": "Default name of next account to be created on create account screen" + }, + "newContact": { + "message": "New contact" + }, + "newContract": { + "message": "New contract" + }, + "newNFTDetectedInImportNFTsMessageStrongText": { + "message": "Settings > Security and privacy" + }, + "newNFTDetectedInImportNFTsMsg": { + "message": "To use Opensea to see your NFTs, turn on 'Display NFT Media' in $1.", + "description": "$1 is used for newNFTDetectedInImportNFTsMessageStrongText" + }, + "newNFTDetectedInNFTsTabMessage": { + "message": "Let MetaMask automatically detect and display NFTs in your wallet." + }, + "newNFTsAutodetected": { + "message": "NFT autodetection" + }, + "newNetworkAdded": { + "message": "“$1” was successfully added!" + }, + "newNetworkEdited": { + "message": "“$1” was successfully edited!" + }, + "newNftAddedMessage": { + "message": "NFT was successfully added!" + }, + "newPassword": { + "message": "New password (8 characters min)" + }, + "newPrivacyPolicyActionButton": { + "message": "Read more" + }, + "newPrivacyPolicyTitle": { + "message": "We’ve updated our privacy policy" + }, + "newTokensImportedMessage": { + "message": "You’ve successfully imported $1.", + "description": "$1 is the string of symbols of all the tokens imported" + }, + "newTokensImportedTitle": { + "message": "Token imported" + }, + "next": { + "message": "Next" + }, + "nextNonceWarning": { + "message": "Nonce is higher than suggested nonce of $1", + "description": "The next nonce according to MetaMask's internal logic" + }, + "nftAddFailedMessage": { + "message": "NFT can’t be added as the ownership details do not match. Make sure you have entered correct information." + }, + "nftAddressError": { + "message": "This token is an NFT. Add on the $1", + "description": "$1 is a clickable link with text defined by the 'importNFTPage' key" + }, + "nftAlreadyAdded": { + "message": "NFT has already been added." + }, + "nftAutoDetectionEnabled": { + "message": "NFT autodetection enabled" + }, + "nftDisclaimer": { + "message": "Disclaimer: MetaMask pulls the media file from the source url. This url sometimes gets changed by the marketplace on which the NFT was minted." + }, + "nftOptions": { + "message": "NFT Options" + }, + "nftTokenIdPlaceholder": { + "message": "Enter the token id" + }, + "nftWarningContent": { + "message": "You're granting access to $1, including any you might own in the future. The party on the other end can transfer these NFTs from your wallet at any time without asking you until you revoke this approval. $2", + "description": "$1 is nftWarningContentBold bold part, $2 is Learn more link" + }, + "nftWarningContentBold": { + "message": "all your $1 NFTs", + "description": "$1 is name of the collection" + }, + "nftWarningContentGrey": { + "message": "Proceed with caution." + }, + "nfts": { + "message": "NFTs" + }, + "nftsPreviouslyOwned": { + "message": "Previously Owned" + }, + "nickname": { + "message": "Nickname" + }, + "noAccountsFound": { + "message": "No accounts found for the given search query" + }, + "noAddressForName": { + "message": "No address has been set for this name." + }, + "noConnectedAccountDescription": { + "message": "Select an account you want to use on this site to continue." + }, + "noConnectedAccountTitle": { + "message": "MetaMask isn’t connected to this site" + }, + "noConversionDateAvailable": { + "message": "No currency conversion date available" + }, + "noConversionRateAvailable": { + "message": "No conversion rate available" + }, + "noDomainResolution": { + "message": "No resolution for domain provided." + }, + "noHardwareWalletOrSnapsSupport": { + "message": "Snaps, and most hardware wallets, will not work with your current browser version." + }, + "noNFTs": { + "message": "No NFTs yet" + }, + "noNetworksFound": { + "message": "No networks found for the given search query" + }, + "noSnaps": { + "message": "You don't have any snaps installed." + }, + "noThanks": { + "message": "No thanks" + }, + "noTransactions": { + "message": "You have no transactions" + }, + "noWebcamFound": { + "message": "Your computer's webcam was not found. Please try again." + }, + "noWebcamFoundTitle": { + "message": "Webcam not found" + }, + "nonCustodialAccounts": { + "message": "MetaMask Institutional allows you to use non-custodial accounts, if you plan to use these accounts backup the Secret Recovery Phrase." + }, + "nonce": { + "message": "Nonce" + }, + "nonceField": { + "message": "Customize transaction nonce" + }, + "nonceFieldDesc": { + "message": "Turn this on to change the nonce (transaction number) when sending assets. This is an advanced feature, use cautiously." + }, + "nonceFieldHeading": { + "message": "Custom nonce" + }, + "notBusy": { + "message": "Not busy" + }, + "notCurrentAccount": { + "message": "Is this the correct account? It's different from the currently selected account in your wallet" + }, + "notEnoughBalance": { + "message": "Insufficient balance" + }, + "notEnoughGas": { + "message": "Not enough gas" + }, + "notRightNow": { + "message": "Not right now" + }, + "note": { + "message": "Note" + }, + "notePlaceholder": { + "message": "The approver will see this note when approving the transaction at the custodian." + }, + "notificationDetail": { + "message": "Details" + }, + "notificationDetailBaseFee": { + "message": "Base fee (GWEI)" + }, + "notificationDetailGasLimit": { + "message": "Gas limit (units)" + }, + "notificationDetailGasUsed": { + "message": "Gas used (units)" + }, + "notificationDetailMaxFee": { + "message": "Max fee per gas" + }, + "notificationDetailNetwork": { + "message": "Network" + }, + "notificationDetailNetworkFee": { + "message": "Network fee" + }, + "notificationDetailPriorityFee": { + "message": "Priority fee (GWEI)" + }, + "notificationItemCheckBlockExplorer": { + "message": "Check on the Block Explorer" + }, + "notificationItemCollection": { + "message": "Collection" + }, + "notificationItemConfirmed": { + "message": "Confirmed" + }, + "notificationItemError": { + "message": "Unable to retrieve fees currently" + }, + "notificationItemFrom": { + "message": "From" + }, + "notificationItemLidoStakeReadyToBeWithdrawn": { + "message": "Withdrawal Ready" + }, + "notificationItemLidoStakeReadyToBeWithdrawnMessage": { + "message": "You can now withdraw your unstaked $1" + }, + "notificationItemLidoWithdrawalRequestedMessage": { + "message": "Your request to unstake $1 has been sent" + }, + "notificationItemNFTReceivedFrom": { + "message": "Received NFT from" + }, + "notificationItemNFTSentTo": { + "message": "Sent NFT to" + }, + "notificationItemNetwork": { + "message": "Network" + }, + "notificationItemRate": { + "message": "Rate (fee included)" + }, + "notificationItemReceived": { + "message": "Received" + }, + "notificationItemReceivedFrom": { + "message": "Received from" + }, + "notificationItemSent": { + "message": "Sent" + }, + "notificationItemSentTo": { + "message": "Sent to" + }, + "notificationItemStakeCompleted": { + "message": "Stake completed" + }, + "notificationItemStaked": { + "message": "Staked" + }, + "notificationItemStakingProvider": { + "message": "Staking Provider" + }, + "notificationItemStatus": { + "message": "Status" + }, + "notificationItemSwapped": { + "message": "Swapped" + }, + "notificationItemSwappedFor": { + "message": "for" + }, + "notificationItemTo": { + "message": "To" + }, + "notificationItemTransactionId": { + "message": "Transaction ID" + }, + "notificationItemUnStakeCompleted": { + "message": "UnStaking complete" + }, + "notificationItemUnStaked": { + "message": "Unstaked" + }, + "notificationItemUnStakingRequested": { + "message": "Unstaking requested" + }, + "notificationTransactionFailedMessage": { + "message": "Transaction $1 failed! $2", + "description": "Content of the browser notification that appears when a transaction fails" + }, + "notificationTransactionFailedMessageMMI": { + "message": "Transaction failed! $1", + "description": "Content of the browser notification that appears when a transaction fails in MMI" + }, + "notificationTransactionFailedTitle": { + "message": "Failed transaction", + "description": "Title of the browser notification that appears when a transaction fails" + }, + "notificationTransactionSuccessMessage": { + "message": "Transaction $1 confirmed!", + "description": "Content of the browser notification that appears when a transaction is confirmed" + }, + "notificationTransactionSuccessTitle": { + "message": "Confirmed transaction", + "description": "Title of the browser notification that appears when a transaction is confirmed" + }, + "notificationTransactionSuccessView": { + "message": "View on $1", + "description": "Additional content in a notification that appears when a transaction is confirmed and has a block explorer URL." + }, + "notifications": { + "message": "Notifications" + }, + "notificationsDropLedgerFirefoxDescription": { + "message": "Firefox no longer supports U2F, so Ledger won't work with MetaMask on Firefox. Try MetaMask on Google Chrome instead.", + "description": "Description of a notification in the 'See What's New' popup. Describes that ledger will not longer be supported for firefox users and they should use MetaMask on chrome for ledger support instead." + }, + "notificationsDropLedgerFirefoxTitle": { + "message": "Dropping Ledger Support for Firefox", + "description": "Title for a notification in the 'See What's New' popup. Tells firefox users that ledger support is being dropped." + }, + "notificationsFeatureToggle": { + "message": "Enable Wallet Notifications", + "description": "Experimental feature title" + }, + "notificationsFeatureToggleDescription": { + "message": "This enables wallet notifications like send/receive funds or nfts and feature announcements.", + "description": "Description of the experimental notifications feature" + }, + "notificationsMarkAllAsRead": { + "message": "Mark all as read" + }, + "notificationsPageEmptyTitle": { + "message": "Nothing to see here" + }, + "notificationsPageErrorContent": { + "message": "Please, try to visit this page again." + }, + "notificationsPageErrorTitle": { + "message": "There has been an error" + }, + "notificationsPageNoNotificationsContent": { + "message": "You have not received any notifications yet." + }, + "notificationsSettingsBoxError": { + "message": "Something went wrong. Please try again." + }, + "notificationsSettingsPageAllowNotifications": { + "message": "Stay in the loop on what’s happening in your wallet with notifications. To use notifications, we use a profile to sync some settings across your devices. $1" + }, + "notificationsSettingsPageAllowNotificationsLink": { + "message": "Learn how we protect your privacy while using this feature." + }, + "numberOfNewTokensDetectedPlural": { + "message": "$1 new tokens found in this account", + "description": "$1 is the number of new tokens detected" + }, + "numberOfNewTokensDetectedSingular": { + "message": "1 new token found in this account" + }, + "numberOfTokens": { + "message": "Number of tokens" + }, + "ofTextNofM": { + "message": "of" + }, + "off": { + "message": "Off" + }, + "offlineForMaintenance": { + "message": "Offline for maintenance" + }, + "ok": { + "message": "Ok" + }, + "on": { + "message": "On" + }, + "onboardedMetametricsAccept": { + "message": "I agree" + }, + "onboardedMetametricsDisagree": { + "message": "No thanks" + }, + "onboardedMetametricsKey1": { + "message": "Latest developments" + }, + "onboardedMetametricsKey2": { + "message": "Product features" + }, + "onboardedMetametricsKey3": { + "message": "Other relevant promotional materials" + }, + "onboardedMetametricsLink": { + "message": "MetaMetrics" + }, + "onboardedMetametricsParagraph1": { + "message": "In addition to $1, we'd like to use data to understand how you interact with marketing communications.", + "description": "$1 represents the 'onboardedMetametricsLink' locale string" + }, + "onboardedMetametricsParagraph2": { + "message": "This helps us personalize what we share with you, like:" + }, + "onboardedMetametricsParagraph3": { + "message": "Remember, we never sell the data you provide and you can opt out any time." + }, + "onboardedMetametricsTitle": { + "message": "Help us enhance your experience" + }, + "onboarding": { + "message": "Onboarding" + }, + "onboardingAdvancedPrivacyIPFSDescription": { + "message": "The IPFS gateway makes it possible to access and view data hosted by third parties. You can add a custom IPFS gateway or continue using the default." + }, + "onboardingAdvancedPrivacyIPFSInvalid": { + "message": "Please enter a valid URL" + }, + "onboardingAdvancedPrivacyIPFSTitle": { + "message": "Add custom IPFS Gateway" + }, + "onboardingAdvancedPrivacyIPFSValid": { + "message": "IPFS gateway URL is valid" + }, + "onboardingAdvancedPrivacyNetworkButton": { + "message": "Add custom network" + }, + "onboardingAdvancedPrivacyNetworkDescription": { + "message": "We use Infura as our remote procedure call (RPC) provider to offer the most reliable and private access to Ethereum data we can. You can choose your own RPC, but remember that any RPC will receive your IP address and Ethereum wallet to make transactions. Read our $1 to learn more about how Infura handles data." + }, + "onboardingAdvancedPrivacyNetworkTitle": { + "message": "Choose your network" + }, + "onboardingCreateWallet": { + "message": "Create a new wallet" + }, + "onboardingImportWallet": { + "message": "Import an existing wallet" + }, + "onboardingMetametricsAgree": { + "message": "I agree" + }, + "onboardingMetametricsDescription": { + "message": "We’d like to gather basic usage and diagnostics data to improve MetaMask. Know that we never sell the data you provide here." + }, + "onboardingMetametricsDescription2": { + "message": "When we gather metrics, it will always be..." + }, + "onboardingMetametricsInfuraTerms": { + "message": "We’ll let you know if we decide to use this data for other purposes. You can review our $1 for more information. Remember, you can go to settings and opt out at any time.", + "description": "$1 represents `onboardingMetametricsInfuraTermsPolicy`" + }, + "onboardingMetametricsInfuraTermsPolicy": { + "message": "Privacy Policy" + }, + "onboardingMetametricsModalTitle": { + "message": "Add custom network" + }, + "onboardingMetametricsNeverCollect": { + "message": "$1 clicks and views on the app are stored, but other details (like your public address) are not.", + "description": "$1 represents `onboardingMetametricsNeverCollectEmphasis`" + }, + "onboardingMetametricsNeverCollectEmphasis": { + "message": "Private:" + }, + "onboardingMetametricsNeverCollectIP": { + "message": "$1 we temporarily use your IP address to detect a general location (like your country or region), but it's never stored.", + "description": "$1 represents `onboardingMetametricsNeverCollectIPEmphasis`" + }, + "onboardingMetametricsNeverCollectIPEmphasis": { + "message": "General:" + }, + "onboardingMetametricsNeverSellData": { + "message": "$1 you decide if you want to share or delete your usage data via settings any time.", + "description": "$1 represents `onboardingMetametricsNeverSellDataEmphasis`" + }, + "onboardingMetametricsNeverSellDataEmphasis": { + "message": "Optional:" + }, + "onboardingMetametricsPrivacyDescription": { + "message": "Learn how we protect your privacy while collecting usage data for your profile." + }, + "onboardingMetametricsTitle": { + "message": "Help us improve MetaMask" + }, + "onboardingMetametricsUseDataCheckbox": { + "message": "We’ll use this data to learn how you interact with our marketing communications. We may share relevant news (like product features)." + }, + "onboardingPinExtensionBillboardAccess": { + "message": "Full access" + }, + "onboardingPinExtensionBillboardDescription": { + "message": "These extensions can see and change information" + }, + "onboardingPinExtensionBillboardDescription2": { + "message": "on this site." + }, + "onboardingPinExtensionBillboardTitle": { + "message": "Extensions" + }, + "onboardingPinExtensionChrome": { + "message": "Click the browser extension icon" + }, + "onboardingPinExtensionDescription": { + "message": "Pin MetaMask on your browser so it's accessible and easy to view transaction confirmations." + }, + "onboardingPinExtensionDescription2": { + "message": "You can open MetaMask by clicking on the extension and access your wallet with 1 click." + }, + "onboardingPinExtensionDescription3": { + "message": "Click browser extension icon to access it instantly" + }, + "onboardingPinExtensionLabel": { + "message": "Pin MetaMask" + }, + "onboardingPinExtensionStep1": { + "message": "1" + }, + "onboardingPinExtensionStep2": { + "message": "2" + }, + "onboardingPinExtensionTitle": { + "message": "Your MetaMask install is complete!" + }, + "onboardingPinMmiExtensionLabel": { + "message": "Pin MetaMask Institutional" + }, + "onboardingUsePhishingDetectionDescription": { + "message": "Phishing detection alerts rely on communication with $1. jsDeliver will have access to your IP address. View $2.", + "description": "The $1 is the word 'jsDeliver', from key 'jsDeliver' and $2 is the words Privacy Policy from key 'privacyMsg', both separated here so that it can be wrapped as a link" + }, + "oneDayAbbreviation": { + "message": "1D", + "description": "Shortened form of '1 day'" + }, + "oneMonthAbbreviation": { + "message": "1M", + "description": "Shortened form of '1 month'" + }, + "oneWeekAbbreviation": { + "message": "1W", + "description": "Shortened form of '1 week'" + }, + "oneYearAbbreviation": { + "message": "1Y", + "description": "Shortened form of '1 year'" + }, + "onekey": { + "message": "OneKey" + }, + "onlyAddTrustedNetworks": { + "message": "A malicious network provider can lie about the state of the blockchain and record your network activity. Only add custom networks you trust." + }, + "onlyConnectTrust": { + "message": "Only connect with sites you trust. $1", + "description": "Text displayed above the buttons for connection confirmation. $1 is the link to the learn more web page." + }, + "openCustodianApp": { + "message": "Open $1 app", + "description": "The $1 is the name of the Custodian that will be open" + }, + "openFullScreenForLedgerWebHid": { + "message": "Go to full screen to connect your Ledger.", + "description": "Shown to the user on the confirm screen when they are viewing MetaMask in a popup window but need to connect their ledger via webhid." + }, + "openInBlockExplorer": { + "message": "Open in block explorer" + }, + "openSeaNew": { + "message": "OpenSea" + }, + "operationFailed": { + "message": "Operation Failed" + }, + "optional": { + "message": "Optional" + }, + "optionalWithParanthesis": { + "message": "(Optional)" + }, + "options": { + "message": "Options" + }, + "or": { + "message": "or" + }, + "origin": { + "message": "Origin" + }, + "osTheme": { + "message": "System" + }, + "otherSnaps": { + "message": "other snaps", + "description": "Used in the 'permission_rpc' message." + }, + "outdatedBrowserNotification": { + "message": "Your browser is out of date. If you don't update your browser, you won't be able to get security patches and new features from MetaMask." + }, + "padlock": { + "message": "Padlock" + }, + "parameters": { + "message": "Parameters" + }, + "participateInMetaMetrics": { + "message": "Participate in MetaMetrics" + }, + "participateInMetaMetricsDescription": { + "message": "Participate in MetaMetrics to help us make MetaMask better" + }, + "password": { + "message": "Password" + }, + "passwordMmiTermsWarning": { + "message": "I understand that MetaMask Institutional cannot recover this password for me. $1" + }, + "passwordNotLongEnough": { + "message": "Password not long enough" + }, + "passwordSetupDetails": { + "message": "This password will unlock your MetaMask wallet only on this device. MetaMask can not recover this password." + }, + "passwordStrength": { + "message": "Password strength: $1", + "description": "Return password strength to the user when user wants to create password." + }, + "passwordStrengthDescription": { + "message": "A strong password can improve the security of your wallet should your device be stolen or compromised." + }, + "passwordTermsWarning": { + "message": "I understand that MetaMask cannot recover this password for me. $1" + }, + "passwordsDontMatch": { + "message": "Passwords don't match" + }, + "pasteJWTToken": { + "message": "Paste or drop your token here:" + }, + "pastePrivateKey": { + "message": "Enter your private key string here:", + "description": "For importing an account from a private key" + }, + "paymasterInUse": { + "message": "The gas for this transaction will be paid by a paymaster.", + "description": "Alert shown in transaction confirmation if paymaster in use." + }, + "pending": { + "message": "Pending" + }, + "pendingTransactionInfo": { + "message": "This transaction will not process until that one is complete." + }, + "pendingTransactionMultiple": { + "message": "You have ($1) pending transactions." + }, + "pendingTransactionSingle": { + "message": "You have (1) pending transaction.", + "description": "$1 is count of pending transactions" + }, + "permissionDetails": { + "message": "Permission details" + }, + "permissionRequest": { + "message": "Permission request" + }, + "permissionRequested": { + "message": "Requested now" + }, + "permissionRequestedForAccounts": { + "message": "Requested now for $1", + "description": "Permission cell status for requested permission including accounts, rendered as AvatarGroup which is $1." + }, + "permissionRevoked": { + "message": "Revoked in this update" + }, + "permissionRevokedForAccounts": { + "message": "Revoked in this update for $1", + "description": "Permission cell status for revoked permission including accounts, rendered as AvatarGroup which is $1." + }, + "permission_accessNamedSnap": { + "message": "Connect to $1.", + "description": "The description for the `wallet_snap` permission. $1 is the human-readable name of the snap." + }, + "permission_accessNetwork": { + "message": "Access the internet.", + "description": "The description of the `endowment:network-access` permission." + }, + "permission_accessNetworkDescription": { + "message": "Allow $1 to access the internet. This can be used to both send and receive data with third-party servers.", + "description": "An extended description of the `endowment:network-access` permission. $1 is the snap name." + }, + "permission_accessSnap": { + "message": "Connect to the $1 snap.", + "description": "The description for the `wallet_snap` permission. $1 is the name of the snap." + }, + "permission_accessSnapDescription": { + "message": "Allow the website or snap to interact with $1.", + "description": "The description for the `wallet_snap_*` permission. $1 is the name of the Snap." + }, + "permission_cronjob": { + "message": "Schedule and execute periodic actions.", + "description": "The description for the `snap_cronjob` permission" + }, + "permission_cronjobDescription": { + "message": "Allow $1 to perform actions that run periodically at fixed times, dates, or intervals. This can be used to trigger time-sensitive interactions or notifications.", + "description": "An extended description for the `snap_cronjob` permission. $1 is the snap name." + }, + "permission_dialog": { + "message": "Display dialog windows in MetaMask.", + "description": "The description for the `snap_dialog` permission" + }, + "permission_dialogDescription": { + "message": "Allow $1 to display MetaMask popups with custom text, input field, and buttons to approve or reject an action.\nCan be used to create e.g. alerts, confirmations, and opt-in flows for a snap.", + "description": "An extended description for the `snap_dialog` permission. $1 is the snap name." + }, + "permission_ethereumAccounts": { + "message": "See address, account balance, activity and suggest transactions to approve", + "description": "The description for the `eth_accounts` permission" + }, + "permission_ethereumProvider": { + "message": "Access the Ethereum provider.", + "description": "The description for the `endowment:ethereum-provider` permission" + }, + "permission_ethereumProviderDescription": { + "message": "Allow $1 to communicate with MetaMask directly, in order for it to read data from the blockchain and suggest messages and transactions.", + "description": "An extended description for the `endowment:ethereum-provider` permission. $1 is the snap name." + }, + "permission_getEntropy": { + "message": "Derive arbitrary keys unique to $1.", + "description": "The description for the `snap_getEntropy` permission. $1 is the snap name." + }, + "permission_getEntropyDescription": { + "message": "Allow $1 to derive arbitrary keys unique to $1, without exposing them. These keys are separate from your MetaMask account(s) and not related to your private keys or Secret Recovery Phrase. Other snaps cannot access this information.", + "description": "An extended description for the `snap_getEntropy` permission. $1 is the snap name." + }, + "permission_getLocale": { + "message": "View your preferred language.", + "description": "The description for the `snap_getLocale` permission" + }, + "permission_getLocaleDescription": { + "message": "Let $1 access your preferred language from your MetaMask settings. This can be used to localize and display $1's content using your language.", + "description": "An extended description for the `snap_getLocale` permission. $1 is the snap name." + }, + "permission_homePage": { + "message": "Display a custom screen", + "description": "The description for the `endowment:page-home` permission" + }, + "permission_homePageDescription": { + "message": "Let $1 display a custom home screen in MetaMask. This can be used for user interfaces, configuration, and dashboards.", + "description": "An extended description for the `endowment:page-home` permission. $1 is the snap name." + }, + "permission_keyring": { + "message": "Allow requests for adding and controlling Ethereum accounts", + "description": "The description for the `endowment:keyring` permission" + }, + "permission_keyringDescription": { + "message": "Let $1 receive requests to add or remove accounts, plus sign and transact on behalf of these accounts.", + "description": "An extended description for the `endowment:keyring` permission. $1 is the snap name." + }, + "permission_lifecycleHooks": { + "message": "Use lifecycle hooks.", + "description": "The description for the `endowment:lifecycle-hooks` permission" + }, + "permission_lifecycleHooksDescription": { + "message": "Allow $1 to use lifecycle hooks to run code at specific times during its lifecycle.", + "description": "An extended description for the `endowment:lifecycle-hooks` permission. $1 is the snap name." + }, + "permission_manageAccounts": { + "message": "Add and control Ethereum accounts", + "description": "The description for `snap_manageAccounts` permission" + }, + "permission_manageAccountsDescription": { + "message": "Allow $1 to add or remove Ethereum accounts, then transact and sign with these accounts.", + "description": "An extended description for the `snap_manageAccounts` permission. $1 is the snap name." + }, + "permission_manageBip32Keys": { + "message": "Manage $1 accounts.", + "description": "The description for the `snap_getBip32Entropy` permission. $1 is a derivation path, e.g. 'm/44'/0'/0' (secp256k1)'." + }, + "permission_manageBip44AndBip32KeysDescription": { + "message": "Allow $1 to manage accounts and assets on the requested network. These accounts are derived and backed up using your secret recovery phrase (without revealing it). With the power to derive keys, $1 can support a variety of blockchain protocols beyond Ethereum (EVMs).", + "description": "An extended description for the `snap_getBip44Entropy` and `snap_getBip44Entropy` permissions. $1 is the snap name." + }, + "permission_manageBip44Keys": { + "message": "Manage $1 accounts.", + "description": "The description for the `snap_getBip44Entropy` permission. $1 is the name of a protocol, e.g. 'Filecoin'." + }, + "permission_manageState": { + "message": "Store and manage its data on your device.", + "description": "The description for the `snap_manageState` permission" + }, + "permission_manageStateDescription": { + "message": "Allow $1 to store, update, and retrieve data securely with encryption. Other snaps cannot access this information.", + "description": "An extended description for the `snap_manageState` permission. $1 is the snap name." + }, + "permission_nameLookup": { + "message": "Provide domain and address lookups.", + "description": "The description for the `endowment:name-lookup` permission." + }, + "permission_nameLookupDescription": { + "message": "Allow the snap to fetch and display address and domain lookups in different parts of the MetaMask UI.", + "description": "An extended description for the `endowment:name-lookup` permission." + }, + "permission_notifications": { + "message": "Show notifications.", + "description": "The description for the `snap_notify` permission" + }, + "permission_notificationsDescription": { + "message": "Allow $1 to display notifications within MetaMask. A short notification text can be triggered by a snap for actionable or time-sensitive information.", + "description": "An extended description for the `snap_notify` permission. $1 is the snap name." + }, + "permission_rpc": { + "message": "Allow $1 to communicate directly with $2.", + "description": "The description for the `endowment:rpc` permission. $1 is 'other snaps' or 'websites', $2 is the snap name." + }, + "permission_rpcDescription": { + "message": "Allow $1 to send messages to $2 and receive a response from $2.", + "description": "An extended description for the `endowment:rpc` permission. $1 is 'other snaps' or 'websites', $2 is the snap name." + }, + "permission_rpcDescriptionOriginList": { + "message": "$1 and $2", + "description": "A list of allowed origins where $2 is the last origin of the list and $1 is the rest of the list separated by ','." + }, + "permission_signatureInsight": { + "message": "Display signature insights modal.", + "description": "The description for the `endowment:signature-insight` permission" + }, + "permission_signatureInsightDescription": { + "message": "Allow $1 to display a modal with insights on any signature request before approval. This can be used for anti-phishing and security solutions.", + "description": "An extended description for the `endowment:signature-insight` permission. $1 is the snap name." + }, + "permission_signatureInsightOrigin": { + "message": "See the origins of websites that initiate a signature request", + "description": "The description for the `signatureOrigin` caveat, to be used with the `endowment:signature-insight` permission" + }, + "permission_signatureInsightOriginDescription": { + "message": "Allow $1 to see the origin (URI) of websites that initiate signature requests. This can be used for anti-phishing and security solutions.", + "description": "An extended description for the `signatureOrigin` caveat, to be used with the `endowment:signature-insight` permission. $1 is the snap name." + }, + "permission_transactionInsight": { + "message": "Fetch and display transaction insights.", + "description": "The description for the `endowment:transaction-insight` permission" + }, + "permission_transactionInsightDescription": { + "message": "Allow $1 to decode transactions and show insights within the MetaMask UI. This can be used for anti-phishing and security solutions.", + "description": "An extended description for the `endowment:transaction-insight` permission. $1 is the snap name." + }, + "permission_transactionInsightOrigin": { + "message": "See the origins of websites that suggest transactions", + "description": "The description for the `transactionOrigin` caveat, to be used with the `endowment:transaction-insight` permission" + }, + "permission_transactionInsightOriginDescription": { + "message": "Allow $1 to see the origin (URI) of websites that suggest transactions. This can be used for anti-phishing and security solutions.", + "description": "An extended description for the `transactionOrigin` caveat, to be used with the `endowment:transaction-insight` permission. $1 is the snap name." + }, + "permission_unknown": { + "message": "Unknown permission: $1", + "description": "$1 is the name of a requested permission that is not recognized." + }, + "permission_viewBip32PublicKeys": { + "message": "View your public key for $1 ($2).", + "description": "The description for the `snap_getBip32PublicKey` permission. $1 is a derivation path, e.g. 'm/44'/0'/0''. $2 is the elliptic curve name, e.g. 'secp256k1'." + }, + "permission_viewBip32PublicKeysDescription": { + "message": "Allow $2 to view your public keys (and addresses) for $1. This does not grant any control of accounts or assets.", + "description": "An extended description for the `snap_getBip32PublicKey` permission. $1 is a derivation path (name). $2 is the snap name." + }, + "permission_viewNamedBip32PublicKeys": { + "message": "View your public key for $1.", + "description": "The description for the `snap_getBip32PublicKey` permission. $1 is a name for the derivation path, e.g., 'Ethereum accounts'." + }, + "permission_walletSwitchEthereumChain": { + "message": "Switch to and use the following network", + "description": "The label for the `wallet_switchEthereumChain` permission" + }, + "permission_webAssembly": { + "message": "Support for WebAssembly.", + "description": "The description of the `endowment:webassembly` permission." + }, + "permission_webAssemblyDescription": { + "message": "Allow $1 to access low-level execution environments via WebAssembly.", + "description": "An extended description of the `endowment:webassembly` permission. $1 is the snap name." + }, + "permissions": { + "message": "Permissions" + }, + "permissionsPageEmptyContent": { + "message": "Nothing to see here" + }, + "permissionsPageEmptySubContent": { + "message": "This is where you can see the permissions you've given to installed Snaps or connected sites." + }, + "permissionsPageTourDescription": { + "message": "This is your control panel for managing permissions given to connected sites and installed Snaps." + }, + "permissionsPageTourTitle": { + "message": "Connected sites are now permissions" + }, + "permitSimulationDetailInfo": { + "message": "You're giving the spender permission to spend this many tokens from your account." + }, + "personalAddressDetected": { + "message": "Personal address detected. Input the token contract address." + }, + "petnamesEnabledToggle": { + "message": "Allow nicknames" + }, + "petnamesEnabledToggleDescription": { + "message": "This lets you assign a nickname to any address. We’ll suggest names for addresses that you interact with when possible." + }, + "pinExtensionDescription": { + "message": "Navigate to the extension menu and pin MetaMask Institutional for seamless access." + }, + "pinExtensionTitle": { + "message": "Pin extension" + }, + "pinToTop": { + "message": "Pin to top" + }, + "pleaseConfirm": { + "message": "Please confirm" + }, + "plusMore": { + "message": "+ $1 more", + "description": "$1 is the number of additional items" + }, + "plusXMore": { + "message": "+ $1 more", + "description": "$1 is a number of additional but unshown items in a list- this message will be shown in place of those items" + }, + "popularCustomNetworks": { + "message": "Popular custom networks" + }, + "popularNetworkAddToolTip": { + "message": "Some of these networks rely on third parties. The connections may be less reliable or enable third-parties to track activity. $1", + "description": "$1 is Learn more link" + }, + "portfolio": { + "message": "Portfolio" + }, + "portfolioDashboard": { + "message": "Portfolio Dashboard" + }, + "preparingSwap": { + "message": "Preparing swap..." + }, + "prev": { + "message": "Prev" + }, + "price": { + "message": "Price" + }, + "priceUnavailable": { + "message": "price unavailable" + }, + "primaryCurrencySetting": { + "message": "Primary currency" + }, + "primaryCurrencySettingDescription": { + "message": "Select native to prioritize displaying values in the native currency of the chain (e.g. ETH). Select Fiat to prioritize displaying values in your selected fiat currency." + }, + "primaryType": { + "message": "Primary type" + }, + "priorityFee": { + "message": "Priority fee" + }, + "priorityFeeProperCase": { + "message": "Priority Fee" + }, + "privacy": { + "message": "Privacy" + }, + "privacyMsg": { + "message": "Privacy policy" + }, + "privateKey": { + "message": "Private Key", + "description": "select this type of file to use to import an account" + }, + "privateKeyCopyWarning": { + "message": "Private key for $1", + "description": "$1 represents the account name" + }, + "privateKeyHidden": { + "message": "The private key is hidden", + "description": "Explains that the private key input is hidden" + }, + "privateKeyShow": { + "message": "Show/Hide the private key input", + "description": "Describes a toggle that is used to show or hide the private key input" + }, + "privateKeyShown": { + "message": "This private key is being shown", + "description": "Explains that the private key input is being shown" + }, + "privateKeyWarning": { + "message": "Warning: Never disclose this key. Anyone with your private keys can steal any assets held in your account." + }, + "privateNetwork": { + "message": "Private network" + }, + "proceedWithTransaction": { + "message": "I want to proceed anyway" + }, + "productAnnouncements": { + "message": "Product announcements" + }, + "profileSync": { + "message": "Profile Sync" + }, + "profileSyncConfirmation": { + "message": "If you turn off profile sync, you won’t be able to receive notifications." + }, + "profileSyncDescription": { + "message": "Creates a profile that MetaMask uses to sync some settings among your devices. This is required to get notifications. $1." + }, + "profileSyncPrivacyLink": { + "message": "Learn how we protect your privacy" + }, + "proposedApprovalLimit": { + "message": "Proposed approval limit" + }, + "provide": { + "message": "Provide" + }, + "publicAddress": { + "message": "Public address" + }, + "pushPlatformNotificationsFundsReceivedDescription": { + "message": "You received $1 $2" + }, + "pushPlatformNotificationsFundsReceivedDescriptionDefault": { + "message": "You received some tokens" + }, + "pushPlatformNotificationsFundsReceivedTitle": { + "message": "Funds received" + }, + "pushPlatformNotificationsFundsSentDescription": { + "message": "You successfully sent $1 $2" + }, + "pushPlatformNotificationsFundsSentDescriptionDefault": { + "message": "You successfully sent some tokens" + }, + "pushPlatformNotificationsFundsSentTitle": { + "message": "Funds sent" + }, + "pushPlatformNotificationsNftReceivedDescription": { + "message": "You received new NFTs" + }, + "pushPlatformNotificationsNftReceivedTitle": { + "message": "NFT received" + }, + "pushPlatformNotificationsNftSentDescription": { + "message": "You have successfully sent an NFT" + }, + "pushPlatformNotificationsNftSentTitle": { + "message": "NFT sent" + }, + "pushPlatformNotificationsStakingLidoStakeCompletedDescription": { + "message": "Your Lido stake was successful" + }, + "pushPlatformNotificationsStakingLidoStakeCompletedTitle": { + "message": "Stake complete" + }, + "pushPlatformNotificationsStakingLidoStakeReadyToBeWithdrawnDescription": { + "message": "Your Lido stake is now ready to be withdrawn" + }, + "pushPlatformNotificationsStakingLidoStakeReadyToBeWithdrawnTitle": { + "message": "Stake ready for withdrawal" + }, + "pushPlatformNotificationsStakingLidoWithdrawalCompletedDescription": { + "message": "Your Lido withdrawal was successful" + }, + "pushPlatformNotificationsStakingLidoWithdrawalCompletedTitle": { + "message": "Withdrawal completed" + }, + "pushPlatformNotificationsStakingLidoWithdrawalRequestedDescription": { + "message": "Your Lido withdrawal request was submitted" + }, + "pushPlatformNotificationsStakingLidoWithdrawalRequestedTitle": { + "message": "Withdrawal requested" + }, + "pushPlatformNotificationsStakingRocketpoolStakeCompletedDescription": { + "message": "Your RocketPool stake was successful" + }, + "pushPlatformNotificationsStakingRocketpoolStakeCompletedTitle": { + "message": "Stake complete" + }, + "pushPlatformNotificationsStakingRocketpoolUnstakeCompletedDescription": { + "message": "Your RocketPool unstake was successful" + }, + "pushPlatformNotificationsStakingRocketpoolUnstakeCompletedTitle": { + "message": "Unstake complete" + }, + "pushPlatformNotificationsSwapCompletedDescription": { + "message": "Your MetaMask Swap was successful" + }, + "pushPlatformNotificationsSwapCompletedTitle": { + "message": "Swap completed" + }, + "queued": { + "message": "Queued" + }, + "quoteRate": { + "message": "Quote rate" + }, + "rank": { + "message": "Rank" + }, + "reAddAccounts": { + "message": "re-add any other accounts" + }, + "reAdded": { + "message": "re-added" + }, + "readdToken": { + "message": "You can add this token back in the future by going to “Import token” in your accounts options menu." + }, + "receive": { + "message": "Receive" + }, + "receiveTokensCamelCase": { + "message": "Receive tokens" + }, + "recipientAddressPlaceholder": { + "message": "Enter public address (0x) or ENS name" + }, + "recipientAddressPlaceholderFlask": { + "message": "Enter public address (0x) or domain name" + }, + "recommendedGasLabel": { + "message": "Recommended" + }, + "recoveryPhraseReminderBackupStart": { + "message": "Start here" + }, + "recoveryPhraseReminderConfirm": { + "message": "Got it" + }, + "recoveryPhraseReminderHasBackedUp": { + "message": "Always keep your Secret Recovery Phrase in a secure and secret place" + }, + "recoveryPhraseReminderHasNotBackedUp": { + "message": "Need to backup your Secret Recovery Phrase again?" + }, + "recoveryPhraseReminderItemOne": { + "message": "Never share your Secret Recovery Phrase with anyone" + }, + "recoveryPhraseReminderItemTwo": { + "message": "The MetaMask team will never ask for your Secret Recovery Phrase" + }, + "recoveryPhraseReminderSubText": { + "message": "Your Secret Recovery Phrase controls all of your accounts." + }, + "recoveryPhraseReminderTitle": { + "message": "Protect your funds" + }, + "redesignedConfirmationsEnabledToggle": { + "message": "Improved signature requests" + }, + "redesignedConfirmationsToggleDescription": { + "message": "Turn this on to see signature requests in an enhanced format." + }, + "refreshList": { + "message": "Refresh list" + }, + "reject": { + "message": "Reject" + }, + "rejectAll": { + "message": "Reject all" + }, + "rejectRequestsDescription": { + "message": "You are about to batch reject $1 requests." + }, + "rejectRequestsN": { + "message": "Reject $1 requests" + }, + "rejectTxsDescription": { + "message": "You are about to batch reject $1 transactions." + }, + "rejectTxsN": { + "message": "Reject $1 transactions" + }, + "rejected": { + "message": "Rejected" + }, + "remember": { + "message": "Remember:" + }, + "remove": { + "message": "Remove" + }, + "removeAccount": { + "message": "Remove account" + }, + "removeAccountDescription": { + "message": "This account will be removed from your wallet. Please make sure you have the original Secret Recovery Phrase or private key for this imported account before continuing. You can import or create accounts again from the account drop-down. " + }, + "removeJWT": { + "message": "Remove custodian token" + }, + "removeJWTDescription": { + "message": "Are you sure you want to remove this token? All accounts assigned to this token will be removed from extension as well: " + }, + "removeKeyringSnap": { + "message": "Removing this Snap removes these accounts from MetaMask:" + }, + "removeKeyringSnapToolTip": { + "message": "The snap controls the accounts, and by removing it, the accounts will be removed from MetaMask, too, but they will remain in the blockchain." + }, + "removeNFT": { + "message": "Remove NFT" + }, + "removeNftErrorMessage": { + "message": "We could not remove this NFT." + }, + "removeNftMessage": { + "message": "NFT was successfully removed!" + }, + "removeSnap": { + "message": "Remove Snap" + }, + "removeSnapAccountDescription": { + "message": "If you proceed, this account will no longer be available in MetaMask." + }, + "removeSnapAccountTitle": { + "message": "Remove account" + }, + "removeSnapConfirmation": { + "message": "Are you sure you want to remove $1?", + "description": "$1 represents the name of the snap" + }, + "removeSnapDescription": { + "message": "This action will delete the snap, its data and revoke your given permissions." + }, + "replace": { + "message": "replace" + }, + "reportIssue": { + "message": "Report an issue" + }, + "requestFlaggedAsMaliciousFallbackCopyReason": { + "message": "The security provider has not shared additional details" + }, + "requestFlaggedAsMaliciousFallbackCopyReasonTitle": { + "message": "Request flagged as malicious" + }, + "requestFrom": { + "message": "Request from" + }, + "requestFromInfo": { + "message": "This is the site asking for your signature." + }, + "requestFromTransactionDescription": { + "message": "This is the site asking for your confirmation." + }, + "requestMayNotBeSafe": { + "message": "Request may not be safe" + }, + "requestMayNotBeSafeError": { + "message": "The security provider didn't detect any known malicious activity, but it still may not be safe to continue." + }, + "requestNotVerified": { + "message": "Request not verified" + }, + "requestNotVerifiedError": { + "message": "Because of an error, this request was not verified by the security provider. Proceed with caution." + }, + "requestsAwaitingAcknowledgement": { + "message": "requests waiting to be acknowledged" + }, + "required": { + "message": "Required" + }, + "reset": { + "message": "Reset" + }, + "resetStates": { + "message": "Reset States" + }, + "resetWallet": { + "message": "Reset wallet" + }, + "resetWalletSubHeader": { + "message": "MetaMask does not keep a copy of your password. If you’re having trouble unlocking your account, you will need to reset your wallet. You can do this by providing the Secret Recovery Phrase you used when you set up your wallet." + }, + "resetWalletUsingSRP": { + "message": "This action will delete your current wallet and Secret Recovery Phrase from this device, along with the list of accounts you’ve curated. After resetting with a Secret Recovery Phrase, you’ll see a list of accounts based on the Secret Recovery Phrase you use to reset. This new list will automatically include accounts that have a balance. You’ll also be able to $1 created previously. Custom accounts that you’ve imported will need to be $2, and any custom tokens you’ve added to an account will need to be $3 as well." + }, + "resetWalletWarning": { + "message": "Make sure you’re using the correct Secret Recovery Phrase before proceeding. You will not be able to undo this." + }, + "restartMetamask": { + "message": "Restart MetaMask" + }, + "restore": { + "message": "Restore" + }, + "restoreUserData": { + "message": "Restore user data" + }, + "restoreUserDataDescription": { + "message": "You can restore data like contacts and preferences from a backup file." + }, + "resultPageError": { + "message": "Error" + }, + "resultPageErrorDefaultMessage": { + "message": "The operation failed." + }, + "resultPageSuccess": { + "message": "Success" + }, + "resultPageSuccessDefaultMessage": { + "message": "The operation completed successfully." + }, + "retryTransaction": { + "message": "Retry transaction" + }, + "reusedTokenNameWarning": { + "message": "A token here reuses a symbol from another token you watch, this can be confusing or deceptive." + }, + "revealSeedWords": { + "message": "Reveal Secret Recovery Phrase" + }, + "revealSeedWordsDescription1": { + "message": "The $1 provides $2", + "description": "This is a sentence consisting of link using 'revealSeedWordsSRPName' as $1 and bolded text using 'revealSeedWordsDescription3' as $2." + }, + "revealSeedWordsDescription2": { + "message": "MetaMask is a $1. That means you're the owner of your SRP.", + "description": "$1 is text link with the message from 'revealSeedWordsNonCustodialWallet'" + }, + "revealSeedWordsDescription3": { + "message": "full access to your wallet and funds.\n" + }, + "revealSeedWordsNonCustodialWallet": { + "message": "non-custodial wallet" + }, + "revealSeedWordsQR": { + "message": "QR" + }, + "revealSeedWordsSRPName": { + "message": "Secret Recovery Phrase (SRP)" + }, + "revealSeedWordsText": { + "message": "Text" + }, + "revealSeedWordsWarning": { + "message": "Make sure no one is looking at your screen. $1", + "description": "$1 is bolded text using the message from 'revealSeedWordsWarning2'" + }, + "revealSeedWordsWarning2": { + "message": "MetaMask Support will never request this.", + "description": "The bolded texted in the second part of 'revealSeedWordsWarning'" + }, + "revealSensitiveContent": { + "message": "Reveal sensitive content" + }, + "revealTheSeedPhrase": { + "message": "Reveal seed phrase" + }, + "reviewAlerts": { + "message": "Review alerts" + }, + "revokeAllTokensTitle": { + "message": "Revoke permission to access and transfer all of your $1?", + "description": "$1 is the symbol of the token for which the user is revoking approval" + }, + "revokeAllTokensTitleWithoutSymbol": { + "message": "Revoke permission to access and transfer all of your NFTs from $1?", + "description": "$1 is a link to contract on the block explorer when we're not able to retrieve a erc721 or erc1155 name" + }, + "revokeApproveForAllDescription": { + "message": "This revokes the permission for a third party to access and transfer all of your $1 without further notice.", + "description": "$1 is either a string or link of a given token symbol or name" + }, + "revokeApproveForAllDescriptionWithoutSymbol": { + "message": "This revokes the permission for a third party to access and transfer all of your NFTs from $1 without further notice.", + "description": "$1 is a link to contract on the block explorer when we're not able to retrieve a erc721 or erc1155 name" + }, + "revokePermission": { + "message": "Revoke permission" + }, + "revokeSpendingCap": { + "message": "Revoke spending cap for your $1", + "description": "$1 is a token symbol" + }, + "revokeSpendingCapTooltipText": { + "message": "This third party will be unable to spend any more of your current or future tokens." + }, + "rpcUrl": { + "message": "New RPC URL" + }, + "safeTransferFrom": { + "message": "Safe transfer from" + }, + "save": { + "message": "Save" + }, + "scanInstructions": { + "message": "Place the QR code in front of your camera" + }, + "scanQrCode": { + "message": "Scan QR code" + }, + "scrollDown": { + "message": "Scroll down" + }, + "search": { + "message": "Search" + }, + "searchAccounts": { + "message": "Search accounts" + }, + "searchNfts": { + "message": "Search NFTs" + }, + "searchTokens": { + "message": "Search tokens" + }, + "secretRecoveryPhrase": { + "message": "Secret Recovery Phrase" + }, + "secureWallet": { + "message": "Secure wallet" + }, + "security": { + "message": "Security" + }, + "securityAlert": { + "message": "Security alert from $1 and $2" + }, + "securityAlerts": { + "message": "Security alerts" + }, + "securityAlertsDescription": { + "message": "This feature alerts you to malicious activity by actively reviewing transaction and signature requests. $1", + "description": "Link to learn more about security alerts" + }, + "securityAndPrivacy": { + "message": "Security & privacy" + }, + "securityProviderPoweredBy": { + "message": "Powered by $1", + "description": "The security provider that is providing data" + }, + "seeDetails": { + "message": "See details" + }, + "seedPhraseConfirm": { + "message": "Confirm Secret Recovery Phrase" + }, + "seedPhraseEnterMissingWords": { + "message": "Confirm Secret Recovery Phrase" + }, + "seedPhraseIntroNotRecommendedButtonCopy": { + "message": "Remind me later (not recommended)" + }, + "seedPhraseIntroRecommendedButtonCopy": { + "message": "Secure my wallet (recommended)" + }, + "seedPhraseIntroSidebarBulletFour": { + "message": "Write down and store in multiple secret places" + }, + "seedPhraseIntroSidebarBulletOne": { + "message": "Save in a password manager" + }, + "seedPhraseIntroSidebarBulletThree": { + "message": "Store in a safe deposit box" + }, + "seedPhraseIntroSidebarCopyOne": { + "message": "Your Secret Recovery Phrase is a 12-word phrase that is the “master key” to your wallet and your funds" + }, + "seedPhraseIntroSidebarCopyThree": { + "message": "If someone asks for your recovery phrase they are likely trying to scam you and steal your wallet funds." + }, + "seedPhraseIntroSidebarCopyTwo": { + "message": "Never, ever share your Secret Recovery Phrase, not even with MetaMask!" + }, + "seedPhraseIntroSidebarTitleOne": { + "message": "What is a Secret Recovery Phrase?" + }, + "seedPhraseIntroSidebarTitleThree": { + "message": "Should I share my Secret Recovery Phrase?" + }, + "seedPhraseIntroSidebarTitleTwo": { + "message": "How do I save my Secret Recovery Phrase?" + }, + "seedPhraseIntroTitle": { + "message": "Secure your wallet" + }, + "seedPhraseIntroTitleCopy": { + "message": "Before getting started, watch this short video to learn about your Secret Recovery Phrase and how to keep your wallet safe." + }, + "seedPhraseReq": { + "message": "Secret Recovery Phrases contain 12, 15, 18, 21, or 24 words" + }, + "seedPhraseWriteDownDetails": { + "message": "Write down this 12-word Secret Recovery Phrase and save it in a place that you trust and only you can access." + }, + "seedPhraseWriteDownHeader": { + "message": "Write down your Secret Recovery Phrase" + }, + "select": { + "message": "Select" + }, + "selectAccounts": { + "message": "Select the account(s) to use on this site" + }, + "selectAccountsForSnap": { + "message": "Select the account(s) to use with this snap" + }, + "selectAll": { + "message": "Select all" + }, + "selectAllAccounts": { + "message": "Select all accounts" + }, + "selectAnAccount": { + "message": "Select an account" + }, + "selectAnAccountAlreadyConnected": { + "message": "This account has already been connected to MetaMask" + }, + "selectAnAccountHelp": { + "message": "Select the custodian accounts to use in MetaMask Institutional." + }, + "selectEnableDisplayMediaPrivacyPreference": { + "message": "Turn on Display NFT Media" + }, + "selectHdPath": { + "message": "Select HD path" + }, + "selectJWT": { + "message": "Select token" + }, + "selectNFTPrivacyPreference": { + "message": "Enable NFT Autodetection" + }, + "selectPathHelp": { + "message": "If you don't see the accounts you expect, try switching the HD path or current selected network." + }, + "selectType": { + "message": "Select Type" + }, + "selectingAllWillAllow": { + "message": "Selecting all will allow this site to view all of your current accounts. Make sure you trust this site." + }, + "send": { + "message": "Send" + }, + "sendBugReport": { + "message": "Send us a bug report." + }, + "sendNoContactsConversionText": { + "message": "click here" + }, + "sendNoContactsDescription": { + "message": "Contacts allow you to safely send transactions to another account multiple times. To create a contact, $1", + "description": "$1 represents the action text 'click here'" + }, + "sendNoContactsTitle": { + "message": "You don't have any contacts yet" + }, + "sendSelectReceiveAsset": { + "message": "Select asset to receive" + }, + "sendSelectSendAsset": { + "message": "Select asset to send" + }, + "sendSpecifiedTokens": { + "message": "Send $1", + "description": "Symbol of the specified token" + }, + "sendSwapSubmissionWarning": { + "message": "Clicking this button will immediately initiate your swap transaction. Please review your transaction details before proceeding." + }, + "sendTokenAsToken": { + "message": "Send $1 as $2", + "description": "Used in the transaction display list to describe a swap and send. $1 and $2 are the symbols of tokens in involved in the swap." + }, + "sendingAsset": { + "message": "Sending $1" + }, + "sendingDisabled": { + "message": "Sending of ERC-1155 NFT assets is not yet supported." + }, + "sendingNativeAsset": { + "message": "Sending $1", + "description": "$1 represents the native currency symbol for the current network (e.g. ETH or BNB)" + }, + "sendingToTokenContractWarning": { + "message": "Warning: you are about to send to a token contract which could result in a loss of funds. $1", + "description": "$1 is a clickable link with text defined by the 'learnMoreUpperCase' key. The link will open to a support article regarding the known contract address warning" + }, + "sendingZeroAmount": { + "message": "You are sending 0 $1." + }, + "sepolia": { + "message": "Sepolia test network" + }, + "serviceWorkerKeepAlive": { + "message": "Service Worker Keep Alive" + }, + "setAdvancedPrivacySettingsDetails": { + "message": "MetaMask uses these trusted third-party services to enhance product usability and safety." + }, + "setApprovalForAll": { + "message": "Set approval for all" + }, + "setApprovalForAllTitle": { + "message": "Approve $1 with no spend limit", + "description": "The token symbol that is being approved" + }, + "settingAddSnapAccount": { + "message": "Add account Snap" + }, + "settings": { + "message": "Settings" + }, + "settingsSearchMatchingNotFound": { + "message": "No matching results found." + }, + "settingsSubHeadingSignaturesAndTransactions": { + "message": "Signature and transaction requests" + }, + "show": { + "message": "Show" + }, + "showAccount": { + "message": "Show account" + }, + "showExtensionInFullSizeView": { + "message": "Show extension in full-size view" + }, + "showExtensionInFullSizeViewDescription": { + "message": "Turn this on to make full-size view your default when you click the extension icon." + }, + "showFiatConversionInTestnets": { + "message": "Show conversion on test networks" + }, + "showFiatConversionInTestnetsDescription": { + "message": "Select this to show fiat conversion on test networks" + }, + "showHexData": { + "message": "Show hex data" + }, + "showHexDataDescription": { + "message": "Select this to show the hex data field on the send screen" + }, + "showIncomingTransactions": { + "message": "Show incoming transactions" + }, + "showIncomingTransactionsDescription": { + "message": "This relies on $1 which will have access to your Ethereum address and your IP address. $2", + "description": "$1 is the link to etherscan url and $2 is the link to the privacy policy of consensys APIs" + }, + "showIncomingTransactionsExplainer": { + "message": "This relies on different third-party APIs for each network, which expose your Ethereum address and your IP address." + }, + "showLess": { + "message": "Show less" + }, + "showMore": { + "message": "Show more" + }, + "showNft": { + "message": "Show NFT" + }, + "showPermissions": { + "message": "Show permissions" + }, + "showPrivateKey": { + "message": "Show private key" + }, + "showTestnetNetworks": { + "message": "Show test networks" + }, + "showTestnetNetworksDescription": { + "message": "Select this to show test networks in network list" + }, + "sigRequest": { + "message": "Signature request" + }, + "sign": { + "message": "Sign" + }, + "signatureRequest": { + "message": "Signature request" + }, + "signatureRequestGuidance": { + "message": "Only sign this message if you fully understand the content and trust the requesting site." + }, + "signatureRequestWarning": { + "message": "Signing this message could be dangerous. You may be giving total control of your account and assets to the party on the other end of this message. That means they could drain your account at any time. Proceed with caution. $1." + }, + "signed": { + "message": "Signed" + }, + "signin": { + "message": "Sign-In" + }, + "signing": { + "message": "Signing" + }, + "signingInWith": { + "message": "Signing in with" + }, + "simulationDetailsFailed": { + "message": "There was an error loading your estimation." + }, + "simulationDetailsFiatNotAvailable": { + "message": "Not Available" + }, + "simulationDetailsIncomingHeading": { + "message": "You receive" + }, + "simulationDetailsNoBalanceChanges": { + "message": "No changes predicted for your wallet" + }, + "simulationDetailsOutgoingHeading": { + "message": "You send" + }, + "simulationDetailsTitle": { + "message": "Estimated changes" + }, + "simulationDetailsTitleTooltip": { + "message": "Estimated changes are what might happen if you go through with this transaction. This is just a prediction, not a guarantee." + }, + "simulationDetailsTotalFiat": { + "message": "Total = $1", + "description": "$1 is the total amount in fiat currency on one side of the transaction" + }, + "simulationDetailsTransactionReverted": { + "message": "This transaction is likely to fail" + }, + "simulationErrorMessageV2": { + "message": "We were not able to estimate gas. There might be an error in the contract and this transaction may fail." + }, + "simulationsSettingDescription": { + "message": "Turn this on to estimate balance changes of transactions before you confirm them. This doesn't guarantee the final outcome of your transactions. $1" + }, + "simulationsSettingSubHeader": { + "message": "Estimate balance changes" + }, + "siweIssued": { + "message": "Issued" + }, + "siweNetwork": { + "message": "Network" + }, + "siweRequestId": { + "message": "Request ID" + }, + "siweResources": { + "message": "Resources" + }, + "siweSignatureSimulationDetailInfo": { + "message": "You’re signing into a site and there are no predicted changes to your account." + }, + "siweURI": { + "message": "URL" + }, + "skip": { + "message": "Skip" + }, + "skipAccountSecurity": { + "message": "Skip account security?" + }, + "skipAccountSecurityDetails": { + "message": "I understand that until I back up my Secret Recovery Phrase, I may lose my accounts and all of their assets." + }, + "smartContracts": { + "message": "Smart contracts" + }, + "smartSwapsErrorNotEnoughFunds": { + "message": "Not enough funds for a smart swap." + }, + "smartSwapsErrorUnavailable": { + "message": "Smart Swaps are temporarily unavailable." + }, + "smartTransactionCancelled": { + "message": "Your transaction was canceled" + }, + "smartTransactionCancelledDescription": { + "message": "Your transaction couldn't be completed, so it was canceled to save you from paying unnecessary gas fees." + }, + "smartTransactionError": { + "message": "Your transaction failed" + }, + "smartTransactionErrorDescription": { + "message": "Sudden market changes can cause failures. If the problem continues, reach out to MetaMask customer support." + }, + "smartTransactionPending": { + "message": "Submitting your transaction" + }, + "smartTransactionSuccess": { + "message": "Your transaction is complete" + }, + "smartTransactionTakingTooLong": { + "message": "Sorry for the wait" + }, + "smartTransactionTakingTooLongDescription": { + "message": "If your transaction is not finalized within $1, it will be canceled and you will not be charged for gas.", + "description": "$1 is remaining time in seconds" + }, + "smartTransactions": { + "message": "Smart Transactions" + }, + "smartTransactionsBenefit1": { + "message": "99.5% success rate" + }, + "smartTransactionsBenefit2": { + "message": "Saves you money" + }, + "smartTransactionsBenefit3": { + "message": "Real-time updates" + }, + "smartTransactionsDescription": { + "message": "Unlock higher success rates, frontrunning protection, and better visibility with Smart Transactions." + }, + "smartTransactionsDescription2": { + "message": "Only available on Ethereum. Enable or disable any time in settings. $1", + "description": "$1 is an external link to learn more about Smart Transactions" + }, + "smartTransactionsOptItModalTitle": { + "message": "Enhanced Transaction Protection" + }, + "snapAccountCreated": { + "message": "Account created" + }, + "snapAccountCreatedDescription": { + "message": "Your new account is ready to use!" + }, + "snapAccountCreationFailed": { + "message": "Account creation failed" + }, + "snapAccountCreationFailedDescription": { + "message": "$1 didn't manage to create an account for you.", + "description": "$1 is the snap name" + }, + "snapAccountRedirectFinishSigningTitle": { + "message": "Finish signing" + }, + "snapAccountRedirectSiteDescription": { + "message": "Follow the instructions from $1" + }, + "snapAccountRemovalFailed": { + "message": "Account removal failed" + }, + "snapAccountRemovalFailedDescription": { + "message": "$1 didn't manage to remove this account for you.", + "description": "$1 is the snap name" + }, + "snapAccountRemoved": { + "message": "Account removed" + }, + "snapAccountRemovedDescription": { + "message": "This account will no longer be available to use in MetaMask." + }, + "snapAccounts": { + "message": "Account Snaps" + }, + "snapAccountsDescription": { + "message": "Accounts controlled by third-party Snaps." + }, + "snapConnectTo": { + "message": "Connect to $1", + "description": "$1 is the website URL or a Snap name. Used for Snaps pre-approved connections." + }, + "snapConnectionPermissionDescription": { + "message": "Let $1 automatically connect to $2 without your approval.", + "description": "Used for Snap pre-approved connections. $1 is the Snap name, $2 is a website URL." + }, + "snapConnectionWarning": { + "message": "$1 wants to use $2", + "description": "$2 is the snap and $1 is the dapp requesting connection to the snap." + }, + "snapContent": { + "message": "This content is coming from $1", + "description": "This is shown when a snap shows transaction insight information in the confirmation UI. $1 is a link to the snap's settings page with the link text being the name of the snap." + }, + "snapDetailWebsite": { + "message": "Website" + }, + "snapHomeMenu": { + "message": "Snap Home Menu" + }, + "snapInstallRequest": { + "message": "Installing $1 gives it the following permissions.", + "description": "$1 is the snap name." + }, + "snapInstallSuccess": { + "message": "Installation complete" + }, + "snapInstallWarningCheck": { + "message": "$1 wants permission to do the following:", + "description": "Warning message used in popup displayed on snap install. $1 is the snap name." + }, + "snapInstallWarningHeading": { + "message": "Proceed with caution" + }, + "snapInstallWarningPermissionDescriptionForBip32View": { + "message": "Allow $1 to view your public keys (and addresses). This does not grant any control of accounts or assets.", + "description": "An extended description for the `snap_getBip32PublicKey` permission used for tooltip on Snap Install Warning screen (popup/modal). $1 is the snap name." + }, + "snapInstallWarningPermissionDescriptionForEntropy": { + "message": "Allow $1 Snap to manage accounts and assets on the requested network(s). These accounts are derived and backed up using your secret recovery phrase (without revealing it). With the power to derive keys, $1 can support a variety of blockchain protocols beyond Ethereum (EVMs).", + "description": "An extended description for the `snap_getBip44Entropy` and `snap_getBip44Entropy` permissions used for tooltip on Snap Install Warning screen (popup/modal). $1 is the snap name." + }, + "snapInstallWarningPermissionNameForEntropy": { + "message": "Manage $1 accounts", + "description": "Permission name used for the Permission Cell component displayed on warning popup when installing a Snap. $1 is list of account types." + }, + "snapInstallWarningPermissionNameForViewPublicKey": { + "message": "View your public key for $1", + "description": "Permission name used for the Permission Cell component displayed on warning popup when installing a Snap. $1 is list of account types." + }, + "snapInstallationErrorDescription": { + "message": "$1 couldn’t be installed.", + "description": "Error description used when snap installation fails. $1 is the snap name." + }, + "snapInstallationErrorTitle": { + "message": "Installation failed", + "description": "Error title used when snap installation fails." + }, + "snapResultError": { + "message": "Error" + }, + "snapResultSuccess": { + "message": "Success" + }, + "snapResultSuccessDescription": { + "message": "$1 is ready to use" + }, + "snapUpdateAlertDescription": { + "message": "Get the latest version of $1", + "description": "Description used in Snap update alert banner when snap update is available. $1 is the Snap name." + }, + "snapUpdateAvailable": { + "message": "Update available" + }, + "snapUpdateErrorDescription": { + "message": "$1 couldn’t be updated.", + "description": "Error description used when snap update fails. $1 is the snap name." + }, + "snapUpdateErrorTitle": { + "message": "Update failed", + "description": "Error title used when snap update fails." + }, + "snapUpdateRequest": { + "message": "Updating $1 gives it the following permissions.", + "description": "$1 is the Snap name." + }, + "snapUpdateSuccess": { + "message": "Update complete" + }, + "snapUrlIsBlocked": { + "message": "This Snap wants to take you to a blocked site. $1." + }, + "snaps": { + "message": "Snaps" + }, + "snapsConnected": { + "message": "Snaps connected" + }, + "snapsNoInsight": { + "message": "The snap didn't return any insight" + }, + "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.", + "description": "First part of a message in popup modal displayed when installing a snap for the first time. $1 is terms of use link." + }, + "snapsPrivacyWarningSecondMessage": { + "message": "Any information you share with Third Party Services will be collected directly by those Third Party Services in accordance with their privacy policies. Please refer to their privacy policies for more information.", + "description": "Second part of a message in popup modal displayed when installing a snap for the first time." + }, + "snapsPrivacyWarningThirdMessage": { + "message": "Consensys has no access to information you share with Third Party Services.", + "description": "Third part of a message in popup modal displayed when installing a snap for the first time." + }, + "snapsSettings": { + "message": "Snap settings" + }, + "snapsTermsOfUse": { + "message": "Terms of Use" + }, + "snapsToggle": { + "message": "A snap will only run if it is enabled" + }, + "snapsUIError": { + "message": "Contact the creators of $1 for further support.", + "description": "This is shown when the insight snap throws an error. $1 is the snap name" + }, + "someNetworksMayPoseSecurity": { + "message": "Some networks may pose security and/or privacy risks. Understand the risks before adding & using a network." + }, + "somethingDoesntLookRight": { + "message": "Something doesn't look right? $1", + "description": "A false positive message for users to contact support. $1 is a link to the support page." + }, + "somethingIsWrong": { + "message": "Something's gone wrong. Try reloading the page." + }, + "somethingWentWrong": { + "message": "Oops! Something went wrong." + }, + "source": { + "message": "Source" + }, + "speed": { + "message": "Speed" + }, + "speedUp": { + "message": "Speed up" + }, + "speedUpCancellation": { + "message": "Speed up this cancellation" + }, + "speedUpExplanation": { + "message": "We’ve updated the gas fee based on current network conditions and have increased it by at least 10% (required by the network)." + }, + "speedUpPopoverTitle": { + "message": "Speed up transaction" + }, + "speedUpTooltipText": { + "message": "New gas fee" + }, + "speedUpTransaction": { + "message": "Speed up this transaction" + }, + "spendLimitInsufficient": { + "message": "Spend limit insufficient" + }, + "spendLimitInvalid": { + "message": "Spend limit invalid; must be a positive number" + }, + "spendLimitPermission": { + "message": "Spend limit permission" + }, + "spendLimitRequestedBy": { + "message": "Spend limit requested by $1", + "description": "Origin of the site requesting the spend limit" + }, + "spendLimitTooLarge": { + "message": "Spend limit too large" + }, + "spender": { + "message": "Spender" + }, + "spendingCap": { + "message": "Spending cap" + }, + "spendingCapError": { + "message": "Error: Enter numbers only" + }, + "spendingCapErrorDescription": { + "message": "Only enter a number that you're comfortable with $1 accessing now or in the future. You can always increase the token limit later.", + "description": "$1 is origin of the site requesting the token limit" + }, + "spendingCapRequest": { + "message": "Spending cap request for your $1" + }, + "srpInputNumberOfWords": { + "message": "I have a $1-word phrase", + "description": "This is the text for each option in the dropdown where a user selects how many words their secret recovery phrase has during import. The $1 is the number of words (either 12, 15, 18, 21, or 24)." + }, + "srpPasteFailedTooManyWords": { + "message": "Paste failed because it contained over 24 words. A secret recovery phrase can have a maximum of 24 words.", + "description": "Description of SRP paste error when the pasted content has too many words" + }, + "srpPasteTip": { + "message": "You can paste your entire secret recovery phrase into any field", + "description": "Our secret recovery phrase input is split into one field per word. This message explains to users that they can paste their entire secrete recovery phrase into any field, and we will handle it correctly." + }, + "srpSecurityQuizGetStarted": { + "message": "Get started" + }, + "srpSecurityQuizImgAlt": { + "message": "An eye with a keyhole in the center, and three floating password fields" + }, + "srpSecurityQuizIntroduction": { + "message": "To reveal your Secret Recovery Phrase, you need to correctly answer two questions" + }, + "srpSecurityQuizQuestionOneQuestion": { + "message": "If you lose your Secret Recovery Phrase, MetaMask..." + }, + "srpSecurityQuizQuestionOneRightAnswer": { + "message": "Can’t help you" + }, + "srpSecurityQuizQuestionOneRightAnswerDescription": { + "message": "Write it down, engrave it on metal, or keep it in multiple secret spots so you never lose it. If you lose it, it’s gone forever." + }, + "srpSecurityQuizQuestionOneRightAnswerTitle": { + "message": "Right! No one can help get your Secret Recovery Phrase back" + }, + "srpSecurityQuizQuestionOneWrongAnswer": { + "message": "Can get it back for you" + }, + "srpSecurityQuizQuestionOneWrongAnswerDescription": { + "message": "If you lose your Secret Recovery Phrase, it’s gone forever. No one can help you get it back, no matter what they might say." + }, + "srpSecurityQuizQuestionOneWrongAnswerTitle": { + "message": "Wrong! No one can help get your Secret Recovery Phrase back" + }, + "srpSecurityQuizQuestionTwoQuestion": { + "message": "If anyone, even a support agent, asks for your Secret Recovery Phrase..." + }, + "srpSecurityQuizQuestionTwoRightAnswer": { + "message": "You’re being scammed" + }, + "srpSecurityQuizQuestionTwoRightAnswerDescription": { + "message": "Anyone claiming to need your Secret Recovery Phrase is lying to you. If you share it with them, they will steal your assets." + }, + "srpSecurityQuizQuestionTwoRightAnswerTitle": { + "message": "Correct! Sharing your Secret Recovery Phrase is never a good idea" + }, + "srpSecurityQuizQuestionTwoWrongAnswer": { + "message": "You should give it to them" + }, + "srpSecurityQuizQuestionTwoWrongAnswerDescription": { + "message": "Anyone claiming to need your Secret Recovery Phrase is lying to you. If you share it with them, they will steal your assets." + }, + "srpSecurityQuizQuestionTwoWrongAnswerTitle": { + "message": "Nope! Never share your Secret Recovery Phrase with anyone, ever" + }, + "srpSecurityQuizTitle": { + "message": "Security quiz" + }, + "srpToggleShow": { + "message": "Show/Hide this word of the secret recovery phrase", + "description": "Describes a toggle that is used to show or hide a single word of the secret recovery phrase" + }, + "srpWordHidden": { + "message": "This word is hidden", + "description": "Explains that a word in the secret recovery phrase is hidden" + }, + "srpWordShown": { + "message": "This word is being shown", + "description": "Explains that a word in the secret recovery phrase is being shown" + }, + "stable": { + "message": "Stable" + }, + "stableLowercase": { + "message": "stable" + }, + "stake": { + "message": "Stake" + }, + "startYourJourney": { + "message": "Start your journey with $1", + "description": "$1 is the token symbol" + }, + "startYourJourneyDescription": { + "message": "Get started with web3 by adding some $1 to your wallet.", + "description": "$1 is the token symbol" + }, + "stateLogError": { + "message": "Error in retrieving state logs." + }, + "stateLogFileName": { + "message": "MetaMask state logs" + }, + "stateLogs": { + "message": "State logs" + }, + "stateLogsDescription": { + "message": "State logs contain your public account addresses and sent transactions." + }, + "states": { + "message": "States" + }, + "status": { + "message": "Status" + }, + "statusNotConnected": { + "message": "Not connected" + }, + "statusNotConnectedAccount": { + "message": "No accounts connected" + }, + "step1LatticeWallet": { + "message": "Connect your Lattice1" + }, + "step1LatticeWalletMsg": { + "message": "You can connect MetaMask to your Lattice1 device once it is set up and online. Unlock your device and have your Device ID ready.", + "description": "$1 represents the `hardwareWalletSupportLinkConversion` localization key" + }, + "step1LedgerWallet": { + "message": "Download Ledger app" + }, + "step1LedgerWalletMsg": { + "message": "Download, set up, and enter your password to unlock $1.", + "description": "$1 represents the `ledgerLiveApp` localization value" + }, + "step1TrezorWallet": { + "message": "Connect your Trezor" + }, + "step1TrezorWalletMsg": { + "message": "Plug your Trezor directly into your computer and unlock it. Make sure you use the correct passphrase.", + "description": "$1 represents the `hardwareWalletSupportLinkConversion` localization key" + }, + "step2LedgerWallet": { + "message": "Connect your Ledger" + }, + "step2LedgerWalletMsg": { + "message": "Plug your Ledger directly into your computer, then unlock it and open the Ethereum app.", + "description": "$1 represents the `hardwareWalletSupportLinkConversion` localization key" + }, + "stillGettingMessage": { + "message": "Still getting this message?" + }, + "strong": { + "message": "Strong" + }, + "stxCancelled": { + "message": "Swap would have failed" + }, + "stxCancelledDescription": { + "message": "Your transaction would have failed and was cancelled to protect you from paying unnecessary gas fees." + }, + "stxCancelledSubDescription": { + "message": "Try your swap again. We’ll be here to protect you against similar risks next time." + }, + "stxEstimatedCompletion": { + "message": "Estimated completion in < $1", + "description": "$1 is remeaning time in minutes and seconds, e.g. 0:10" + }, + "stxFailure": { + "message": "Swap failed" + }, + "stxFailureDescription": { + "message": "Sudden market changes can cause failures. If the problem persists, please reach out to $1.", + "description": "This message is shown to a user if their swap fails. The $1 will be replaced by support.metamask.io" + }, + "stxOptInDescription": { + "message": "Turn on Smart Transactions for more reliable and secure transactions on Ethereum Mainnet. $1" + }, + "stxPendingPrivatelySubmittingSwap": { + "message": "Privately submitting your Swap..." + }, + "stxPendingPubliclySubmittingSwap": { + "message": "Publicly submitting your Swap..." + }, + "stxSuccess": { + "message": "Swap complete!" + }, + "stxSuccessDescription": { + "message": "Your $1 is now available.", + "description": "$1 is a token symbol, e.g. ETH" + }, + "stxSwapCompleteIn": { + "message": "Swap will complete in <", + "description": "'<' means 'less than', e.g. Swap will complete in < 2:59" + }, + "stxTryingToCancel": { + "message": "Trying to cancel your transaction..." + }, + "stxUnknown": { + "message": "Status unknown" + }, + "stxUnknownDescription": { + "message": "A transaction has been successful but we’re unsure what it is. This may be due to submitting another transaction while this swap was processing." + }, + "stxUserCancelled": { + "message": "Swap cancelled" + }, + "stxUserCancelledDescription": { + "message": "Your transaction has been cancelled and you did not pay any unnecessary gas fees." + }, + "submit": { + "message": "Submit" + }, + "submitted": { + "message": "Submitted" + }, + "suggestedBySnap": { + "message": "Suggested by $1", + "description": "$1 is the snap name" + }, + "suggestedTokenName": { + "message": "Suggested name:" + }, + "suggestedTokenSymbol": { + "message": "Suggested ticker symbol:" + }, + "support": { + "message": "Support" + }, + "supportCenter": { + "message": "Visit our support center" + }, + "surveyConversion": { + "message": "Take our survey" + }, + "surveyTitle": { + "message": "Shape the future of MetaMask" + }, + "swap": { + "message": "Swap" + }, + "swapAdjustSlippage": { + "message": "Adjust slippage" + }, + "swapAggregator": { + "message": "Aggregator" + }, + "swapAllowSwappingOf": { + "message": "Allow swapping of $1", + "description": "Shows a user that they need to allow a token for swapping on their hardware wallet" + }, + "swapAmountReceived": { + "message": "Guaranteed amount" + }, + "swapAmountReceivedInfo": { + "message": "This is the minimum amount you will receive. You may receive more depending on slippage." + }, + "swapAndSend": { + "message": "Swap & Send" + }, + "swapAnyway": { + "message": "Swap anyway" + }, + "swapApproval": { + "message": "Approve $1 for swaps", + "description": "Used in the transaction display list to describe a transaction that is an approve call on a token that is to be swapped.. $1 is the symbol of a token that has been approved." + }, + "swapApproveNeedMoreTokens": { + "message": "You need $1 more $2 to complete this swap", + "description": "Tells the user how many more of a given token they need for a specific swap. $1 is an amount of tokens and $2 is the token symbol." + }, + "swapAreYouStillThere": { + "message": "Are you still there?" + }, + "swapAreYouStillThereDescription": { + "message": "We’re ready to show you the latest quotes when you want to continue" + }, + "swapBuildQuotePlaceHolderText": { + "message": "No tokens available matching $1", + "description": "Tells the user that a given search string does not match any tokens in our token lists. $1 can be any string of text" + }, + "swapConfirmWithHwWallet": { + "message": "Confirm with your hardware wallet" + }, + "swapContinueSwapping": { + "message": "Continue swapping" + }, + "swapContractDataDisabledErrorDescription": { + "message": "In the Ethereum app on your Ledger, go to \"Settings\" and allow contract data. Then, try your swap again." + }, + "swapContractDataDisabledErrorTitle": { + "message": "Contract data is not enabled on your Ledger" + }, + "swapCustom": { + "message": "custom" + }, + "swapDecentralizedExchange": { + "message": "Decentralized exchange" + }, + "swapDirectContract": { + "message": "Direct contract" + }, + "swapEditLimit": { + "message": "Edit limit" + }, + "swapEnableDescription": { + "message": "This is required and gives MetaMask permission to swap your $1.", + "description": "Gives the user info about the required approval transaction for swaps. $1 will be the symbol of a token being approved for swaps." + }, + "swapEnableTokenForSwapping": { + "message": "This will $1 for swapping", + "description": "$1 is for the 'enableToken' key, e.g. 'enable ETH'" + }, + "swapEnterAmount": { + "message": "Enter an amount" + }, + "swapEstimatedNetworkFees": { + "message": "Estimated network fees" + }, + "swapEstimatedNetworkFeesInfo": { + "message": "This is an estimate of the network fee that will be used to complete your swap. The actual amount may change according to network conditions." + }, + "swapFailedErrorDescriptionWithSupportLink": { + "message": "Transaction failures happen and we are here to help. If this issue persists, you can reach our customer support at $1 for further assistance.", + "description": "This message is shown to a user if their swap fails. The $1 will be replaced by support.metamask.io" + }, + "swapFailedErrorTitle": { + "message": "Swap failed" + }, + "swapFetchingQuote": { + "message": "Fetching quote" + }, + "swapFetchingQuoteNofN": { + "message": "Fetching quote $1 of $2", + "description": "A count of possible quotes shown to the user while they are waiting for quotes to be fetched. $1 is the number of quotes already loaded, and $2 is the total number of resources that we check for quotes. Keep in mind that not all resources will have a quote for a particular swap." + }, + "swapFetchingQuotes": { + "message": "Fetching quotes..." + }, + "swapFetchingQuotesErrorDescription": { + "message": "Hmmm... something went wrong. Try again, or if errors persist, contact customer support." + }, + "swapFetchingQuotesErrorTitle": { + "message": "Error fetching quotes" + }, + "swapFetchingTokens": { + "message": "Fetching tokens..." + }, + "swapFromTo": { + "message": "The swap of $1 to $2", + "description": "Tells a user that they need to confirm on their hardware wallet a swap of 2 tokens. $1 is a source token and $2 is a destination token" + }, + "swapGasFeesDetails": { + "message": "Gas fees are estimated and will fluctuate based on network traffic and transaction complexity." + }, + "swapGasFeesLearnMore": { + "message": "Learn more about gas fees" + }, + "swapGasFeesSplit": { + "message": "Gas fees on the previous screen are split between these two transactions." + }, + "swapGasFeesSummary": { + "message": "Gas fees are paid to crypto miners who process transactions on the $1 network. MetaMask does not profit from gas fees.", + "description": "$1 is the selected network, e.g. Ethereum or BSC" + }, + "swapHighSlippage": { + "message": "High slippage" + }, + "swapHighSlippageWarning": { + "message": "Slippage amount is very high." + }, + "swapIncludesMMFee": { + "message": "Includes a $1% MetaMask fee.", + "description": "Provides information about the fee that metamask takes for swaps. $1 is a decimal number." + }, + "swapIncludesMMFeeAlt": { + "message": "Quote reflects $1% MetaMask fee", + "description": "Provides information about the fee that metamask takes for swaps using the latest copy. $1 is a decimal number." + }, + "swapIncludesMetaMaskFeeViewAllQuotes": { + "message": "Includes a $1% MetaMask fee – $2", + "description": "Provides information about the fee that metamask takes for swaps. $1 is a decimal number and $2 is a link to view all quotes." + }, + "swapLearnMore": { + "message": "Learn more about Swaps" + }, + "swapLiquiditySourceInfo": { + "message": "We search multiple liquidity sources (exchanges, aggregators and professional market makers) to compare exchange rates and network fees." + }, + "swapLowSlippage": { + "message": "Low slippage" + }, + "swapLowSlippageError": { + "message": "Transaction may fail, max slippage too low." + }, + "swapMaxSlippage": { + "message": "Max slippage" + }, + "swapMetaMaskFee": { + "message": "MetaMask fee" + }, + "swapMetaMaskFeeDescription": { + "message": "The fee of $1% is automatically factored into this quote. You pay it in exchange for a license to use MetaMask's liquidity provider information aggregation software.", + "description": "Provides information about the fee that metamask takes for swaps. $1 is a decimal number." + }, + "swapNQuotesWithDot": { + "message": "$1 quotes.", + "description": "$1 is the number of quotes that the user can select from when opening the list of quotes on the 'view quote' screen" + }, + "swapNewQuoteIn": { + "message": "New quotes in $1", + "description": "Tells the user the amount of time until the currently displayed quotes are update. $1 is a time that is counting down from 1:00 to 0:00" + }, + "swapNoTokensAvailable": { + "message": "No tokens available matching $1", + "description": "Tells the user that a given search string does not match any tokens in our token lists. $1 can be any string of text" + }, + "swapOnceTransactionHasProcess": { + "message": "Your $1 will be added to your account once this transaction has processed.", + "description": "This message communicates the token that is being transferred. It is shown on the awaiting swap screen. The $1 will be a token symbol." + }, + "swapPriceDifference": { + "message": "You are about to swap $1 $2 (~$3) for $4 $5 (~$6).", + "description": "This message represents the price slippage for the swap. $1 and $4 are a number (ex: 2.89), $2 and $5 are symbols (ex: ETH), and $3 and $6 are fiat currency amounts." + }, + "swapPriceDifferenceTitle": { + "message": "Price difference of ~$1%", + "description": "$1 is a number (ex: 1.23) that represents the price difference." + }, + "swapPriceImpactTooltip": { + "message": "Price impact is the difference between the current market price and the amount received during transaction execution. Price impact is a function of the size of your trade relative to the size of the liquidity pool." + }, + "swapPriceUnavailableDescription": { + "message": "Price impact could not be determined due to lack of market price data. Please confirm that you are comfortable with the amount of tokens you are about to receive before swapping." + }, + "swapPriceUnavailableTitle": { + "message": "Check your rate before proceeding" + }, + "swapProcessing": { + "message": "Processing" + }, + "swapQuoteDetails": { + "message": "Quote details" + }, + "swapQuoteNofM": { + "message": "$1 of $2", + "description": "A count of possible quotes shown to the user while they are waiting for quotes to be fetched. $1 is the number of quotes already loaded, and $2 is the total number of resources that we check for quotes. Keep in mind that not all resources will have a quote for a particular swap." + }, + "swapQuoteSource": { + "message": "Quote source" + }, + "swapQuotesExpiredErrorDescription": { + "message": "Please request new quotes to get the latest rates." + }, + "swapQuotesExpiredErrorTitle": { + "message": "Quotes timeout" + }, + "swapQuotesNotAvailableDescription": { + "message": "Reduce the size of your trade or use a different token." + }, + "swapQuotesNotAvailableErrorDescription": { + "message": "Try adjusting the amount or slippage settings and try again." + }, + "swapQuotesNotAvailableErrorTitle": { + "message": "No quotes available" + }, + "swapRate": { + "message": "Rate" + }, + "swapReceiving": { + "message": "Receiving" + }, + "swapReceivingInfoTooltip": { + "message": "This is an estimate. The exact amount depends on slippage." + }, + "swapRequestForQuotation": { + "message": "Request for quotation" + }, + "swapReviewSwap": { + "message": "Review swap" + }, + "swapSearchNameOrAddress": { + "message": "Search name or paste address" + }, + "swapSelect": { + "message": "Select" + }, + "swapSelectAQuote": { + "message": "Select a quote" + }, + "swapSelectAToken": { + "message": "Select token" + }, + "swapSelectQuotePopoverDescription": { + "message": "Below are all the quotes gathered from multiple liquidity sources." + }, + "swapSelectToken": { + "message": "Select token" + }, + "swapShowLatestQuotes": { + "message": "Show latest quotes" + }, + "swapSlippageHighDescription": { + "message": "The slippage entered ($1%) is considered very high and may result in a bad rate", + "description": "$1 is the amount of % for slippage" + }, + "swapSlippageHighTitle": { + "message": "High slippage" + }, + "swapSlippageLowDescription": { + "message": "A value this low ($1%) may result in a failed swap", + "description": "$1 is the amount of % for slippage" + }, + "swapSlippageLowTitle": { + "message": "Low slippage" + }, + "swapSlippageNegative": { + "message": "Slippage must be greater or equal to zero" + }, + "swapSlippageNegativeDescription": { + "message": "Slippage must be greater or equal to zero" + }, + "swapSlippageNegativeTitle": { + "message": "Increase slippage to continue" + }, + "swapSlippageOverLimitDescription": { + "message": "Slippage tolerance must be 15% or less. Anything higher will result in a bad rate." + }, + "swapSlippageOverLimitTitle": { + "message": "Very high slippage" + }, + "swapSlippagePercent": { + "message": "$1%", + "description": "$1 is the amount of % for slippage" + }, + "swapSlippageTooltip": { + "message": "If the price changes between the time your order is placed and confirmed it’s called “slippage”. Your swap will automatically cancel if slippage exceeds your “slippage tolerance” setting." + }, + "swapSlippageZeroDescription": { + "message": "There are fewer zero-slippage quote providers which will result in a less competitive quote." + }, + "swapSlippageZeroTitle": { + "message": "Sourcing zero-slippage providers" + }, + "swapSource": { + "message": "Liquidity source" + }, + "swapSuggested": { + "message": "Swap suggested" + }, + "swapSuggestedGasSettingToolTipMessage": { + "message": "Swaps are complex and time sensitive transactions. We recommend this gas fee for a good balance between cost and confidence of a successful Swap." + }, + "swapSwapFrom": { + "message": "Swap from" + }, + "swapSwapSwitch": { + "message": "Switch token order" + }, + "swapSwapTo": { + "message": "Swap to" + }, + "swapToConfirmWithHwWallet": { + "message": "to confirm with your hardware wallet" + }, + "swapTokenAddedManuallyDescription": { + "message": "Verify this token on $1 and make sure it is the token you want to trade.", + "description": "$1 points the user to etherscan as a place they can verify information about a token. $1 is replaced with the translation for \"etherscan\"" + }, + "swapTokenAddedManuallyTitle": { + "message": "Token added manually" + }, + "swapTokenAvailable": { + "message": "Your $1 has been added to your account.", + "description": "This message is shown after a swap is successful and communicates the exact amount of tokens the user has received for a swap. The $1 is a decimal number of tokens followed by the token symbol." + }, + "swapTokenBalanceUnavailable": { + "message": "We were unable to retrieve your $1 balance", + "description": "This message communicates to the user that their balance of a given token is currently unavailable. $1 will be replaced by a token symbol" + }, + "swapTokenNotAvailable": { + "message": "Token is not available to swap in this region" + }, + "swapTokenToToken": { + "message": "Swap $1 to $2", + "description": "Used in the transaction display list to describe a swap. $1 and $2 are the symbols of tokens in involved in a swap." + }, + "swapTokenVerificationAddedManually": { + "message": "This token has been added manually." + }, + "swapTokenVerificationMessage": { + "message": "Always confirm the token address on $1.", + "description": "Points the user to Etherscan as a place they can verify information about a token. $1 is replaced with the translation for \"Etherscan\" followed by an info icon that shows more info on hover." + }, + "swapTokenVerificationOnlyOneSource": { + "message": "Only verified on 1 source." + }, + "swapTokenVerificationSources": { + "message": "Verified on $1 sources.", + "description": "Indicates the number of token information sources that recognize the symbol + address. $1 is a decimal number." + }, + "swapTokenVerifiedOn1SourceDescription": { + "message": "$1 is only verified on 1 source. Consider verifying it on $2 before proceeding.", + "description": "$1 is a token name, $2 points the user to etherscan as a place they can verify information about a token. $1 is replaced with the translation for \"etherscan\"" + }, + "swapTokenVerifiedOn1SourceTitle": { + "message": "Potentially inauthentic token" + }, + "swapTooManyDecimalsError": { + "message": "$1 allows up to $2 decimals", + "description": "$1 is a token symbol and $2 is the max. number of decimals allowed for the token" + }, + "swapTransactionComplete": { + "message": "Transaction complete" + }, + "swapTwoTransactions": { + "message": "2 transactions" + }, + "swapUnknown": { + "message": "Unknown" + }, + "swapVerifyTokenExplanation": { + "message": "Multiple tokens can use the same name and symbol. Check $1 to verify this is the token you're looking for.", + "description": "This appears in a tooltip next to the verifyThisTokenOn message. It gives the user more information about why they should check the token on a block explorer. $1 will be the name or url of the block explorer, which will be the translation of 'etherscan' or a block explorer url specified for a custom network." + }, + "swapYourTokenBalance": { + "message": "$1 $2 available to swap", + "description": "Tells the user how much of a token they have in their balance. $1 is a decimal number amount of tokens, and $2 is a token symbol" + }, + "swapZeroSlippage": { + "message": "0% Slippage" + }, + "swapsAdvancedOptions": { + "message": "Advanced options" + }, + "swapsExcessiveSlippageWarning": { + "message": "Slippage amount is too high and will result in a bad rate. Please reduce your slippage tolerance to a value below 15%." + }, + "swapsMaxSlippage": { + "message": "Slippage tolerance" + }, + "swapsNotEnoughForTx": { + "message": "Not enough $1 to complete this transaction", + "description": "Tells the user that they don't have enough of a token for a proposed swap. $1 is a token symbol" + }, + "swapsNotEnoughToken": { + "message": "Not enough $1", + "description": "Tells the user that they don't have enough of a token for a proposed swap. $1 is a token symbol" + }, + "swapsViewInActivity": { + "message": "View in activity" + }, + "switch": { + "message": "Switch" + }, + "switchEthereumChainConfirmationDescription": { + "message": "This will switch the selected network within MetaMask to a previously added network:" + }, + "switchEthereumChainConfirmationTitle": { + "message": "Allow this site to switch the network?" + }, + "switchInputCurrency": { + "message": "Switch input currency" + }, + "switchNetwork": { + "message": "Switch network" + }, + "switchNetworks": { + "message": "Switch networks" + }, + "switchToNetwork": { + "message": "Switch to $1", + "description": "$1 represents the custom network that has previously been added" + }, + "switchToThisAccount": { + "message": "Switch to this account" + }, + "switchedNetworkToastDecline": { + "message": "Don't show again" + }, + "switchedNetworkToastMessage": { + "message": "$1 is now active on $2", + "description": "$1 represents the account name, $2 represents the network name" + }, + "switchedTo": { + "message": "You're now using" + }, + "switchingNetworksCancelsPendingConfirmations": { + "message": "Switching networks will cancel all pending confirmations" + }, + "symbol": { + "message": "Symbol" + }, + "symbolBetweenZeroTwelve": { + "message": "Symbol must be 11 characters or fewer." + }, + "tenPercentIncreased": { + "message": "10% increase" + }, + "terms": { + "message": "Terms of use" + }, + "termsOfService": { + "message": "Terms of service" + }, + "termsOfUseAgreeText": { + "message": " I agree to the Terms of Use, which apply to my use of MetaMask and all of its features" + }, + "termsOfUseFooterText": { + "message": "Please scroll to read all sections" + }, + "termsOfUseTitle": { + "message": "Our Terms of Use have updated" + }, + "testNetworks": { + "message": "Test networks" + }, + "theme": { + "message": "Theme" + }, + "themeDescription": { + "message": "Choose your preferred MetaMask theme." + }, + "thingsToKeep": { + "message": "Keep in mind:" + }, + "thirdPartySoftware": { + "message": "Third-party software notice", + "description": "Title of a popup modal displayed when installing a snap for the first time." + }, + "thisCollection": { + "message": "this collection" + }, + "threeMonthsAbbreviation": { + "message": "3M", + "description": "Shortened form of '3 months'" + }, + "time": { + "message": "Time" + }, + "tips": { + "message": "Tips" + }, + "to": { + "message": "To" + }, + "toAddress": { + "message": "To: $1", + "description": "$1 is the address to include in the To label. It is typically shortened first using shortenAddress" + }, + "toggleEthSignBannerDescription": { + "message": "You’re at risk for phishing attacks. Protect yourself by turning off eth_sign." + }, + "toggleEthSignDescriptionField": { + "message": "If you enable this setting, you might get signature requests that aren’t readable. By signing a message you don't understand, you could be agreeing to give away your funds and NFTs." + }, + "toggleEthSignField": { + "message": "Eth_sign requests" + }, + "toggleEthSignModalBannerBoldText": { + "message": " you might be getting scammed" + }, + "toggleEthSignModalBannerText": { + "message": "If you've been asked to turn this setting on," + }, + "toggleEthSignModalCheckBox": { + "message": "I understand that I can lose all of my funds and NFTs if I enable eth_sign requests. " + }, + "toggleEthSignModalDescription": { + "message": "Allowing eth_sign requests can make you vulnerable to phishing attacks. Always review the URL and be careful when signing messages that contain code." + }, + "toggleEthSignModalFormError": { + "message": "The text is incorrect" + }, + "toggleEthSignModalFormLabel": { + "message": "Enter “I only sign what I understand” to continue" + }, + "toggleEthSignModalFormValidation": { + "message": "I only sign what I understand" + }, + "toggleEthSignModalTitle": { + "message": "Use at your own risk" + }, + "toggleEthSignOff": { + "message": "OFF (Recommended)" + }, + "toggleEthSignOn": { + "message": "ON (Not recommended)" + }, + "toggleRequestQueueDescription": { + "message": "This allows you to select a network for each site instead of a single selected network for all sites. This feature will prevent you from switching networks manually, which may break your user experience on certain sites." + }, + "toggleRequestQueueField": { + "message": "Select networks for each site" + }, + "toggleRequestQueueOff": { + "message": "Off" + }, + "toggleRequestQueueOn": { + "message": "On" + }, + "token": { + "message": "Token" + }, + "tokenAddress": { + "message": "Token address" + }, + "tokenAlreadyAdded": { + "message": "Token has already been added." + }, + "tokenAutoDetection": { + "message": "Token autodetection" + }, + "tokenContractAddress": { + "message": "Token contract address" + }, + "tokenDecimal": { + "message": "Token decimal" + }, + "tokenDecimalFetchFailed": { + "message": "Token decimal required. Find it on: $1" + }, + "tokenDecimalTitle": { + "message": "Token decimal:" + }, + "tokenDetails": { + "message": "Token details" + }, + "tokenFoundTitle": { + "message": "1 new token found" + }, + "tokenId": { + "message": "Token ID" + }, + "tokenList": { + "message": "Token lists" + }, + "tokenScamSecurityRisk": { + "message": "token scams and security risks" + }, + "tokenShowUp": { + "message": "Your tokens may not automatically show up in your wallet. " + }, + "tokenStandard": { + "message": "Token standard" + }, + "tokenSymbol": { + "message": "Token symbol" + }, + "tokens": { + "message": "Tokens" + }, + "tokensFoundTitle": { + "message": "$1 new tokens found", + "description": "$1 is the number of new tokens detected" + }, + "tokensInCollection": { + "message": "Tokens in collection" + }, + "tooltipApproveButton": { + "message": "I understand" + }, + "tooltipSatusConnected": { + "message": "connected" + }, + "tooltipSatusConnectedUpperCase": { + "message": "Connected" + }, + "tooltipSatusNotConnected": { + "message": "not connected" + }, + "total": { + "message": "Total" + }, + "totalVolume": { + "message": "Total volume" + }, + "transaction": { + "message": "transaction" + }, + "transactionCancelAttempted": { + "message": "Transaction cancel attempted with estimated gas fee of $1 at $2" + }, + "transactionCancelSuccess": { + "message": "Transaction successfully cancelled at $2" + }, + "transactionConfirmed": { + "message": "Transaction confirmed at $2." + }, + "transactionCreated": { + "message": "Transaction created with a value of $1 at $2." + }, + "transactionDataFunction": { + "message": "Function" + }, + "transactionDetailDappGasMoreInfo": { + "message": "Site suggested" + }, + "transactionDetailDappGasTooltip": { + "message": "Edit to use MetaMask's recommended gas fee based on the latest block." + }, + "transactionDetailGasHeading": { + "message": "Estimated gas fee" + }, + "transactionDetailGasTooltipConversion": { + "message": "Learn more about gas fees" + }, + "transactionDetailGasTooltipExplanation": { + "message": "Gas fees are set by the network and fluctuate based on network traffic and transaction complexity." + }, + "transactionDetailGasTooltipIntro": { + "message": "Gas fees are paid to crypto miners who process transactions on the $1 network. MetaMask does not profit from gas fees." + }, + "transactionDetailGasTotalSubtitle": { + "message": "Amount + gas fee" + }, + "transactionDetailLayer2GasHeading": { + "message": "Layer 2 gas fee" + }, + "transactionDetailMultiLayerTotalSubtitle": { + "message": "Amount + fees" + }, + "transactionDropped": { + "message": "Transaction dropped at $2." + }, + "transactionError": { + "message": "Transaction error. Exception thrown in contract code." + }, + "transactionErrorNoContract": { + "message": "Trying to call a function on a non-contract address." + }, + "transactionErrored": { + "message": "Transaction encountered an error." + }, + "transactionFailed": { + "message": "Transaction Failed" + }, + "transactionFee": { + "message": "Transaction fee" + }, + "transactionHistoryBaseFee": { + "message": "Base fee (GWEI)" + }, + "transactionHistoryL1GasLabel": { + "message": "Total L1 gas fee" + }, + "transactionHistoryL2GasLimitLabel": { + "message": "L2 gas limit" + }, + "transactionHistoryL2GasPriceLabel": { + "message": "L2 gas price" + }, + "transactionHistoryMaxFeePerGas": { + "message": "Max fee per gas" + }, + "transactionHistoryPriorityFee": { + "message": "Priority fee (GWEI)" + }, + "transactionHistoryTotalGasFee": { + "message": "Total gas fee" + }, + "transactionNote": { + "message": "Transaction note" + }, + "transactionResubmitted": { + "message": "Transaction resubmitted with estimated gas fee increased to $1 at $2" + }, + "transactionSettings": { + "message": "Transaction settings" + }, + "transactionSubmitted": { + "message": "Transaction submitted with estimated gas fee of $1 at $2." + }, + "transactionUpdated": { + "message": "Transaction updated at $2." + }, + "transactions": { + "message": "Transactions" + }, + "transfer": { + "message": "Transfer" + }, + "transferFrom": { + "message": "Transfer from" + }, + "trillionAbbreviation": { + "message": "T", + "description": "Shortened form of 'trillion'" + }, + "troubleConnectingToLedgerU2FOnFirefox": { + "message": "We're having trouble connecting your Ledger. $1", + "description": "$1 is a link to the wallet connection guide;" + }, + "troubleConnectingToLedgerU2FOnFirefox2": { + "message": "Review our hardware wallet connection guide and try again.", + "description": "$1 of the ledger wallet connection guide" + }, + "troubleConnectingToLedgerU2FOnFirefoxLedgerSolution": { + "message": "If you're on the latest version of Firefox, you might be experiencing an issue related to Firefox dropping U2F support. Learn how to fix this issue $1.", + "description": "It is a link to the ledger website for the workaround." + }, + "troubleConnectingToLedgerU2FOnFirefoxLedgerSolution2": { + "message": "here", + "description": "Second part of the error message; It is a link to the ledger website for the workaround." + }, + "troubleConnectingToWallet": { + "message": "We had trouble connecting to your $1, try reviewing $2 and try again.", + "description": "$1 is the wallet device name; $2 is a link to wallet connection guide" + }, + "troubleStarting": { + "message": "MetaMask had trouble starting. This error could be intermittent, so try restarting the extension." + }, + "trustSiteApprovePermission": { + "message": "By granting permission, you are allowing the following $1 to access your funds" + }, + "tryAgain": { + "message": "Try again" + }, + "turnOff": { + "message": "Turn off" + }, + "turnOffMetamaskNotificationsError": { + "message": "There was an error in disabling the notifications. Please try again later." + }, + "turnOn": { + "message": "Turn on" + }, + "turnOnMetamaskNotifications": { + "message": "Turn on notifications" + }, + "turnOnMetamaskNotificationsButton": { + "message": "Turn on" + }, + "turnOnMetamaskNotificationsError": { + "message": "There was an error in creating the notifications. Please try again later." + }, + "turnOnMetamaskNotificationsMessageFirst": { + "message": "Stay in the loop on what's happening in your wallet with notifications." + }, + "turnOnMetamaskNotificationsMessagePrivacyBold": { + "message": "notifications settings." + }, + "turnOnMetamaskNotificationsMessagePrivacyLink": { + "message": "Learn how we protect your privacy while using this feature." + }, + "turnOnMetamaskNotificationsMessageSecond": { + "message": "To use wallet notifications, we use a profile to sync some settings across your devices. $1" + }, + "turnOnMetamaskNotificationsMessageThird": { + "message": "You can turn off notifications at any time in the $1" + }, + "turnOnTokenDetection": { + "message": "Turn on enhanced token detection" + }, + "tutorial": { + "message": "Tutorial" + }, + "twelveHrTitle": { + "message": "12hr:" + }, + "typeYourSRP": { + "message": "Type your Secret Recovery Phrase" + }, + "u2f": { + "message": "U2F", + "description": "A name on an API for the browser to interact with devices that support the U2F protocol. On some browsers we use it to connect MetaMask to Ledger devices." + }, + "unMatchedChain": { + "message": "According to our records, this URL does not match a known provider for this chain ID." + }, + "unapproved": { + "message": "Unapproved" + }, + "units": { + "message": "units" + }, + "unknown": { + "message": "Unknown" + }, + "unknownCollection": { + "message": "Unnamed collection" + }, + "unknownNetwork": { + "message": "Unknown private network" + }, + "unknownNetworkForKeyEntropy": { + "message": "Unknown network", + "description": "Displayed on places like Snap install warning when regular name is not available." + }, + "unknownQrCode": { + "message": "Error: We couldn't identify that QR code" + }, + "unlimited": { + "message": "Unlimited" + }, + "unlock": { + "message": "Unlock" + }, + "unlockMessage": { + "message": "The decentralized web awaits" + }, + "unpin": { + "message": "Unpin" + }, + "unrecognizedChain": { + "message": "This custom network is not recognized", + "description": "$1 is a clickable link with text defined by the 'unrecognizedChanLinkText' key. The link will open to instructions for users to validate custom network details." + }, + "unsendableAsset": { + "message": "Sending NFT (ERC-721) tokens is not currently supported", + "description": "This is an error message we show the user if they attempt to send an NFT asset type, for which currently don't support sending" + }, + "unverifiedContractAddressMessage": { + "message": "We cannot verify this contract. Make sure you trust this address." + }, + "upArrow": { + "message": "up arrow" + }, + "update": { + "message": "Update" + }, + "updateOrEditNetworkInformations": { + "message": "Update your information or" + }, + "updateRequest": { + "message": "Update request" + }, + "updatedWithDate": { + "message": "Updated $1" + }, + "uploadDropFile": { + "message": "Drop your file here" + }, + "uploadFile": { + "message": "Upload file" + }, + "urlErrorMsg": { + "message": "URLs require the appropriate HTTP/HTTPS prefix." + }, + "urlExistsErrorMsg": { + "message": "This URL is currently used by the $1 network." + }, + "use4ByteResolution": { + "message": "Decode smart contracts" + }, + "use4ByteResolutionDescription": { + "message": "To improve user experience, we customize the activity tab with messages based on the smart contracts you interact with. MetaMask uses a service called 4byte.directory to decode data and show you a version of a smart contract that's easier to read. This helps reduce your chances of approving malicious smart contract actions, but can result in your IP address being shared." + }, + "useMultiAccountBalanceChecker": { + "message": "Batch account balance requests" + }, + "useMultiAccountBalanceCheckerSettingDescription": { + "message": "Get faster balance updates by batching account balance requests. This lets us fetch your account balances together, so you get quicker updates for an improved experience. When this feature is off, third parties may be less likely to associate your accounts with each other." + }, + "useNftDetection": { + "message": "Autodetect NFTs" + }, + "useNftDetectionDescriptionText": { + "message": "Let MetaMask add NFTs you own using third-party services. Autodetecting NFTs exposes your IP and account address to these services. Enabling this feature could associate your IP address with your Ethereum address and display fake NFTs airdropped by scammers. You can add tokens manually to avoid this risk." + }, + "usePhishingDetection": { + "message": "Use phishing detection" + }, + "usePhishingDetectionDescription": { + "message": "Display a warning for phishing domains targeting Ethereum users" + }, + "useSafeChainsListValidation": { + "message": "Network details check" + }, + "useSafeChainsListValidationDescription": { + "message": "MetaMask uses a third-party service called $1 to show accurate and standardized network details. This reduces your chances of connecting to malicious or incorrect network. When using this feature, your IP address is exposed to chainid.network." + }, + "useSafeChainsListValidationWebsite": { + "message": "chainid.network", + "description": "useSafeChainsListValidationWebsite is separated from the rest of the text so that we can bold the third party service name in the middle of them" + }, + "useSiteSuggestion": { + "message": "Use site suggestion" + }, + "useTokenDetectionPrivacyDesc": { + "message": "Automatically displaying tokens sent to your account involves communication with third party servers to fetch token’s images. Those serves will have access to your IP address." + }, + "usedByClients": { + "message": "Used by a variety of different clients" + }, + "userName": { + "message": "Username" + }, + "userOpContractDeployError": { + "message": "Contract deployment from a smart contract account is not supported" + }, + "verifyContractDetails": { + "message": "Verify third-party details" + }, + "verifyThisTokenOn": { + "message": "Verify this token on $1", + "description": "Points the user to etherscan as a place they can verify information about a token. $1 is replaced with the translation for \"etherscan\"" + }, + "verifyThisUnconfirmedTokenOn": { + "message": "Verify this token on $1 and make sure this is the token you want to trade.", + "description": "Points the user to etherscan as a place they can verify information about a token. $1 is replaced with the translation for \"etherscan\"" + }, + "version": { + "message": "Version" + }, + "view": { + "message": "View" + }, + "viewActivity": { + "message": "View activity" + }, + "viewAllDetails": { + "message": "View all details" + }, + "viewAllQuotes": { + "message": "view all quotes" + }, + "viewContact": { + "message": "View contact" + }, + "viewDetails": { + "message": "View details" + }, + "viewFullTransactionDetails": { + "message": "View full transaction details" + }, + "viewMore": { + "message": "View more" + }, + "viewOnBlockExplorer": { + "message": "View on block explorer" + }, + "viewOnCustomBlockExplorer": { + "message": "View $1 at $2", + "description": "$1 is the action type. e.g (Account, Transaction, Swap) and $2 is the Custom Block Explorer URL" + }, + "viewOnEtherscan": { + "message": "View $1 on Etherscan", + "description": "$1 is the action type. e.g (Account, Transaction, Swap)" + }, + "viewOnExplorer": { + "message": "View on explorer" + }, + "viewOnOpensea": { + "message": "View on Opensea" + }, + "viewTransaction": { + "message": "View transaction" + }, + "viewinCustodianApp": { + "message": "View in custodian app" + }, + "viewinExplorer": { + "message": "View $1 in explorer", + "description": "$1 is the action type. e.g (Account, Transaction, Swap)" + }, + "visitSite": { + "message": "Visit site" + }, + "visitWebSite": { + "message": "Visit our website" + }, + "wallet": { + "message": "Wallet" + }, + "walletConnectionGuide": { + "message": "our hardware wallet connection guide" + }, + "walletCreationSuccessDetail": { + "message": "You’ve successfully protected your wallet. Keep your Secret Recovery Phrase safe and secret -- it’s your responsibility!" + }, + "walletCreationSuccessReminder1": { + "message": "MetaMask can’t recover your Secret Recovery Phrase." + }, + "walletCreationSuccessReminder2": { + "message": "MetaMask will never ask you for your Secret Recovery Phrase." + }, + "walletCreationSuccessReminder3": { + "message": "$1 with anyone or risk your funds being stolen", + "description": "$1 is separated as walletCreationSuccessReminder3BoldSection so that we can bold it" + }, + "walletCreationSuccessReminder3BoldSection": { + "message": "Never share your Secret Recovery Phrase", + "description": "This string is localized separately from walletCreationSuccessReminder3 so that we can bold it" + }, + "walletCreationSuccessTitle": { + "message": "Wallet creation successful" + }, + "wantToAddThisNetwork": { + "message": "Want to add this network?" + }, + "wantsToAddThisAsset": { + "message": "This allows the following asset to be added to your wallet." + }, + "warning": { + "message": "Warning" + }, + "warningFromSnap": { + "message": "Warning from $1", + "description": "$1 represents the name of the snap" + }, + "warningTooltipText": { + "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" + }, + "weak": { + "message": "Weak" + }, + "web3": { + "message": "Web3" + }, + "web3ShimUsageNotification": { + "message": "We noticed that the current website tried to use the removed window.web3 API. If the site appears to be broken, please click $1 for more information.", + "description": "$1 is a clickable link." + }, + "webhid": { + "message": "WebHID", + "description": "Refers to a interface for connecting external devices to the browser. Used for connecting ledger to the browser. Read more here https://developer.mozilla.org/en-US/docs/Web/API/WebHID_API" + }, + "websites": { + "message": "websites", + "description": "Used in the 'permission_rpc' message." + }, + "welcomeBack": { + "message": "Welcome back!" + }, + "welcomeExploreDescription": { + "message": "Store, send and spend crypto currencies and assets." + }, + "welcomeExploreTitle": { + "message": "Explore decentralized apps" + }, + "welcomeLoginDescription": { + "message": "Use your MetaMask to login to decentralized apps - no signup needed." + }, + "welcomeLoginTitle": { + "message": "Say hello to your wallet" + }, + "welcomeToMetaMask": { + "message": "Let's get started" + }, + "welcomeToMetaMaskIntro": { + "message": "Trusted by millions, MetaMask is a secure wallet making the world of web3 accessible to all." + }, + "whatsNew": { + "message": "What's new", + "description": "This is the title of a popup that gives users notifications about new features and updates to MetaMask." + }, + "whatsThis": { + "message": "What's this?" + }, + "wrongChainId": { + "message": "This chain ID doesn’t match the network name." + }, + "wrongNetworkName": { + "message": "According to our records, the network name may not correctly match this chain ID." + }, + "xOfYPending": { + "message": "$1 of $2 pending", + "description": "$1 and $2 are intended to be two numbers, where $2 is a total number of pending confirmations, and $1 is a count towards that total" + }, + "yes": { + "message": "Yes" + }, + "you": { + "message": "You" + }, + "youHaveAddedAll": { + "message": "You've added all the popular networks. You can discover more networks $1 Or you can $2", + "description": "$1 is a link with the text 'here' and $2 is a button with the text 'add more networks manually'" + }, + "youNeedToAllowCameraAccess": { + "message": "You need to allow camera access to use this feature." + }, + "youSign": { + "message": "You are signing" + }, + "yourAccounts": { + "message": "Your accounts" + }, + "yourActivity": { + "message": "Your activity" + }, + "yourBalance": { + "message": "Your balance" + }, + "yourFundsMayBeAtRisk": { + "message": "Your funds may be at risk" + }, + "yourNFTmayBeAtRisk": { + "message": "Your NFT may be at risk" + }, + "yourPrivateSeedPhrase": { + "message": "Your Secret Recovery Phrase" + }, + "yourTransactionConfirmed": { + "message": "Transaction already confirmed" + }, + "yourTransactionJustConfirmed": { + "message": "We weren't able to cancel your transaction before it was confirmed on the blockchain." + }, + "zeroGasPriceOnSpeedUpError": { + "message": "Zero gas price on speed up" + } +} diff --git a/app/_locales/es/messages.json b/app/_locales/es/messages.json index 4d3ec28c7198..b63388b29807 100644 --- a/app/_locales/es/messages.json +++ b/app/_locales/es/messages.json @@ -275,7 +275,7 @@ "description": "$1 is Learn more link" }, "addNewAccount": { - "message": "Añadir una cuenta nueva" + "message": "Añadir una cuenta nueva de Ethereum" }, "addNewToken": { "message": "Agregar nuevo token" @@ -1260,9 +1260,6 @@ "data": { "message": "Datos" }, - "dataBackupSeemsCorrupt": { - "message": "No se pueden restaurar sus datos. El archivo parece estar corrupto." - }, "dataHex": { "message": "Hex" }, @@ -1552,6 +1549,9 @@ "message": "activar $1", "description": "$1 is a token symbol, e.g. ETH" }, + "enableTokenAutoDetection": { + "message": "Activar la autodetección de tokens" + }, "enabled": { "message": "Activado" }, @@ -2495,9 +2495,6 @@ "message": "Asegúrese de que nadie esté mirando", "description": "Warning to users to be care while creating and saving their new Secret Recovery Phrase" }, - "manageInSettings": { - "message": "Administrar en configuración" - }, "max": { "message": "Máx." }, @@ -2928,6 +2925,9 @@ "noSnaps": { "message": "No tiene ningún snap instalado." }, + "noThanks": { + "message": "No, gracias" + }, "noTransactions": { "message": "No tiene transacciones" }, @@ -3097,7 +3097,7 @@ }, "notificationTransactionSuccessView": { "message": "Ver en $1", - "description": "Additional content in browser notification that appears when a transaction is confirmed and has a block explorer URL" + "description": "Additional content in a notification that appears when a transaction is confirmed and has a block explorer URL." }, "notifications": { "message": "Notificaciones" @@ -4021,12 +4021,6 @@ "restore": { "message": "Restaurar" }, - "restoreFailed": { - "message": "No se pueden restaurar sus datos desde el archivo proporcionado" - }, - "restoreSuccessful": { - "message": "Sus datos se restauraron correctamente" - }, "restoreUserData": { "message": "Restaure sus datos de usuario" }, @@ -5866,7 +5860,7 @@ "message": "Detección automática de NFT" }, "useNftDetectionDescriptionText": { - "message": "Permita que MetaMask agregue NFT de su propiedad mediante servicios de terceros (como OpenSea). La detección automática de NFT expone su IP y dirección de cuenta a estos servicios. Habilitar esta función podría asociar su dirección IP con su dirección de Ethereum y mostrar NFT enviados mediante airdrop por estafadores. Puede agregar tokens manualmente para evitar este riesgo." + "message": "Deje que MetaMask agregue los NFT de su propiedad mediante servicios de terceros. La detección automática de los NFT expone su IP y dirección de cuenta a estos servicios. Habilitar esta función podría asociar su dirección IP con su dirección de Ethereum y mostrar NFT falsos enviados mediante airdrop por estafadores. Puede agregar tókenes manualmente para evitar este riesgo." }, "usePhishingDetection": { "message": "Usar detección de phishing" diff --git a/app/_locales/fr/messages.json b/app/_locales/fr/messages.json index 35973f9e23ff..4dfad65c8fe1 100644 --- a/app/_locales/fr/messages.json +++ b/app/_locales/fr/messages.json @@ -275,7 +275,7 @@ "description": "$1 is Learn more link" }, "addNewAccount": { - "message": "Ajouter un nouveau compte" + "message": "Ajouter un nouveau compte Ethereum" }, "addNewToken": { "message": "Ajouter un nouveau jeton" @@ -1263,9 +1263,6 @@ "data": { "message": "Données" }, - "dataBackupSeemsCorrupt": { - "message": "Impossible de restaurer vos données. Le fichier semble corrompu." - }, "dataHex": { "message": "Hex" }, @@ -1555,6 +1552,9 @@ "message": "activer $1", "description": "$1 is a token symbol, e.g. ETH" }, + "enableTokenAutoDetection": { + "message": "Activer la détection automatique des jetons" + }, "enabled": { "message": "Activé" }, @@ -2498,9 +2498,6 @@ "message": "Assurez-vous que personne ne regarde votre écran", "description": "Warning to users to be care while creating and saving their new Secret Recovery Phrase" }, - "manageInSettings": { - "message": "Gérer dans les paramètres" - }, "max": { "message": "Max." }, @@ -2931,6 +2928,9 @@ "noSnaps": { "message": "Aucun Snap installé" }, + "noThanks": { + "message": "Non merci" + }, "noTransactions": { "message": "Aucune transaction" }, @@ -3100,7 +3100,7 @@ }, "notificationTransactionSuccessView": { "message": "Consulter sur $1", - "description": "Additional content in browser notification that appears when a transaction is confirmed and has a block explorer URL" + "description": "Additional content in a notification that appears when a transaction is confirmed and has a block explorer URL." }, "notifications": { "message": "Notifications" @@ -4024,12 +4024,6 @@ "restore": { "message": "Restaurer" }, - "restoreFailed": { - "message": "Impossible de restaurer vos données à partir du fichier fourni" - }, - "restoreSuccessful": { - "message": "Vos données ont été restaurées avec succès" - }, "restoreUserData": { "message": "Restaurer les données de l’utilisateur" }, @@ -5869,7 +5863,7 @@ "message": "Détection automatique des NFT" }, "useNftDetectionDescriptionText": { - "message": "Laissez MetaMask ajouter les NFT que vous possédez en utilisant des services tiers (comme OpenSea). La détection automatique des NFT expose votre adresse IP et l’adresse de votre compte à ces services. Si vous activez cette fonctionnalité, un lien pourrait être établi entre votre adresse IP et votre adresse Ethereum, et entrainer l’affichage de faux NFT distribués par des escrocs. Vous pouvez ajouter des jetons manuellement pour éviter ce risque." + "message": "Laissez MetaMask ajouter les NFT que vous possédez en utilisant des services tiers. La détection automatique des NFT révèle votre adresse IP et l’adresse de votre compte à ces services. Si vous activez cette fonctionnalité, un lien pourrait être établi entre votre adresse IP et votre adresse Ethereum, et entrainer l’affichage de faux NFT parachutés par des arnaqueurs. Vous pouvez ajouter des jetons manuellement pour éviter ce risque." }, "usePhishingDetection": { "message": "Utiliser la fonction anti-hameçonnage" diff --git a/app/_locales/hi/messages.json b/app/_locales/hi/messages.json index 59aebe129f11..6c7e8dbc38db 100644 --- a/app/_locales/hi/messages.json +++ b/app/_locales/hi/messages.json @@ -275,7 +275,7 @@ "description": "$1 is Learn more link" }, "addNewAccount": { - "message": "एक नया अकाउंट जोड़ें" + "message": "एक नया Ethereum अकाउंट जोड़ें" }, "addNewToken": { "message": "नया टोकन जोड़ें" @@ -1260,9 +1260,6 @@ "data": { "message": "डेटा" }, - "dataBackupSeemsCorrupt": { - "message": "आपका डेटा रीस्टोर नहीं किया जा सकता। लगता है फाइल करप्ट हुई है।" - }, "dataHex": { "message": "हेक्स" }, @@ -1552,6 +1549,9 @@ "message": "$1 इनेबल करें", "description": "$1 is a token symbol, e.g. ETH" }, + "enableTokenAutoDetection": { + "message": "टोकन ऑटोडिटेक्शन चालू करें" + }, "enabled": { "message": "इनेबल किया गया" }, @@ -2495,9 +2495,6 @@ "message": "पक्का करें कि इसे कोई भी नहीं देख रहा है", "description": "Warning to users to be care while creating and saving their new Secret Recovery Phrase" }, - "manageInSettings": { - "message": "सेटिंग्स में मैनेज करें" - }, "max": { "message": "अधिकतम" }, @@ -2928,6 +2925,9 @@ "noSnaps": { "message": "आपके पास इंस्टाल किए गए Snap नहीं हैं।" }, + "noThanks": { + "message": "जी नहीं, धन्यवाद" + }, "noTransactions": { "message": "आपके पास कोई ट्रांसेक्शन नहीं है" }, @@ -3097,7 +3097,7 @@ }, "notificationTransactionSuccessView": { "message": "$1 पर देखें", - "description": "Additional content in browser notification that appears when a transaction is confirmed and has a block explorer URL" + "description": "Additional content in a notification that appears when a transaction is confirmed and has a block explorer URL." }, "notifications": { "message": "सूचनाएं" @@ -4021,12 +4021,6 @@ "restore": { "message": "रीस्टोर करें" }, - "restoreFailed": { - "message": "प्रदान की गई फाइल से आपके डेटा को रीस्टोर नहीं किया जा सकता" - }, - "restoreSuccessful": { - "message": "आपके डेटा को सफलतापूर्वक रीस्टोर किया गया है" - }, "restoreUserData": { "message": "यूज़र डेटा रीस्टोर करें" }, @@ -5866,7 +5860,7 @@ "message": "NFT को ऑटो-डिटेक्ट करें" }, "useNftDetectionDescriptionText": { - "message": "MetaMask को थर्ड पार्टी सर्विसेज़ (जैसे OpenSea) का उपयोग करके आपके NFTs जोड़ने दें। NFTs को ऑटोडिटेक्ट करने से आपका आईपी और अकाउंट एड्रेस इन सेवाओं के सामने आ जाता है। इस फ़ीचर को चालू करने से आपका आईपी ​​एड्रेस आपके Ethereum एड्रेस के साथ जुड़ सकता है और धोखाधड़ी करने वाले लोगों द्वारा एयरड्रॉप किए गए नकली NFTs दिख सकते हैं। आप मैन्युअल तरीके से टोकन जोड़कर इस ख़तरे से बच सकते हैं।" + "message": "MetaMask को थर्ड पार्टी सर्विसेज़ का उपयोग करके आपके NFTs जोड़ने दें। NFTs को ऑटोडिटेक्ट करने से आपका आईपी और अकाउंट एड्रेस इन सेवाओं के सामने आ जाता है। इस फ़ीचर को चालू करने से आपका आईपी ​​एड्रेस आपके Ethereum एड्रेस के साथ जुड़ सकता है और धोखाधड़ी करने वाले लोगों द्वारा एयरड्रॉप किए गए नकली NFTs दिख सकते हैं। आप मैन्युअल तरीके से टोकन जोड़कर इस ख़तरे से बच सकते हैं।" }, "usePhishingDetection": { "message": "फिशिंग डिटेक्शन का इस्तेमाल करें" diff --git a/app/_locales/id/messages.json b/app/_locales/id/messages.json index 0d8144d1f0fe..4505c7bf8215 100644 --- a/app/_locales/id/messages.json +++ b/app/_locales/id/messages.json @@ -275,7 +275,7 @@ "description": "$1 is Learn more link" }, "addNewAccount": { - "message": "Tambahkan akun baru" + "message": "Tambahkan akun Ethereum baru" }, "addNewToken": { "message": "Tambahkan token baru" @@ -1263,9 +1263,6 @@ "data": { "message": "Data" }, - "dataBackupSeemsCorrupt": { - "message": "Tidak dapat memulihkan data Anda. File tampaknya rusak." - }, "dataHex": { "message": "Hex" }, @@ -1555,6 +1552,9 @@ "message": "aktifkan $1", "description": "$1 is a token symbol, e.g. ETH" }, + "enableTokenAutoDetection": { + "message": "Aktifkan autodeteksi token" + }, "enabled": { "message": "Diaktifkan" }, @@ -2498,9 +2498,6 @@ "message": "Pastikan tidak ada yang melihat layar Anda", "description": "Warning to users to be care while creating and saving their new Secret Recovery Phrase" }, - "manageInSettings": { - "message": "Kelola di pengaturan" - }, "max": { "message": "Maks" }, @@ -2931,6 +2928,9 @@ "noSnaps": { "message": "Belum ada snap yang diinstal." }, + "noThanks": { + "message": "Tidak, terima kasih" + }, "noTransactions": { "message": "Anda tidak memiliki transaksi" }, @@ -3100,7 +3100,7 @@ }, "notificationTransactionSuccessView": { "message": "Lihat di $1", - "description": "Additional content in browser notification that appears when a transaction is confirmed and has a block explorer URL" + "description": "Additional content in a notification that appears when a transaction is confirmed and has a block explorer URL." }, "notifications": { "message": "Notifikasi" @@ -4024,12 +4024,6 @@ "restore": { "message": "Pulihkan" }, - "restoreFailed": { - "message": "Tidak dapat memulihkan data Anda dari berkas yang disediakan" - }, - "restoreSuccessful": { - "message": "Data Anda telah berhasil dipulihkan" - }, "restoreUserData": { "message": "Pulihkan data pengguna" }, @@ -5869,7 +5863,7 @@ "message": "Deteksi otomatis NFT" }, "useNftDetectionDescriptionText": { - "message": "Izinkan MetaMask menambahkan NFT yang Anda miliki menggunakan layanan pihak ketiga (seperti OpenSea). Autodeteksi NFT mengekspos alamat IP dan akun Anda ke layanan ini. Mengaktifkan fitur ini dapat mengaitkan alamat IP dengan alamat Ethereum Anda dan menampilkan NFT palsu yang di-airdrop oleh penipu. Anda dapat menambahkan token secara manual untuk menghindari risiko ini." + "message": "Izinkan MetaMask menambahkan NFT yang Anda miliki menggunakan layanan pihak ketiga. Autodeteksi NFT mengekspos alamat IP dan akun Anda ke layanan ini. Mengaktifkan fitur ini dapat mengaitkan alamat IP dengan alamat Ethereum Anda dan menampilkan NFT palsu yang di-airdrop oleh penipu. Anda dapat menambahkan token secara manual untuk menghindari risiko ini." }, "usePhishingDetection": { "message": "Gunakan deteksi phishing" diff --git a/app/_locales/it/messages.json b/app/_locales/it/messages.json index 2ca832f39006..cea9dde6fcfb 100644 --- a/app/_locales/it/messages.json +++ b/app/_locales/it/messages.json @@ -633,9 +633,6 @@ "darkTheme": { "message": "Scuro" }, - "dataBackupSeemsCorrupt": { - "message": "Impossibile ripristinare i tuoi dati. Il file sembra essere corrotto." - }, "decimal": { "message": "Precisione Decimali" }, diff --git a/app/_locales/ja/messages.json b/app/_locales/ja/messages.json index 464bd37d8236..b58515cf6eb1 100644 --- a/app/_locales/ja/messages.json +++ b/app/_locales/ja/messages.json @@ -275,7 +275,7 @@ "description": "$1 is Learn more link" }, "addNewAccount": { - "message": "新しいアカウントを追加" + "message": "新しいイーサリアムアカウントを追加" }, "addNewToken": { "message": "新しいトークンを追加" @@ -1260,9 +1260,6 @@ "data": { "message": "データ" }, - "dataBackupSeemsCorrupt": { - "message": "データを復元できません。ファイルが破損しているようです。" - }, "dataHex": { "message": "16進法" }, @@ -1552,6 +1549,9 @@ "message": "$1を有効にする", "description": "$1 is a token symbol, e.g. ETH" }, + "enableTokenAutoDetection": { + "message": "トークンの自動検出を有効にする" + }, "enabled": { "message": "有効" }, @@ -2495,9 +2495,6 @@ "message": "誰にも見られていないことを確認してください", "description": "Warning to users to be care while creating and saving their new Secret Recovery Phrase" }, - "manageInSettings": { - "message": "設定で管理" - }, "max": { "message": "最大" }, @@ -2928,6 +2925,9 @@ "noSnaps": { "message": "snapがインストールされていません" }, + "noThanks": { + "message": "結構です" + }, "noTransactions": { "message": "トランザクションがありません" }, @@ -3097,7 +3097,7 @@ }, "notificationTransactionSuccessView": { "message": "$1で表示", - "description": "Additional content in browser notification that appears when a transaction is confirmed and has a block explorer URL" + "description": "Additional content in a notification that appears when a transaction is confirmed and has a block explorer URL." }, "notifications": { "message": "通知" @@ -4021,12 +4021,6 @@ "restore": { "message": "復元" }, - "restoreFailed": { - "message": "提供されたファイルからデータを復元できません" - }, - "restoreSuccessful": { - "message": "データが復元されました" - }, "restoreUserData": { "message": "ユーザーデータの復元" }, @@ -5866,7 +5860,7 @@ "message": "NFTを自動検出" }, "useNftDetectionDescriptionText": { - "message": "MetaMaskが、サードパーティサービス (例: OpenSea) を使って、ユーザーが所有するNFTを追加することを許可します。NFTの自動検出機能を利用すると、ユーザーのIPとアカウントアドレスがこれらのサービスに公開されます。この機能を有効にした場合、ユーザーのIPアドレスとイーサリアムアドレスが紐付けられ、詐欺師によりエアドロップされた偽のNFTが表示される可能性があります。このリスクを回避するため、手動でトークンを追加することもできます。" + "message": "MetaMaskが、サードパーティサービス を使って、ユーザーが所有するNFTを追加することを許可します。NFTの自動検出機能を利用すると、ユーザーのIPとアカウントアドレスがこれらのサービスに公開されます。この機能を有効にした場合、ユーザーのIPアドレスとイーサリアムアドレスが紐付けられ、詐欺師によりエアドロップされた偽のNFTが表示される可能性があります。このリスクを回避するため、手動でトークンを追加することもできます。" }, "usePhishingDetection": { "message": "フィッシング検出を使用" diff --git a/app/_locales/ko/messages.json b/app/_locales/ko/messages.json index 2eaf06ce7afd..7cf214159133 100644 --- a/app/_locales/ko/messages.json +++ b/app/_locales/ko/messages.json @@ -275,7 +275,7 @@ "description": "$1 is Learn more link" }, "addNewAccount": { - "message": "새 계정 추가" + "message": "새 이더리움 계정 추가" }, "addNewToken": { "message": "신규 토큰 추가" @@ -1260,9 +1260,6 @@ "data": { "message": "데이터" }, - "dataBackupSeemsCorrupt": { - "message": "사용자 데이터를 복구할 수 없습니다. 파일이 손상된 것 같습니다." - }, "dataHex": { "message": "헥스" }, @@ -1552,6 +1549,9 @@ "message": "$1 활성화", "description": "$1 is a token symbol, e.g. ETH" }, + "enableTokenAutoDetection": { + "message": "토큰 자동 감지 활성화" + }, "enabled": { "message": "활성화됨" }, @@ -2495,9 +2495,6 @@ "message": "다른 사람이 이 화면을 보고 있지는 않은지 확인하세요.", "description": "Warning to users to be care while creating and saving their new Secret Recovery Phrase" }, - "manageInSettings": { - "message": "설정에서 관리" - }, "max": { "message": "최대" }, @@ -2928,6 +2925,9 @@ "noSnaps": { "message": "설치된 스냅이 없습니다" }, + "noThanks": { + "message": "괜찮습니다" + }, "noTransactions": { "message": "트랜잭션이 없습니다." }, @@ -3097,7 +3097,7 @@ }, "notificationTransactionSuccessView": { "message": "$1에서 보기", - "description": "Additional content in browser notification that appears when a transaction is confirmed and has a block explorer URL" + "description": "Additional content in a notification that appears when a transaction is confirmed and has a block explorer URL." }, "notifications": { "message": "알림" @@ -4021,12 +4021,6 @@ "restore": { "message": "복구" }, - "restoreFailed": { - "message": "제공된 파일에서 데이터를 복구할 수 없습니다." - }, - "restoreSuccessful": { - "message": "데이터가 성공적으로 복구되었습니다." - }, "restoreUserData": { "message": "사용자 데이터 복구" }, @@ -5866,7 +5860,7 @@ "message": "NFT 자동 감지" }, "useNftDetectionDescriptionText": { - "message": "MetaMask가 제삼자 서비스(예: OpenSea)를 이용하여 회원님의 NFT를 추가할 수 있도록 합니다. NFT를 자동 감지하면 이러한 서비스에 IP와 계정 주소가 노출됩니다. 이 기능을 켜면 IP 주소가 이더리움 주소와 연결되고 사기꾼이 에어드랍한 가짜 NFT가 표시될 수 있습니다. 이러한 위험을 피하기 위해 수동으로 토큰을 추가할 수 있습니다." + "message": "MetaMask가 제삼자 서비스를 이용하여 회원님의 NFT를 추가할 수 있도록 합니다. NFT를 자동 감지하면 이러한 서비스에 IP와 계정 주소가 노출됩니다. 이 기능을 켜면 IP 주소가 이더리움 주소와 연결되고 사기꾼이 에어드랍한 가짜 NFT가 표시될 수 있습니다. 이러한 위험을 피하기 위해 수동으로 토큰을 추가할 수 있습니다." }, "usePhishingDetection": { "message": "피싱 감지 사용" diff --git a/app/_locales/pt/messages.json b/app/_locales/pt/messages.json index c391a94b5f6a..65f524354ce0 100644 --- a/app/_locales/pt/messages.json +++ b/app/_locales/pt/messages.json @@ -275,7 +275,7 @@ "description": "$1 is Learn more link" }, "addNewAccount": { - "message": "Adicionar uma nova conta" + "message": "Adicionar uma nova conta Ethereum" }, "addNewToken": { "message": "Adicionar novo token" @@ -1263,9 +1263,6 @@ "data": { "message": "Dados" }, - "dataBackupSeemsCorrupt": { - "message": "Não é possível restaurar seus dados. O arquivo parece estar corrompido." - }, "dataHex": { "message": "Hex" }, @@ -1555,6 +1552,9 @@ "message": "ativar $1", "description": "$1 is a token symbol, e.g. ETH" }, + "enableTokenAutoDetection": { + "message": "Ativar detecção automática de tokens" + }, "enabled": { "message": "Ativado" }, @@ -2498,9 +2498,6 @@ "message": "Certifique-se de que ninguém está olhando", "description": "Warning to users to be care while creating and saving their new Secret Recovery Phrase" }, - "manageInSettings": { - "message": "Gerenciar em configurações" - }, "max": { "message": "Máximo" }, @@ -2931,6 +2928,9 @@ "noSnaps": { "message": "Você não tem nenhum snap instalado." }, + "noThanks": { + "message": "Não, obrigado" + }, "noTransactions": { "message": "Você não tem transações" }, @@ -3100,7 +3100,7 @@ }, "notificationTransactionSuccessView": { "message": "Ver em $1", - "description": "Additional content in browser notification that appears when a transaction is confirmed and has a block explorer URL" + "description": "Additional content in a notification that appears when a transaction is confirmed and has a block explorer URL." }, "notifications": { "message": "Notificações" @@ -4024,12 +4024,6 @@ "restore": { "message": "Restaurar" }, - "restoreFailed": { - "message": "Não foi possível restaurar seus dados a partir do arquivo fornecido" - }, - "restoreSuccessful": { - "message": "Seus dados foram restaurados com sucesso" - }, "restoreUserData": { "message": "Restaurar dados do usuário" }, @@ -5869,7 +5863,7 @@ "message": "Detectar NFTs automaticamente" }, "useNftDetectionDescriptionText": { - "message": "Permita que a MetaMask use serviços de terceiros (como o OpenSea) para adicionar os NFTs que você possui. A detecção automática de NFTs expõe seu endereço IP e o endereço da sua conta a esses serviços. Ativar esse recurso pode associar seu endereço IP ao seu endereço Ethereum e exibir NFTs falsos enviados por golpistas via AirDrop. Você pode adicionar tokens manualmente para evitar esse risco." + "message": "Permita que a MetaMask use serviços de terceiros para adicionar os NFTs que você possui. A detecção automática de NFTs expõe seu endereço IP e o endereço da sua conta a esses serviços. Ativar esse recurso pode associar seu endereço IP ao seu endereço Ethereum e exibir NFTs falsos enviados por golpistas. Você pode adicionar tokens manualmente para evitar esse risco." }, "usePhishingDetection": { "message": "Usar detecção de phishing" diff --git a/app/_locales/ru/messages.json b/app/_locales/ru/messages.json index 3a0e2ed664c6..fb4b81508abc 100644 --- a/app/_locales/ru/messages.json +++ b/app/_locales/ru/messages.json @@ -275,7 +275,7 @@ "description": "$1 is Learn more link" }, "addNewAccount": { - "message": "Добавить новый счет" + "message": "Добавить новый счет Ethereum" }, "addNewToken": { "message": "Добавить новый токен" @@ -1263,9 +1263,6 @@ "data": { "message": "Данные" }, - "dataBackupSeemsCorrupt": { - "message": "Не удается восстановить ваши данные. Файл, по-видимому, поврежден." - }, "dataHex": { "message": "Шестнадцатиричные" }, @@ -1555,6 +1552,9 @@ "message": "активирует для $1", "description": "$1 is a token symbol, e.g. ETH" }, + "enableTokenAutoDetection": { + "message": "Включите автоопределение токена" + }, "enabled": { "message": "Включено" }, @@ -2498,9 +2498,6 @@ "message": "Убедитесь, что никто не смотрит", "description": "Warning to users to be care while creating and saving their new Secret Recovery Phrase" }, - "manageInSettings": { - "message": "Управляйте в настройках" - }, "max": { "message": "Макс." }, @@ -2931,6 +2928,9 @@ "noSnaps": { "message": "Snaps не установлены." }, + "noThanks": { + "message": "Нет, спасибо" + }, "noTransactions": { "message": "У вас нет транзакций" }, @@ -3100,7 +3100,7 @@ }, "notificationTransactionSuccessView": { "message": "Смотреть на $1", - "description": "Additional content in browser notification that appears when a transaction is confirmed and has a block explorer URL" + "description": "Additional content in a notification that appears when a transaction is confirmed and has a block explorer URL." }, "notifications": { "message": "Уведомления" @@ -4024,12 +4024,6 @@ "restore": { "message": "Восстановить" }, - "restoreFailed": { - "message": "Не удается восстановить ваши данные из предоставленного файла" - }, - "restoreSuccessful": { - "message": "Ваши данные успешно восстановлены" - }, "restoreUserData": { "message": "Восстановить пользовательские данные" }, @@ -5869,7 +5863,7 @@ "message": "Автоопределение NFT" }, "useNftDetectionDescriptionText": { - "message": "Разрешает MetaMask добавлять ваши собственные NFT с помощью сторонних сервисов (например, OpenSea). Автоопределение NFT раскрывает ваш IP-адрес и адрес счета этим сервисам. Включение этой функции может связать ваш IP-адрес с вашим адресом Ethereum и отобразить поддельные NFT-файлы, аирдропнутые мошенниками. Вы можете добавлять токены вручную, чтобы избежать этой угрозы." + "message": "Разрешает MetaMask добавлять ваши собственные NFT с помощью сторонних сервисов. Автоопределение NFT раскрывает ваш IP-адрес и адрес счета этим сервисам. Включение этой функции может связать ваш IP-адрес с вашим адресом Ethereum и отобразить поддельные NFT-файлы, аирдропнутые мошенниками. Вы можете добавлять токены вручную, чтобы избежать этой угрозы." }, "usePhishingDetection": { "message": "Использовать обнаружение фишинга" diff --git a/app/_locales/tl/messages.json b/app/_locales/tl/messages.json index 90e423b1fe9c..c63b729393e6 100644 --- a/app/_locales/tl/messages.json +++ b/app/_locales/tl/messages.json @@ -275,7 +275,7 @@ "description": "$1 is Learn more link" }, "addNewAccount": { - "message": "Magdagdag ng bagong account" + "message": "Magdagdag ng bagong account sa Ethereum" }, "addNewToken": { "message": "Magdagdag ng bagong token" @@ -1260,9 +1260,6 @@ "data": { "message": "Datos" }, - "dataBackupSeemsCorrupt": { - "message": "Hindi maibalik ang iyong data. Mukhang sira ang file." - }, "dataHex": { "message": "Hex" }, @@ -1552,6 +1549,9 @@ "message": "paganahin ang $1", "description": "$1 is a token symbol, e.g. ETH" }, + "enableTokenAutoDetection": { + "message": "Paganahin ang autodetection ng token" + }, "enabled": { "message": "Pinagana" }, @@ -2201,7 +2201,7 @@ "message": "Nakikipag-ugnayan sa" }, "interactingWithTransactionDescription": { - "message": "Sa kontrata na ito ka nakikipag-ugnayan ka. Protektahan ang iyong sarili mula sa mga scammer sa pamamagitan ng pagberipika ng mga detalye." + "message": "Nakikipag-ugnayan ka sa kontrato na ito. Protektahan ang iyong sarili mula sa mga scammer sa pamamagitan ng pagberipika ng mga detalye." }, "invalidAddress": { "message": "Di-wastong address" @@ -2495,9 +2495,6 @@ "message": "Siguraduhing walang tumitingin", "description": "Warning to users to be care while creating and saving their new Secret Recovery Phrase" }, - "manageInSettings": { - "message": "Pamahalaan sa mga setting" - }, "max": { "message": "Max" }, @@ -2928,6 +2925,9 @@ "noSnaps": { "message": "Wala kang naka-install na snaps." }, + "noThanks": { + "message": "Salamat na lang" + }, "noTransactions": { "message": "Wala kang transaksyon" }, @@ -3097,7 +3097,7 @@ }, "notificationTransactionSuccessView": { "message": "Tingnan sa $1", - "description": "Additional content in browser notification that appears when a transaction is confirmed and has a block explorer URL" + "description": "Additional content in a notification that appears when a transaction is confirmed and has a block explorer URL." }, "notifications": { "message": "Mga Abiso" @@ -4021,12 +4021,6 @@ "restore": { "message": "Ibalik" }, - "restoreFailed": { - "message": "Hindi maibalik ang iyong data mula sa file na ibinigay" - }, - "restoreSuccessful": { - "message": "Ang iyong data ay matagumpay na naibalik" - }, "restoreUserData": { "message": "Ibalik ang data ng user" }, @@ -5866,7 +5860,7 @@ "message": "Awtomatikong tuklasin ang mga NFT" }, "useNftDetectionDescriptionText": { - "message": "Hayaan ang MetaMask na magdagdag ng mga NFT na iyong pag-aari gamit ang mga third-party na serbisyo (tulad ng OpenSea). Ang awtomatikong pagtuklas ng mga NFT ay naglalantad sa iyong IP at address ng account sa mga serbisyong ito. Ang pag-enable sa tampok na ito ay maaaring mag-ugnay sa iyong IP sa iyong Ethereum address at magpakita ng pekeng NFT na in-airdrop ng mga manloloko. Maaari kang magdagdag ng token sa manwal na paraan para iwasan ang panganib na ito." + "message": "Hayaan ang MetaMask na magdagdag ng mga NFT na iyong pag-aari gamit ang mga third-party na serbisyo. Ang awtomatikong pagtuklas ng mga NFT ay naglalantad sa iyong IP at address ng account sa mga serbisyong ito. Ang pag-enable sa tampok na ito ay maaaring mag-ugnay sa iyong IP sa iyong Ethereum address at magpakita ng pekeng NFT na in-airdrop ng mga scammer. Maaari kang magdagdag ng token sa manu-manong paraan para iwasan ang panganib na ito." }, "usePhishingDetection": { "message": "Gumamit ng phishing detection" diff --git a/app/_locales/tr/messages.json b/app/_locales/tr/messages.json index 3f599c8c6162..6bcdc12b45ab 100644 --- a/app/_locales/tr/messages.json +++ b/app/_locales/tr/messages.json @@ -275,7 +275,7 @@ "description": "$1 is Learn more link" }, "addNewAccount": { - "message": "Yeni bir hesap ekleyin" + "message": "Yeni bir Ethereum hesabı ekle" }, "addNewToken": { "message": "Yeni token ekleyin" @@ -1263,9 +1263,6 @@ "data": { "message": "Veri" }, - "dataBackupSeemsCorrupt": { - "message": "Verileriniz geri yüklenemiyor. Dosya bozuk gibi görünüyor." - }, "dataHex": { "message": "On Altılı" }, @@ -1555,6 +1552,9 @@ "message": "şunu etkinleştir: $1", "description": "$1 is a token symbol, e.g. ETH" }, + "enableTokenAutoDetection": { + "message": "Otomatik token algılamayı etkinleştir" + }, "enabled": { "message": "Etkinleştirildi" }, @@ -2498,9 +2498,6 @@ "message": "Hiç kimsenin bakmadığından emin olun", "description": "Warning to users to be care while creating and saving their new Secret Recovery Phrase" }, - "manageInSettings": { - "message": "Ayarlar kısmında yönet" - }, "max": { "message": "Maksimum" }, @@ -2931,6 +2928,9 @@ "noSnaps": { "message": "Yüklü snapiniz yok." }, + "noThanks": { + "message": "Hayır, istemiyorum" + }, "noTransactions": { "message": "İşleminiz yok" }, @@ -3100,7 +3100,7 @@ }, "notificationTransactionSuccessView": { "message": "$1 üzerinde görüntüle", - "description": "Additional content in browser notification that appears when a transaction is confirmed and has a block explorer URL" + "description": "Additional content in a notification that appears when a transaction is confirmed and has a block explorer URL." }, "notifications": { "message": "Bildirimler" @@ -4024,12 +4024,6 @@ "restore": { "message": "Geri Yükle" }, - "restoreFailed": { - "message": "Sunulan dosyadan verileriniz geri yüklenemiyor" - }, - "restoreSuccessful": { - "message": "Verileriniz başarılı bir şekilde geri yüklendi" - }, "restoreUserData": { "message": "Kullanıcı verilerini geri yükle" }, @@ -5869,7 +5863,7 @@ "message": "NFT'leri otomatik algıla" }, "useNftDetectionDescriptionText": { - "message": "MetaMask'in üçüncü taraf hizmetleri (OpenSea gibi) kullanarak size ait olan NFT'leri eklemesine izin verin. NFT'leri otomatik algılama, IP adresinizi ve hesap adresinizi bu hizmetlerle paylaşır. Bu özelliğin etkinleştirilmesi IP adresinizi Ethereum adresinizle ilişkilendirebilir ve dolandırıcılar tarafından airdrop'u gerçekleştirilen sahte NFT'leri gösterebilir. Bu riski önlemek için tokenleri elle ekleyebilirsiniz." + "message": "MetaMask'in üçüncü taraf hizmetleri kullanarak size ait olan NFT'leri eklemesine izin verin. NFT'leri otomatik algılama, IP adresinizi ve hesap adresinizi bu hizmetlerle paylaşır. Bu özelliğin etkinleştirilmesi IP adresinizi Ethereum adresinizle ilişkilendirebilir ve dolandırıcılar tarafından airdrop'u gerçekleştirilen sahte NFT'leri gösterebilir. Bu riski önlemek için tokenleri elle ekleyebilirsiniz." }, "usePhishingDetection": { "message": "Kimlik avı algılama kullan" diff --git a/app/_locales/vi/messages.json b/app/_locales/vi/messages.json index 56279bd7062d..bf7d870b5669 100644 --- a/app/_locales/vi/messages.json +++ b/app/_locales/vi/messages.json @@ -275,7 +275,7 @@ "description": "$1 is Learn more link" }, "addNewAccount": { - "message": "Thêm tài khoản" + "message": "Thêm tài khoản Ethereum mới" }, "addNewToken": { "message": "Thêm token mới" @@ -1260,9 +1260,6 @@ "data": { "message": "Dữ liệu" }, - "dataBackupSeemsCorrupt": { - "message": "Không thể khôi phục dữ liệu của bạn. Tập tin có vẻ đã bị hỏng." - }, "dataHex": { "message": "Thập lục phân" }, @@ -1552,6 +1549,9 @@ "message": "bật $1", "description": "$1 is a token symbol, e.g. ETH" }, + "enableTokenAutoDetection": { + "message": "Bật tính năng tự động phát hiện token" + }, "enabled": { "message": "Đã bật" }, @@ -2495,9 +2495,6 @@ "message": "Đảm bảo không có ai đang nhìn", "description": "Warning to users to be care while creating and saving their new Secret Recovery Phrase" }, - "manageInSettings": { - "message": "Quản lý trong phần cài đặt" - }, "max": { "message": "Tối đa" }, @@ -2928,6 +2925,9 @@ "noSnaps": { "message": "Bạn chưa cài đặt bất kỳ Snap nào." }, + "noThanks": { + "message": "Không, cảm ơn" + }, "noTransactions": { "message": "Bạn không có giao dịch nào" }, @@ -3097,7 +3097,7 @@ }, "notificationTransactionSuccessView": { "message": "Xem trên $1", - "description": "Additional content in browser notification that appears when a transaction is confirmed and has a block explorer URL" + "description": "Additional content in a notification that appears when a transaction is confirmed and has a block explorer URL." }, "notifications": { "message": "Thông báo" @@ -4021,12 +4021,6 @@ "restore": { "message": "Khôi phục" }, - "restoreFailed": { - "message": "Không thể khôi phục dữ liệu của bạn từ tập tin được cung cấp" - }, - "restoreSuccessful": { - "message": "Dữ liệu của bạn đã được khôi phục thành công" - }, "restoreUserData": { "message": "Khôi phục dữ liệu người dùng" }, @@ -5866,7 +5860,7 @@ "message": "Tự động phát hiện NFT" }, "useNftDetectionDescriptionText": { - "message": "Cho phép MetaMask thêm các NFT mà bạn sở hữu bằng cách sử dụng dịch vụ của bên thứ ba (như OpenSea). Tính năng Tự động phát hiện NFT sẽ làm lộ địa chỉ IP và tài khoản của bạn với các dịch vụ này. Việc bật tính năng này có thể liên kết địa chỉ IP của bạn với địa chỉ Ethereum của bạn và hiển thị các NFT giả do những kẻ lừa đảo phát tán. Bạn có thể thêm token theo cách thủ công để tránh rủi ro này." + "message": "Cho phép MetaMask thêm các NFT mà bạn sở hữu bằng cách sử dụng dịch vụ của bên thứ ba. Tính năng Tự động phát hiện NFT sẽ làm lộ địa chỉ IP và tài khoản của bạn với các dịch vụ này. Việc bật tính năng này có thể liên kết địa chỉ IP của bạn với địa chỉ Ethereum của bạn và hiển thị các NFT giả do những kẻ lừa đảo phát tán. Bạn có thể thêm token theo cách thủ công để tránh rủi ro này." }, "usePhishingDetection": { "message": "Sử dụng tính năng phát hiện lừa đảo" diff --git a/app/_locales/zh_CN/messages.json b/app/_locales/zh_CN/messages.json index 21fe318fab45..112788564b8c 100644 --- a/app/_locales/zh_CN/messages.json +++ b/app/_locales/zh_CN/messages.json @@ -1260,9 +1260,6 @@ "data": { "message": "数据" }, - "dataBackupSeemsCorrupt": { - "message": "无法还原数据。文件似乎已损坏。" - }, "dataHex": { "message": "十六进制" }, @@ -1552,6 +1549,9 @@ "message": "启用 $1", "description": "$1 is a token symbol, e.g. ETH" }, + "enableTokenAutoDetection": { + "message": "启用代币自动检测" + }, "enabled": { "message": "已启用" }, @@ -2495,9 +2495,6 @@ "message": "请确保没有人在看您的屏幕", "description": "Warning to users to be care while creating and saving their new Secret Recovery Phrase" }, - "manageInSettings": { - "message": "在设置中管理" - }, "max": { "message": "最大" }, @@ -2928,6 +2925,9 @@ "noSnaps": { "message": "您没有安装 snap。" }, + "noThanks": { + "message": "不,谢谢" + }, "noTransactions": { "message": "您没有任何交易" }, @@ -3097,7 +3097,7 @@ }, "notificationTransactionSuccessView": { "message": "在 $1 上查看", - "description": "Additional content in browser notification that appears when a transaction is confirmed and has a block explorer URL" + "description": "Additional content in a notification that appears when a transaction is confirmed and has a block explorer URL." }, "notifications": { "message": "通知" @@ -4021,12 +4021,6 @@ "restore": { "message": "恢复" }, - "restoreFailed": { - "message": "无法使用所提供的文件恢复数据" - }, - "restoreSuccessful": { - "message": "您的数据已成功恢复" - }, "restoreUserData": { "message": "恢复用户数据" }, @@ -5866,7 +5860,7 @@ "message": "自动检测NFT" }, "useNftDetectionDescriptionText": { - "message": "让 MetaMask 使用第三方服务(如 OpenSea)来添加您所拥有的 NFT。打开自动检测 NFT 功能后,这些服务就可以看到您的 IP 地址和账户地址。启用此功能可能会将您的 IP 地址与以太坊地址关联起来,并显示骗子空投的虚假 NFT。您可以手动添加代币以避免此风险。" + "message": "让 MetaMask 使用第三方服务来添加您所拥有的 NFT。打开自动检测 NFT 功能后,这些服务就可以看到您的 IP 地址和账户地址。启用此功能可能会将您的 IP 地址与以太坊地址关联起来,并显示骗子空投的虚假 NFT。您可以手动添加代币以避免此风险。" }, "usePhishingDetection": { "message": "使用网络钓鱼检测" diff --git a/app/scripts/background.js b/app/scripts/background.js index 6d6a1b4392ee..658cb03005c9 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -46,7 +46,7 @@ import Migrator from './lib/migrator'; import ExtensionPlatform from './platforms/extension'; import LocalStore from './lib/local-store'; import ReadOnlyNetworkStore from './lib/network-store'; -import { SENTRY_BACKGROUND_STATE } from './lib/setupSentry'; +import { SENTRY_BACKGROUND_STATE } from './constants/sentry-state'; import createStreamSink from './lib/createStreamSink'; import NotificationManager, { @@ -75,8 +75,7 @@ import { TRIGGER_TYPES } from './controllers/metamask-notifications/constants/no const BADGE_COLOR_APPROVAL = '#0376C9'; // eslint-disable-next-line @metamask/design-tokens/color-no-hex const BADGE_COLOR_NOTIFICATION = '#D73847'; -const BADGE_LABEL_APPROVAL = '\u22EF'; // unicode ellipsis -const BADGE_MAX_NOTIFICATION_COUNT = 9; +const BADGE_MAX_COUNT = 9; // Setup global hook for improved Sentry state snapshots during initialization const inTest = process.env.IN_TEST; @@ -950,6 +949,17 @@ export function setupController( controller.txController.initApprovals(); + /** + * Formats a count for display as a badge label. + * + * @param {number} count - The count to be formatted. + * @param {number} maxCount - The maximum count to display before using the '+' suffix. + * @returns {string} The formatted badge label. + */ + function getBadgeLabel(count, maxCount) { + return count > maxCount ? `${maxCount}+` : String(count); + } + /** * Updates the Web Extension's "badge" number, on the little fox in the toolbar. * The number reflects the current number of pending transactions or message signatures needing user approval. @@ -962,12 +972,9 @@ export function setupController( let badgeColor = BADGE_COLOR_APPROVAL; if (pendingApprovalCount) { - label = BADGE_LABEL_APPROVAL; + label = getBadgeLabel(pendingApprovalCount, BADGE_MAX_COUNT); } else if (unreadNotificationsCount > 0) { - label = - unreadNotificationsCount > BADGE_MAX_NOTIFICATION_COUNT - ? `${BADGE_MAX_NOTIFICATION_COUNT}+` - : String(unreadNotificationsCount); + label = getBadgeLabel(unreadNotificationsCount, BADGE_MAX_COUNT); badgeColor = BADGE_COLOR_NOTIFICATION; } diff --git a/app/scripts/constants/sentry-state.ts b/app/scripts/constants/sentry-state.ts new file mode 100644 index 000000000000..cacb8b34489e --- /dev/null +++ b/app/scripts/constants/sentry-state.ts @@ -0,0 +1,417 @@ +import { AllProperties } from '../../../shared/modules/object.utils'; + +export const MMI_SENTRY_BACKGROUND_STATE = { + MMIController: { + opts: true, + }, + CustodyController: { + store: true, + }, + MmiConfigurationController: { + store: true, + configurationClient: true, + }, +}; + +// This describes the subset of background controller state attached to errors +// sent to Sentry These properties have some potential to be useful for +// debugging, and they do not contain any identifiable information. +export const SENTRY_BACKGROUND_STATE = { + AccountsController: { + internalAccounts: { + accounts: false, + selectedAccount: false, + }, + }, + AccountTracker: { + accounts: false, + accountsByChainId: false, + currentBlockGasLimit: true, + currentBlockGasLimitByChainId: true, + }, + AddressBookController: { + addressBook: false, + }, + AlertController: { + alertEnabledness: true, + unconnectedAccountAlertShownOrigins: false, + web3ShimUsageOrigins: false, + }, + AnnouncementController: { + announcements: false, + }, + AuthenticationController: { + isSignedIn: false, + }, + NetworkOrderController: { + orderedNetworkList: [], + }, + AccountOrderController: { + pinnedAccountList: [], + hiddenAccountList: [], + }, + AppMetadataController: { + currentAppVersion: true, + currentMigrationVersion: true, + previousAppVersion: true, + previousMigrationVersion: true, + showTokenAutodetectModalOnUpgrade: false, + }, + ApprovalController: { + approvalFlows: false, + pendingApprovals: false, + pendingApprovalCount: false, + }, + AppStateController: { + browserEnvironment: true, + connectedStatusPopoverHasBeenShown: true, + currentPopupId: false, + onboardingDate: false, + currentExtensionPopupId: false, + defaultHomeActiveTabName: true, + fullScreenGasPollTokens: true, + hadAdvancedGasFeesSetPriorToMigration92_3: true, + nftsDetectionNoticeDismissed: true, + nftsDropdownState: true, + notificationGasPollTokens: true, + outdatedBrowserWarningLastShown: true, + popupGasPollTokens: true, + qrHardware: true, + recoveryPhraseReminderHasBeenShown: true, + recoveryPhraseReminderLastShown: true, + showBetaHeader: true, + showPermissionsTour: true, + showNetworkBanner: true, + showAccountBanner: true, + switchedNetworkDetails: false, + switchedNetworkNeverShowMessage: false, + showTestnetMessageInDropdown: true, + surveyLinkLastClickedOrClosed: true, + snapsInstallPrivacyWarningShown: true, + termsOfUseLastAgreed: true, + timeoutMinutes: true, + trezorModel: true, + usedNetworks: true, + }, + MultichainBalancesController: { + balances: false, + }, + BridgeController: { + bridgeState: { + bridgeFeatureFlags: { + extensionSupport: false, + }, + }, + }, + CronjobController: { + jobs: false, + }, + CurrencyController: { + currentCurrency: true, + currencyRates: true, + }, + DecryptMessageController: { + unapprovedDecryptMsgs: false, + unapprovedDecryptMsgCount: true, + }, + EncryptionPublicKeyController: { + unapprovedEncryptionPublicKeyMsgs: false, + unapprovedEncryptionPublicKeyMsgCount: true, + }, + EnsController: { + ensResolutionsByAddress: false, + ensEntries: false, + }, + GasFeeController: { + estimatedGasFeeTimeBounds: true, + gasEstimateType: true, + gasFeeEstimates: true, + gasFeeEstimatesByChainId: true, + nonRPCGasFeeApisDisabled: false, + }, + KeyringController: { + isUnlocked: true, + keyrings: false, + }, + LoggingController: { + logs: false, + }, + MetamaskNotificationsController: { + subscriptionAccountsSeen: false, + isMetamaskNotificationsFeatureSeen: false, + isMetamaskNotificationsEnabled: false, + isFeatureAnnouncementsEnabled: false, + metamaskNotificationsList: false, + metamaskNotificationsReadList: false, + isCheckingAccountsPresence: false, + isFetchingMetamaskNotifications: false, + isUpdatingMetamaskNotifications: false, + isUpdatingMetamaskNotificationsAccount: false, + }, + MetaMetricsController: { + eventsBeforeMetricsOptIn: false, + fragments: false, + metaMetricsId: true, + participateInMetaMetrics: true, + previousUserTraits: false, + segmentApiCalls: false, + traits: false, + dataCollectionForMarketing: false, + }, + NameController: { + names: false, + nameSources: false, + useExternalNameSources: false, + }, + NetworkController: { + networkConfigurations: false, + networksMetadata: true, + providerConfig: { + chainId: true, + id: true, + nickname: true, + rpcPrefs: false, + rpcUrl: false, + ticker: true, + type: true, + }, + selectedNetworkClientId: false, + }, + NftController: { + allNftContracts: false, + allNfts: false, + ignoredNfts: false, + }, + NotificationController: { + notifications: false, + }, + OnboardingController: { + completedOnboarding: true, + firstTimeFlowType: true, + onboardingTabs: false, + seedPhraseBackedUp: true, + }, + PPOMController: { + securityAlertsEnabled: false, + storageMetadata: [], + versionInfo: [], + }, + PermissionController: { + subjects: false, + }, + PermissionLogController: { + permissionActivityLog: false, + permissionHistory: false, + }, + PhishingController: {}, + PreferencesController: { + advancedGasFee: true, + currentLocale: true, + disabledRpcMethodPreferences: true, + dismissSeedBackUpReminder: true, + featureFlags: true, + forgottenPassword: true, + identities: false, + incomingTransactionsPreferences: true, + isIpfsGatewayEnabled: false, + ipfsGateway: false, + knownMethodData: false, + ledgerTransportType: true, + lostIdentities: false, + openSeaEnabled: true, + preferences: { + autoLockTimeLimit: true, + hideZeroBalanceTokens: true, + redesignedConfirmationsEnabled: true, + isRedesignedConfirmationsDeveloperEnabled: false, + showExtensionInFullSizeView: true, + showFiatInTestnets: true, + showTestNetworks: true, + smartTransactionsOptInStatus: true, + useNativeCurrencyAsPrimaryCurrency: true, + petnamesEnabled: true, + showTokenAutodetectModal: false, + }, + useExternalServices: false, + selectedAddress: false, + snapRegistryList: false, + theme: true, + signatureSecurityAlertResponses: false, + use4ByteResolution: true, + useAddressBarEnsResolution: true, + useBlockie: true, + useCurrencyRateCheck: true, + useMultiAccountBalanceChecker: true, + useNftDetection: true, + useNonceField: true, + usePhishDetect: true, + useTokenDetection: true, + useRequestQueue: true, + useTransactionSimulations: true, + enableMV3TimestampSave: true, + }, + PushPlatformNotificationsController: { + fcmToken: false, + }, + MultichainRatesController: { + fiatCurrency: true, + rates: true, + cryptocurrencies: true, + }, + QueuedRequestController: { + queuedRequestCount: true, + }, + SelectedNetworkController: { domains: false }, + SignatureController: { + unapprovedMsgCount: true, + unapprovedMsgs: false, + unapprovedPersonalMsgCount: true, + unapprovedPersonalMsgs: false, + unapprovedTypedMessages: false, + unapprovedTypedMessagesCount: true, + }, + SmartTransactionsController: { + smartTransactionsState: { + fees: { + approvalTxFees: true, + tradeTxFees: true, + }, + liveness: true, + smartTransactions: false, + userOptIn: true, + userOptInV2: true, + }, + }, + SnapController: { + unencryptedSnapStates: false, + snapStates: false, + snaps: false, + }, + SnapInterface: { + interfaces: false, + }, + SnapsRegistry: { + database: false, + lastUpdated: false, + databaseUnavailable: false, + }, + SubjectMetadataController: { + subjectMetadata: false, + }, + SwapsController: { + swapsState: { + approveTxId: false, + customApproveTxData: false, + customGasPrice: true, + customMaxFeePerGas: true, + customMaxGas: true, + customMaxPriorityFeePerGas: true, + errorKey: true, + fetchParams: true, + quotes: false, + quotesLastFetched: true, + quotesPollingLimitEnabled: true, + routeState: true, + saveFetchedQuotes: true, + selectedAggId: true, + swapsFeatureFlags: true, + swapsFeatureIsLive: true, + swapsQuotePrefetchingRefreshTime: true, + swapsQuoteRefreshTime: true, + swapsStxBatchStatusRefreshTime: true, + swapsStxStatusDeadline: true, + swapsStxGetTransactionsRefreshTime: true, + swapsStxMaxFeeMultiplier: true, + swapsUserFeeLevel: true, + tokens: false, + topAggId: false, + tradeTxId: false, + }, + }, + TokenDetectionController: { + [AllProperties]: false, + }, + TokenListController: { + preventPollingOnNetworkRestart: true, + tokenList: false, + tokensChainsCache: { + [AllProperties]: false, + }, + }, + TokenRatesController: { + marketData: false, + }, + TokensController: { + allDetectedTokens: { + [AllProperties]: false, + }, + allIgnoredTokens: { + [AllProperties]: false, + }, + allTokens: { + [AllProperties]: false, + }, + detectedTokens: false, + ignoredTokens: false, + tokens: false, + }, + TransactionController: { + transactions: false, + lastFetchedBlockNumbers: false, + methodData: false, + }, + TxController: { + transactions: false, + }, + UserOperationController: { + userOperations: false, + }, + UserStorageController: { + isProfileSyncingEnabled: true, + isProfileSyncingUpdateLoading: false, + }, + ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) + ...MMI_SENTRY_BACKGROUND_STATE, + ///: END:ONLY_INCLUDE_IF +}; + +const flattenedBackgroundStateMask = Object.values( + SENTRY_BACKGROUND_STATE, +).reduce((partialBackgroundState, controllerState: object) => { + return { + ...partialBackgroundState, + ...controllerState, + }; +}, {}); + +// This describes the subset of Redux state attached to errors sent to Sentry +// These properties have some potential to be useful for debugging, and they do +// not contain any identifiable information. +export const SENTRY_UI_STATE = { + gas: true, + history: true, + metamask: { + ...flattenedBackgroundStateMask, + // This property comes from the background but isn't in controller state + isInitialized: true, + // These properties are in the `metamask` slice but not in the background state + customNonceValue: true, + isAccountMenuOpen: true, + isNetworkMenuOpen: true, + nextNonce: true, + pendingTokens: false, + welcomeScreenSeen: true, + confirmationExchangeRates: true, + useSafeChainsListValidation: true, + bitcoinSupportEnabled: false, + ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps) + addSnapAccountEnabled: false, + snapsAddSnapAccountModalDismissed: false, + ///: END:ONLY_INCLUDE_IF + switchedNetworkDetails: false, + switchedNetworkNeverShowMessage: false, + newPrivacyPolicyToastClickedOrClosed: false, + newPrivacyPolicyToastShownDate: false, + }, + unconnectedAccount: true, +}; diff --git a/app/scripts/controllers/authentication/authentication-controller.test.ts b/app/scripts/controllers/authentication/authentication-controller.test.ts index e13b999de517..1cd01dfb74fc 100644 --- a/app/scripts/controllers/authentication/authentication-controller.test.ts +++ b/app/scripts/controllers/authentication/authentication-controller.test.ts @@ -1,6 +1,7 @@ import { ControllerMessenger } from '@metamask/base-controller'; import AuthenticationController, { AllowedActions, + AllowedEvents, AuthenticationControllerState, } from './authentication-controller'; import { @@ -26,7 +27,7 @@ describe('authentication/authentication-controller - constructor() tests', () => test('should initialize with default state', () => { const metametrics = createMockAuthMetaMetrics(); const controller = new AuthenticationController({ - messenger: createAuthenticationMessenger(), + messenger: createMockAuthenticationMessenger().messenger, metametrics, }); @@ -37,7 +38,7 @@ describe('authentication/authentication-controller - constructor() tests', () => test('should initialize with override state', () => { const metametrics = createMockAuthMetaMetrics(); const controller = new AuthenticationController({ - messenger: createAuthenticationMessenger(), + messenger: createMockAuthenticationMessenger().messenger, state: mockSignedInState(), metametrics, }); @@ -81,6 +82,20 @@ describe('authentication/authentication-controller - performSignIn() tests', () await testAndAssertFailingEndpoints('token'); }); + // When the wallet is locked, we are unable to call the snap + test('Should error when wallet is locked', async () => { + const { messenger, mockKeyringControllerGetState } = + createMockAuthenticationMessenger(); + const metametrics = createMockAuthMetaMetrics(); + + // Mock wallet is locked + mockKeyringControllerGetState.mockReturnValue({ isUnlocked: false }); + + const controller = new AuthenticationController({ messenger, metametrics }); + + await expect(controller.performSignIn()).rejects.toThrow(); + }); + async function testAndAssertFailingEndpoints( endpointFail: 'nonce' | 'login' | 'token', ) { @@ -173,6 +188,8 @@ describe('authentication/authentication-controller - getBearerToken() tests', () const metametrics = createMockAuthMetaMetrics(); const { messenger } = createMockAuthenticationMessenger(); mockAuthenticationFlowEndpoints(); + + // Invalid/old state const originalState = mockSignedInState(); if (originalState.sessionData) { originalState.sessionData.accessToken = 'ACCESS_TOKEN_1'; @@ -192,6 +209,36 @@ describe('authentication/authentication-controller - getBearerToken() tests', () expect(result).toBeDefined(); expect(result).toBe(MOCK_ACCESS_TOKEN); }); + + // If the state is invalid, we need to re-login. + // But as wallet is locked, we will not be able to call the snap + test('Should throw error if wallet is locked', async () => { + const metametrics = createMockAuthMetaMetrics(); + const { messenger, mockKeyringControllerGetState } = + createMockAuthenticationMessenger(); + mockAuthenticationFlowEndpoints(); + + // Invalid/old state + const originalState = mockSignedInState(); + if (originalState.sessionData) { + originalState.sessionData.accessToken = 'ACCESS_TOKEN_1'; + + const d = new Date(); + d.setMinutes(d.getMinutes() - 31); // expires at 30 mins + originalState.sessionData.expiresIn = d.toString(); + } + + // Mock wallet is locked + mockKeyringControllerGetState.mockReturnValue({ isUnlocked: false }); + + const controller = new AuthenticationController({ + messenger, + state: originalState, + metametrics, + }); + + await expect(controller.getBearerToken()).rejects.toThrow(); + }); }); describe('authentication/authentication-controller - getSessionProfile() tests', () => { @@ -226,6 +273,8 @@ describe('authentication/authentication-controller - getSessionProfile() tests', const metametrics = createMockAuthMetaMetrics(); const { messenger } = createMockAuthenticationMessenger(); mockAuthenticationFlowEndpoints(); + + // Invalid/old state const originalState = mockSignedInState(); if (originalState.sessionData) { originalState.sessionData.profile.identifierId = 'ID_1'; @@ -246,14 +295,47 @@ describe('authentication/authentication-controller - getSessionProfile() tests', expect(result.identifierId).toBe(MOCK_LOGIN_RESPONSE.profile.identifier_id); expect(result.profileId).toBe(MOCK_LOGIN_RESPONSE.profile.profile_id); }); + + // If the state is invalid, we need to re-login. + // But as wallet is locked, we will not be able to call the snap + test('Should throw error if wallet is locked', async () => { + const metametrics = createMockAuthMetaMetrics(); + const { messenger, mockKeyringControllerGetState } = + createMockAuthenticationMessenger(); + mockAuthenticationFlowEndpoints(); + + // Invalid/old state + const originalState = mockSignedInState(); + if (originalState.sessionData) { + originalState.sessionData.profile.identifierId = 'ID_1'; + + const d = new Date(); + d.setMinutes(d.getMinutes() - 31); // expires at 30 mins + originalState.sessionData.expiresIn = d.toString(); + } + + // Mock wallet is locked + mockKeyringControllerGetState.mockReturnValue({ isUnlocked: false }); + + const controller = new AuthenticationController({ + messenger, + state: originalState, + metametrics, + }); + + await expect(controller.getSessionProfile()).rejects.toThrow(); + }); }); function createAuthenticationMessenger() { - const messenger = new ControllerMessenger(); + const messenger = new ControllerMessenger(); return messenger.getRestricted({ name: 'AuthenticationController', - allowedActions: [`SnapController:handleRequest`], - allowedEvents: [], + allowedActions: [ + `SnapController:handleRequest`, + 'KeyringController:getState', + ], + allowedEvents: ['KeyringController:lock', 'KeyringController:unlock'], }); } @@ -265,6 +347,10 @@ function createMockAuthenticationMessenger() { .fn() .mockResolvedValue('MOCK_SIGNED_MESSAGE'); + const mockKeyringControllerGetState = jest + .fn() + .mockReturnValue({ isUnlocked: true }); + mockCall.mockImplementation((...args) => { const [actionType, params] = args; if (actionType === 'SnapController:handleRequest') { @@ -281,14 +367,19 @@ function createMockAuthenticationMessenger() { ); } - function exhaustedMessengerMocks(action: never) { - throw new Error(`MOCK_FAIL - unsupported messenger call: ${action}`); + if (actionType === 'KeyringController:getState') { + return mockKeyringControllerGetState(); } - return exhaustedMessengerMocks(actionType); + throw new Error(`MOCK_FAIL - unsupported messenger call: ${actionType}`); }); - return { messenger, mockSnapGetPublicKey, mockSnapSignMessage }; + return { + messenger, + mockSnapGetPublicKey, + mockSnapSignMessage, + mockKeyringControllerGetState, + }; } function mockAuthenticationFlowEndpoints(params?: { diff --git a/app/scripts/controllers/authentication/authentication-controller.ts b/app/scripts/controllers/authentication/authentication-controller.ts index 24da63bd58bf..4bcb1528c323 100644 --- a/app/scripts/controllers/authentication/authentication-controller.ts +++ b/app/scripts/controllers/authentication/authentication-controller.ts @@ -3,8 +3,12 @@ import { RestrictedControllerMessenger, StateMetadata, } from '@metamask/base-controller'; +import type { + KeyringControllerGetStateAction, + KeyringControllerLockEvent, + KeyringControllerUnlockEvent, +} from '@metamask/keyring-controller'; import { HandleSnapRequest } from '@metamask/snaps-controllers'; -import { UserStorageControllerDisableProfileSyncing } from '../user-storage/user-storage-controller'; import { createSnapPublicKeyRequest, createSnapSignMessageRequest, @@ -86,15 +90,19 @@ export type AuthenticationControllerIsSignedIn = ActionsObj['isSignedIn']; // Allowed Actions export type AllowedActions = | HandleSnapRequest - | UserStorageControllerDisableProfileSyncing; + | KeyringControllerGetStateAction; + +export type AllowedEvents = + | KeyringControllerLockEvent + | KeyringControllerUnlockEvent; // Messenger export type AuthenticationControllerMessenger = RestrictedControllerMessenger< typeof controllerName, Actions | AllowedActions, - never, + AllowedEvents, AllowedActions['type'], - never + AllowedEvents['type'] >; /** @@ -108,6 +116,25 @@ export default class AuthenticationController extends BaseController< > { #metametrics: MetaMetricsAuth; + #isUnlocked = false; + + #keyringController = { + setupLockedStateSubscriptions: () => { + const { isUnlocked } = this.messagingSystem.call( + 'KeyringController:getState', + ); + this.#isUnlocked = isUnlocked; + + this.messagingSystem.subscribe('KeyringController:unlock', () => { + this.#isUnlocked = true; + }); + + this.messagingSystem.subscribe('KeyringController:lock', () => { + this.#isUnlocked = false; + }); + }, + }; + constructor({ messenger, state, @@ -130,6 +157,7 @@ export default class AuthenticationController extends BaseController< this.#metametrics = metametrics; + this.#keyringController.setupLockedStateSubscriptions(); this.#registerMessageHandlers(); } @@ -271,8 +299,6 @@ export default class AuthenticationController extends BaseController< }; } catch (e) { console.error('Failed to authenticate', e); - // Disable Profile Syncing - this.messagingSystem.call('UserStorageController:disableProfileSyncing'); const errorMessage = e instanceof Error ? e.message : JSON.stringify(e ?? ''); throw new Error( @@ -299,28 +325,60 @@ export default class AuthenticationController extends BaseController< return THIRTY_MIN_MS > diffMs; } + #_snapPublicKeyCache: string | undefined; + /** * Returns the auth snap public key. * * @returns The snap public key. */ - #snapGetPublicKey(): Promise { - return this.messagingSystem.call( + async #snapGetPublicKey(): Promise { + if (this.#_snapPublicKeyCache) { + return this.#_snapPublicKeyCache; + } + + if (!this.#isUnlocked) { + throw new Error( + '#snapGetPublicKey - unable to call snap, wallet is locked', + ); + } + + const result = (await this.messagingSystem.call( 'SnapController:handleRequest', createSnapPublicKeyRequest(), - ) as Promise; + )) as string; + + this.#_snapPublicKeyCache = result; + + return result; } + #_snapSignMessageCache: Record<`metamask:${string}`, string> = {}; + /** * Signs a specific message using an underlying auth snap. * * @param message - A specific tagged message to sign. * @returns A Signature created by the snap. */ - #snapSignMessage(message: `metamask:${string}`): Promise { - return this.messagingSystem.call( + async #snapSignMessage(message: `metamask:${string}`): Promise { + if (this.#_snapSignMessageCache[message]) { + return this.#_snapSignMessageCache[message]; + } + + if (!this.#isUnlocked) { + throw new Error( + '#snapSignMessage - unable to call snap, wallet is locked', + ); + } + + const result = (await this.messagingSystem.call( 'SnapController:handleRequest', createSnapSignMessageRequest(message), - ) as Promise; + )) as string; + + this.#_snapSignMessageCache[message] = result; + + return result; } } diff --git a/app/scripts/controllers/metamask-notifications/metamask-notifications.test.ts b/app/scripts/controllers/metamask-notifications/metamask-notifications.test.ts index d93d48536c13..c96ccfe2e989 100644 --- a/app/scripts/controllers/metamask-notifications/metamask-notifications.test.ts +++ b/app/scripts/controllers/metamask-notifications/metamask-notifications.test.ts @@ -50,6 +50,10 @@ import * as OnChainNotifications from './services/onchain-notifications'; import { UserStorage } from './types/user-storage/user-storage'; import * as MetamaskNotificationsUtils from './utils/utils'; +// 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({ @@ -627,6 +631,7 @@ function mockNotificationMessenger() { name: 'MetamaskNotificationsController', allowedActions: [ 'KeyringController:getAccounts', + 'KeyringController:getState', 'AuthenticationController:getBearerToken', 'AuthenticationController:isSignedIn', 'PushPlatformNotificationsController:disablePushNotifications', @@ -639,6 +644,8 @@ function mockNotificationMessenger() { ], allowedEvents: [ 'KeyringController:stateChange', + 'KeyringController:lock', + 'KeyringController:unlock', 'PushPlatformNotificationsController:onNewNotifications', ], }); @@ -735,6 +742,10 @@ function mockNotificationMessenger() { return mockPerformSetStorage(params[0], params[1]); } + if (actionType === 'KeyringController:getState') { + return { isUnlocked: true } as MockVar; + } + function exhaustedMessengerMocks(action: never) { return new Error(`MOCK_FAIL - unsupported messenger call: ${action}`); } diff --git a/app/scripts/controllers/metamask-notifications/metamask-notifications.ts b/app/scripts/controllers/metamask-notifications/metamask-notifications.ts index b9ad4572742b..5e63669f6cd5 100644 --- a/app/scripts/controllers/metamask-notifications/metamask-notifications.ts +++ b/app/scripts/controllers/metamask-notifications/metamask-notifications.ts @@ -2,7 +2,6 @@ import { BaseController, RestrictedControllerMessenger, ControllerGetStateAction, - ControllerStateChangeEvent, StateMetadata, } from '@metamask/base-controller'; import log from 'loglevel'; @@ -10,6 +9,9 @@ import { toChecksumHexAddress } from '@metamask/controller-utils'; import { KeyringControllerGetAccountsAction, KeyringControllerStateChangeEvent, + KeyringControllerGetStateAction, + KeyringControllerLockEvent, + KeyringControllerUnlockEvent, } from '@metamask/keyring-controller'; import { AuthenticationControllerGetBearerToken, @@ -196,6 +198,7 @@ export type Actions = export type AllowedActions = // Keyring Controller Requests | KeyringControllerGetAccountsAction + | KeyringControllerGetStateAction // Auth Controller Requests | AuthenticationControllerGetBearerToken | AuthenticationControllerIsSignedIn @@ -209,26 +212,25 @@ export type AllowedActions = | PushPlatformNotificationsControllerDisablePushNotifications | PushPlatformNotificationsControllerUpdateTriggerPushNotifications; -// Events -export type MetamaskNotificationsControllerMessengerEvents = - ControllerStateChangeEvent< - typeof controllerName, - MetamaskNotificationsControllerState - >; +export type Events = + | MetamaskNotificationsControllerNotificationsListUpdatedEvent + | MetamaskNotificationsControllerMarkNotificationsAsRead; // Allowed Events export type AllowedEvents = + // Keyring Events | KeyringControllerStateChangeEvent - | PushPlatformNotificationsControllerOnNewNotificationEvent - | MetamaskNotificationsControllerNotificationsListUpdatedEvent - | MetamaskNotificationsControllerMarkNotificationsAsRead; + | KeyringControllerLockEvent + | KeyringControllerUnlockEvent + // Push Notification Events + | PushPlatformNotificationsControllerOnNewNotificationEvent; // Type for the messenger of MetamaskNotificationsController export type MetamaskNotificationsControllerMessenger = RestrictedControllerMessenger< typeof controllerName, Actions | AllowedActions, - AllowedEvents, + Events | AllowedEvents, AllowedActions['type'], AllowedEvents['type'] >; @@ -241,6 +243,34 @@ export class MetamaskNotificationsController extends BaseController< 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( @@ -278,22 +308,34 @@ export class MetamaskNotificationsController extends BaseController< #pushNotifications = { enablePushNotifications: async (UUIDs: string[]) => { - return await this.messagingSystem.call( - 'PushPlatformNotificationsController:enablePushNotifications', - UUIDs, - ); + try { + await this.messagingSystem.call( + 'PushPlatformNotificationsController:enablePushNotifications', + UUIDs, + ); + } catch (e) { + log.error('Silently failed to enable push notifications', e); + } }, disablePushNotifications: async (UUIDs: string[]) => { - return await this.messagingSystem.call( - 'PushPlatformNotificationsController:disablePushNotifications', - UUIDs, - ); + try { + await this.messagingSystem.call( + 'PushPlatformNotificationsController:disablePushNotifications', + UUIDs, + ); + } catch (e) { + log.error('Silently failed to disable push notifications', e); + } }, updatePushNotifications: async (UUIDs: string[]) => { - return await this.messagingSystem.call( - 'PushPlatformNotificationsController:updateTriggerPushNotifications', - UUIDs, - ); + try { + await this.messagingSystem.call( + 'PushPlatformNotificationsController:updateTriggerPushNotifications', + UUIDs, + ); + } catch (e) { + log.error('Silently failed to update push notifications', e); + } }, subscribe: () => { this.messagingSystem.subscribe( @@ -307,6 +349,12 @@ export class MetamaskNotificationsController extends BaseController< if (!this.state.isMetamaskNotificationsEnabled) { return; } + if (this.#isPushNotificationsSetup) { + return; + } + if (!this.#isUnlocked) { + return; + } const storage = await this.#getUserStorage(); if (!storage) { @@ -315,6 +363,7 @@ export class MetamaskNotificationsController extends BaseController< const uuids = MetamaskNotificationsUtils.getAllUUIDs(storage); await this.#pushNotifications.enablePushNotifications(uuids); + this.#isPushNotificationsSetup = true; }, }; @@ -422,6 +471,9 @@ export class MetamaskNotificationsController extends BaseController< this.#registerMessageHandlers(); this.#clearLoadingStates(); + this.#keyringController.setupLockedStateSubscriptions( + this.#pushNotifications.initializePushNotifications, + ); this.#accounts.initialize(); this.#pushNotifications.initializePushNotifications(); this.#accounts.subscribe(); diff --git a/app/scripts/controllers/metamask-notifications/services/feature-announcements.ts b/app/scripts/controllers/metamask-notifications/services/feature-announcements.ts index 0fb911497cd7..8b3c3870627e 100644 --- a/app/scripts/controllers/metamask-notifications/services/feature-announcements.ts +++ b/app/scripts/controllers/metamask-notifications/services/feature-announcements.ts @@ -26,7 +26,7 @@ export type ContentfulResult = { async function fetchFromContentful( url: string, - retries = 3, + retries = 1, retryDelay = 1000, ): Promise { let lastError: Error | null = null; @@ -113,10 +113,17 @@ async function fetchFeatureAnnouncementNotifications(): Promise< export async function getFeatureAnnouncementNotifications(): Promise< Notification[] > { - const rawNotifications = await fetchFeatureAnnouncementNotifications(); - const notifications = rawNotifications.map((notification) => - processFeatureAnnouncement(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 notifications; + return []; } diff --git a/app/scripts/controllers/metamask-notifications/utils/utils.ts b/app/scripts/controllers/metamask-notifications/utils/utils.ts index f71f9e568dcd..547a38b217d5 100644 --- a/app/scripts/controllers/metamask-notifications/utils/utils.ts +++ b/app/scripts/controllers/metamask-notifications/utils/utils.ts @@ -603,7 +603,7 @@ export async function makeApiCall( endpoint: string, method: 'POST' | 'DELETE', body: T, - retries = 3, + retries = 1, retryDelay = 1000, ): Promise { const options: RequestInit = { diff --git a/app/scripts/controllers/metametrics.js b/app/scripts/controllers/metametrics.js index c7bd148e62d3..8aafa0893d53 100644 --- a/app/scripts/controllers/metametrics.js +++ b/app/scripts/controllers/metametrics.js @@ -454,16 +454,15 @@ export default class MetaMetricsController { * if not set */ async setParticipateInMetaMetrics(participateInMetaMetrics) { - let { metaMetricsId } = this.state; - if (participateInMetaMetrics && !metaMetricsId) { - // We also need to start sentry automatic session tracking at this point - await globalThis.sentry?.startSession(); - metaMetricsId = this.generateMetaMetricsId(); - } else if (participateInMetaMetrics === false) { - // We also need to stop sentry automatic session tracking at this point - await globalThis.sentry?.endSession(); - } + const { metaMetricsId: existingMetaMetricsId } = this.state; + + const metaMetricsId = + participateInMetaMetrics && !existingMetaMetricsId + ? this.generateMetaMetricsId() + : existingMetaMetricsId; + this.store.updateState({ participateInMetaMetrics, metaMetricsId }); + if (participateInMetaMetrics) { this.trackEventsAfterMetricsOptIn(); this.clearEventsAfterMetricsOptIn(); @@ -1017,7 +1016,8 @@ export default class MetaMetricsController { // to be updated to work with the new tracking plan. I think we should use // a config setting for this instead of trying to match the event name const isSendFlow = Boolean(payload.event.match(/^send|^confirm/iu)); - if (isSendFlow) { + // do not filter if excludeMetaMetricsId is explicitly set to false + if (options?.excludeMetaMetricsId !== false && isSendFlow) { excludeMetaMetricsId = true; } // If we are tracking sensitive data we will always use the anonymousId diff --git a/app/scripts/controllers/metametrics.test.js b/app/scripts/controllers/metametrics.test.js index 43834f01a4b9..4206de67a7fd 100644 --- a/app/scripts/controllers/metametrics.test.js +++ b/app/scripts/controllers/metametrics.test.js @@ -143,10 +143,7 @@ function getMetaMetricsController({ describe('MetaMetricsController', function () { const now = new Date(); beforeEach(function () { - globalThis.sentry = { - startSession: jest.fn(), - endSession: jest.fn(), - }; + globalThis.sentry = {}; jest.useFakeTimers().setSystemTime(now.getTime()); jest.spyOn(Utils, 'generateRandomId').mockReturnValue('DUMMY_RANDOM_ID'); }); @@ -316,7 +313,6 @@ describe('MetaMetricsController', function () { metaMetricsController.state.participateInMetaMetrics, ).toStrictEqual(null); await metaMetricsController.setParticipateInMetaMetrics(true); - expect(globalThis.sentry.startSession).toHaveBeenCalledTimes(1); expect( metaMetricsController.state.participateInMetaMetrics, ).toStrictEqual(true); @@ -339,7 +335,6 @@ describe('MetaMetricsController', function () { it('should not nullify the metaMetricsId when set to false', async function () { const metaMetricsController = getMetaMetricsController(); await metaMetricsController.setParticipateInMetaMetrics(false); - expect(globalThis.sentry.endSession).toHaveBeenCalledTimes(1); expect(metaMetricsController.state.metaMetricsId).toStrictEqual( TEST_META_METRICS_ID, ); diff --git a/app/scripts/controllers/swaps.constants.ts b/app/scripts/controllers/swaps.constants.ts index d959c02c439e..6228dd2bcb66 100644 --- a/app/scripts/controllers/swaps.constants.ts +++ b/app/scripts/controllers/swaps.constants.ts @@ -1,4 +1,5 @@ import { + FALLBACK_SMART_TRANSACTIONS_DEADLINE, FALLBACK_SMART_TRANSACTIONS_MAX_FEE_MULTIPLIER, FALLBACK_SMART_TRANSACTIONS_REFRESH_TIME, } from '../../../shared/constants/smartTransactions'; @@ -42,6 +43,7 @@ export const swapsControllerInitialState: { swapsState: SwapsControllerState } = swapsQuoteRefreshTime: FALLBACK_QUOTE_REFRESH_TIME, swapsQuotePrefetchingRefreshTime: FALLBACK_QUOTE_REFRESH_TIME, swapsStxBatchStatusRefreshTime: FALLBACK_SMART_TRANSACTIONS_REFRESH_TIME, + swapsStxStatusDeadline: FALLBACK_SMART_TRANSACTIONS_DEADLINE, swapsStxGetTransactionsRefreshTime: FALLBACK_SMART_TRANSACTIONS_REFRESH_TIME, swapsStxMaxFeeMultiplier: FALLBACK_SMART_TRANSACTIONS_MAX_FEE_MULTIPLIER, diff --git a/app/scripts/controllers/swaps.test.js b/app/scripts/controllers/swaps.test.js index e5059467532d..63b910f35b0e 100644 --- a/app/scripts/controllers/swaps.test.js +++ b/app/scripts/controllers/swaps.test.js @@ -120,6 +120,7 @@ const EMPTY_INIT_STATE = { swapsStxGetTransactionsRefreshTime: FALLBACK_SMART_TRANSACTIONS_REFRESH_TIME, swapsStxMaxFeeMultiplier: FALLBACK_SMART_TRANSACTIONS_MAX_FEE_MULTIPLIER, + swapsStxStatusDeadline: 180, swapsUserFeeLevel: '', saveFetchedQuotes: false, }, @@ -1002,6 +1003,7 @@ describe('SwapsController', function () { const swapsQuotePrefetchingRefreshTime = 0; const swapsStxBatchStatusRefreshTime = 0; const swapsStxGetTransactionsRefreshTime = 0; + const swapsStxStatusDeadline = 0; swapsController.store.updateState({ swapsState: { tokens, @@ -1012,6 +1014,7 @@ describe('SwapsController', function () { swapsQuotePrefetchingRefreshTime, swapsStxBatchStatusRefreshTime, swapsStxGetTransactionsRefreshTime, + swapsStxStatusDeadline, }, }); diff --git a/app/scripts/controllers/swaps.ts b/app/scripts/controllers/swaps.ts index b0915b2ed21f..daa6c6954ff1 100644 --- a/app/scripts/controllers/swaps.ts +++ b/app/scripts/controllers/swaps.ts @@ -24,6 +24,7 @@ import { CHAIN_IDS } from '../../../shared/constants/network'; import { FALLBACK_SMART_TRANSACTIONS_MAX_FEE_MULTIPLIER, FALLBACK_SMART_TRANSACTIONS_REFRESH_TIME, + FALLBACK_SMART_TRANSACTIONS_DEADLINE, } from '../../../shared/constants/smartTransactions'; import { DEFAULT_ERC20_APPROVE_GAS, @@ -593,7 +594,8 @@ export default class SwapsController { ): Promise<[string | null, Record] | Record> { const { marketData } = this.getTokenRatesState(); const chainId = this._getCurrentChainId(); - const tokenConversionRates = marketData[chainId]; + + const tokenConversionRates = marketData?.[chainId] ?? {}; const { swapsState: { customGasPrice, customMaxPriorityFeePerGas }, @@ -944,6 +946,9 @@ export default class SwapsController { swapsStxMaxFeeMultiplier: swapsNetworkConfig?.stxMaxFeeMultiplier || FALLBACK_SMART_TRANSACTIONS_MAX_FEE_MULTIPLIER, + swapsStxStatusDeadline: + swapsNetworkConfig?.stxStatusDeadline || + FALLBACK_SMART_TRANSACTIONS_DEADLINE, }, }); } diff --git a/app/scripts/controllers/user-storage/user-storage-controller.test.ts b/app/scripts/controllers/user-storage/user-storage-controller.test.ts index b70141f6eaab..8d3f61d6ab21 100644 --- a/app/scripts/controllers/user-storage/user-storage-controller.test.ts +++ b/app/scripts/controllers/user-storage/user-storage-controller.test.ts @@ -17,6 +17,7 @@ import { } from './mocks/mockStorage'; import UserStorageController, { AllowedActions, + AllowedEvents, } from './user-storage-controller'; import { mockEndpointGetUserStorage, @@ -73,6 +74,24 @@ describe('user-storage/user-storage-controller - performGetStorage() tests', () ).rejects.toThrow(); }); + test('rejects if wallet is locked', async () => { + const { messengerMocks } = arrangeMocks(); + + // Mock wallet is locked + messengerMocks.mockKeyringControllerGetState.mockReturnValue({ + isUnlocked: false, + }); + + const controller = new UserStorageController({ + messenger: messengerMocks.messenger, + getMetaMetricsState: () => true, + }); + + await expect( + controller.performGetStorage('notification_settings'), + ).rejects.toThrow(); + }); + // @ts-expect-error This is missing from the Mocha type definitions test.each([ [ @@ -146,6 +165,24 @@ describe('user-storage/user-storage-controller - performSetStorage() tests', () ).rejects.toThrow(); }); + test('rejects if wallet is locked', async () => { + const { messengerMocks } = arrangeMocks(); + + // Mock wallet is locked + messengerMocks.mockKeyringControllerGetState.mockReturnValue({ + isUnlocked: false, + }); + + const controller = new UserStorageController({ + messenger: messengerMocks.messenger, + getMetaMetricsState: () => true, + }); + + await expect( + controller.performSetStorage('notification_settings', 'new data'), + ).rejects.toThrow(); + }); + // @ts-expect-error This is missing from the Mocha type definitions test.each([ [ @@ -288,10 +325,11 @@ describe('user-storage/user-storage-controller - enableProfileSyncing() tests', function mockUserStorageMessenger() { const messenger = new ControllerMessenger< AllowedActions, - never + AllowedEvents >().getRestricted({ name: 'UserStorageController', allowedActions: [ + 'KeyringController:getState', 'SnapController:handleRequest', 'AuthenticationController:getBearerToken', 'AuthenticationController:getSessionProfile', @@ -301,7 +339,7 @@ function mockUserStorageMessenger() { 'MetamaskNotificationsController:disableMetamaskNotifications', 'MetamaskNotificationsController:selectIsMetamaskNotificationsEnabled', ], - allowedEvents: [], + allowedEvents: ['KeyringController:lock', 'KeyringController:unlock'], }); const mockSnapGetPublicKey = jest.fn().mockResolvedValue('MOCK_PUBLIC_KEY'); @@ -346,6 +384,10 @@ function mockUserStorageMessenger() { MetamaskNotificationsControllerDisableMetamaskNotifications['handler'] >().mockResolvedValue(); + const mockKeyringControllerGetState = typedMockFn< + () => { isUnlocked: boolean } + >().mockReturnValue({ isUnlocked: true }); + jest.spyOn(messenger, 'call').mockImplementation((...args) => { const [actionType, params] = args; if (actionType === 'SnapController:handleRequest') { @@ -396,6 +438,10 @@ function mockUserStorageMessenger() { return mockAuthPerformSignOut(); } + if (actionType === 'KeyringController:getState') { + return mockKeyringControllerGetState(); + } + function exhaustedMessengerMocks(action: never) { throw new Error(`MOCK_FAIL - unsupported messenger call: ${action}`); } @@ -414,5 +460,6 @@ function mockUserStorageMessenger() { mockMetamaskNotificationsIsMetamaskNotificationsEnabled, mockMetamaskNotificationsDisableNotifications, mockAuthPerformSignOut, + mockKeyringControllerGetState, }; } diff --git a/app/scripts/controllers/user-storage/user-storage-controller.ts b/app/scripts/controllers/user-storage/user-storage-controller.ts index bcbc81618da8..9a286f801330 100644 --- a/app/scripts/controllers/user-storage/user-storage-controller.ts +++ b/app/scripts/controllers/user-storage/user-storage-controller.ts @@ -3,6 +3,11 @@ import { RestrictedControllerMessenger, StateMetadata, } from '@metamask/base-controller'; +import type { + KeyringControllerGetStateAction, + KeyringControllerLockEvent, + KeyringControllerUnlockEvent, +} from '@metamask/keyring-controller'; import { HandleSnapRequest } from '@metamask/snaps-controllers'; import { AuthenticationControllerGetBearerToken, @@ -27,7 +32,7 @@ export type UserStorageControllerState = { /** * Condition used by UI and to determine if we can use some of the User Storage methods. */ - isProfileSyncingEnabled: boolean; + isProfileSyncingEnabled: boolean | null; /** * Loading state for the profile syncing update */ @@ -75,8 +80,9 @@ export type UserStorageControllerEnableProfileSyncing = export type UserStorageControllerDisableProfileSyncing = ActionsObj['disableProfileSyncing']; -// Allowed Actions export type AllowedActions = + // Keyring Requests + | KeyringControllerGetStateAction // Snap Requests | HandleSnapRequest // Auth Requests @@ -89,13 +95,17 @@ export type AllowedActions = | MetamaskNotificationsControllerDisableMetamaskNotifications | MetamaskNotificationsControllerSelectIsMetamaskNotificationsEnabled; +export type AllowedEvents = + | KeyringControllerLockEvent + | KeyringControllerUnlockEvent; + // Messenger export type UserStorageControllerMessenger = RestrictedControllerMessenger< typeof controllerName, Actions | AllowedActions, - never, + AllowedEvents, AllowedActions['type'], - never + AllowedEvents['type'] >; /** @@ -151,6 +161,25 @@ export default class UserStorageController extends BaseController< }, }; + #isUnlocked = false; + + #keyringController = { + setupLockedStateSubscriptions: () => { + const { isUnlocked } = this.messagingSystem.call( + 'KeyringController:getState', + ); + this.#isUnlocked = isUnlocked; + + this.messagingSystem.subscribe('KeyringController:unlock', () => { + this.#isUnlocked = true; + }); + + this.messagingSystem.subscribe('KeyringController:lock', () => { + this.#isUnlocked = false; + }); + }, + }; + getMetaMetricsState: () => boolean; constructor(params: { @@ -166,6 +195,7 @@ export default class UserStorageController extends BaseController< }); this.getMetaMetricsState = params.getMetaMetricsState; + this.#keyringController.setupLockedStateSubscriptions(); this.#registerMessageHandlers(); } @@ -365,17 +395,33 @@ export default class UserStorageController extends BaseController< return storageKey; } + #_snapSignMessageCache: Record<`metamask:${string}`, string> = {}; + /** * Signs a specific message using an underlying auth snap. * * @param message - A specific tagged message to sign. * @returns A Signature created by the snap. */ - #snapSignMessage(message: `metamask:${string}`): Promise { - return this.messagingSystem.call( + async #snapSignMessage(message: `metamask:${string}`): Promise { + if (this.#_snapSignMessageCache[message]) { + return this.#_snapSignMessageCache[message]; + } + + if (!this.#isUnlocked) { + throw new Error( + '#snapSignMessage - unable to call snap, wallet is locked', + ); + } + + const result = (await this.messagingSystem.call( 'SnapController:handleRequest', createSnapSignMessageRequest(message), - ) as Promise; + )) as string; + + this.#_snapSignMessageCache[message] = result; + + return result; } async #setIsProfileSyncingUpdateLoading( diff --git a/app/scripts/lib/ComposableObservableStore.js b/app/scripts/lib/ComposableObservableStore.js index 80d9c483d77a..def67b51240f 100644 --- a/app/scripts/lib/ComposableObservableStore.js +++ b/app/scripts/lib/ComposableObservableStore.js @@ -74,7 +74,12 @@ export default class ComposableObservableStore extends ObservableStore { ); } - initialState[key] = store.state ?? store.getState?.(); + const initialStoreState = store.state ?? store.getState?.(); + + initialState[key] = + this.persist && config[key].metadata + ? getPersistentState(initialStoreState, config[key].metadata) + : initialStoreState; } this.updateState(initialState); } diff --git a/app/scripts/lib/ComposableObservableStore.test.js b/app/scripts/lib/ComposableObservableStore.test.js index f00120ac27bd..7abc87cb52b6 100644 --- a/app/scripts/lib/ComposableObservableStore.test.js +++ b/app/scripts/lib/ComposableObservableStore.test.js @@ -25,10 +25,12 @@ class OldExampleController extends BaseControllerV1 { class ExampleController extends BaseController { static defaultState = { bar: 'bar', + baz: 'baz', }; static metadata = { bar: { persist: true, anonymous: true }, + baz: { persist: false, anonymous: true }, }; constructor({ messenger }) { @@ -41,8 +43,8 @@ class ExampleController extends BaseController { } updateBar(contents) { - this.update(() => { - return { bar: contents }; + this.update((state) => { + state.bar = contents; }); } } @@ -94,7 +96,9 @@ describe('ComposableObservableStore', () => { const store = new ComposableObservableStore({ controllerMessenger }); store.updateStructure({ Example: exampleController }); exampleController.updateBar('state'); - expect(store.getState()).toStrictEqual({ Example: { bar: 'state' } }); + expect(store.getState()).toStrictEqual({ + Example: { bar: 'state', baz: 'baz' }, + }); }); it('should update structure with all three types of stores', () => { @@ -114,7 +118,7 @@ describe('ComposableObservableStore', () => { exampleController.updateBar('state'); oldExampleController.updateBaz('state'); expect(store.getState()).toStrictEqual({ - Example: { bar: 'state' }, + Example: { bar: 'state', baz: 'baz' }, OldExample: { baz: 'state' }, Store: 'state', }); @@ -139,7 +143,7 @@ describe('ComposableObservableStore', () => { }); expect(store.getState()).toStrictEqual({ - Example: { bar: 'state' }, + Example: { bar: 'state', baz: 'baz' }, OldExample: { baz: 'state' }, Store: 'state', }); @@ -160,6 +164,34 @@ describe('ComposableObservableStore', () => { }); }); + it('should strip non-persisted state from initial state with all three types of stores', () => { + const controllerMessenger = new ControllerMessenger(); + const exampleStore = new ObservableStore(); + const exampleController = new ExampleController({ + messenger: controllerMessenger, + }); + const oldExampleController = new OldExampleController(); + exampleStore.putState('state'); + exampleController.updateBar('state'); + oldExampleController.updateBaz('state'); + const store = new ComposableObservableStore({ + controllerMessenger, + persist: true, + }); + + store.updateStructure({ + Example: exampleController, + OldExample: oldExampleController, + Store: exampleStore, + }); + + expect(store.getState()).toStrictEqual({ + Example: { bar: 'state' }, + OldExample: { baz: 'state' }, + Store: 'state', + }); + }); + it('should return flattened state', () => { const controllerMessenger = new ControllerMessenger(); const fooStore = new ObservableStore({ foo: 'foo' }); diff --git a/app/scripts/lib/sentry-filter-events.ts b/app/scripts/lib/sentry-filter-events.ts index 50d2f4c77245..9c802d872c15 100644 --- a/app/scripts/lib/sentry-filter-events.ts +++ b/app/scripts/lib/sentry-filter-events.ts @@ -1,73 +1,37 @@ -import { - Event as SentryEvent, - EventProcessor, - Hub, - Integration, -} from '@sentry/types'; -import { logger } from '@sentry/utils'; +import { Event as SentryEvent, Integration } from '@sentry/types'; + +const NAME = 'FilterEvents'; /** * Filter events when MetaMetrics is disabled. + * + * @param options - Options bag. + * @param options.getMetaMetricsEnabled - Function that returns whether MetaMetrics is enabled. + * @param options.log - Function to log messages. */ -export class FilterEvents implements Integration { - /** - * Property that holds the integration name. - */ - public static id = 'FilterEvents'; - - /** - * Another property that holds the integration name. - * - * I don't know why this exists, but the other Sentry integrations have it. - */ - public name: string = FilterEvents.id; - - /** - * A function that returns whether MetaMetrics is enabled. This should also - * return `false` if state has not yet been initialzed. - * - * @returns `true` if MetaMask's state has been initialized, and MetaMetrics - * is enabled, `false` otherwise. - */ - private getMetaMetricsEnabled: () => Promise; +export function filterEvents({ + getMetaMetricsEnabled, + log, +}: { + getMetaMetricsEnabled: () => Promise; + log: (message: string) => void; +}): Integration { + return { + name: NAME, + processEvent: async (event: SentryEvent) => { + // This integration is required in addition to the custom transport as it provides an + // asynchronous context which we may need in order to read the persisted state from the + // store, so it can later be added to the event via the `beforeSend` overload. + // It also provides a more native solution for discarding events, but any + // session requests will always be handled by the custom transport. + const metricsEnabled = await getMetaMetricsEnabled(); - /** - * @param options - Constructor options. - * @param options.getMetaMetricsEnabled - A function that returns whether - * MetaMetrics is enabled. This should also return `false` if state has not - * yet been initialzed. - */ - constructor({ - getMetaMetricsEnabled, - }: { - getMetaMetricsEnabled: () => Promise; - }) { - this.getMetaMetricsEnabled = getMetaMetricsEnabled; - } - - /** - * Setup the integration. - * - * @param addGlobalEventProcessor - A function that allows adding a global - * event processor. - * @param getCurrentHub - A function that returns the current Sentry hub. - */ - public setupOnce( - addGlobalEventProcessor: (callback: EventProcessor) => void, - getCurrentHub: () => Hub, - ): void { - addGlobalEventProcessor(async (currentEvent: SentryEvent) => { - // Sentry integrations use the Sentry hub to get "this" references, for - // reasons I don't fully understand. - // eslint-disable-next-line consistent-this - const self = getCurrentHub().getIntegration(FilterEvents); - if (self) { - if (!(await self.getMetaMetricsEnabled())) { - logger.warn(`Event dropped due to MetaMetrics setting.`); - return null; - } + if (!metricsEnabled) { + log('Event dropped as metrics disabled'); + return null; } - return currentEvent; - }); - } + + return event; + }, + }; } diff --git a/app/scripts/lib/setup-initial-state-hooks.js b/app/scripts/lib/setup-initial-state-hooks.js index 9b3f18e09b3e..3cd13a77e29e 100644 --- a/app/scripts/lib/setup-initial-state-hooks.js +++ b/app/scripts/lib/setup-initial-state-hooks.js @@ -1,8 +1,8 @@ import { maskObject } from '../../../shared/modules/object.utils'; import ExtensionPlatform from '../platforms/extension'; +import { SENTRY_BACKGROUND_STATE } from '../constants/sentry-state'; import LocalStore from './local-store'; import ReadOnlyNetworkStore from './network-store'; -import { SENTRY_BACKGROUND_STATE } from './setupSentry'; const platform = new ExtensionPlatform(); diff --git a/app/scripts/lib/setupSentry.js b/app/scripts/lib/setupSentry.js index e85be05ba2c3..47d96c259727 100644 --- a/app/scripts/lib/setupSentry.js +++ b/app/scripts/lib/setupSentry.js @@ -1,19 +1,31 @@ import * as Sentry from '@sentry/browser'; -import { Dedupe, ExtraErrorData } from '@sentry/integrations'; - -import { AllProperties } from '../../../shared/modules/object.utils'; -import { FilterEvents } from './sentry-filter-events'; +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'; + +const projectLogger = createProjectLogger('sentry'); + +export const log = createModuleLogger( + projectLogger, + globalThis.document ? 'ui' : 'background', +); + +const internalLog = createModuleLogger(log, 'internal'); + /* eslint-disable prefer-destructuring */ // Destructuring breaks the inlining of the environment variables +const METAMASK_BUILD_TYPE = process.env.METAMASK_BUILD_TYPE; const METAMASK_DEBUG = process.env.METAMASK_DEBUG; const METAMASK_ENVIRONMENT = process.env.METAMASK_ENVIRONMENT; -const SENTRY_DSN_DEV = - process.env.SENTRY_DSN_DEV || - 'https://f59f3dd640d2429d9d0e2445a87ea8e1@sentry.io/273496'; -const METAMASK_BUILD_TYPE = process.env.METAMASK_BUILD_TYPE; -const IN_TEST = process.env.IN_TEST; +const RELEASE = process.env.METAMASK_VERSION; +const SENTRY_DSN = process.env.SENTRY_DSN; +const SENTRY_DSN_DEV = process.env.SENTRY_DSN_DEV; +const SENTRY_DSN_MMI = process.env.SENTRY_MMI_DSN; /* eslint-enable prefer-destructuring */ export const ERROR_URL_ALLOWLIST = { @@ -24,420 +36,56 @@ export const ERROR_URL_ALLOWLIST = { SEGMENT: 'segment.io', }; -export const MMI_SENTRY_BACKGROUND_STATE = { - MMIController: { - opts: true, - }, - CustodyController: { - store: true, - }, - MmiConfigurationController: { - store: true, - configurationClient: true, - }, -}; +export default function setupSentry() { + if (!RELEASE) { + throw new Error('Missing release'); + } -// This describes the subset of background controller state attached to errors -// sent to Sentry These properties have some potential to be useful for -// debugging, and they do not contain any identifiable information. -export const SENTRY_BACKGROUND_STATE = { - AccountsController: { - internalAccounts: { - accounts: false, - selectedAccount: false, - }, - }, - AccountTracker: { - accounts: false, - accountsByChainId: false, - currentBlockGasLimit: true, - currentBlockGasLimitByChainId: true, - }, - AddressBookController: { - addressBook: false, - }, - AlertController: { - alertEnabledness: true, - unconnectedAccountAlertShownOrigins: false, - web3ShimUsageOrigins: false, - }, - AnnouncementController: { - announcements: false, - }, - AuthenticationController: { - isSignedIn: false, - }, - NetworkOrderController: { - orderedNetworkList: [], - }, - AccountOrderController: { - pinnedAccountList: [], - hiddenAccountList: [], - }, - AppMetadataController: { - currentAppVersion: true, - currentMigrationVersion: true, - previousAppVersion: true, - previousMigrationVersion: true, - showTokenAutodetectModalOnUpgrade: false, - }, - ApprovalController: { - approvalFlows: false, - pendingApprovals: false, - pendingApprovalCount: false, - }, - AppStateController: { - browserEnvironment: true, - connectedStatusPopoverHasBeenShown: true, - currentPopupId: false, - onboardingDate: false, - currentExtensionPopupId: false, - defaultHomeActiveTabName: true, - fullScreenGasPollTokens: true, - hadAdvancedGasFeesSetPriorToMigration92_3: true, - nftsDetectionNoticeDismissed: true, - nftsDropdownState: true, - notificationGasPollTokens: true, - outdatedBrowserWarningLastShown: true, - popupGasPollTokens: true, - qrHardware: true, - recoveryPhraseReminderHasBeenShown: true, - recoveryPhraseReminderLastShown: true, - showBetaHeader: true, - showPermissionsTour: true, - showNetworkBanner: true, - showAccountBanner: true, - switchedNetworkDetails: false, - switchedNetworkNeverShowMessage: false, - showTestnetMessageInDropdown: true, - surveyLinkLastClickedOrClosed: true, - snapsInstallPrivacyWarningShown: true, - termsOfUseLastAgreed: true, - timeoutMinutes: true, - trezorModel: true, - usedNetworks: true, - }, - MultichainBalancesController: { - balances: false, - }, - BridgeController: { - bridgeState: { - bridgeFeatureFlags: { - extensionSupport: false, - }, - }, - }, - CronjobController: { - jobs: false, - }, - CurrencyController: { - currentCurrency: true, - currencyRates: true, - }, - DecryptMessageController: { - unapprovedDecryptMsgs: false, - unapprovedDecryptMsgCount: true, - }, - EncryptionPublicKeyController: { - unapprovedEncryptionPublicKeyMsgs: false, - unapprovedEncryptionPublicKeyMsgCount: true, - }, - EnsController: { - ensResolutionsByAddress: false, - ensEntries: false, - }, - GasFeeController: { - estimatedGasFeeTimeBounds: true, - gasEstimateType: true, - gasFeeEstimates: true, - gasFeeEstimatesByChainId: true, - nonRPCGasFeeApisDisabled: false, - }, - KeyringController: { - isUnlocked: true, - keyrings: false, - }, - LoggingController: { - logs: false, - }, - MetamaskNotificationsController: { - subscriptionAccountsSeen: false, - isMetamaskNotificationsFeatureSeen: false, - isMetamaskNotificationsEnabled: false, - isFeatureAnnouncementsEnabled: false, - metamaskNotificationsList: false, - metamaskNotificationsReadList: false, - isCheckingAccountsPresence: false, - isFetchingMetamaskNotifications: false, - isUpdatingMetamaskNotifications: false, - isUpdatingMetamaskNotificationsAccount: false, - }, - MetaMetricsController: { - eventsBeforeMetricsOptIn: false, - fragments: false, - metaMetricsId: true, - participateInMetaMetrics: true, - previousUserTraits: false, - segmentApiCalls: false, - traits: false, - dataCollectionForMarketing: false, - }, - NameController: { - names: false, - nameSources: false, - useExternalNameSources: false, - }, - NetworkController: { - networkConfigurations: false, - networksMetadata: true, - providerConfig: { - chainId: true, - id: true, - nickname: true, - rpcPrefs: false, - rpcUrl: false, - ticker: true, - type: true, - }, - selectedNetworkClientId: false, - }, - NftController: { - allNftContracts: false, - allNfts: false, - ignoredNfts: false, - }, - NotificationController: { - notifications: false, - }, - OnboardingController: { - completedOnboarding: true, - firstTimeFlowType: true, - onboardingTabs: false, - seedPhraseBackedUp: true, - }, - PPOMController: { - securityAlertsEnabled: false, - storageMetadata: [], - versionInfo: [], - }, - PermissionController: { - subjects: false, - }, - PermissionLogController: { - permissionActivityLog: false, - permissionHistory: false, - }, - PhishingController: {}, - PreferencesController: { - advancedGasFee: true, - currentLocale: true, - disabledRpcMethodPreferences: true, - dismissSeedBackUpReminder: true, - featureFlags: true, - forgottenPassword: true, - identities: false, - incomingTransactionsPreferences: true, - isIpfsGatewayEnabled: false, - ipfsGateway: false, - knownMethodData: false, - ledgerTransportType: true, - lostIdentities: false, - openSeaEnabled: true, - preferences: { - autoLockTimeLimit: true, - hideZeroBalanceTokens: true, - redesignedConfirmationsEnabled: true, - isRedesignedConfirmationsDeveloperEnabled: false, - showExtensionInFullSizeView: true, - showFiatInTestnets: true, - showTestNetworks: true, - smartTransactionsOptInStatus: true, - useNativeCurrencyAsPrimaryCurrency: true, - petnamesEnabled: true, - showTokenAutodetectModal: false, - }, - useExternalServices: false, - selectedAddress: false, - snapRegistryList: false, - theme: true, - signatureSecurityAlertResponses: false, - use4ByteResolution: true, - useAddressBarEnsResolution: true, - useBlockie: true, - useCurrencyRateCheck: true, - useMultiAccountBalanceChecker: true, - useNftDetection: true, - useNonceField: true, - usePhishDetect: true, - useTokenDetection: true, - useRequestQueue: true, - useTransactionSimulations: true, - enableMV3TimestampSave: true, - }, - PushPlatformNotificationsController: { - fcmToken: false, - }, - MultichainRatesController: { - fiatCurrency: true, - rates: true, - cryptocurrencies: true, - }, - QueuedRequestController: { - queuedRequestCount: true, - }, - SelectedNetworkController: { domains: false }, - SignatureController: { - unapprovedMsgCount: true, - unapprovedMsgs: false, - unapprovedPersonalMsgCount: true, - unapprovedPersonalMsgs: false, - unapprovedTypedMessages: false, - unapprovedTypedMessagesCount: true, - }, - SmartTransactionsController: { - smartTransactionsState: { - fees: { - approvalTxFees: true, - tradeTxFees: true, - }, - liveness: true, - smartTransactions: false, - userOptIn: true, - userOptInV2: true, - }, - }, - SnapController: { - unencryptedSnapStates: false, - snapStates: false, - snaps: false, - }, - SnapInterface: { - interfaces: false, - }, - SnapsRegistry: { - database: false, - lastUpdated: false, - databaseUnavailable: false, - }, - SubjectMetadataController: { - subjectMetadata: false, - }, - SwapsController: { - swapsState: { - approveTxId: false, - customApproveTxData: false, - customGasPrice: true, - customMaxFeePerGas: true, - customMaxGas: true, - customMaxPriorityFeePerGas: true, - errorKey: true, - fetchParams: true, - quotes: false, - quotesLastFetched: true, - quotesPollingLimitEnabled: true, - routeState: true, - saveFetchedQuotes: true, - selectedAggId: true, - swapsFeatureFlags: true, - swapsFeatureIsLive: true, - swapsQuotePrefetchingRefreshTime: true, - swapsQuoteRefreshTime: true, - swapsStxBatchStatusRefreshTime: true, - swapsStxGetTransactionsRefreshTime: true, - swapsStxMaxFeeMultiplier: true, - swapsUserFeeLevel: true, - tokens: false, - topAggId: false, - tradeTxId: false, - }, - }, - TokenDetectionController: { - [AllProperties]: false, - }, - TokenListController: { - preventPollingOnNetworkRestart: true, - tokenList: false, - tokensChainsCache: { - [AllProperties]: false, - }, - }, - TokenRatesController: { - marketData: false, - }, - TokensController: { - allDetectedTokens: { - [AllProperties]: false, - }, - allIgnoredTokens: { - [AllProperties]: false, - }, - allTokens: { - [AllProperties]: false, - }, - detectedTokens: false, - ignoredTokens: false, - tokens: false, - }, - TransactionController: { - transactions: false, - lastFetchedBlockNumbers: false, - methodData: false, - }, - TxController: { - transactions: false, - }, - UserOperationController: { - userOperations: false, - }, - UserStorageController: { - isProfileSyncingEnabled: true, - isProfileSyncingUpdateLoading: false, - }, - ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) - ...MMI_SENTRY_BACKGROUND_STATE, - ///: END:ONLY_INCLUDE_IF -}; + if (!getSentryTarget()) { + log('Skipped initialization'); + return undefined; + } + + log('Initializing'); + + integrateLogging(); + setSentryClient(); -const flattenedBackgroundStateMask = Object.values( - SENTRY_BACKGROUND_STATE, -).reduce((partialBackgroundState, controllerState) => { return { - ...partialBackgroundState, - ...controllerState, + ...Sentry, + getMetaMetricsEnabled, }; -}, {}); - -// This describes the subset of Redux state attached to errors sent to Sentry -// These properties have some potential to be useful for debugging, and they do -// not contain any identifiable information. -export const SENTRY_UI_STATE = { - gas: true, - history: true, - metamask: { - ...flattenedBackgroundStateMask, - // This property comes from the background but isn't in controller state - isInitialized: true, - // These properties are in the `metamask` slice but not in the background state - customNonceValue: true, - isAccountMenuOpen: true, - isNetworkMenuOpen: true, - nextNonce: true, - pendingTokens: false, - welcomeScreenSeen: true, - confirmationExchangeRates: true, - useSafeChainsListValidation: true, - bitcoinSupportEnabled: false, - ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps) - addSnapAccountEnabled: false, - snapsAddSnapAccountModalDismissed: false, - ///: END:ONLY_INCLUDE_IF - switchedNetworkDetails: false, - switchedNetworkNeverShowMessage: false, - newPrivacyPolicyToastClickedOrClosed: false, - newPrivacyPolicyToastShownDate: false, - }, - unconnectedAccount: true, -}; +} + +function getClientOptions() { + const environment = getSentryEnvironment(); + const sentryTarget = getSentryTarget(); + + return { + beforeBreadcrumb: beforeBreadcrumb(), + beforeSend: (report) => rewriteReport(report), + debug: METAMASK_DEBUG, + dsn: sentryTarget, + dist: isManifestV3 ? 'mv3' : 'mv2', + environment, + integrations: [ + Sentry.dedupeIntegration(), + Sentry.extraErrorDataIntegration(), + Sentry.browserTracingIntegration(), + filterEvents({ getMetaMetricsEnabled, log }), + ], + release: RELEASE, + // Client reports are automatically sent when a page's visibility changes to + // "hidden", but cancelled (with an Error) that gets logged to the console. + // Our test infra sometimes reports these errors as unexpected failures, + // which results in test flakiness. We don't use these client reports, so + // we can safely turn them off by setting the `sendClientReports` option to + // `false`. + sendClientReports: false, + tracesSampleRate: 0.01, + transport: makeTransport, + }; +} /** * Returns whether MetaMetrics is enabled, given the application state. @@ -482,15 +130,12 @@ function getMetaMetricsEnabledFromPersistedState(persistedState) { * Returns whether onboarding has completed, given the application state. * * @param {Record} appState - Application state - * @returns `true` if MetaMask's state has been initialized, and MetaMetrics - * is enabled, `false` otherwise. + * @returns `true` if onboarding has completed, `false` otherwise. */ function getOnboardingCompleteFromAppState(appState) { // during initialization after loading persisted state if (appState.persistedState) { - return Boolean( - appState.persistedState.data?.OnboardingController?.completedOnboarding, - ); + return getOnboardingCompleteFromPersistedState(appState.persistedState); // After initialization } else if (appState.state) { // UI @@ -504,215 +149,115 @@ function getOnboardingCompleteFromAppState(appState) { return false; } -export default function setupSentry({ release, getState }) { - if (!release) { - throw new Error('Missing release'); - } else if (METAMASK_DEBUG && !IN_TEST) { - /** - * Workaround until the following issue is resolved - * https://github.com/MetaMask/metamask-extension/issues/15691 - * The IN_TEST condition allows the e2e tests to run with both - * yarn start:test and yarn build:test - */ - return undefined; +/** + * Returns whether onboarding has completed, given the persisted state. + * + * @param {Record} persistedState - Persisted state + * @returns `true` if onboarding has completed, `false` otherwise. + */ +function getOnboardingCompleteFromPersistedState(persistedState) { + return Boolean( + persistedState.data?.OnboardingController?.completedOnboarding, + ); +} + +function getSentryEnvironment() { + if (METAMASK_BUILD_TYPE === 'main') { + return METAMASK_ENVIRONMENT; } - const environment = - METAMASK_BUILD_TYPE === 'main' - ? METAMASK_ENVIRONMENT - : `${METAMASK_ENVIRONMENT}-${METAMASK_BUILD_TYPE}`; - - let sentryTarget; - if (METAMASK_ENVIRONMENT === 'production') { - if (!process.env.SENTRY_DSN) { - throw new Error( - `Missing SENTRY_DSN environment variable in production environment`, - ); - } + return `${METAMASK_ENVIRONMENT}-${METAMASK_BUILD_TYPE}`; +} - ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) - sentryTarget = process.env.SENTRY_MMI_DSN; - ///: END:ONLY_INCLUDE_IF +function getSentryTarget() { + if (METAMASK_ENVIRONMENT !== 'production') { + return SENTRY_DSN_DEV; + } - ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) - sentryTarget = process.env.SENTRY_DSN; - ///: END:ONLY_INCLUDE_IF + if (METAMASK_BUILD_TYPE === 'mmi') { + return SENTRY_DSN_MMI; + } - console.log( - `Setting up Sentry Remote Error Reporting for '${environment}': SENTRY_DSN`, - ); - } else { - console.log( - `Setting up Sentry Remote Error Reporting for '${environment}': SENTRY_DSN_DEV`, + if (!SENTRY_DSN) { + throw new Error( + `Missing SENTRY_DSN environment variable in production environment`, ); - sentryTarget = SENTRY_DSN_DEV; } - /** - * Returns whether MetaMetrics is enabled. If the application hasn't yet - * been initialized, the persisted state will be used (if any). - * - * @returns `true` if MetaMetrics is enabled, `false` otherwise. - */ - async function getMetaMetricsEnabled() { - if (METAMASK_BUILD_TYPE === 'mmi') { - return true; - } + return SENTRY_DSN; +} - const appState = getState(); - if (appState.state || appState.persistedState) { - return getMetaMetricsEnabledFromAppState(appState); - } - // If we reach here, it means the error was thrown before initialization - // completed, and before we loaded the persisted state for the first time. - try { - const persistedState = await globalThis.stateHooks.getPersistedState(); - return getMetaMetricsEnabledFromPersistedState(persistedState); - } catch (error) { - console.error(error); - return false; - } +/** + * Returns whether MetaMetrics is enabled. If the application hasn't yet + * been initialized, the persisted state will be used (if any). + * + * @returns `true` if MetaMetrics is enabled, `false` otherwise. + */ +async function getMetaMetricsEnabled() { + if (METAMASK_BUILD_TYPE === 'mmi') { + return true; + } + + const appState = getState(); + + if (appState.state || appState.persistedState) { + return ( + getMetaMetricsEnabledFromAppState(appState) && + getOnboardingCompleteFromAppState(appState) + ); } + // If we reach here, it means the error was thrown before initialization + // completed, and before we loaded the persisted state for the first time. + try { + const persistedState = await globalThis.stateHooks.getPersistedState(); + return ( + getMetaMetricsEnabledFromPersistedState(persistedState) && + getOnboardingCompleteFromPersistedState(persistedState) + ); + } catch (error) { + log('Error retrieving persisted state', error); + return false; + } +} + +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); + }); + /** - * Returns whether Sentry should be enabled or not. If the build type is mmi - * it will always be enabled, if it's main it will first check for MetaMetrics - * value before returning true or false - * - * @returns `true` if Sentry should be enabled, depends on the build type and - * whether MetaMetrics is on or off for all build types except mmi + * 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. + * This emulates NW.js which disables these validations. + * https://docs.sentry.io/platforms/javascript/best-practices/shared-environments/ */ - async function getSentryEnabled() { - // For MMI we want Sentry always logging, doesn't depend on MetaMetrics being on or off - if (METAMASK_BUILD_TYPE === 'mmi') { - return true; - } - return getMetaMetricsEnabled(); - } + globalThis.nw = {}; - Sentry.init({ - dsn: sentryTarget, - debug: METAMASK_DEBUG, - /** - * autoSessionTracking defaults to true and operates by sending a session - * packet to sentry. This session packet does not appear to be filtered out - * via our beforeSend or FilterEvents integration. To avoid sending a - * request before we have the state tree and can validate the users - * preferences, we initiate this to false. Later, in startSession and - * endSession we modify this option and start the session or end the - * session manually. - * - * In sentry-install we call toggleSession after the page loads and state - * is available, this handles initiating the session for a user who has - * opted into MetaMetrics. This script is ran in both the background and UI - * so it should be effective at starting the session in both places. - * - * In the MetaMetricsController the session is manually started or stopped - * when the user opts in or out of MetaMetrics. This occurs in the - * setParticipateInMetaMetrics function which is exposed to the UI via the - * MetaMaskController. - * - * In actions.ts, after sending the updated participateInMetaMetrics flag - * to the background, we call toggleSession to ensure sentry is kept in - * sync with the user's preference. - * - * Types for the global Sentry object, and the new methods added as part of - * this effort were added to global.d.ts in the types folder. - */ - autoSessionTracking: false, + log('Updating client', { environment, - integrations: [ - /** - * Filtering of events must happen in this FilterEvents custom - * integration instead of in the beforeSend handler because the Dedupe - * integration is unaware of the beforeSend functionality. If an event is - * queued in the sentry context, additional events of the same name will - * be filtered out by Dedupe even if the original event was not sent due - * to the beforeSend method returning null. - * - * @see https://github.com/MetaMask/metamask-extension/pull/15677 - */ - new FilterEvents({ getMetaMetricsEnabled }), - new Dedupe(), - new ExtraErrorData(), - new Sentry.BrowserProfilingIntegration(), - ], + dsn, release, - /** - * Setting a value for `tracesSampleRate` enables performance monitoring in Sentry. - * Once performance monitoring is enabled, transactions are sent to Sentry every time - * a user loads a page or navigates within the app. - * Since the amount of traffic the app gets is important, this means a lot of - * transactions are sent. By setting `tracesSampleRate` to a value lower than 1.0, we - * reduce the volume of transactions to a more reasonable amount. - */ - tracesSampleRate: 0.01, - beforeSend: (report) => rewriteReport(report, getState), - beforeBreadcrumb: beforeBreadcrumb(getState), - // Client reports are automatically sent when a page's visibility changes to - // "hidden", but cancelled (with an Error) that gets logged to the console. - // Our test infra sometimes reports these errors as unexpected failures, - // which results in test flakiness. We don't use these client reports, so - // we can safely turn them off by setting the `sendClientReports` option to - // `false`. - sendClientReports: false, }); - /** - * As long as a reference to the Sentry Hub can be found, and the user has - * opted into MetaMetrics, change the autoSessionTracking option and start - * a new sentry session. - */ - const startSession = async () => { - const hub = Sentry.getCurrentHub?.(); - const options = hub.getClient?.().getOptions?.() ?? {}; - if (hub && (await getSentryEnabled()) === true) { - options.autoSessionTracking = true; - hub.startSession(); - } - }; + Sentry.registerSpanErrorInstrumentation(); + Sentry.init(clientOptions); - /** - * As long as a reference to the Sentry Hub can be found, and the user has - * opted out of MetaMetrics, change the autoSessionTracking option and end - * the current sentry session. - */ - const endSession = async () => { - const hub = Sentry.getCurrentHub?.(); - const options = hub.getClient?.().getOptions?.() ?? {}; - if (hub && (await getSentryEnabled()) === false) { - options.autoSessionTracking = false; - hub.endSession(); - } - }; - - /** - * Call the appropriate method (either startSession or endSession) depending - * on the state of metaMetrics optin and the state of autoSessionTracking on - * the Sentry client. - */ - const toggleSession = async () => { - const hub = Sentry.getCurrentHub?.(); - const options = hub.getClient?.().getOptions?.() ?? { - autoSessionTracking: false, - }; - const isSentryEnabled = await getSentryEnabled(); - if (isSentryEnabled === true && options.autoSessionTracking === false) { - await startSession(); - } else if ( - isSentryEnabled === false && - options.autoSessionTracking === true - ) { - await endSession(); - } - }; + addDebugListeners(); - return { - ...Sentry, - startSession, - endSession, - toggleSession, - }; + return true; } /** @@ -734,10 +279,9 @@ function hideUrlIfNotInternal(url) { /** * Returns a method that handles the Sentry breadcrumb using a specific method to get the extension state * - * @param {Function} getState - A method that returns the state of the extension * @returns {(breadcrumb: object) => object} A method that modifies a Sentry breadcrumb object */ -export function beforeBreadcrumb(getState) { +export function beforeBreadcrumb() { return (breadcrumb) => { if (!getState) { return null; @@ -783,11 +327,9 @@ export function removeUrlsFromBreadCrumb(breadcrumb) { * return value of the second parameter passed to the function. * * @param {object} report - A Sentry event object: https://develop.sentry.dev/sdk/event-payloads/ - * @param {Function} getState - A function that should return an object representing some amount - * of app state that we wish to submit with our error reports * @returns {object} A modified Sentry event object. */ -export function rewriteReport(report, getState) { +export function rewriteReport(report) { try { // simplify certain complex error messages (e.g. Ethjs) simplifyErrorMessages(report); @@ -800,16 +342,21 @@ export function rewriteReport(report, getState) { sanitizeAddressesFromErrorMessages(report); // modify report urls rewriteReportUrls(report); + // append app state - if (getState) { - const appState = getState(); - if (!report.extra) { - report.extra = {}; - } - report.extra.appState = appState; + const appState = getState(); + + if (!report.extra) { + report.extra = {}; + } + + report.extra.appState = appState; + if (browser.runtime && browser.runtime.id) { + report.extra.extensionId = browser.runtime.id; } + report.extra.installType = installType; } catch (err) { - console.warn(err); + log('Error rewriting report', err); } return report; } @@ -921,3 +468,74 @@ function toMetamaskUrl(origUrl) { const metamaskUrl = `/metamask${filePath}`; return metamaskUrl; } + +function getState() { + return globalThis.stateHooks?.getSentryState?.() || {}; +} + +function integrateLogging() { + if (!METAMASK_DEBUG) { + return; + } + + for (const loggerType of ['log', 'error']) { + logger[loggerType] = (...args) => { + const message = args[0].replace(`Sentry Logger [${loggerType}]: `, ''); + internalLog(message, ...args.slice(1)); + }; + } + + log('Integrated logging'); +} + +function addDebugListeners() { + if (!METAMASK_DEBUG) { + return; + } + + const client = Sentry.getClient(); + + client?.on('beforeEnvelope', (event) => { + if (isCompletedSessionEnvelope(event)) { + log('Completed session', event); + } + }); + + client?.on('afterSendEvent', (event) => { + const type = getEventType(event); + log(type, event); + }); + + log('Added debug listeners'); +} + +function makeTransport(options) { + return Sentry.makeFetchTransport(options, async (...args) => { + const metricsEnabled = await getMetaMetricsEnabled(); + + if (!metricsEnabled) { + throw new Error('Network request skipped as metrics disabled'); + } + + return await fetch(...args); + }); +} + +function isCompletedSessionEnvelope(envelope) { + const type = envelope?.[1]?.[0]?.[0]?.type; + const data = envelope?.[1]?.[0]?.[1] ?? {}; + + return type === 'session' && data.status === 'exited'; +} + +function getEventType(event) { + if (event.type === 'transaction') { + return 'Trace'; + } + + if (event.level === 'error') { + return 'Error'; + } + + return 'Event'; +} diff --git a/app/scripts/lib/stream-utils.js b/app/scripts/lib/stream-utils.js index db50728669fd..4c9a26a457bc 100644 --- a/app/scripts/lib/stream-utils.js +++ b/app/scripts/lib/stream-utils.js @@ -18,7 +18,8 @@ export function setupMultiplex(connectionStream) { */ mux.ignoreStream(EXTENSION_MESSAGES.CONNECTION_READY); pipeline(connectionStream, mux, connectionStream, (err) => { - if (err) { + // For context and todos related to the error message match, see https://github.com/MetaMask/metamask-extension/issues/26337 + if (err && !err.message?.match('Premature close')) { console.error(err); } }); diff --git a/app/scripts/lib/transaction/metrics.ts b/app/scripts/lib/transaction/metrics.ts index b03bac3e2d5e..5ecabc36862b 100644 --- a/app/scripts/lib/transaction/metrics.ts +++ b/app/scripts/lib/transaction/metrics.ts @@ -36,7 +36,10 @@ import { getSwapsTokensReceivedFromTxMeta, TRANSACTION_ENVELOPE_TYPE_NAMES, } from '../../../../shared/lib/transactions-controller-utils'; -import { getBlockaidMetricsProps } from '../../../../ui/helpers/utils/metrics'; +import { + getBlockaidMetricsProps, + getSwapAndSendMetricsProps, +} from '../../../../ui/helpers/utils/metrics'; import { getSmartTransactionMetricsProperties } from '../../../../shared/modules/metametrics'; import { getSnapAndHardwareInfoForMetrics, @@ -898,7 +901,9 @@ async function buildEventFragmentProperties({ let transactionApprovalAmountVsProposedRatio; let transactionApprovalAmountVsBalanceRatio; let transactionType = TransactionType.simpleSend; - if (type === TransactionType.cancel) { + if (type === TransactionType.swapAndSend) { + transactionType = TransactionType.swapAndSend; + } else if (type === TransactionType.cancel) { transactionType = TransactionType.cancel; } else if (type === TransactionType.retry && originalType) { transactionType = originalType; @@ -1002,6 +1007,9 @@ async function buildEventFragmentProperties({ transactionMeta, ); + const swapAndSendMetricsProperties = + getSwapAndSendMetricsProps(transactionMeta); + /** The transaction status property is not considered sensitive and is now included in the non-anonymous event */ let properties = { chain_id: chainId, @@ -1027,6 +1035,7 @@ async function buildEventFragmentProperties({ // ui_customizations must come after ...blockaidProperties ui_customizations: uiCustomizations.length > 0 ? uiCustomizations : null, ...smartTransactionMetricsProperties, + ...swapAndSendMetricsProperties, // TODO: Replace `any` with type // eslint-disable-next-line @typescript-eslint/no-explicit-any } as Record; diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 1709ada647df..fb251430b6c5 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -1,5 +1,5 @@ import EventEmitter from 'events'; -import { pipeline } from 'readable-stream'; +import { finished, pipeline } from 'readable-stream'; import { AssetsContractController, CurrencyRateController, @@ -1403,9 +1403,11 @@ export default class MetamaskController extends EventEmitter { messenger: this.controllerMessenger.getRestricted({ name: 'AuthenticationController', allowedActions: [ + 'KeyringController:getState', 'SnapController:handleRequest', 'UserStorageController:disableProfileSyncing', ], + allowedEvents: ['KeyringController:lock', 'KeyringController:unlock'], }), metametrics: { getMetaMetricsId: () => this.metaMetricsController.getMetaMetricsId(), @@ -1419,6 +1421,7 @@ export default class MetamaskController extends EventEmitter { messenger: this.controllerMessenger.getRestricted({ name: 'UserStorageController', allowedActions: [ + 'KeyringController:getState', 'SnapController:handleRequest', 'AuthenticationController:getBearerToken', 'AuthenticationController:getSessionProfile', @@ -1428,6 +1431,7 @@ export default class MetamaskController extends EventEmitter { 'MetamaskNotificationsController:disableMetamaskNotifications', 'MetamaskNotificationsController:selectIsMetamaskNotificationsEnabled', ], + allowedEvents: ['KeyringController:lock', 'KeyringController:unlock'], }), }); @@ -1445,9 +1449,10 @@ export default class MetamaskController extends EventEmitter { 'PushPlatformNotificationsController:onNewNotifications', (notification) => { this.metaMetricsController.trackEvent({ - event: MetaMetricsEventName.PushNotificationReceived, category: MetaMetricsEventCategory.PushNotifications, + event: MetaMetricsEventName.NotificationReceived, properties: { + notification_channel: 'push', notification_type: notification.type, chain_id: notification?.chain_id, }, @@ -1458,11 +1463,14 @@ export default class MetamaskController extends EventEmitter { 'PushPlatformNotificationsController:pushNotificationClicked', (notification) => { this.metaMetricsController.trackEvent({ - event: MetaMetricsEventName.PushNotificationClicked, category: MetaMetricsEventCategory.PushNotifications, + event: MetaMetricsEventName.NotificationClicked, properties: { + notification_id: notification.id, notification_type: notification.type, chain_id: notification?.chain_id, + notification_is_read: notification.isRead, + click_type: 'push_notification', }, }); }, @@ -1473,6 +1481,7 @@ export default class MetamaskController extends EventEmitter { name: 'MetamaskNotificationsController', allowedActions: [ 'KeyringController:getAccounts', + 'KeyringController:getState', 'AuthenticationController:getBearerToken', 'AuthenticationController:isSignedIn', 'UserStorageController:enableProfileSyncing', @@ -1485,6 +1494,8 @@ export default class MetamaskController extends EventEmitter { ], allowedEvents: [ 'KeyringController:stateChange', + 'KeyringController:lock', + 'KeyringController:unlock', 'PushPlatformNotificationsController:onNewNotifications', ], }), @@ -4968,14 +4979,39 @@ export default class MetamaskController extends EventEmitter { this.once('startUISync', startUISync); } - outStream.on('end', () => { - this.activeControllerConnections -= 1; - this.emit( - 'controllerConnectionChanged', - this.activeControllerConnections, - ); - this.removeListener('update', handleUpdate); - }); + const outstreamEndHandler = () => { + if (!outStream.mmFinished) { + this.activeControllerConnections -= 1; + this.emit( + 'controllerConnectionChanged', + this.activeControllerConnections, + ); + outStream.mmFinished = true; + this.removeListener('update', handleUpdate); + } + }; + + // The presence of both of the below handlers may be redundant. + // After upgrading metamask/object-multiples to v2.0.0, which included + // an upgrade of readable-streams from v2 to v3, we saw that the + // `outStream.on('end'` handler was almost never being called. This seems to + // related to how v3 handles errors vs how v2 handles errors; there + // are "premature close" errors in both cases, although in the case + // of v2 they don't prevent `outStream.on('end'` from being called. + // At the time that this comment was committed, it was known that we + // need to investigate and resolve the underlying error, however, + // for expediency, we are not addressing them at this time. Instead, we + // can observe that `readableStream.finished` preserves the same + // functionality as we had when we relied on readable-stream v2. Meanwhile, + // the `outStream.on('end')` handler was observed to have been called at least once. + // In an abundance of caution to prevent against unexpected future behavioral changes in + // streams implementations, we redundantly use multiple paths to attach the same event handler. + // The outstreamEndHandler therefore needs to be idempotent, which introduces the `mmFinished` property. + + outStream.mmFinished = false; + finished(outStream, outstreamEndHandler); + outStream.once('close', outstreamEndHandler); + outStream.once('end', outstreamEndHandler); } /** @@ -5035,7 +5071,8 @@ export default class MetamaskController extends EventEmitter { } }); connectionId && this.removeConnection(origin, connectionId); - if (err) { + // For context and todos related to the error message match, see https://github.com/MetaMask/metamask-extension/issues/26337 + if (err && !err.message?.match('Premature close')) { log.error(err); } }, @@ -5558,7 +5595,8 @@ export default class MetamaskController extends EventEmitter { pipeline(configStream, outStream, (err) => { configStream.destroy(); - if (err) { + // For context and todos related to the error message match, see https://github.com/MetaMask/metamask-extension/issues/26337 + if (err && !err.message?.match('Premature close')) { log.error(err); } }); diff --git a/app/scripts/metamask-controller.test.js b/app/scripts/metamask-controller.test.js index 1ad12736fe2a..88f1926c23d2 100644 --- a/app/scripts/metamask-controller.test.js +++ b/app/scripts/metamask-controller.test.js @@ -29,6 +29,7 @@ import { } from '@metamask/assets-controllers'; import { TrezorKeyring } from '@metamask/eth-trezor-keyring'; import { LedgerKeyring } from '@metamask/eth-ledger-bridge-keyring'; +import ObjectMultiplex from '@metamask/object-multiplex'; import { NETWORK_TYPES } from '../../shared/constants/network'; import { createTestProviderTools } from '../../test/stub/provider'; import { HardwareDeviceNames } from '../../shared/constants/hardware-wallets'; @@ -1403,9 +1404,288 @@ describe('MetaMaskController', () => { }); metamaskController.setupTrustedCommunication(streamTest, messageSender); + await promise; streamTest.end(); }); + + it('uses a new multiplex to set up a connection', () => { + jest.spyOn(metamaskController, 'setupControllerConnection'); + + const streamTest = createThroughStream((chunk, _, cb) => { + cb(chunk); + }); + + metamaskController.setupTrustedCommunication(streamTest, {}); + + expect(metamaskController.setupControllerConnection).toHaveBeenCalled(); + expect( + metamaskController.setupControllerConnection, + ).toHaveBeenCalledWith( + expect.objectContaining({ + _name: 'controller', + _parent: expect.any(ObjectMultiplex), + }), + ); + }); + + const createTestStream = () => { + const { + promise: onFinishedCallbackPromise, + resolve: onFinishedCallbackResolve, + } = deferredPromise(); + const { promise: onStreamEndPromise, resolve: onStreamEndResolve } = + deferredPromise(); + const testStream = createThroughStream((chunk, _, cb) => { + expect(chunk.name).toStrictEqual('controller'); + onStreamEndResolve(); + cb(); + }); + + return { + onFinishedCallbackPromise, + onStreamEndPromise, + onFinishedCallbackResolve, + testStream, + }; + }; + + it('sets up a controller connection which emits a controllerConnectionChanged event when the controller connection is created and ended, and activeControllerConnections are updated accordingly', async () => { + const mockControllerConnectionChangedHandler = jest.fn(); + + const { + onStreamEndPromise, + onFinishedCallbackPromise, + onFinishedCallbackResolve, + testStream, + } = createTestStream(); + + metamaskController.on( + 'controllerConnectionChanged', + (activeControllerConnections) => { + mockControllerConnectionChangedHandler(activeControllerConnections); + if ( + mockControllerConnectionChangedHandler.mock.calls.length === 2 + ) { + onFinishedCallbackResolve(); + } + }, + ); + + expect(metamaskController.activeControllerConnections).toBe(0); + + metamaskController.setupTrustedCommunication(testStream, {}); + + expect(mockControllerConnectionChangedHandler).toHaveBeenCalledTimes(1); + expect(mockControllerConnectionChangedHandler).toHaveBeenLastCalledWith( + 1, + ); + + expect(metamaskController.activeControllerConnections).toBe(1); + + await onStreamEndPromise; + testStream.end(); + + await onFinishedCallbackPromise; + + expect(metamaskController.activeControllerConnections).toBe(0); + expect(mockControllerConnectionChangedHandler).toHaveBeenCalledTimes(2); + expect(mockControllerConnectionChangedHandler).toHaveBeenLastCalledWith( + 0, + ); + }); + + it('can be called multiple times to set up multiple controller connections, which can be ended independently', async () => { + const mockControllerConnectionChangedHandler = jest.fn(); + + const testStreams = [ + createTestStream(), + createTestStream(), + createTestStream(), + createTestStream(), + createTestStream(), + ]; + metamaskController.on( + 'controllerConnectionChanged', + (activeControllerConnections) => { + const initialChangeHandlerCallCount = + mockControllerConnectionChangedHandler.mock.calls.length; + mockControllerConnectionChangedHandler(activeControllerConnections); + + if ( + initialChangeHandlerCallCount === 5 && + activeControllerConnections === 4 + ) { + testStreams[1].onFinishedCallbackResolve(); + } + if ( + initialChangeHandlerCallCount === 7 && + activeControllerConnections === 2 + ) { + testStreams[3].onFinishedCallbackResolve(); + testStreams[4].onFinishedCallbackResolve(); + } + if ( + initialChangeHandlerCallCount === 9 && + activeControllerConnections === 0 + ) { + testStreams[2].onFinishedCallbackResolve(); + testStreams[0].onFinishedCallbackResolve(); + } + }, + ); + + metamaskController.setupTrustedCommunication( + testStreams[0].testStream, + {}, + ); + metamaskController.setupTrustedCommunication( + testStreams[1].testStream, + {}, + ); + metamaskController.setupTrustedCommunication( + testStreams[2].testStream, + {}, + ); + metamaskController.setupTrustedCommunication( + testStreams[3].testStream, + {}, + ); + metamaskController.setupTrustedCommunication( + testStreams[4].testStream, + {}, + ); + + expect(metamaskController.activeControllerConnections).toBe(5); + + await testStreams[1].promise; + testStreams[1].testStream.end(); + + await testStreams[1].onFinishedCallbackPromise; + + expect(metamaskController.activeControllerConnections).toBe(4); + + await testStreams[3].promise; + testStreams[3].testStream.end(); + + await testStreams[4].promise; + testStreams[4].testStream.end(); + + await testStreams[3].onFinishedCallbackPromise; + await testStreams[4].onFinishedCallbackPromise; + + expect(metamaskController.activeControllerConnections).toBe(2); + + await testStreams[2].promise; + testStreams[2].testStream.end(); + + await testStreams[0].promise; + testStreams[0].testStream.end(); + + await testStreams[2].onFinishedCallbackPromise; + await testStreams[0].onFinishedCallbackPromise; + + expect(metamaskController.activeControllerConnections).toBe(0); + }); + + // this test could be improved by testing for actual behavior of handlers, + // without touching rawListeners from test + it('attaches listeners for trusted communication streams and removes them as streams close', async () => { + jest + .spyOn(metamaskController, 'triggerNetworkrequests') + .mockImplementation(); + jest + .spyOn(metamaskController.onboardingController.store, 'getState') + .mockReturnValue({ completedOnboarding: true }); + const mockControllerConnectionChangedHandler = jest.fn(); + + const testStreams = [ + createTestStream(), + createTestStream(2), + createTestStream(3), + createTestStream(4), + createTestStream(5), + ]; + const baseUpdateListenerCount = + metamaskController.rawListeners('update').length; + + metamaskController.on( + 'controllerConnectionChanged', + (activeControllerConnections) => { + const initialChangeHandlerCallCount = + mockControllerConnectionChangedHandler.mock.calls.length; + mockControllerConnectionChangedHandler(activeControllerConnections); + if ( + initialChangeHandlerCallCount === 8 && + activeControllerConnections === 1 + ) { + testStreams[1].onFinishedCallbackResolve(); + testStreams[3].onFinishedCallbackResolve(); + testStreams[4].onFinishedCallbackResolve(); + testStreams[2].onFinishedCallbackResolve(); + } + if ( + initialChangeHandlerCallCount === 9 && + activeControllerConnections === 0 + ) { + testStreams[0].onFinishedCallbackResolve(); + } + }, + ); + + metamaskController.setupTrustedCommunication( + testStreams[0].testStream, + {}, + ); + metamaskController.setupTrustedCommunication( + testStreams[1].testStream, + {}, + ); + metamaskController.setupTrustedCommunication( + testStreams[2].testStream, + {}, + ); + metamaskController.setupTrustedCommunication( + testStreams[3].testStream, + {}, + ); + metamaskController.setupTrustedCommunication( + testStreams[4].testStream, + {}, + ); + + await testStreams[1].promise; + + expect(metamaskController.rawListeners('update')).toHaveLength( + baseUpdateListenerCount + 5, + ); + + testStreams[1].testStream.end(); + await testStreams[3].promise; + testStreams[3].testStream.end(); + testStreams[3].testStream.end(); + + await testStreams[4].promise; + testStreams[4].testStream.end(); + await testStreams[2].promise; + testStreams[2].testStream.end(); + await testStreams[1].onFinishedCallbackPromise; + await testStreams[3].onFinishedCallbackPromise; + await testStreams[4].onFinishedCallbackPromise; + await testStreams[2].onFinishedCallbackPromise; + expect(metamaskController.rawListeners('update')).toHaveLength( + baseUpdateListenerCount + 1, + ); + + await testStreams[0].promise; + testStreams[0].testStream.end(); + + await testStreams[0].onFinishedCallbackPromise; + + expect(metamaskController.rawListeners('update')).toHaveLength( + baseUpdateListenerCount, + ); + }); }); describe('#markPasswordForgotten', () => { diff --git a/app/scripts/migrations/088.test.ts b/app/scripts/migrations/088.test.ts index 51f9ebcd2ed1..45538047d470 100644 --- a/app/scripts/migrations/088.test.ts +++ b/app/scripts/migrations/088.test.ts @@ -9,6 +9,8 @@ global.sentry = { captureException: sentryCaptureExceptionMock, }; +const invalidKeys = ['null', 'undefined']; + describe('migration #88', () => { afterEach(() => { jest.resetAllMocks(); @@ -207,28 +209,55 @@ describe('migration #88', () => { }); }); - it('deletes undefined-keyed properties from state of NftController.allNftContracts', async () => { - const oldStorage = { - meta: { version: 87 }, - data: { + for (const invalidKey of invalidKeys) { + it(`deletes ${invalidKey}-keyed properties from state of NftController.allNftContracts`, async () => { + const oldStorage = { + meta: { version: 87 }, + data: { + NftController: { + allNftContracts: { + '0x111': { + '16': [ + { + name: 'Contract 1', + address: '0xaaa', + }, + ], + [invalidKey]: [ + { + name: 'Contract 2', + address: '0xbbb', + }, + ], + }, + '0x222': { + '64': [ + { + name: 'Contract 3', + address: '0xccc', + }, + ], + }, + }, + }, + }, + }; + + const newStorage = await migrate(oldStorage); + + expect(newStorage.data).toStrictEqual({ NftController: { allNftContracts: { '0x111': { - '16': [ + '0x10': [ { name: 'Contract 1', address: '0xaaa', }, ], - undefined: [ - { - name: 'Contract 2', - address: '0xbbb', - }, - ], }, '0x222': { - '64': [ + '0x40': [ { name: 'Contract 3', address: '0xccc', @@ -237,34 +266,9 @@ describe('migration #88', () => { }, }, }, - }, - }; - - const newStorage = await migrate(oldStorage); - - expect(newStorage.data).toStrictEqual({ - NftController: { - allNftContracts: { - '0x111': { - '0x10': [ - { - name: 'Contract 1', - address: '0xaaa', - }, - ], - }, - '0x222': { - '0x40': [ - { - name: 'Contract 3', - address: '0xccc', - }, - ], - }, - }, - }, + }); }); - }); + } it('does not convert chain IDs in NftController.allNftContracts which are already hex strings', async () => { const oldStorage = { @@ -525,14 +529,59 @@ describe('migration #88', () => { }); }); - it('deletes undefined-keyed properties from state of NftController.allNfts', async () => { - const oldStorage = { - meta: { version: 87 }, - data: { + for (const invalidKey of invalidKeys) { + it(`deletes ${invalidKey}-keyed properties from state of NftController.allNfts`, async () => { + const oldStorage = { + meta: { version: 87 }, + data: { + NftController: { + allNfts: { + '0x111': { + '16': [ + { + name: 'NFT 1', + description: 'Description for NFT 1', + image: 'nft1.jpg', + standard: 'ERC721', + tokenId: '1', + address: '0xaaa', + }, + ], + [invalidKey]: [ + { + name: 'NFT 2', + description: 'Description for NFT 2', + image: 'nft2.jpg', + standard: 'ERC721', + tokenId: '2', + address: '0xbbb', + }, + ], + }, + '0x222': { + '64': [ + { + name: 'NFT 3', + description: 'Description for NFT 3', + image: 'nft3.jpg', + standard: 'ERC721', + tokenId: '3', + address: '0xccc', + }, + ], + }, + }, + }, + }, + }; + + const newStorage = await migrate(oldStorage); + + expect(newStorage.data).toStrictEqual({ NftController: { allNfts: { '0x111': { - '16': [ + '0x10': [ { name: 'NFT 1', description: 'Description for NFT 1', @@ -542,19 +591,9 @@ describe('migration #88', () => { address: '0xaaa', }, ], - undefined: [ - { - name: 'NFT 2', - description: 'Description for NFT 2', - image: 'nft2.jpg', - standard: 'ERC721', - tokenId: '2', - address: '0xbbb', - }, - ], }, '0x222': { - '64': [ + '0x40': [ { name: 'NFT 3', description: 'Description for NFT 3', @@ -567,42 +606,9 @@ describe('migration #88', () => { }, }, }, - }, - }; - - const newStorage = await migrate(oldStorage); - - expect(newStorage.data).toStrictEqual({ - NftController: { - allNfts: { - '0x111': { - '0x10': [ - { - name: 'NFT 1', - description: 'Description for NFT 1', - image: 'nft1.jpg', - standard: 'ERC721', - tokenId: '1', - address: '0xaaa', - }, - ], - }, - '0x222': { - '0x40': [ - { - name: 'NFT 3', - description: 'Description for NFT 3', - image: 'nft3.jpg', - standard: 'ERC721', - tokenId: '3', - address: '0xccc', - }, - ], - }, - }, - }, + }); }); - }); + } it('does not convert chain IDs in NftController.allNfts which are already hex strings', async () => { const oldStorage = { @@ -946,13 +952,52 @@ describe('migration #88', () => { }); }); - it('deletes undefined-keyed properties from state of TokenListController.tokensChainsCache', async () => { - const oldStorage = { - meta: { version: 87 }, - data: { + for (const invalidKey of invalidKeys) { + it(`deletes ${invalidKey}-keyed properties from state of TokenListController.tokensChainsCache`, async () => { + const oldStorage = { + meta: { version: 87 }, + data: { + TokenListController: { + tokensChainsCache: { + '16': { + timestamp: 111111, + data: { + '0x111': { + address: '0x111', + symbol: 'TEST1', + decimals: 1, + occurrences: 1, + name: 'Token 1', + iconUrl: 'https://url/to/token1.png', + aggregators: [], + }, + }, + }, + [invalidKey]: { + timestamp: 222222, + data: { + '0x222': { + address: '0x222', + symbol: 'TEST2', + decimals: 1, + occurrences: 1, + name: 'Token 2', + iconUrl: 'https://url/to/token2.png', + aggregators: [], + }, + }, + }, + }, + }, + }, + }; + + const newStorage = await migrate(oldStorage); + + expect(newStorage.data).toStrictEqual({ TokenListController: { tokensChainsCache: { - '16': { + '0x10': { timestamp: 111111, data: { '0x111': { @@ -966,48 +1011,11 @@ describe('migration #88', () => { }, }, }, - undefined: { - timestamp: 222222, - data: { - '0x222': { - address: '0x222', - symbol: 'TEST2', - decimals: 1, - occurrences: 1, - name: 'Token 2', - iconUrl: 'https://url/to/token2.png', - aggregators: [], - }, - }, - }, }, }, - }, - }; - - const newStorage = await migrate(oldStorage); - - expect(newStorage.data).toStrictEqual({ - TokenListController: { - tokensChainsCache: { - '0x10': { - timestamp: 111111, - data: { - '0x111': { - address: '0x111', - symbol: 'TEST1', - decimals: 1, - occurrences: 1, - name: 'Token 1', - iconUrl: 'https://url/to/token1.png', - aggregators: [], - }, - }, - }, - }, - }, + }); }); - }); + } it('does not convert chain IDs in TokenListController.tokensChainsCache which are already hex strings', async () => { const oldStorage = { @@ -1241,13 +1249,51 @@ describe('migration #88', () => { }); }); - it('deletes undefined keyed properties from TokensController.allTokens', async () => { - const oldStorage = { - meta: { version: 87 }, - data: { + for (const invalidKey of invalidKeys) { + it(`deletes ${invalidKey} keyed properties from TokensController.allTokens`, async () => { + const oldStorage = { + meta: { version: 87 }, + data: { + TokensController: { + allTokens: { + '16': { + '0x111': [ + { + address: '0xaaa', + decimals: 1, + symbol: 'TEST1', + }, + ], + }, + '32': { + '0x222': [ + { + address: '0xbbb', + decimals: 1, + symbol: 'TEST2', + }, + ], + }, + [invalidKey]: { + '0x333': [ + { + address: '0xbbb', + decimals: 1, + symbol: 'TEST2', + }, + ], + }, + }, + }, + }, + }; + + const newStorage = await migrate(oldStorage); + + expect(newStorage.data).toStrictEqual({ TokensController: { allTokens: { - '16': { + '0x10': { '0x111': [ { address: '0xaaa', @@ -1256,7 +1302,7 @@ describe('migration #88', () => { }, ], }, - '32': { + '0x20': { '0x222': [ { address: '0xbbb', @@ -1265,47 +1311,11 @@ describe('migration #88', () => { }, ], }, - undefined: { - '0x333': [ - { - address: '0xbbb', - decimals: 1, - symbol: 'TEST2', - }, - ], - }, }, }, - }, - }; - - const newStorage = await migrate(oldStorage); - - expect(newStorage.data).toStrictEqual({ - TokensController: { - allTokens: { - '0x10': { - '0x111': [ - { - address: '0xaaa', - decimals: 1, - symbol: 'TEST1', - }, - ], - }, - '0x20': { - '0x222': [ - { - address: '0xbbb', - decimals: 1, - symbol: 'TEST2', - }, - ], - }, - }, - }, + }); }); - }); + } it('does not convert chain IDs in TokensController.allTokens which are already hex strings', async () => { const oldStorage = { @@ -1456,51 +1466,53 @@ describe('migration #88', () => { }); }); - it('deletes undefined-keyed properties from TokensController.allIgnoredTokens', async () => { - const oldStorage = { - meta: { version: 87 }, - data: { - TokensController: { - allIgnoredTokens: { - '16': { - '0x1': { - '0x111': ['0xaaa'], + for (const invalidKey of invalidKeys) { + it(`deletes ${invalidKey}-keyed properties from TokensController.allIgnoredTokens`, async () => { + const oldStorage = { + meta: { version: 87 }, + data: { + TokensController: { + allIgnoredTokens: { + '16': { + '0x1': { + '0x111': ['0xaaa'], + }, }, - }, - '32': { - '0x2': { - '0x222': ['0xbbb'], + '32': { + '0x2': { + '0x222': ['0xbbb'], + }, }, - }, - undefined: { - '0x2': { - '0x222': ['0xbbb'], + [invalidKey]: { + '0x2': { + '0x222': ['0xbbb'], + }, }, }, }, }, - }, - }; + }; - const newStorage = await migrate(oldStorage); + const newStorage = await migrate(oldStorage); - expect(newStorage.data).toStrictEqual({ - TokensController: { - allIgnoredTokens: { - '0x10': { - '0x1': { - '0x111': ['0xaaa'], + expect(newStorage.data).toStrictEqual({ + TokensController: { + allIgnoredTokens: { + '0x10': { + '0x1': { + '0x111': ['0xaaa'], + }, }, - }, - '0x20': { - '0x2': { - '0x222': ['0xbbb'], + '0x20': { + '0x2': { + '0x222': ['0xbbb'], + }, }, }, }, - }, + }); }); - }); + } it('does not convert chain IDs in TokensController.allIgnoredTokens which are already hex strings', async () => { const oldStorage = { @@ -1635,41 +1647,43 @@ describe('migration #88', () => { }); }); - it('deletes undefined-keyed properties from TokensController.allDetectedTokens', async () => { - const oldStorage = { - meta: { version: 87 }, - data: { - TokensController: { - allDetectedTokens: { - '16': { - '0x1': { - '0x111': ['0xaaa'], + for (const invalidKey of invalidKeys) { + it(`deletes ${invalidKey}-keyed properties from TokensController.allDetectedTokens`, async () => { + const oldStorage = { + meta: { version: 87 }, + data: { + TokensController: { + allDetectedTokens: { + '16': { + '0x1': { + '0x111': ['0xaaa'], + }, }, - }, - undefined: { - '0x2': { - '0x222': ['0xbbb'], + [invalidKey]: { + '0x2': { + '0x222': ['0xbbb'], + }, }, }, }, }, - }, - }; + }; - const newStorage = await migrate(oldStorage); + const newStorage = await migrate(oldStorage); - expect(newStorage.data).toStrictEqual({ - TokensController: { - allDetectedTokens: { - '0x10': { - '0x1': { - '0x111': ['0xaaa'], + expect(newStorage.data).toStrictEqual({ + TokensController: { + allDetectedTokens: { + '0x10': { + '0x1': { + '0x111': ['0xaaa'], + }, }, }, }, - }, + }); }); - }); + } it('does not convert chain IDs in TokensController.allDetectedTokens which are already hex strings', async () => { const oldStorage = { diff --git a/app/scripts/migrations/088.ts b/app/scripts/migrations/088.ts index 6fda62183d2e..274b93624a85 100644 --- a/app/scripts/migrations/088.ts +++ b/app/scripts/migrations/088.ts @@ -59,7 +59,11 @@ function migrateData(state: Record): void { if (isObject(nftContractsByChainId)) { for (const chainId of Object.keys(nftContractsByChainId)) { - if (chainId === 'undefined' || chainId === undefined) { + if ( + chainId === 'undefined' || + chainId === undefined || + chainId === 'null' + ) { delete nftContractsByChainId[chainId]; } } @@ -96,7 +100,11 @@ function migrateData(state: Record): void { if (isObject(nftsByChainId)) { for (const chainId of Object.keys(nftsByChainId)) { - if (chainId === 'undefined' || chainId === undefined) { + if ( + chainId === 'undefined' || + chainId === undefined || + chainId === 'null' + ) { delete nftsByChainId[chainId]; } } @@ -142,7 +150,11 @@ function migrateData(state: Record): void { for (const chainId of Object.keys( tokenListControllerState.tokensChainsCache, )) { - if (chainId === 'undefined' || chainId === undefined) { + if ( + chainId === 'undefined' || + chainId === undefined || + chainId === 'null' + ) { delete tokenListControllerState.tokensChainsCache[chainId]; } } @@ -183,7 +195,11 @@ function migrateData(state: Record): void { const { allTokens } = tokensControllerState; for (const chainId of Object.keys(allTokens)) { - if (chainId === 'undefined' || chainId === undefined) { + if ( + chainId === 'undefined' || + chainId === undefined || + chainId === 'null' + ) { delete allTokens[chainId]; } } @@ -212,7 +228,11 @@ function migrateData(state: Record): void { const { allIgnoredTokens } = tokensControllerState; for (const chainId of Object.keys(allIgnoredTokens)) { - if (chainId === 'undefined' || chainId === undefined) { + if ( + chainId === 'undefined' || + chainId === undefined || + chainId === 'null' + ) { delete allIgnoredTokens[chainId]; } } @@ -241,7 +261,11 @@ function migrateData(state: Record): void { const { allDetectedTokens } = tokensControllerState; for (const chainId of Object.keys(allDetectedTokens)) { - if (chainId === 'undefined' || chainId === undefined) { + if ( + chainId === 'undefined' || + chainId === undefined || + chainId === 'null' + ) { delete allDetectedTokens[chainId]; } } diff --git a/app/scripts/migrations/120.1.test.ts b/app/scripts/migrations/120.1.test.ts new file mode 100644 index 000000000000..83cacd2e3c12 --- /dev/null +++ b/app/scripts/migrations/120.1.test.ts @@ -0,0 +1,75 @@ +import { migrate, version } from './120.1'; + +const oldVersion = 120; + +describe('migration #120.1', () => { + afterEach(() => jest.resetAllMocks()); + + it('updates the version metadata', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: {}, + }; + + const newStorage = await migrate(oldStorage); + expect(newStorage.meta).toStrictEqual({ version }); + }); + + it('sets isProfileSyncingEnabled to null', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + UserStorageController: { + isProfileSyncingEnabled: true, + isProfileSyncingUpdateLoading: false, + }, + }, + }; + + const newStorage = await migrate(oldStorage); + expect(newStorage.data.UserStorageController).toStrictEqual({ + isProfileSyncingEnabled: null, + isProfileSyncingUpdateLoading: false, + }); + }); + + it('initializes a default user storage state if it did not exist before', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + OtherController: {}, + }, + }; + + const expectedStorageData = { + OtherController: {}, + UserStorageController: { + isProfileSyncingEnabled: null, + }, + }; + + const newStorage = await migrate(oldStorage); + expect(newStorage.data).toStrictEqual(expectedStorageData); + }); + + it('should do nothing if existing UserStorageController state is malformed', async () => { + const actAssertInvalidUserStorageState = async (state: unknown) => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + OtherController: {}, + UserStorageController: state, + }, + }; + + const newStorage = await migrate(oldStorage); + expect(newStorage.data).toStrictEqual(oldStorage.data); + }; + + actAssertInvalidUserStorageState('user storage state is not an object'); + actAssertInvalidUserStorageState({ + // missing the isProfileSyncingEnabled field + isProfileSyncingUpdateLoading: false, + }); + }); +}); diff --git a/app/scripts/migrations/120.1.ts b/app/scripts/migrations/120.1.ts new file mode 100644 index 000000000000..bf68b6c4815a --- /dev/null +++ b/app/scripts/migrations/120.1.ts @@ -0,0 +1,52 @@ +import { cloneDeep, isObject } from 'lodash'; +import { hasProperty } from '@metamask/utils'; +import { captureException } from '@sentry/browser'; + +type VersionedData = { + meta: { version: number }; + data: Record; +}; + +export const version = 120.1; + +/** + * Add a default value for importTime in the InternalAccount + * + * @param originalVersionedData + */ +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 { + // Existing users who do not have UserStorageController state. + // Provide some initial state & nullify `isProfileSyncingEnabled` + if (!hasProperty(state, 'UserStorageController')) { + state.UserStorageController = { + isProfileSyncingEnabled: null, + }; + return state; + } + + if ( + !isObject(state.UserStorageController) || + !hasProperty(state.UserStorageController, 'isProfileSyncingEnabled') + ) { + captureException( + `Migration ${version}: Invalid UserStorageController state: ${typeof state.UserStorageController}`, + ); + return state; + } + + // Existing users who do have UserStorageController state. + // nullify `isProfileSyncingEnabled` + state.UserStorageController.isProfileSyncingEnabled = null; + return state; +} diff --git a/app/scripts/migrations/120.2.test.ts b/app/scripts/migrations/120.2.test.ts new file mode 100644 index 000000000000..9cd64275580b --- /dev/null +++ b/app/scripts/migrations/120.2.test.ts @@ -0,0 +1,576 @@ +import { cloneDeep } from 'lodash'; +import { migrate, version } from './120.2'; + +const sentryCaptureExceptionMock = jest.fn(); + +global.sentry = { + captureException: sentryCaptureExceptionMock, +}; + +const oldVersion = 120.1; + +describe('migration #120.2', () => { + afterEach(() => { + jest.resetAllMocks(); + }); + + it('updates the version metadata', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: {}, + }; + + const newStorage = await migrate(cloneDeep(oldStorage)); + + expect(newStorage.meta).toStrictEqual({ version }); + }); + + describe('SelectedNetworkController', () => { + it('does nothing if SelectedNetworkController state is not set', async () => { + const oldState = { + OtherController: {}, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual(oldState); + }); + + it('removes SelectedNetworkController state if SelectedNetworkController state is not itself an object', async () => { + const oldState = { + SelectedNetworkController: 'foo', + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual({}); + }); + + it('removes SelectedNetworkController state if "perDomainNetwork" property is present', async () => { + const oldState = { + SelectedNetworkController: { + domains: { + 'https://metamask.io': { + network: 'mainnet', + }, + }, + perDomainNetwork: true, + }, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual({}); + }); + + it('leaves "domains" state unchanged in SelectedNetworkController if "perDomainNetwork" property is not present in SelectedNetworkController state', async () => { + const oldState = { + SelectedNetworkController: { + domains: { + 'https://metamask.io': { + network: 'mainnet', + }, + 'https://test.io': { + network: 'linea', + }, + 'https://uniswap.io': { + network: 'optimism', + }, + }, + }, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual(oldState); + }); + + it('still migrates SelectedNetworkController state if other controllers have invalid state', async () => { + const oldState = { + NetworkController: 'invalid', + SelectedNetworkController: { + domains: { + 'https://metamask.io': { + network: 'mainnet', + }, + }, + perDomainNetwork: true, + }, + SnapController: 'invalid', + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data.SelectedNetworkController).toBeUndefined(); + }); + }); + + describe('SnapController', () => { + it('does nothing if SnapController state is not set', async () => { + const oldState = { + PreferencesController: {}, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual(oldState); + }); + + it('captures an error and leaves state unchanged if SnapController state is corrupted', async () => { + const oldState = { + SnapController: 'invalid', + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual(oldState); + expect(sentryCaptureExceptionMock).toHaveBeenCalledWith( + new Error( + `Migration ${version}: Invalid SnapController state of type 'string'`, + ), + ); + }); + + it('strips SnapController.snapErrors if it exists', async () => { + const oldState = { + SnapController: { + snapErrors: {}, + snapStates: {}, + unencryptedSnapStates: {}, + snaps: {}, + }, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual({ + SnapController: { + snapStates: {}, + unencryptedSnapStates: {}, + snaps: {}, + }, + }); + }); + + it('does nothing if SnapController.snapErrors doesnt exist', async () => { + const oldState = { + SnapController: { + snapStates: {}, + unencryptedSnapStates: {}, + snaps: {}, + }, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual(oldState); + }); + + it('still migrates SnapController state if other controllers have invalid state', async () => { + const oldState = { + NetworkController: 'invalid', + SelectedNetworkController: 'invalid', + SnapController: { + snapErrors: {}, + snapStates: {}, + unencryptedSnapStates: {}, + snaps: {}, + }, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data.SnapController).toEqual({ + snapStates: {}, + unencryptedSnapStates: {}, + snaps: {}, + }); + }); + }); + + describe('NetworkController', () => { + it('does nothing if NetworkController state is not set', async () => { + const oldState = { + PreferencesController: {}, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual(oldState); + }); + + it('captures an error and leaves state unchanged if NetworkController state is corrupted', async () => { + const oldState = { + NetworkController: 'invalid', + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual(oldState); + expect(sentryCaptureExceptionMock).toHaveBeenCalledWith( + new Error( + `Migration ${version}: Invalid NetworkController state of type 'string'`, + ), + ); + }); + + it('captures an error and leaves state unchanged if providerConfig state is corrupted', async () => { + const oldState = { + NetworkController: { + providerConfig: 'invalid', + }, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual(oldState); + expect(sentryCaptureExceptionMock).toHaveBeenCalledWith( + new Error( + `Migration ${version}: Invalid NetworkController providerConfig state of type 'string'`, + ), + ); + }); + + it('captures an error and leaves state unchanged if networkConfigurations state is corrupted', async () => { + const oldState = { + NetworkController: { + networkConfigurations: 'invalid', + providerConfig: {}, + }, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual(oldState); + expect(sentryCaptureExceptionMock).toHaveBeenCalledWith( + new Error( + `Migration ${version}: Invalid NetworkController networkConfigurations state of type 'string'`, + ), + ); + }); + + it('does nothing if obsolete properties and providerConfig are not set', async () => { + const oldState = { + NetworkController: { + selectedNetworkClientId: 'example', + }, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual(oldState); + }); + + it('does nothing if obsolete properties are not set and providerConfig is set to undefined', async () => { + const oldState = { + NetworkController: { + // This should be impossible because `undefined` cannot be returned from persisted state, + // it's not valid JSON. But a bug in migration 14 ends up setting this to `undefined`. + providerConfig: undefined, + selectedNetworkClientId: 'example', + }, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual(oldState); + }); + + it('does nothing if obsolete properties and providerConfig id are not set', async () => { + const oldState = { + NetworkController: { + providerConfig: {}, + selectedNetworkClientId: 'example', + }, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual(oldState); + }); + + it('does not remove a valid providerConfig id', async () => { + const oldState = { + NetworkController: { + networkConfigurations: { + 'valid-id': {}, + }, + providerConfig: { + id: 'valid-id', + }, + selectedNetworkClientId: 'example', + }, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual(oldState); + }); + + it('removes all obsolete properties', async () => { + const oldState = { + NetworkController: { + networkDetails: {}, + networkId: 'example', + networkStatus: 'example', + previousProviderStore: 'example', + provider: 'example', + selectedNetworkClientId: 'example', + }, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual({ + NetworkController: { + selectedNetworkClientId: 'example', + }, + }); + }); + + it('removes providerConfig id if network configuration is missing', async () => { + const oldState = { + NetworkController: { + providerConfig: { + id: 'invalid-id', + }, + selectedNetworkClientId: 'example', + }, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data.NetworkController).toEqual({ + providerConfig: {}, + selectedNetworkClientId: 'example', + }); + }); + + it('removes providerConfig id that does not match any network configuration', async () => { + const oldState = { + NetworkController: { + networkConfigurations: { + 'valid-id': {}, + }, + providerConfig: { + id: 'invalid-id', + }, + selectedNetworkClientId: 'example', + }, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data.NetworkController).toEqual({ + networkConfigurations: { + 'valid-id': {}, + }, + providerConfig: {}, + selectedNetworkClientId: 'example', + }); + }); + + it('removes providerConfig id with an invalid type', async () => { + const oldState = { + NetworkController: { + networkConfigurations: { + '123': {}, + }, + providerConfig: { + id: 123, + }, + selectedNetworkClientId: 'example', + }, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data.NetworkController).toEqual({ + networkConfigurations: { + '123': {}, + }, + providerConfig: {}, + selectedNetworkClientId: 'example', + }); + }); + + it('still migrates NetworkController state if other controllers have invalid state', async () => { + const oldState = { + NetworkController: { + networkDetails: {}, + networkId: 'example', + networkStatus: 'example', + previousProviderStore: 'example', + provider: 'example', + selectedNetworkClientId: 'example', + }, + SelectedNetworkController: 'invalid', + SnapController: 'invalid', + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data.NetworkController).toEqual({ + selectedNetworkClientId: 'example', + }); + }); + }); + + describe('PhishingController', () => { + it('does nothing if PhishingController state is not set', async () => { + const oldState = { + PreferencesController: {}, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual(oldState); + }); + + it('captures an error and leaves state unchanged if PhishingController state is corrupted', async () => { + const oldState = { + PhishingController: 'invalid', + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual(oldState); + expect(sentryCaptureExceptionMock).toHaveBeenCalledWith( + new Error( + `Migration ${version}: Invalid PhishingController state of type 'string'`, + ), + ); + }); + + it('does nothing if obsolete properties are not set', async () => { + const oldState = { + PhishingController: { + phishingLists: [], + }, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual(oldState); + }); + + it('removes all obsolete properties', async () => { + const oldState = { + PhishingController: { + listState: {}, + phishingLists: [], + }, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual({ + PhishingController: { + phishingLists: [], + }, + }); + }); + + it('still migrates PhishingController state if other controllers have invalid state', async () => { + const oldState = { + NetworkController: 'invalid', + PhishingController: { + listState: {}, + phishingLists: [], + }, + SelectedNetworkController: 'invalid', + SnapController: 'invalid', + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data.PhishingController).toEqual({ + phishingLists: [], + }); + }); + }); +}); diff --git a/app/scripts/migrations/120.2.ts b/app/scripts/migrations/120.2.ts new file mode 100644 index 000000000000..06e6cc091b9e --- /dev/null +++ b/app/scripts/migrations/120.2.ts @@ -0,0 +1,197 @@ +import { cloneDeep } from 'lodash'; +import { hasProperty, isObject } from '@metamask/utils'; +import log from 'loglevel'; + +type VersionedData = { + meta: { version: number }; + data: Record; +}; + +export const version = 120.2; + +/** + * This migration removes any dangling instances of SelectedNetworkController.perDomainNetwork and SnapController.snapErrors + * + * @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; +} + +/** + * Remove obsolete SnapController state + * + * The `snapErrors` property was never intended to be persisted, but the initial state for this + * property was accidentally persisted for some users due to a bug. See #26280 for details. + * + * @param state - The persisted MetaMask state, keyed by controller. + */ +function removeObsoleteSnapControllerState( + state: Record, +): void { + if (!hasProperty(state, 'SnapController')) { + return; + } else if (!isObject(state.SnapController)) { + global.sentry?.captureException?.( + new Error( + `Migration ${version}: Invalid SnapController state of type '${typeof state.SnapController}'`, + ), + ); + return; + } + + delete state.SnapController.snapErrors; +} + +/** + * Remove obsolete `perDomainNetwork` property from SelectedNetworkController state. + * + * We don't know exactly why yet, but we see from Sentry that some users have this property still + * in state. It is no longer used. + * + * If we detect that the state is corrupted or that this property is present, we are fixing it by + * erasing the state. The consequences of this state being erased are minimal, and this was easier + * than fixing state corruption without resetting it. + * + * @param state - The persisted MetaMask state, keyed by controller. + */ +function removeObsoleteSelectedNetworkControllerState( + state: Record, +): void { + if (!hasProperty(state, 'SelectedNetworkController')) { + return; + } + if (!isObject(state.SelectedNetworkController)) { + console.error( + `Migration ${version}: Invalid SelectedNetworkController state of type '${typeof state.SelectedNetworkController}'`, + ); + delete state.SelectedNetworkController; + } else if (hasProperty(state.SelectedNetworkController, 'perDomainNetwork')) { + delete state.SelectedNetworkController; + } +} + +/** + * Remove obsolete NetworkController state. + * + * We don't know exactly why yet, but we see from Sentry that some users have these properties + * in state. They should have been removed by migrations long ago. They are no longer used. + * + * @param state - The persisted MetaMask state, keyed by controller. + */ +function removeObsoleteNetworkControllerState( + state: Record, +): void { + if (!hasProperty(state, 'NetworkController')) { + return; + } else if (!isObject(state.NetworkController)) { + global.sentry?.captureException?.( + new Error( + `Migration ${version}: Invalid NetworkController state of type '${typeof state.NetworkController}'`, + ), + ); + return; + } + + const networkControllerState = state.NetworkController; + + // Check for invalid `providerConfig.id`, and remove if found + if ( + hasProperty(networkControllerState, 'providerConfig') && + // This should be impossible because `undefined` cannot be returned from persisted state, + // it's not valid JSON. But a bug in migration 14 ends up setting this to `undefined`. + networkControllerState.providerConfig !== undefined + ) { + if (!isObject(networkControllerState.providerConfig)) { + global.sentry?.captureException?.( + new Error( + `Migration ${version}: Invalid NetworkController providerConfig state of type '${typeof state + .NetworkController.providerConfig}'`, + ), + ); + return; + } + const { providerConfig } = networkControllerState; + + const validNetworkConfigurationIds = []; + if (hasProperty(networkControllerState, 'networkConfigurations')) { + if (!isObject(networkControllerState.networkConfigurations)) { + global.sentry?.captureException?.( + new Error( + `Migration ${version}: Invalid NetworkController networkConfigurations state of type '${typeof networkControllerState.networkConfigurations}'`, + ), + ); + return; + } + + validNetworkConfigurationIds.push( + ...Object.keys(networkControllerState.networkConfigurations), + ); + } + + if (hasProperty(providerConfig, 'id')) { + if ( + typeof providerConfig.id !== 'string' || + !validNetworkConfigurationIds.includes(providerConfig.id) + ) { + log.warn( + `Migration ${version}: Removing invalid provider id ${providerConfig.id}`, + ); + delete providerConfig.id; + } + } + } + + delete networkControllerState.networkDetails; + delete networkControllerState.networkId; + delete networkControllerState.networkStatus; + delete networkControllerState.previousProviderStore; + delete networkControllerState.provider; +} + +/** + * Remove obsolete `listState` property from PhishingController state. + * + * We don't know exactly why yet, but we see from Sentry that some users have this property still + * in state. It is no longer used. + * + * @param state - The persisted MetaMask state, keyed by controller. + */ +function removeObsoletePhishingControllerState( + state: Record, +): void { + if (!hasProperty(state, 'PhishingController')) { + return; + } else if (!isObject(state.PhishingController)) { + global.sentry.captureException( + new Error( + `Migration ${version}: Invalid PhishingController state of type '${typeof state.PhishingController}'`, + ), + ); + return; + } + if (hasProperty(state.PhishingController, 'listState')) { + delete state.PhishingController.listState; + } +} + +/** + * Remove obsolete controller state. + * + * @param state - The persisted MetaMask state, keyed by controller. + */ +function transformState(state: Record): void { + removeObsoleteSnapControllerState(state); + removeObsoleteSelectedNetworkControllerState(state); + removeObsoleteNetworkControllerState(state); + removeObsoletePhishingControllerState(state); +} diff --git a/app/scripts/migrations/120.4.test.ts b/app/scripts/migrations/120.4.test.ts new file mode 100644 index 000000000000..d37098a325b7 --- /dev/null +++ b/app/scripts/migrations/120.4.test.ts @@ -0,0 +1,231 @@ +import { cloneDeep } from 'lodash'; +import { migrate, version } from './120.4'; + +const sentryCaptureExceptionMock = jest.fn(); + +global.sentry = { + captureException: sentryCaptureExceptionMock, +}; + +const oldVersion = 120.3; + +describe('migration #120.4', () => { + afterEach(() => { + jest.resetAllMocks(); + }); + + it('updates the version metadata', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: {}, + }; + + const newStorage = await migrate(cloneDeep(oldStorage)); + + expect(newStorage.meta).toStrictEqual({ version }); + }); + + describe('CurrencyController', () => { + it('does nothing if CurrencyController state is not set', async () => { + const oldState = { + OtherController: {}, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual(oldState); + }); + + it('captures an error and leaves state unchanged if CurrencyController state is corrupted', async () => { + const oldState = { + CurrencyController: 'invalid', + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual(oldState); + expect(sentryCaptureExceptionMock).toHaveBeenCalledWith( + new Error( + `Migration ${version}: Invalid CurrencyController state of type 'string'`, + ), + ); + }); + + it('deletes obsolete properties from the CurrencyController state', async () => { + const oldState = { + CurrencyController: { + conversionDate: 'test', + conversionRate: 'test', + nativeCurrency: 'test', + pendingCurrentCurrency: 'test', + pendingNativeCurrency: 'test', + usdConversionRate: 'test', + }, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual({ CurrencyController: {} }); + }); + + it('does not delete non-obsolete properties from the CurrencyController state', async () => { + const oldState = { + CurrencyController: { + currencyRates: { test: 123 }, + conversionRate: 'test', + }, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual({ + CurrencyController: { currencyRates: { test: 123 } }, + }); + }); + }); + + describe('PhishingController', () => { + it('does nothing if PhishingController state is not set', async () => { + const oldState = { + OtherController: {}, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual(oldState); + }); + + it('captures an error and leaves state unchanged if PhishingController state is corrupted', async () => { + const oldState = { + PhishingController: 'invalid', + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual(oldState); + expect(sentryCaptureExceptionMock).toHaveBeenCalledWith( + new Error( + `Migration ${version}: Invalid PhishingController state of type 'string'`, + ), + ); + }); + + it('deletes obsolete properties from the PhishingController state', async () => { + const oldState = { + PhishingController: { + phishing: 'test', + lastFetched: 'test', + }, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual({ PhishingController: {} }); + }); + + it('does not delete non-obsolete properties from the PhishingController state', async () => { + const oldState = { + PhishingController: { + phishing: 'test', + phishingLists: 'test', + }, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual({ + PhishingController: { phishingLists: 'test' }, + }); + }); + }); + + describe('NetworkController', () => { + it('does nothing if NetworkController state is not set', async () => { + const oldState = { + OtherController: {}, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual(oldState); + }); + + it('captures an error and leaves state unchanged if NetworkController state is corrupted', async () => { + const oldState = { + NetworkController: 'invalid', + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual(oldState); + expect(sentryCaptureExceptionMock).toHaveBeenCalledWith( + new Error( + `Migration ${version}: Invalid NetworkController state of type 'string'`, + ), + ); + }); + + it('deletes obsolete properties from the NetworkController state', async () => { + const oldState = { + NetworkController: { + network: 'test', + }, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual({ NetworkController: {} }); + }); + + it('does not delete non-obsolete properties from the NetworkController state', async () => { + const oldState = { + NetworkController: { + network: 'test', + selectedNetworkClientId: 'test', + }, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual({ + NetworkController: { selectedNetworkClientId: 'test' }, + }); + }); + }); +}); diff --git a/app/scripts/migrations/120.4.ts b/app/scripts/migrations/120.4.ts new file mode 100644 index 000000000000..fc4162b61d7b --- /dev/null +++ b/app/scripts/migrations/120.4.ts @@ -0,0 +1,119 @@ +import { hasProperty, isObject } from '@metamask/utils'; +import { cloneDeep } from 'lodash'; + +type VersionedData = { + meta: { version: number }; + data: Record; +}; + +export const version = 120.4; + +/** + * This migration removes properties from the CurrencyController state that + * are no longer used. There presence in state causes "No metadata found" errors + * + * @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; +} + +/** + * Remove obsolete CurrencyController state + * + * The six properties deleted here were no longer used as of + * assets-controllers v18.0.0 + * + * See https://github.com/MetaMask/core/pull/1805 for the removal of these + * properties from the controller. + * + * @param state - The persisted MetaMask state, keyed by controller. + */ +function removeObsoleteCurrencyControllerState( + state: Record, +): void { + if (!hasProperty(state, 'CurrencyController')) { + return; + } else if (!isObject(state.CurrencyController)) { + global.sentry?.captureException?.( + new Error( + `Migration ${version}: Invalid CurrencyController state of type '${typeof state.CurrencyController}'`, + ), + ); + return; + } + + delete state.CurrencyController.conversionDate; + delete state.CurrencyController.conversionRate; + delete state.CurrencyController.nativeCurrency; + delete state.CurrencyController.pendingCurrentCurrency; + delete state.CurrencyController.pendingNativeCurrency; + delete state.CurrencyController.usdConversionRate; +} + +/** + * Remove obsolete PhishingController state + * + * @param state - The persisted MetaMask state, keyed by controller. + */ +function removeObsoletePhishingControllerState( + state: Record, +): void { + if (!hasProperty(state, 'PhishingController')) { + return; + } else if (!isObject(state.PhishingController)) { + global.sentry?.captureException?.( + new Error( + `Migration ${version}: Invalid PhishingController state of type '${typeof state.PhishingController}'`, + ), + ); + return; + } + + delete state.PhishingController.phishing; + delete state.PhishingController.lastFetched; +} + +/** + * Remove obsolete NetworkController state + * + * @param state - The persisted MetaMask state, keyed by controller. + */ +function removeObsoleteNetworkControllerState( + state: Record, +): void { + if (!hasProperty(state, 'NetworkController')) { + return; + } else if (!isObject(state.NetworkController)) { + global.sentry?.captureException?.( + new Error( + `Migration ${version}: Invalid NetworkController state of type '${typeof state.NetworkController}'`, + ), + ); + return; + } + + delete state.NetworkController.network; +} + +/** + * Remove obsolete controller state. + * + * @param state - The persisted MetaMask state, keyed by controller. + */ +function transformState(state: Record): void { + removeObsoleteCurrencyControllerState(state); + removeObsoletePhishingControllerState(state); + removeObsoleteNetworkControllerState(state); +} diff --git a/app/scripts/migrations/120.5.test.ts b/app/scripts/migrations/120.5.test.ts new file mode 100644 index 000000000000..1b19e5e66e95 --- /dev/null +++ b/app/scripts/migrations/120.5.test.ts @@ -0,0 +1,255 @@ +import { cloneDeep } from 'lodash'; +import { migrate, version } from './120.5'; + +const oldVersion = 120.4; + +describe('migration #120.5', () => { + afterEach(() => { + jest.resetAllMocks(); + }); + + it('updates the version metadata', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: {}, + }; + + const newStorage = await migrate(cloneDeep(oldStorage)); + + expect(newStorage.meta).toStrictEqual({ version }); + }); + + it('does nothing if SelectedNetworkController state is not set', async () => { + const oldState = { + NetworkController: { + networkConfigurations: { + 123: {}, + }, + }, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual(oldState); + }); + + it('deletes the SelectedNetworkController state if it is corrupted', async () => { + const oldState = { + NetworkController: { + networkConfigurations: { + 123: {}, + }, + }, + SelectedNetworkController: 'invalid', + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual({ + NetworkController: { + networkConfigurations: { + 123: {}, + }, + }, + }); + }); + + it('deletes the SelectedNetworkController state if it is missing the domains state', async () => { + const oldState = { + NetworkController: { + networkConfigurations: { + 123: {}, + }, + }, + SelectedNetworkController: { + somethingElse: {}, + }, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual({ + NetworkController: { + networkConfigurations: { + 123: {}, + }, + }, + }); + }); + + it('deletes the SelectedNetworkController state if the domains state is corrupted', async () => { + const oldState = { + NetworkController: { + networkConfigurations: { + 123: {}, + }, + }, + SelectedNetworkController: { + domains: 'invalid', + }, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual({ + NetworkController: { + networkConfigurations: { + 123: {}, + }, + }, + }); + }); + + it('deletes the SelectedNetworkController state if NetworkController state is missing', async () => { + const oldState = { + SelectedNetworkController: { + domains: {}, + }, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual({}); + }); + + it('deletes the SelectedNetworkController state if NetworkController state is corrupted', async () => { + const oldState = { + NetworkController: 'invalid', + SelectedNetworkController: { + domains: {}, + }, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual({ + NetworkController: 'invalid', + }); + }); + + it('deletes the SelectedNetworkController state if NetworkController has no networkConfigurations', async () => { + const oldState = { + NetworkController: {}, + SelectedNetworkController: { + domains: {}, + }, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual({ + NetworkController: {}, + }); + }); + + it('deletes the SelectedNetworkController state if NetworkController networkConfigurations state is corrupted', async () => { + const oldState = { + NetworkController: { networkConfigurations: 'invalid' }, + SelectedNetworkController: { + domains: {}, + }, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual({ + NetworkController: { networkConfigurations: 'invalid' }, + }); + }); + + it('does nothing if SelectedNetworkController domains state is empty', async () => { + const oldState = { + NetworkController: { networkConfigurations: {} }, + SelectedNetworkController: { + domains: {}, + }, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual(oldState); + }); + + it('does nothing if SelectedNetworkController domains state is valid', async () => { + const oldState = { + NetworkController: { + networkConfigurations: { + 123: {}, + }, + }, + SelectedNetworkController: { + domains: { + 'example1.test': '123', + 'example2.test': 'mainnet', + 'example3.test': 'goerli', + 'example4.test': 'sepolia', + 'example5.test': 'linea-goerli', + 'example6.test': 'linea-sepolia', + 'example7.test': 'linea-mainnet', + }, + }, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual(oldState); + }); + + it('deletes the SelectedNetworkController state if an invalid networkConfigurationId is found', async () => { + const oldState = { + NetworkController: { + networkConfigurations: { + 123: {}, + }, + }, + SelectedNetworkController: { + domains: { + 'domain.test': '456', + }, + }, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual({ + NetworkController: { + networkConfigurations: { + 123: {}, + }, + }, + }); + }); +}); diff --git a/app/scripts/migrations/120.5.ts b/app/scripts/migrations/120.5.ts new file mode 100644 index 000000000000..11d1fba86664 --- /dev/null +++ b/app/scripts/migrations/120.5.ts @@ -0,0 +1,126 @@ +import { hasProperty, isObject } from '@metamask/utils'; +import { cloneDeep } from 'lodash'; + +type VersionedData = { + meta: { version: number }; + data: Record; +}; + +export const version = 120.5; + +/** + * This migration removes invalid network configuration IDs from the SelectedNetworkController. + * + * @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; +} + +/** + * A list of InfuraNetworkType values from extension v12.0.1 + * This version of the extension uses `@metamask/network-controller@18.1.2`, which in turn uses + * the types from `@metamask/controller-utils@9.1.0` + * + * See https://github.com/MetaMask/core/blob/34542cf6e808f294fd83c7c5f70d1bc7418f8a9e/packages/controller-utils/src/types.ts#L4 + * + * Hard-coded here rather than imported so that this migration continues to work correctly as these + * constants get updated in the future. + */ +const infuraNetworkTypes = [ + 'mainnet', + 'goerli', + 'sepolia', + 'linea-goerli', + 'linea-sepolia', + 'linea-mainnet', +]; + +/** + * Remove invalid network configuration IDs from the SelectedNetworkController. + * + * @param state - The persisted MetaMask state, keyed by controller. + */ +function transformState(state: Record): void { + if (!hasProperty(state, 'SelectedNetworkController')) { + return; + } + if (!isObject(state.SelectedNetworkController)) { + console.error( + `Migration ${version}: Invalid SelectedNetworkController state of type '${typeof state.SelectedNetworkController}'`, + ); + delete state.SelectedNetworkController; + return; + } else if (!hasProperty(state.SelectedNetworkController, 'domains')) { + console.error( + `Migration ${version}: Missing SelectedNetworkController domains state`, + ); + delete state.SelectedNetworkController; + return; + } else if (!isObject(state.SelectedNetworkController.domains)) { + console.error( + `Migration ${version}: Invalid SelectedNetworkController domains state of type '${typeof state + .SelectedNetworkController.domains}'`, + ); + delete state.SelectedNetworkController; + return; + } + + if (!hasProperty(state, 'NetworkController')) { + delete state.SelectedNetworkController; + return; + } else if (!isObject(state.NetworkController)) { + console.error( + new Error( + `Migration ${version}: Invalid NetworkController state of type '${typeof state.NetworkController}'`, + ), + ); + delete state.SelectedNetworkController; + return; + } else if (!hasProperty(state.NetworkController, 'networkConfigurations')) { + delete state.SelectedNetworkController; + return; + } else if (!isObject(state.NetworkController.networkConfigurations)) { + console.error( + new Error( + `Migration ${version}: Invalid NetworkController networkConfigurations state of type '${typeof state.NetworkController}'`, + ), + ); + delete state.SelectedNetworkController; + return; + } + + const validNetworkConfigurationIds = [ + ...infuraNetworkTypes, + ...Object.keys(state.NetworkController.networkConfigurations), + ]; + const domainMappedNetworkConfigurationIds = Object.values( + state.SelectedNetworkController.domains, + ); + + for (const configurationId of domainMappedNetworkConfigurationIds) { + if ( + typeof configurationId !== 'string' || + !validNetworkConfigurationIds.includes(configurationId) + ) { + console.error( + new Error( + `Migration ${version}: Invalid networkConfigurationId found in SelectedNetworkController state: '${configurationId}'`, + ), + ); + delete state.SelectedNetworkController; + return; + } + } +} diff --git a/app/scripts/migrations/120.6.test.ts b/app/scripts/migrations/120.6.test.ts new file mode 100644 index 000000000000..1a7daa3aa225 --- /dev/null +++ b/app/scripts/migrations/120.6.test.ts @@ -0,0 +1,252 @@ +import { cloneDeep } from 'lodash'; +import { migrate, version } from './120.6'; + +const sentryCaptureExceptionMock = jest.fn(); + +global.sentry = { + captureException: sentryCaptureExceptionMock, +}; + +const oldVersion = 120.5; + +describe('migration #120.6', () => { + afterEach(() => { + jest.resetAllMocks(); + }); + + it('updates the version metadata', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: {}, + }; + + const newStorage = await migrate(oldStorage); + + expect(newStorage.meta).toStrictEqual({ version }); + }); + + it('returns state unchanged if TransactionController state is missing', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + PreferencesController: {}, + }, + }; + const oldStorageDataClone = cloneDeep(oldStorage.data); + + const newStorage = await migrate(oldStorage); + + expect(newStorage.data).toStrictEqual(oldStorageDataClone); + }); + + it('reports error and returns state unchanged if TransactionController state is invalid', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + PreferencesController: {}, + TransactionController: 'invalid', + }, + }; + const oldStorageDataClone = cloneDeep(oldStorage.data); + + const newStorage = await migrate(oldStorage); + + expect(sentryCaptureExceptionMock).toHaveBeenCalledWith( + `Migration ${version}: Invalid TransactionController state of type 'string'`, + ); + expect(newStorage.data).toStrictEqual(oldStorageDataClone); + }); + + it('returns state unchanged if transactions are missing', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + PreferencesController: {}, + TransactionController: {}, + }, + }; + const oldStorageDataClone = cloneDeep(oldStorage.data); + + const newStorage = await migrate(oldStorage); + + expect(newStorage.data).toStrictEqual(oldStorageDataClone); + }); + + it('removes transactions property if it is invalid', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + PreferencesController: {}, + TransactionController: { + transactions: 'invalid', + }, + }, + }; + + const newStorage = await migrate(oldStorage); + + expect(newStorage.data).toStrictEqual({ + PreferencesController: {}, + TransactionController: {}, + }); + }); + + it('reports error and returns state unchanged if there is an invalid transaction', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + PreferencesController: {}, + TransactionController: { + transactions: [ + {}, // empty object is valid for the purposes of this migration + 'invalid', + {}, + ], + }, + }, + }; + const oldStorageDataClone = cloneDeep(oldStorage.data); + + const newStorage = await migrate(oldStorage); + + expect(sentryCaptureExceptionMock).toHaveBeenCalledWith( + `Migration ${version}: Invalid transaction of type 'string'`, + ); + expect(newStorage.data).toStrictEqual(oldStorageDataClone); + }); + + it('reports error and returns state unchanged if there is a transaction with an invalid history property', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + PreferencesController: {}, + TransactionController: { + transactions: [ + {}, // empty object is valid for the purposes of this migration + { + history: 'invalid', + }, + {}, + ], + }, + }, + }; + const oldStorageDataClone = cloneDeep(oldStorage.data); + + const newStorage = await migrate(oldStorage); + + expect(sentryCaptureExceptionMock).toHaveBeenCalledWith( + `Migration ${version}: Invalid transaction history of type 'string'`, + ); + expect(newStorage.data).toStrictEqual(oldStorageDataClone); + }); + + it('returns state unchanged if there are no transactions', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + PreferencesController: {}, + TransactionController: { + transactions: [], + }, + }, + }; + const oldStorageDataClone = cloneDeep(oldStorage.data); + + const newStorage = await migrate(oldStorage); + + expect(newStorage.data).toStrictEqual(oldStorageDataClone); + }); + + it('returns state unchanged if there are no transactions with history', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + PreferencesController: {}, + TransactionController: { + transactions: [{}, {}, {}], + }, + }, + }; + const oldStorageDataClone = cloneDeep(oldStorage.data); + + const newStorage = await migrate(oldStorage); + + expect(newStorage.data).toStrictEqual(oldStorageDataClone); + }); + + it('returns state unchanged if there are no transactions with history exceeding max size', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + PreferencesController: {}, + TransactionController: { + transactions: [ + { + history: [...Array(99).keys()], + }, + { + history: [...Array(100).keys()], + }, + { + history: [], + }, + ], + }, + }, + }; + const oldStorageDataClone = cloneDeep(oldStorage.data); + + const newStorage = await migrate(oldStorage); + + expect(newStorage.data).toStrictEqual(oldStorageDataClone); + }); + + it('trims histories exceeding max size', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + PreferencesController: {}, + TransactionController: { + transactions: [ + { + history: [...Array(99).keys()], + }, + { + history: [...Array(100).keys()], + }, + { + history: [...Array(101).keys()], + }, + { + history: [...Array(1000).keys()], + }, + ], + }, + }, + }; + const oldStorageDataClone = cloneDeep(oldStorage.data); + + const newStorage = await migrate(oldStorage); + + expect(newStorage.data).toStrictEqual({ + ...oldStorageDataClone, + TransactionController: { + transactions: [ + { + history: [...Array(99).keys()], + }, + { + history: [...Array(100).keys()], + }, + { + history: [...Array(100).keys()], + }, + { + history: [...Array(100).keys()], + }, + ], + }, + }); + }); +}); diff --git a/app/scripts/migrations/120.6.ts b/app/scripts/migrations/120.6.ts new file mode 100644 index 000000000000..70be368f0c98 --- /dev/null +++ b/app/scripts/migrations/120.6.ts @@ -0,0 +1,117 @@ +import { RuntimeObject, hasProperty, isObject } from '@metamask/utils'; +import { cloneDeep } from 'lodash'; +import log from 'loglevel'; + +type VersionedData = { + meta: { version: number }; + data: Record; +}; + +export const version = 120.6; + +const MAX_TRANSACTION_HISTORY_LENGTH = 100; + +/** + * This migration trims the size of any large transaction histories. This will + * result in some loss of information, but the impact is minor. The lost data + * is only used in the "Activity log" on the transaction details page. + * + * @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): void { + if (!hasProperty(state, 'TransactionController')) { + log.warn(`Migration ${version}: Missing TransactionController state`); + return; + } else if (!isObject(state.TransactionController)) { + global.sentry?.captureException( + `Migration ${version}: Invalid TransactionController state of type '${typeof state.TransactionController}'`, + ); + return; + } + + const transactionControllerState = state.TransactionController; + + if (!hasProperty(transactionControllerState, 'transactions')) { + log.warn( + `Migration ${version}: Missing TransactionController transactions`, + ); + return; + } else if (!Array.isArray(transactionControllerState.transactions)) { + log.error( + new Error( + `Migration ${version}: Invalid TransactionController transactions state of type '${typeof transactionControllerState.transactions}'`, + ), + ); + delete transactionControllerState.transactions; + return; + } + + const { transactions } = transactionControllerState; + const validTransactions = transactions.filter(isObject); + if (transactions.length !== validTransactions.length) { + const invalidTransaction = transactions.find( + (transaction) => !isObject(transaction), + ); + global.sentry?.captureException( + `Migration ${version}: Invalid transaction of type '${typeof invalidTransaction}'`, + ); + return; + } + + const validHistoryTransactions = validTransactions.filter( + hasValidTransactionHistory, + ); + if (validHistoryTransactions.length !== validTransactions.length) { + const invalidTransaction = validTransactions.find( + (transaction) => !hasValidTransactionHistory(transaction), + ); + global.sentry?.captureException( + `Migration ${version}: Invalid transaction history of type '${typeof invalidTransaction?.history}'`, + ); + return; + } + + for (const transaction of validHistoryTransactions) { + if ( + transaction.history && + transaction.history.length > MAX_TRANSACTION_HISTORY_LENGTH + ) { + transaction.history = transaction.history.slice( + 0, + MAX_TRANSACTION_HISTORY_LENGTH, + ); + } + } +} + +/** + * Check whether the given object has a valid `history` property, or no `history` + * property. We just check that it's an array, we don't validate the contents. + * + * @param transaction - The object to validate. + * @returns True if the given object was valid, false otherwise. + */ +function hasValidTransactionHistory( + transaction: RuntimeObject, +): transaction is RuntimeObject & { + history: undefined | unknown[]; +} { + return ( + !hasProperty(transaction, 'history') || Array.isArray(transaction.history) + ); +} diff --git a/app/scripts/migrations/index.js b/app/scripts/migrations/index.js index 837ad260adc7..2422a23ec241 100644 --- a/app/scripts/migrations/index.js +++ b/app/scripts/migrations/index.js @@ -131,6 +131,12 @@ const migrations = [ require('./118'), require('./119'), require('./120'), + require('./120.1'), + require('./120.2'), + // require('./120.3'), Renamed to 120.6, do not re-use this number + require('./120.4'), + require('./120.5'), + require('./120.6'), require('./121'), require('./122'), ]; diff --git a/app/scripts/sentry-install.js b/app/scripts/sentry-install.js index 99d8ddd39327..cbbc03ab10e2 100644 --- a/app/scripts/sentry-install.js +++ b/app/scripts/sentry-install.js @@ -4,25 +4,4 @@ import setupSentry from './lib/setupSentry'; global.stateHooks = {}; // setup sentry error reporting -global.sentry = setupSentry({ - release: process.env.METAMASK_VERSION, - getState: () => global.stateHooks?.getSentryState?.() || {}, -}); - -/** - * As soon as state is available via getSentryState we can start automatic - * session tracking. - */ -async function waitForStateHooks() { - if (global.stateHooks.getSentryState) { - // sentry is not defined in dev mode, so if sentry doesn't exist at this - // point it means that we are in dev mode and do not need to toggle the - // session. Using optional chaining is sufficient to prevent the error in - // development. - await global.sentry?.startSession(); - } else { - setTimeout(waitForStateHooks, 100); - } -} - -waitForStateHooks(); +global.sentry = setupSentry(); diff --git a/app/scripts/ui.js b/app/scripts/ui.js index d7cf205ab486..bb9785660c34 100644 --- a/app/scripts/ui.js +++ b/app/scripts/ui.js @@ -231,6 +231,24 @@ async function start() { } async function queryCurrentActiveTab(windowType) { + // Shims the activeTab for E2E test runs only if the + // "activeTabOrigin" querystring key=value is set + if (process.env.IN_TEST) { + const searchParams = new URLSearchParams(window.location.search); + const mockUrl = searchParams.get('activeTabOrigin'); + if (mockUrl) { + const { origin, protocol } = new URL(mockUrl); + const returnUrl = { + id: 'mock-site', + title: 'Mock Site', + url: mockUrl, + origin, + protocol, + }; + return returnUrl; + } + } + // At the time of writing we only have the `activeTab` permission which means // that this query will only succeed in the popup context (i.e. after a "browserAction") if (windowType !== ENVIRONMENT_TYPE_POPUP) { diff --git a/attribution.txt b/attribution.txt index 364128f620f3..820ffd0021e7 100644 --- a/attribution.txt +++ b/attribution.txt @@ -50,6 +50,34 @@ 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. +****************************** + +aes-js +3.0.0 +The MIT License (MIT) + +Copyright (c) 2015 Richard Moore + +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. + + + ****************************** aes-js @@ -507,33 +535,6 @@ 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 @@ -669,19 +670,6 @@ 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 @@ -1599,7 +1587,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ****************************** @babel/runtime -7.24.5 +7.24.6 MIT License Copyright (c) 2014-present Sebastian McKenzie and other contributors @@ -2410,7 +2398,7 @@ SOFTWARE. ****************************** @blockaid/ppom_release -1.4.6 <> +1.5.1 <> Blockaid BSL License, Version 1.0 (EPL-1.0) Licensor: Blockaid, Inc. @@ -4476,31 +4464,6 @@ 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 @@ -4645,60 +4608,6 @@ 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 @@ -4753,33 +4662,6 @@ 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 @@ -5429,31 +5311,6 @@ 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 @@ -10351,6 +10208,33 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +****************************** + +@ethersproject/json-wallets +5.7.0 +MIT License + +Copyright (c) 2019 Richard Moore + +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. + + ****************************** @ethersproject/keccak256 @@ -10655,6 +10539,33 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +****************************** + +@ethersproject/wallet +5.7.0 +MIT License + +Copyright (c) 2019 Richard Moore + +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. + + ****************************** @ethersproject/web @@ -11285,33 +11196,6 @@ 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 @@ -11535,7 +11419,7 @@ SOFTWARE. ****************************** fast-xml-parser -4.3.4 +4.4.1 MIT License Copyright (c) 2017 Amit Kumar Gupta @@ -11953,30 +11837,6 @@ 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 @@ -12026,31 +11886,6 @@ 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 @@ -12868,7 +12703,7 @@ SOFTWARE. ****************************** @grpc/grpc-js -1.9.14 +1.9.15 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -17163,87 +16998,6 @@ 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 @@ -18189,7 +17943,7 @@ authors: Maarten Zuidhoorn ****************************** @metamask/accounts-controller -14.0.0 +15.0.0 MIT License Copyright (c) 2018 MetaMask @@ -18319,7 +18073,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ****************************** @metamask/assets-controllers -29.0.0 +30.0.0 MIT License Copyright (c) 2018 MetaMask @@ -18488,6 +18242,32 @@ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +****************************** + +@metamask/controller-utils +10.0.0 +MIT License + +Copyright (c) 2018 MetaMask + +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 + + ****************************** @metamask/controller-utils @@ -19250,7 +19030,7 @@ SOFTWARE. ****************************** @metamask-institutional/custody-controller -0.2.27 +0.2.30 MIT License Copyright (c) 2023 ConsenSys Vertical Apps @@ -19277,7 +19057,7 @@ SOFTWARE. ****************************** @metamask-institutional/custody-keyring -2.0.0 +2.0.3 MIT License Copyright (c) 2023 ConsenSys Vertical Apps @@ -19304,7 +19084,7 @@ SOFTWARE. ****************************** @metamask-institutional/extension -0.3.24 +0.3.27 MIT License Copyright (c) 2023 ConsenSys Vertical Apps @@ -19331,7 +19111,7 @@ SOFTWARE. ****************************** @metamask-institutional/institutional-features -1.3.2 +1.3.5 MIT License Copyright (c) 2023 ConsenSys Vertical Apps @@ -19412,7 +19192,7 @@ SOFTWARE. ****************************** @metamask-institutional/sdk -0.1.27 +0.1.30 MIT License Copyright (c) 2023 ConsenSys Vertical Apps @@ -19466,7 +19246,7 @@ SOFTWARE. ****************************** @metamask-institutional/transaction-update -0.2.2 +0.2.5 MIT License Copyright (c) 2023 ConsenSys Vertical Apps @@ -19520,7 +19300,7 @@ SOFTWARE. ****************************** @metamask-institutional/websocket-client -0.2.2 +0.2.5 MIT License Copyright (c) 2023 ConsenSys Vertical Apps @@ -19607,6 +19387,27 @@ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +****************************** + +@metamask/json-rpc-engine +9.0.0 +ISC License + +Copyright (c) 2022 MetaMask + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ****************************** @metamask/json-rpc-middleware-stream @@ -19628,6 +19429,27 @@ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +****************************** + +@metamask/json-rpc-middleware-stream +8.0.0 +ISC License + +Copyright (c) 2020 MetaMask + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ****************************** @metamask/keyring-api @@ -19638,7 +19460,7 @@ authors: undefined ****************************** @metamask/keyring-api -6.3.1 +6.4.0 license: Custom: https://docs.metamask.io/snaps/ authors: undefined @@ -19805,7 +19627,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ****************************** @metamask/name-controller -6.0.1 +8.0.0 MIT License Copyright (c) 2023 MetaMask @@ -20000,7 +19822,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ****************************** @metamask/obs-store -8.1.0 +9.0.0 ISC License Copyright (c) 2020 MetaMask @@ -20020,23 +19842,28 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ****************************** -@metamask/obs-store -9.0.0 -ISC License +@metamask/permission-controller +10.0.0 +MIT License -Copyright (c) 2020 MetaMask +Copyright (c) 2018 MetaMask -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. +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 SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +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 ****************************** @@ -20092,7 +19919,33 @@ If you have any questions, comments or interest in pursuing any other use cases, ****************************** @metamask/phishing-controller -9.0.2 +10.0.0 +MIT License + +Copyright (c) 2018 MetaMask + +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 + + +****************************** + +@metamask/phishing-controller +9.0.4 MIT License Copyright (c) 2018 MetaMask @@ -20251,7 +20104,7 @@ SOFTWARE. ****************************** @metamask/providers -16.1.0 +17.0.0 MIT License Copyright (c) 2020 MetaMask @@ -20278,7 +20131,7 @@ SOFTWARE. ****************************** @metamask/queued-request-controller -0.10.0 +2.0.0 MIT License Copyright (c) 2023 MetaMask @@ -20485,7 +20338,7 @@ authors: Dan Finlay ****************************** @metamask/smart-transactions-controller -10.0.1 +10.1.2 Copyright ConsenSys Software Inc. 2020. All rights reserved. You acknowledge and agree that ConsenSys Software Inc. (“ConsenSys”) (or ConsenSys’s licensors) own all legal right, title and interest in and to the work, software, application, source code, documentation and any other documents in this repository (collectively, the “Program”), including any intellectual property rights which subsist in the Program (whether those rights happen to be registered or not, and wherever in the world those rights may exist), whether in source code or any other form. @@ -20509,7 +20362,31 @@ If you have any questions, comments or interest in pursuing any other use cases, ****************************** @metamask/snaps-controllers -8.2.0 +8.4.0 +Copyright ConsenSys Software Inc. 2021. All rights reserved. + +You acknowledge and agree that ConsenSys Software Inc. (“ConsenSys”) (or ConsenSys’s licensors) own all legal right, title and interest in and to the work, software, application, source code, documentation and any other documents in this repository (collectively, the “Program”), including any intellectual property rights which subsist in the Program (whether those rights happen to be registered or not, and wherever in the world those rights may exist), whether in source code or any other form. + +Subject to the limited license below, you may not (and you may not permit anyone else to) distribute, publish, copy, modify, merge, combine with another program, create derivative works of, reverse engineer, decompile or otherwise attempt to extract the source code of, the Program or any part thereof, except that you may contribute to this repository. + +You are granted a non-exclusive, non-transferable, non-sublicensable license to distribute, publish, copy, modify, merge, combine with another program or create derivative works of the Program (such resulting program, collectively, the “Resulting Program”) solely for Non-Commercial Use as long as you: + 1. give prominent notice (“Notice”) with each copy of the Resulting Program that the Program is used in the Resulting Program and that the Program is the copyright of ConsenSys; and + 2. subject the Resulting Program and any distribution, publication, copy, modification, merger therewith, combination with another program or derivative works thereof to the same Notice requirement and Non-Commercial Use restriction set forth herein. + +“Non-Commercial Use” means each use as described in clauses (1)-(3) below, as reasonably determined by ConsenSys in its sole discretion: + 1. personal use for research, personal study, private entertainment, hobby projects or amateur pursuits, in each case without any anticipated commercial application; + 2. use by any charitable organization, educational institution, public research organization, public safety or health organization, environmental protection organization or government institution; or + 3. the number of monthly active users of the Resulting Program across all versions thereof and platforms globally do not exceed 10,000 at any time. + +You will not use any trade mark, service mark, trade name, logo of ConsenSys or any other company or organization in a way that is likely or intended to cause confusion about the owner or authorized user of such marks, names or logos. + +If you have any questions, comments or interest in pursuing any other use cases, please reach out to us at metamask.license@consensys.net. + + +****************************** + +@metamask/snaps-controllers +9.0.0 Copyright ConsenSys Software Inc. 2021. All rights reserved. You acknowledge and agree that ConsenSys Software Inc. (“ConsenSys”) (or ConsenSys’s licensors) own all legal right, title and interest in and to the work, software, application, source code, documentation and any other documents in this repository (collectively, the “Program”), including any intellectual property rights which subsist in the Program (whether those rights happen to be registered or not, and wherever in the world those rights may exist), whether in source code or any other form. @@ -20533,7 +20410,7 @@ If you have any questions, comments or interest in pursuing any other use cases, ****************************** @metamask/snaps-execution-environments -6.2.0 +6.4.0 Copyright ConsenSys Software Inc. 2022. All rights reserved. You acknowledge and agree that ConsenSys Software Inc. (“ConsenSys”) (or ConsenSys’s licensors) own all legal right, title and interest in and to the work, software, application, source code, documentation and any other documents in this repository (collectively, the “Program”), including any intellectual property rights which subsist in the Program (whether those rights happen to be registered or not, and wherever in the world those rights may exist), whether in source code or any other form. @@ -20766,7 +20643,7 @@ If you have any questions, comments or interest in pursuing any other use cases, ****************************** @metamask/snaps-rpc-methods -9.1.0 +9.1.3 Copyright ConsenSys Software Inc. 2021. All rights reserved. You acknowledge and agree that ConsenSys Software Inc. (“ConsenSys”) (or ConsenSys’s licensors) own all legal right, title and interest in and to the work, software, application, source code, documentation and any other documents in this repository (collectively, the “Program”), including any intellectual property rights which subsist in the Program (whether those rights happen to be registered or not, and wherever in the world those rights may exist), whether in source code or any other form. @@ -20790,7 +20667,7 @@ If you have any questions, comments or interest in pursuing any other use cases, ****************************** @metamask/snaps-sdk -4.3.0 +5.0.0 ISC License Copyright (c) 2023 MetaMask @@ -20811,7 +20688,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ****************************** @metamask/snaps-utils -7.4.0 +7.6.0 ISC License Copyright (c) 2022 MetaMask @@ -20984,64 +20861,6 @@ 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 @@ -23368,47 +23187,6 @@ 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 @@ -23498,7 +23276,7 @@ authors: Kazuhiko Arase ****************************** qrcode.react -1.0.1 +3.1.0 ISC License Copyright (c) 2015, Paul O’Shannessy @@ -23515,53 +23293,8 @@ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -****************************** - -qr.js -0.0.0 -Copyright (c) 2013 Roman Shtylman - -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. - - -****************************** - -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. +This product bundles QR Code Generator, which is available under a +"MIT" license. For details, see src/third-party/qrcodegen. ****************************** @@ -24015,7 +23748,7 @@ SOFTWARE. ****************************** react-redux -7.2.0 +7.2.9 The MIT License (MIT) Copyright (c) 2015-present Dan Abramov @@ -25864,7 +25597,7 @@ Exhibit B - "Incompatible With Secondary Licenses" Notice ****************************** rpc-websockets -7.11.1 +8.0.1 Copyright (c) Elpheria j.d.o.o. rpc-websockets is an Open Source project licensed under the terms of @@ -28942,13 +28675,6 @@ 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 @@ -29118,6 +28844,33 @@ authors: Mohamed Hegazy SOFTWARE +****************************** + +@types/hoist-non-react-statics +3.3.1 + MIT License + + Copyright (c) Microsoft Corporation. All rights reserved. + + 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 + + ****************************** @types/jsonwebtoken @@ -29388,6 +29141,33 @@ authors: Mohamed Hegazy SOFTWARE +****************************** + +@types/react-redux +7.1.33 + MIT License + + Copyright (c) Microsoft Corporation. + + 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 + + ****************************** @types/react-transition-group @@ -31555,34 +31335,7 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH RE ****************************** ws -7.4.6 -The MIT License (MIT) - -Copyright (c) 2011 Einar Otto Stangvik - -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. - - -****************************** - -ws -7.5.9 +7.5.10 The MIT License (MIT) Copyright (c) 2011 Einar Otto Stangvik @@ -31609,7 +31362,7 @@ SOFTWARE. ****************************** ws -8.16.0 +8.17.1 Copyright (c) 2011 Einar Otto Stangvik Copyright (c) 2013 Arnout Kazemier and contributors Copyright (c) 2016 Luigi Pinca and contributors diff --git a/development/README.md b/development/README.md index 21422e72b3de..b9247f66aa6f 100644 --- a/development/README.md +++ b/development/README.md @@ -55,39 +55,22 @@ 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. -## Sentry +## Debugging Sentry -### Debugging in 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. + - Alternatively, create a free Sentry account with a new organization and project. + - The DSN is specified in: `Settings > Projects > [Project Name] > Client Keys (DSN)`. -To debug in a production Sentry environment: +2. To display Sentry logs, include `DEBUG=metamask:sentry:*` in `.metamaskrc`. -- If you have not already got a Sentry account, you can create a free account on [Sentry](https://sentry.io/) -- Create a New Sentry Organization - - If you already have an existing Sentry account and workspace, open the sidebar drop down menu, then click `Switch organization` followed by `Create a new organization` -- Create a New Project -- Copy the `Public Key` and `Project ID` from the Client Keys section under your projects Settings - - Select `Settings` in the sidebar menu, then select `Projects` in the secondary menu. Click your project then select `Client Keys (DSN)` from the secondary menu. Click the `Configure` button on the `Client Keys` page and copy your `Project Id` and `Public Key` -- Add/replace the `SENTRY_DSN` and `SENTRY_DSN_DEV` variables in `.metamaskrc` - ``` - SENTRY_DSN_DEV=https://{SENTRY_PUBLIC_KEY}@sentry.io/{SENTRY_PROJECT_ID} - SENTRY_DSN=https://{SENTRY_PUBLIC_KEY}@sentry.io/{SENTRY_PROJECT_ID} - ``` -- Build the project to the `./dist/` folder with `yarn dist` +3. To display more verbose logs if not in a developer build, include `METAMASK_DEBUG=true` in `.metamaskrc`. -Errors reported whilst using the extension will be displayed in Sentry's `Issues` page. +4. Ensure metrics are enabled during onboarding or via `Settings > Security & privacy > Participate in MetaMetrics`. -To debug in test build we need to comment out the below:-
-- `setupSentry` function comment the return statement in the `app/scripts/lib -/setupSentry.js` https://github.com/MetaMask/metamask-extension/blob/e3c76ca699e94bacfc43793d28291fa5ddf06752/app/scripts/lib/setupSentry.js#L496 -- `setupStateHooks` function set the if condition to true in the `ui -/index.js` https://github.com/MetaMask/metamask-extension/blob/e3c76ca699e94bacfc43793d28291fa5ddf06752/ui/index.js#L242 +5. To test Sentry via the developer options in the UI, include `ENABLE_SETTINGS_PAGE_DEV_OPTIONS=true` in `.metamaskrc`. -How to trigger Sentry error:- -1. Open the background console. -2. Load the extension app and open the developer console. -3. Toggle the `Participate in MetaMetrics` menu option to the `ON` position. -4. Enter `window.stateHooks.throwTestBackgroundError()` into the developer console. -5. There should now be requests sent to sentry in the background network tab. +6. Alternatively, call `window.stateHooks.throwTestError()` or `window.stateHooks.throwTestBackgroundError()` via the UI console. ## Source Maps diff --git a/development/build/scripts.js b/development/build/scripts.js index de01b9665174..c87197bfaf1c 100644 --- a/development/build/scripts.js +++ b/development/build/scripts.js @@ -78,6 +78,8 @@ const scuttlingConfigBase = { encodeURIComponent: '', console: '', crypto: '', + Map: '', + isFinite: '', // {clear/set}Timeout are "this sensitive" clearTimeout: 'window', setTimeout: 'window', @@ -88,6 +90,7 @@ const scuttlingConfigBase = { appState: '', extra: '', stateHooks: '', + nw: '', }, }; diff --git a/development/metamaskbot-build-announce.js b/development/metamaskbot-build-announce.js index f85d64faa887..5d9ef37850a8 100755 --- a/development/metamaskbot-build-announce.js +++ b/development/metamaskbot-build-announce.js @@ -38,6 +38,12 @@ function getPercentageChange(from, to) { return parseFloat(((to - from) / Math.abs(from)) * 100).toFixed(2); } +function getBetaVersion(commitMsg, defaultVersion = VERSION) { + const versionPattern = /Version\s(v\d+\.\d+\.\d+-beta\.\d+)/u; + + return commitMsg.match(versionPattern)?.[1] ?? defaultVersion; +} + async function start() { const { GITHUB_COMMENT_TOKEN, @@ -46,6 +52,7 @@ async function start() { CIRCLE_BUILD_NUM, CIRCLE_WORKFLOW_JOB_ID, PARENT_COMMIT, + SHA1_COMMIT_TITLE, } = process.env; console.log('CIRCLE_PULL_REQUEST', CIRCLE_PULL_REQUEST); @@ -75,7 +82,9 @@ async function start() { return `${platform}`; }) .join(', '); - const betaBuildLinks = `chrome`; + + const betaVersion = getBetaVersion(SHA1_COMMIT_TITLE, VERSION); + const betaBuildLinks = `chrome`; const flaskBuildLinks = platforms .map((platform) => { const url = diff --git a/development/sentry-publish.js b/development/sentry-publish.js index 3645e04b6771..22ff6156a243 100755 --- a/development/sentry-publish.js +++ b/development/sentry-publish.js @@ -37,10 +37,15 @@ async function start() { default: 0, description: 'The MetaMask extension build version', type: 'number', + }) + .option('dist', { + description: + 'The MetaMask extension build distribution (typically for MV2 builds, omit for MV3)', + type: 'string', }), ); - const { buildType, buildVersion, org, project } = argv; + const { buildType, buildVersion, dist, org, project } = argv; process.env.SENTRY_ORG = org; process.env.SENTRY_PROJECT = project; @@ -75,19 +80,17 @@ async function start() { ]); } - // check if version has artifacts or not - const versionHasArtifacts = - versionAlreadyExists && (await checkIfVersionHasArtifacts(version)); - if (versionHasArtifacts) { - console.log( - `Version "${version}" already has artifacts on Sentry, skipping sourcemap upload`, - ); - return; - } - const additionalUploadArgs = []; + if (dist) { + additionalUploadArgs.push('--dist', dist); + } if (buildType !== loadBuildTypesConfig().default) { - additionalUploadArgs.push('--dist-directory', `dist-${buildType}`); + additionalUploadArgs.push( + '--dist-directory', + dist ? `dist-${buildType}-${dist}` : `dist-${buildType}`, + ); + } else if (dist) { + additionalUploadArgs.push('--dist-directory', `dist-${dist}`); } // upload sentry source and sourcemaps await runInShell('./development/sentry-upload-artifacts.sh', [ @@ -109,17 +112,6 @@ async function checkIfVersionExists(version) { ); } -async function checkIfVersionHasArtifacts(version) { - const [artifact] = await runCommand('sentry-cli', [ - 'releases', - 'files', - version, - 'list', - ]); - // When there's no artifacts, we get a response from the shell like this ['', ''] - return artifact?.length > 0; -} - async function doesNotFail(asyncFn) { try { await asyncFn(); diff --git a/development/sentry-upload-artifacts.sh b/development/sentry-upload-artifacts.sh index 7b712f12de31..d55e8172ba3e 100755 --- a/development/sentry-upload-artifacts.sh +++ b/development/sentry-upload-artifacts.sh @@ -23,6 +23,7 @@ Upload JavaScript bundles and sourcemaps to Sentry Options: -h, --help Show help text -r, --release Sentry release to upload files to (defaults to 'VERSION' environment variable) + -d, --dist Sentry distribution (typically used to identify MV2 builds) --dist-directory The 'dist' directory to use. Defaults to 'dist'. EOF } @@ -30,13 +31,15 @@ EOF function upload_sourcemaps { local release="${1}"; shift local dist_directory="${1}"; shift + local dist="${1}"; shift - sentry-cli releases files "${release}" upload-sourcemaps "${dist_directory}"/chrome/ "${dist_directory}"/sourcemaps/ --rewrite --url-prefix '/metamask' + sentry-cli releases files "${release}" upload-sourcemaps --dist "${dist}" "${dist_directory}"/chrome/ "${dist_directory}"/sourcemaps/ --rewrite --url-prefix '/metamask' } function main { local release=VERSION local dist_directory='dist' + local dist="mv3" while :; do case "${1-default}" in @@ -54,6 +57,16 @@ function main { release="${2}" shift ;; + -d|--dist) + if [[ -z $2 ]] + then + printf "'dist' option requires an argument.\\n" >&2 + printf '%s\n' "${__SEE_HELP_MESSAGE__}" >&2 + exit 1 + fi + dist="${2}" + shift + ;; --dist-directory) if [[ -z $2 ]] then @@ -83,7 +96,7 @@ function main { fi printf 'uploading source files and sourcemaps for Sentry release "%s"...\n' "${release}" - upload_sourcemaps "${release}" "${dist_directory}" + upload_sourcemaps "${release}" "${dist_directory}" "${dist}" printf 'all done!\n' } diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index 4506020c2bee..5b6e9ca3577f 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -2438,6 +2438,7 @@ "@metamask/snaps-controllers>@metamask/base-controller": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, "@metamask/snaps-controllers>@metamask/json-rpc-middleware-stream": true, + "@metamask/snaps-controllers>@metamask/snaps-utils": true, "@metamask/snaps-controllers>@xstate/fsm": true, "@metamask/snaps-controllers>concat-stream": true, "@metamask/snaps-controllers>get-npm-tarball-url": true, @@ -2446,7 +2447,6 @@ "@metamask/snaps-controllers>tar-stream": true, "@metamask/snaps-rpc-methods": true, "@metamask/snaps-sdk": true, - "@metamask/snaps-utils": true, "@metamask/snaps-utils>@metamask/snaps-registry": true, "@metamask/utils": true, "browserify>browserify-zlib": true, @@ -2484,6 +2484,41 @@ "readable-stream": true } }, + "@metamask/snaps-controllers>@metamask/snaps-utils": { + "globals": { + "File": true, + "FileReader": true, + "TextDecoder": true, + "TextEncoder": true, + "URL": true, + "console.error": true, + "console.log": true, + "console.warn": true, + "crypto": true, + "document.body.appendChild": true, + "document.createElement": true, + "fetch": true + }, + "packages": { + "@metamask/permission-controller": true, + "@metamask/rpc-errors": true, + "@metamask/snaps-sdk": true, + "@metamask/snaps-sdk>@metamask/key-tree": true, + "@metamask/snaps-utils>@metamask/slip44": true, + "@metamask/snaps-utils>cron-parser": true, + "@metamask/snaps-utils>fast-json-stable-stringify": true, + "@metamask/snaps-utils>fast-xml-parser": true, + "@metamask/snaps-utils>marked": true, + "@metamask/snaps-utils>rfdc": true, + "@metamask/snaps-utils>validate-npm-package-name": true, + "@metamask/utils": true, + "@metamask/utils>@scure/base": true, + "@noble/hashes": true, + "chalk": true, + "semver": true, + "superstruct": true + } + }, "@metamask/snaps-controllers>concat-stream": { "packages": { "browserify>buffer": true, @@ -2535,22 +2570,93 @@ }, "packages": { "@metamask/post-message-stream": true, + "@metamask/snaps-execution-environments>@metamask/snaps-utils": true, "@metamask/snaps-utils": true, "@metamask/utils": true } }, + "@metamask/snaps-execution-environments>@metamask/snaps-utils": { + "globals": { + "File": true, + "FileReader": true, + "TextDecoder": true, + "TextEncoder": true, + "URL": true, + "console.error": true, + "console.log": true, + "console.warn": true, + "crypto": true, + "document.body.appendChild": true, + "document.createElement": true, + "fetch": true + }, + "packages": { + "@metamask/permission-controller": true, + "@metamask/rpc-errors": true, + "@metamask/snaps-sdk": true, + "@metamask/snaps-sdk>@metamask/key-tree": true, + "@metamask/snaps-utils>@metamask/slip44": true, + "@metamask/snaps-utils>cron-parser": true, + "@metamask/snaps-utils>fast-json-stable-stringify": true, + "@metamask/snaps-utils>fast-xml-parser": true, + "@metamask/snaps-utils>marked": true, + "@metamask/snaps-utils>rfdc": true, + "@metamask/snaps-utils>validate-npm-package-name": true, + "@metamask/utils": true, + "@metamask/utils>@scure/base": true, + "@noble/hashes": true, + "chalk": true, + "semver": true, + "superstruct": true + } + }, "@metamask/snaps-rpc-methods": { "packages": { "@metamask/permission-controller": true, "@metamask/rpc-errors": true, + "@metamask/snaps-rpc-methods>@metamask/snaps-utils": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, - "@metamask/snaps-utils": true, "@metamask/utils": true, "@noble/hashes": true, "superstruct": true } }, + "@metamask/snaps-rpc-methods>@metamask/snaps-utils": { + "globals": { + "File": true, + "FileReader": true, + "TextDecoder": true, + "TextEncoder": true, + "URL": true, + "console.error": true, + "console.log": true, + "console.warn": true, + "crypto": true, + "document.body.appendChild": true, + "document.createElement": true, + "fetch": true + }, + "packages": { + "@metamask/permission-controller": true, + "@metamask/rpc-errors": true, + "@metamask/snaps-sdk": true, + "@metamask/snaps-sdk>@metamask/key-tree": true, + "@metamask/snaps-utils>@metamask/slip44": true, + "@metamask/snaps-utils>cron-parser": true, + "@metamask/snaps-utils>fast-json-stable-stringify": true, + "@metamask/snaps-utils>fast-xml-parser": true, + "@metamask/snaps-utils>marked": true, + "@metamask/snaps-utils>rfdc": true, + "@metamask/snaps-utils>validate-npm-package-name": true, + "@metamask/utils": true, + "@metamask/utils>@scure/base": true, + "@noble/hashes": true, + "chalk": true, + "semver": true, + "superstruct": true + } + }, "@metamask/snaps-sdk": { "globals": { "fetch": true @@ -2896,108 +3002,146 @@ }, "@sentry/browser": { "globals": { - "TextDecoder": true, - "TextEncoder": true, - "XMLHttpRequest": true, + "PerformanceObserver.supportedEntryTypes.includes": true, + "Request": true, + "URL": true, + "XMLHttpRequest.prototype": true, "__SENTRY_DEBUG__": true, "__SENTRY_RELEASE__": true, + "addEventListener": true, + "console.error": true, "indexedDB.open": true, + "performance.timeOrigin": true, "setTimeout": true }, "packages": { - "@sentry/browser>@sentry-internal/tracing": true, + "@sentry/browser>@sentry-internal/browser-utils": true, + "@sentry/browser>@sentry-internal/feedback": true, + "@sentry/browser>@sentry-internal/replay": true, + "@sentry/browser>@sentry-internal/replay-canvas": true, "@sentry/browser>@sentry/core": true, - "@sentry/browser>@sentry/replay": true, "@sentry/utils": true } }, - "@sentry/browser>@sentry-internal/tracing": { + "@sentry/browser>@sentry-internal/browser-utils": { "globals": { - "Headers": true, + "PerformanceEventTiming.prototype": true, "PerformanceObserver": true, - "Request": true, + "XMLHttpRequest.prototype": true, "__SENTRY_DEBUG__": true, "addEventListener": true, - "performance.getEntriesByType": true, - "removeEventListener": true + "clearTimeout": true, + "performance": true, + "removeEventListener": true, + "setTimeout": true }, "packages": { "@sentry/browser>@sentry/core": true, "@sentry/utils": true } }, - "@sentry/browser>@sentry/core": { + "@sentry/browser>@sentry-internal/feedback": { "globals": { + "FormData": true, + "HTMLFormElement": true, "__SENTRY_DEBUG__": true, - "__SENTRY_TRACING__": true, - "clearInterval": true, + "cancelAnimationFrame": true, "clearTimeout": true, - "console.warn": true, - "setInterval": true, + "document.createElement": true, + "document.createElementNS": true, + "document.createTextNode": true, + "isSecureContext": true, + "requestAnimationFrame": true, "setTimeout": true }, "packages": { + "@sentry/browser>@sentry/core": true, "@sentry/utils": true } }, - "@sentry/browser>@sentry/replay": { + "@sentry/browser>@sentry-internal/replay": { "globals": { "Blob": true, "CSSConditionRule": true, "CSSGroupingRule": true, "CSSMediaRule": true, + "CSSRule": true, "CSSSupportsRule": true, + "Document": true, "DragEvent": true, "Element": true, "FormData": true, - "HTMLCanvasElement": true, - "HTMLElement.prototype": true, + "HTMLElement": true, "HTMLFormElement": true, - "HTMLImageElement": true, - "HTMLInputElement.prototype": true, - "HTMLOptionElement.prototype": true, - "HTMLSelectElement.prototype": true, - "HTMLTextAreaElement.prototype": true, "Headers": true, - "ImageData": true, "MouseEvent": true, "MutationObserver": true, + "Node.DOCUMENT_FRAGMENT_NODE": true, "Node.prototype.contains": true, - "PerformanceObserver": true, + "PointerEvent": true, "TextEncoder": true, "URL": true, "URLSearchParams": true, "Worker": true, - "Zone": true, + "__RRWEB_EXCLUDE_IFRAME__": true, + "__RRWEB_EXCLUDE_SHADOW_DOM__": true, "__SENTRY_DEBUG__": true, + "__SENTRY_EXCLUDE_REPLAY_WORKER__": true, "__rrMutationObserver": true, + "addEventListener": true, "clearTimeout": true, + "console.debug": true, "console.error": true, "console.warn": true, + "customElements.get": true, "document": true, "innerHeight": true, "innerWidth": true, "location.href": true, - "pageXOffset": true, - "pageYOffset": true, - "requestAnimationFrame": true, - "setTimeout": true + "location.origin": true, + "parent": true }, "packages": { + "@sentry/browser>@sentry-internal/browser-utils": true, "@sentry/browser>@sentry/core": true, - "@sentry/utils": true, - "browserify>process": true + "@sentry/utils": true } }, - "@sentry/integrations": { + "@sentry/browser>@sentry-internal/replay-canvas": { "globals": { + "Blob": true, + "HTMLCanvasElement": true, + "HTMLImageElement": true, + "ImageData": true, + "URL.createObjectURL": true, + "WeakRef": true, + "Worker": true, + "cancelAnimationFrame": true, + "console.error": true, + "createImageBitmap": true, + "document": true + }, + "packages": { + "@sentry/browser>@sentry/core": true, + "@sentry/utils": true + } + }, + "@sentry/browser>@sentry/core": { + "globals": { + "Headers": true, "Request": true, + "URL": true, "__SENTRY_DEBUG__": true, - "console.log": true + "__SENTRY_TRACING__": true, + "clearInterval": true, + "clearTimeout": true, + "console.log": true, + "console.warn": true, + "setInterval": true, + "setTimeout": true }, "packages": { - "@sentry/utils": true, - "localforage": true + "@sentry/utils": true } }, "@sentry/utils": { @@ -3005,20 +3149,23 @@ "CustomEvent": true, "DOMError": true, "DOMException": true, + "EdgeRuntime": true, "Element": true, "ErrorEvent": true, "Event": true, + "HTMLElement": true, "Headers": true, "Request": true, "Response": true, + "TextDecoder": true, "TextEncoder": true, "URL": true, - "XMLHttpRequest.prototype": true, "__SENTRY_BROWSER_BUNDLE__": true, "__SENTRY_DEBUG__": true, "clearTimeout": true, "console.error": true, "document": true, + "setInterval": true, "setTimeout": true }, "packages": { diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index 4506020c2bee..5b6e9ca3577f 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -2438,6 +2438,7 @@ "@metamask/snaps-controllers>@metamask/base-controller": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, "@metamask/snaps-controllers>@metamask/json-rpc-middleware-stream": true, + "@metamask/snaps-controllers>@metamask/snaps-utils": true, "@metamask/snaps-controllers>@xstate/fsm": true, "@metamask/snaps-controllers>concat-stream": true, "@metamask/snaps-controllers>get-npm-tarball-url": true, @@ -2446,7 +2447,6 @@ "@metamask/snaps-controllers>tar-stream": true, "@metamask/snaps-rpc-methods": true, "@metamask/snaps-sdk": true, - "@metamask/snaps-utils": true, "@metamask/snaps-utils>@metamask/snaps-registry": true, "@metamask/utils": true, "browserify>browserify-zlib": true, @@ -2484,6 +2484,41 @@ "readable-stream": true } }, + "@metamask/snaps-controllers>@metamask/snaps-utils": { + "globals": { + "File": true, + "FileReader": true, + "TextDecoder": true, + "TextEncoder": true, + "URL": true, + "console.error": true, + "console.log": true, + "console.warn": true, + "crypto": true, + "document.body.appendChild": true, + "document.createElement": true, + "fetch": true + }, + "packages": { + "@metamask/permission-controller": true, + "@metamask/rpc-errors": true, + "@metamask/snaps-sdk": true, + "@metamask/snaps-sdk>@metamask/key-tree": true, + "@metamask/snaps-utils>@metamask/slip44": true, + "@metamask/snaps-utils>cron-parser": true, + "@metamask/snaps-utils>fast-json-stable-stringify": true, + "@metamask/snaps-utils>fast-xml-parser": true, + "@metamask/snaps-utils>marked": true, + "@metamask/snaps-utils>rfdc": true, + "@metamask/snaps-utils>validate-npm-package-name": true, + "@metamask/utils": true, + "@metamask/utils>@scure/base": true, + "@noble/hashes": true, + "chalk": true, + "semver": true, + "superstruct": true + } + }, "@metamask/snaps-controllers>concat-stream": { "packages": { "browserify>buffer": true, @@ -2535,22 +2570,93 @@ }, "packages": { "@metamask/post-message-stream": true, + "@metamask/snaps-execution-environments>@metamask/snaps-utils": true, "@metamask/snaps-utils": true, "@metamask/utils": true } }, + "@metamask/snaps-execution-environments>@metamask/snaps-utils": { + "globals": { + "File": true, + "FileReader": true, + "TextDecoder": true, + "TextEncoder": true, + "URL": true, + "console.error": true, + "console.log": true, + "console.warn": true, + "crypto": true, + "document.body.appendChild": true, + "document.createElement": true, + "fetch": true + }, + "packages": { + "@metamask/permission-controller": true, + "@metamask/rpc-errors": true, + "@metamask/snaps-sdk": true, + "@metamask/snaps-sdk>@metamask/key-tree": true, + "@metamask/snaps-utils>@metamask/slip44": true, + "@metamask/snaps-utils>cron-parser": true, + "@metamask/snaps-utils>fast-json-stable-stringify": true, + "@metamask/snaps-utils>fast-xml-parser": true, + "@metamask/snaps-utils>marked": true, + "@metamask/snaps-utils>rfdc": true, + "@metamask/snaps-utils>validate-npm-package-name": true, + "@metamask/utils": true, + "@metamask/utils>@scure/base": true, + "@noble/hashes": true, + "chalk": true, + "semver": true, + "superstruct": true + } + }, "@metamask/snaps-rpc-methods": { "packages": { "@metamask/permission-controller": true, "@metamask/rpc-errors": true, + "@metamask/snaps-rpc-methods>@metamask/snaps-utils": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, - "@metamask/snaps-utils": true, "@metamask/utils": true, "@noble/hashes": true, "superstruct": true } }, + "@metamask/snaps-rpc-methods>@metamask/snaps-utils": { + "globals": { + "File": true, + "FileReader": true, + "TextDecoder": true, + "TextEncoder": true, + "URL": true, + "console.error": true, + "console.log": true, + "console.warn": true, + "crypto": true, + "document.body.appendChild": true, + "document.createElement": true, + "fetch": true + }, + "packages": { + "@metamask/permission-controller": true, + "@metamask/rpc-errors": true, + "@metamask/snaps-sdk": true, + "@metamask/snaps-sdk>@metamask/key-tree": true, + "@metamask/snaps-utils>@metamask/slip44": true, + "@metamask/snaps-utils>cron-parser": true, + "@metamask/snaps-utils>fast-json-stable-stringify": true, + "@metamask/snaps-utils>fast-xml-parser": true, + "@metamask/snaps-utils>marked": true, + "@metamask/snaps-utils>rfdc": true, + "@metamask/snaps-utils>validate-npm-package-name": true, + "@metamask/utils": true, + "@metamask/utils>@scure/base": true, + "@noble/hashes": true, + "chalk": true, + "semver": true, + "superstruct": true + } + }, "@metamask/snaps-sdk": { "globals": { "fetch": true @@ -2896,108 +3002,146 @@ }, "@sentry/browser": { "globals": { - "TextDecoder": true, - "TextEncoder": true, - "XMLHttpRequest": true, + "PerformanceObserver.supportedEntryTypes.includes": true, + "Request": true, + "URL": true, + "XMLHttpRequest.prototype": true, "__SENTRY_DEBUG__": true, "__SENTRY_RELEASE__": true, + "addEventListener": true, + "console.error": true, "indexedDB.open": true, + "performance.timeOrigin": true, "setTimeout": true }, "packages": { - "@sentry/browser>@sentry-internal/tracing": true, + "@sentry/browser>@sentry-internal/browser-utils": true, + "@sentry/browser>@sentry-internal/feedback": true, + "@sentry/browser>@sentry-internal/replay": true, + "@sentry/browser>@sentry-internal/replay-canvas": true, "@sentry/browser>@sentry/core": true, - "@sentry/browser>@sentry/replay": true, "@sentry/utils": true } }, - "@sentry/browser>@sentry-internal/tracing": { + "@sentry/browser>@sentry-internal/browser-utils": { "globals": { - "Headers": true, + "PerformanceEventTiming.prototype": true, "PerformanceObserver": true, - "Request": true, + "XMLHttpRequest.prototype": true, "__SENTRY_DEBUG__": true, "addEventListener": true, - "performance.getEntriesByType": true, - "removeEventListener": true + "clearTimeout": true, + "performance": true, + "removeEventListener": true, + "setTimeout": true }, "packages": { "@sentry/browser>@sentry/core": true, "@sentry/utils": true } }, - "@sentry/browser>@sentry/core": { + "@sentry/browser>@sentry-internal/feedback": { "globals": { + "FormData": true, + "HTMLFormElement": true, "__SENTRY_DEBUG__": true, - "__SENTRY_TRACING__": true, - "clearInterval": true, + "cancelAnimationFrame": true, "clearTimeout": true, - "console.warn": true, - "setInterval": true, + "document.createElement": true, + "document.createElementNS": true, + "document.createTextNode": true, + "isSecureContext": true, + "requestAnimationFrame": true, "setTimeout": true }, "packages": { + "@sentry/browser>@sentry/core": true, "@sentry/utils": true } }, - "@sentry/browser>@sentry/replay": { + "@sentry/browser>@sentry-internal/replay": { "globals": { "Blob": true, "CSSConditionRule": true, "CSSGroupingRule": true, "CSSMediaRule": true, + "CSSRule": true, "CSSSupportsRule": true, + "Document": true, "DragEvent": true, "Element": true, "FormData": true, - "HTMLCanvasElement": true, - "HTMLElement.prototype": true, + "HTMLElement": true, "HTMLFormElement": true, - "HTMLImageElement": true, - "HTMLInputElement.prototype": true, - "HTMLOptionElement.prototype": true, - "HTMLSelectElement.prototype": true, - "HTMLTextAreaElement.prototype": true, "Headers": true, - "ImageData": true, "MouseEvent": true, "MutationObserver": true, + "Node.DOCUMENT_FRAGMENT_NODE": true, "Node.prototype.contains": true, - "PerformanceObserver": true, + "PointerEvent": true, "TextEncoder": true, "URL": true, "URLSearchParams": true, "Worker": true, - "Zone": true, + "__RRWEB_EXCLUDE_IFRAME__": true, + "__RRWEB_EXCLUDE_SHADOW_DOM__": true, "__SENTRY_DEBUG__": true, + "__SENTRY_EXCLUDE_REPLAY_WORKER__": true, "__rrMutationObserver": true, + "addEventListener": true, "clearTimeout": true, + "console.debug": true, "console.error": true, "console.warn": true, + "customElements.get": true, "document": true, "innerHeight": true, "innerWidth": true, "location.href": true, - "pageXOffset": true, - "pageYOffset": true, - "requestAnimationFrame": true, - "setTimeout": true + "location.origin": true, + "parent": true }, "packages": { + "@sentry/browser>@sentry-internal/browser-utils": true, "@sentry/browser>@sentry/core": true, - "@sentry/utils": true, - "browserify>process": true + "@sentry/utils": true } }, - "@sentry/integrations": { + "@sentry/browser>@sentry-internal/replay-canvas": { "globals": { + "Blob": true, + "HTMLCanvasElement": true, + "HTMLImageElement": true, + "ImageData": true, + "URL.createObjectURL": true, + "WeakRef": true, + "Worker": true, + "cancelAnimationFrame": true, + "console.error": true, + "createImageBitmap": true, + "document": true + }, + "packages": { + "@sentry/browser>@sentry/core": true, + "@sentry/utils": true + } + }, + "@sentry/browser>@sentry/core": { + "globals": { + "Headers": true, "Request": true, + "URL": true, "__SENTRY_DEBUG__": true, - "console.log": true + "__SENTRY_TRACING__": true, + "clearInterval": true, + "clearTimeout": true, + "console.log": true, + "console.warn": true, + "setInterval": true, + "setTimeout": true }, "packages": { - "@sentry/utils": true, - "localforage": true + "@sentry/utils": true } }, "@sentry/utils": { @@ -3005,20 +3149,23 @@ "CustomEvent": true, "DOMError": true, "DOMException": true, + "EdgeRuntime": true, "Element": true, "ErrorEvent": true, "Event": true, + "HTMLElement": true, "Headers": true, "Request": true, "Response": true, + "TextDecoder": true, "TextEncoder": true, "URL": true, - "XMLHttpRequest.prototype": true, "__SENTRY_BROWSER_BUNDLE__": true, "__SENTRY_DEBUG__": true, "clearTimeout": true, "console.error": true, "document": true, + "setInterval": true, "setTimeout": true }, "packages": { diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index 4506020c2bee..5b6e9ca3577f 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -2438,6 +2438,7 @@ "@metamask/snaps-controllers>@metamask/base-controller": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, "@metamask/snaps-controllers>@metamask/json-rpc-middleware-stream": true, + "@metamask/snaps-controllers>@metamask/snaps-utils": true, "@metamask/snaps-controllers>@xstate/fsm": true, "@metamask/snaps-controllers>concat-stream": true, "@metamask/snaps-controllers>get-npm-tarball-url": true, @@ -2446,7 +2447,6 @@ "@metamask/snaps-controllers>tar-stream": true, "@metamask/snaps-rpc-methods": true, "@metamask/snaps-sdk": true, - "@metamask/snaps-utils": true, "@metamask/snaps-utils>@metamask/snaps-registry": true, "@metamask/utils": true, "browserify>browserify-zlib": true, @@ -2484,6 +2484,41 @@ "readable-stream": true } }, + "@metamask/snaps-controllers>@metamask/snaps-utils": { + "globals": { + "File": true, + "FileReader": true, + "TextDecoder": true, + "TextEncoder": true, + "URL": true, + "console.error": true, + "console.log": true, + "console.warn": true, + "crypto": true, + "document.body.appendChild": true, + "document.createElement": true, + "fetch": true + }, + "packages": { + "@metamask/permission-controller": true, + "@metamask/rpc-errors": true, + "@metamask/snaps-sdk": true, + "@metamask/snaps-sdk>@metamask/key-tree": true, + "@metamask/snaps-utils>@metamask/slip44": true, + "@metamask/snaps-utils>cron-parser": true, + "@metamask/snaps-utils>fast-json-stable-stringify": true, + "@metamask/snaps-utils>fast-xml-parser": true, + "@metamask/snaps-utils>marked": true, + "@metamask/snaps-utils>rfdc": true, + "@metamask/snaps-utils>validate-npm-package-name": true, + "@metamask/utils": true, + "@metamask/utils>@scure/base": true, + "@noble/hashes": true, + "chalk": true, + "semver": true, + "superstruct": true + } + }, "@metamask/snaps-controllers>concat-stream": { "packages": { "browserify>buffer": true, @@ -2535,22 +2570,93 @@ }, "packages": { "@metamask/post-message-stream": true, + "@metamask/snaps-execution-environments>@metamask/snaps-utils": true, "@metamask/snaps-utils": true, "@metamask/utils": true } }, + "@metamask/snaps-execution-environments>@metamask/snaps-utils": { + "globals": { + "File": true, + "FileReader": true, + "TextDecoder": true, + "TextEncoder": true, + "URL": true, + "console.error": true, + "console.log": true, + "console.warn": true, + "crypto": true, + "document.body.appendChild": true, + "document.createElement": true, + "fetch": true + }, + "packages": { + "@metamask/permission-controller": true, + "@metamask/rpc-errors": true, + "@metamask/snaps-sdk": true, + "@metamask/snaps-sdk>@metamask/key-tree": true, + "@metamask/snaps-utils>@metamask/slip44": true, + "@metamask/snaps-utils>cron-parser": true, + "@metamask/snaps-utils>fast-json-stable-stringify": true, + "@metamask/snaps-utils>fast-xml-parser": true, + "@metamask/snaps-utils>marked": true, + "@metamask/snaps-utils>rfdc": true, + "@metamask/snaps-utils>validate-npm-package-name": true, + "@metamask/utils": true, + "@metamask/utils>@scure/base": true, + "@noble/hashes": true, + "chalk": true, + "semver": true, + "superstruct": true + } + }, "@metamask/snaps-rpc-methods": { "packages": { "@metamask/permission-controller": true, "@metamask/rpc-errors": true, + "@metamask/snaps-rpc-methods>@metamask/snaps-utils": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, - "@metamask/snaps-utils": true, "@metamask/utils": true, "@noble/hashes": true, "superstruct": true } }, + "@metamask/snaps-rpc-methods>@metamask/snaps-utils": { + "globals": { + "File": true, + "FileReader": true, + "TextDecoder": true, + "TextEncoder": true, + "URL": true, + "console.error": true, + "console.log": true, + "console.warn": true, + "crypto": true, + "document.body.appendChild": true, + "document.createElement": true, + "fetch": true + }, + "packages": { + "@metamask/permission-controller": true, + "@metamask/rpc-errors": true, + "@metamask/snaps-sdk": true, + "@metamask/snaps-sdk>@metamask/key-tree": true, + "@metamask/snaps-utils>@metamask/slip44": true, + "@metamask/snaps-utils>cron-parser": true, + "@metamask/snaps-utils>fast-json-stable-stringify": true, + "@metamask/snaps-utils>fast-xml-parser": true, + "@metamask/snaps-utils>marked": true, + "@metamask/snaps-utils>rfdc": true, + "@metamask/snaps-utils>validate-npm-package-name": true, + "@metamask/utils": true, + "@metamask/utils>@scure/base": true, + "@noble/hashes": true, + "chalk": true, + "semver": true, + "superstruct": true + } + }, "@metamask/snaps-sdk": { "globals": { "fetch": true @@ -2896,108 +3002,146 @@ }, "@sentry/browser": { "globals": { - "TextDecoder": true, - "TextEncoder": true, - "XMLHttpRequest": true, + "PerformanceObserver.supportedEntryTypes.includes": true, + "Request": true, + "URL": true, + "XMLHttpRequest.prototype": true, "__SENTRY_DEBUG__": true, "__SENTRY_RELEASE__": true, + "addEventListener": true, + "console.error": true, "indexedDB.open": true, + "performance.timeOrigin": true, "setTimeout": true }, "packages": { - "@sentry/browser>@sentry-internal/tracing": true, + "@sentry/browser>@sentry-internal/browser-utils": true, + "@sentry/browser>@sentry-internal/feedback": true, + "@sentry/browser>@sentry-internal/replay": true, + "@sentry/browser>@sentry-internal/replay-canvas": true, "@sentry/browser>@sentry/core": true, - "@sentry/browser>@sentry/replay": true, "@sentry/utils": true } }, - "@sentry/browser>@sentry-internal/tracing": { + "@sentry/browser>@sentry-internal/browser-utils": { "globals": { - "Headers": true, + "PerformanceEventTiming.prototype": true, "PerformanceObserver": true, - "Request": true, + "XMLHttpRequest.prototype": true, "__SENTRY_DEBUG__": true, "addEventListener": true, - "performance.getEntriesByType": true, - "removeEventListener": true + "clearTimeout": true, + "performance": true, + "removeEventListener": true, + "setTimeout": true }, "packages": { "@sentry/browser>@sentry/core": true, "@sentry/utils": true } }, - "@sentry/browser>@sentry/core": { + "@sentry/browser>@sentry-internal/feedback": { "globals": { + "FormData": true, + "HTMLFormElement": true, "__SENTRY_DEBUG__": true, - "__SENTRY_TRACING__": true, - "clearInterval": true, + "cancelAnimationFrame": true, "clearTimeout": true, - "console.warn": true, - "setInterval": true, + "document.createElement": true, + "document.createElementNS": true, + "document.createTextNode": true, + "isSecureContext": true, + "requestAnimationFrame": true, "setTimeout": true }, "packages": { + "@sentry/browser>@sentry/core": true, "@sentry/utils": true } }, - "@sentry/browser>@sentry/replay": { + "@sentry/browser>@sentry-internal/replay": { "globals": { "Blob": true, "CSSConditionRule": true, "CSSGroupingRule": true, "CSSMediaRule": true, + "CSSRule": true, "CSSSupportsRule": true, + "Document": true, "DragEvent": true, "Element": true, "FormData": true, - "HTMLCanvasElement": true, - "HTMLElement.prototype": true, + "HTMLElement": true, "HTMLFormElement": true, - "HTMLImageElement": true, - "HTMLInputElement.prototype": true, - "HTMLOptionElement.prototype": true, - "HTMLSelectElement.prototype": true, - "HTMLTextAreaElement.prototype": true, "Headers": true, - "ImageData": true, "MouseEvent": true, "MutationObserver": true, + "Node.DOCUMENT_FRAGMENT_NODE": true, "Node.prototype.contains": true, - "PerformanceObserver": true, + "PointerEvent": true, "TextEncoder": true, "URL": true, "URLSearchParams": true, "Worker": true, - "Zone": true, + "__RRWEB_EXCLUDE_IFRAME__": true, + "__RRWEB_EXCLUDE_SHADOW_DOM__": true, "__SENTRY_DEBUG__": true, + "__SENTRY_EXCLUDE_REPLAY_WORKER__": true, "__rrMutationObserver": true, + "addEventListener": true, "clearTimeout": true, + "console.debug": true, "console.error": true, "console.warn": true, + "customElements.get": true, "document": true, "innerHeight": true, "innerWidth": true, "location.href": true, - "pageXOffset": true, - "pageYOffset": true, - "requestAnimationFrame": true, - "setTimeout": true + "location.origin": true, + "parent": true }, "packages": { + "@sentry/browser>@sentry-internal/browser-utils": true, "@sentry/browser>@sentry/core": true, - "@sentry/utils": true, - "browserify>process": true + "@sentry/utils": true } }, - "@sentry/integrations": { + "@sentry/browser>@sentry-internal/replay-canvas": { "globals": { + "Blob": true, + "HTMLCanvasElement": true, + "HTMLImageElement": true, + "ImageData": true, + "URL.createObjectURL": true, + "WeakRef": true, + "Worker": true, + "cancelAnimationFrame": true, + "console.error": true, + "createImageBitmap": true, + "document": true + }, + "packages": { + "@sentry/browser>@sentry/core": true, + "@sentry/utils": true + } + }, + "@sentry/browser>@sentry/core": { + "globals": { + "Headers": true, "Request": true, + "URL": true, "__SENTRY_DEBUG__": true, - "console.log": true + "__SENTRY_TRACING__": true, + "clearInterval": true, + "clearTimeout": true, + "console.log": true, + "console.warn": true, + "setInterval": true, + "setTimeout": true }, "packages": { - "@sentry/utils": true, - "localforage": true + "@sentry/utils": true } }, "@sentry/utils": { @@ -3005,20 +3149,23 @@ "CustomEvent": true, "DOMError": true, "DOMException": true, + "EdgeRuntime": true, "Element": true, "ErrorEvent": true, "Event": true, + "HTMLElement": true, "Headers": true, "Request": true, "Response": true, + "TextDecoder": true, "TextEncoder": true, "URL": true, - "XMLHttpRequest.prototype": true, "__SENTRY_BROWSER_BUNDLE__": true, "__SENTRY_DEBUG__": true, "clearTimeout": true, "console.error": true, "document": true, + "setInterval": true, "setTimeout": true }, "packages": { diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json index bc5d084d0243..dba486305cef 100644 --- a/lavamoat/browserify/mmi/policy.json +++ b/lavamoat/browserify/mmi/policy.json @@ -2530,6 +2530,7 @@ "@metamask/snaps-controllers>@metamask/base-controller": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, "@metamask/snaps-controllers>@metamask/json-rpc-middleware-stream": true, + "@metamask/snaps-controllers>@metamask/snaps-utils": true, "@metamask/snaps-controllers>@xstate/fsm": true, "@metamask/snaps-controllers>concat-stream": true, "@metamask/snaps-controllers>get-npm-tarball-url": true, @@ -2538,7 +2539,6 @@ "@metamask/snaps-controllers>tar-stream": true, "@metamask/snaps-rpc-methods": true, "@metamask/snaps-sdk": true, - "@metamask/snaps-utils": true, "@metamask/snaps-utils>@metamask/snaps-registry": true, "@metamask/utils": true, "browserify>browserify-zlib": true, @@ -2576,6 +2576,41 @@ "readable-stream": true } }, + "@metamask/snaps-controllers>@metamask/snaps-utils": { + "globals": { + "File": true, + "FileReader": true, + "TextDecoder": true, + "TextEncoder": true, + "URL": true, + "console.error": true, + "console.log": true, + "console.warn": true, + "crypto": true, + "document.body.appendChild": true, + "document.createElement": true, + "fetch": true + }, + "packages": { + "@metamask/permission-controller": true, + "@metamask/rpc-errors": true, + "@metamask/snaps-sdk": true, + "@metamask/snaps-sdk>@metamask/key-tree": true, + "@metamask/snaps-utils>@metamask/slip44": true, + "@metamask/snaps-utils>cron-parser": true, + "@metamask/snaps-utils>fast-json-stable-stringify": true, + "@metamask/snaps-utils>fast-xml-parser": true, + "@metamask/snaps-utils>marked": true, + "@metamask/snaps-utils>rfdc": true, + "@metamask/snaps-utils>validate-npm-package-name": true, + "@metamask/utils": true, + "@metamask/utils>@scure/base": true, + "@noble/hashes": true, + "chalk": true, + "semver": true, + "superstruct": true + } + }, "@metamask/snaps-controllers>concat-stream": { "packages": { "browserify>buffer": true, @@ -2627,22 +2662,93 @@ }, "packages": { "@metamask/post-message-stream": true, + "@metamask/snaps-execution-environments>@metamask/snaps-utils": true, "@metamask/snaps-utils": true, "@metamask/utils": true } }, + "@metamask/snaps-execution-environments>@metamask/snaps-utils": { + "globals": { + "File": true, + "FileReader": true, + "TextDecoder": true, + "TextEncoder": true, + "URL": true, + "console.error": true, + "console.log": true, + "console.warn": true, + "crypto": true, + "document.body.appendChild": true, + "document.createElement": true, + "fetch": true + }, + "packages": { + "@metamask/permission-controller": true, + "@metamask/rpc-errors": true, + "@metamask/snaps-sdk": true, + "@metamask/snaps-sdk>@metamask/key-tree": true, + "@metamask/snaps-utils>@metamask/slip44": true, + "@metamask/snaps-utils>cron-parser": true, + "@metamask/snaps-utils>fast-json-stable-stringify": true, + "@metamask/snaps-utils>fast-xml-parser": true, + "@metamask/snaps-utils>marked": true, + "@metamask/snaps-utils>rfdc": true, + "@metamask/snaps-utils>validate-npm-package-name": true, + "@metamask/utils": true, + "@metamask/utils>@scure/base": true, + "@noble/hashes": true, + "chalk": true, + "semver": true, + "superstruct": true + } + }, "@metamask/snaps-rpc-methods": { "packages": { "@metamask/permission-controller": true, "@metamask/rpc-errors": true, + "@metamask/snaps-rpc-methods>@metamask/snaps-utils": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, - "@metamask/snaps-utils": true, "@metamask/utils": true, "@noble/hashes": true, "superstruct": true } }, + "@metamask/snaps-rpc-methods>@metamask/snaps-utils": { + "globals": { + "File": true, + "FileReader": true, + "TextDecoder": true, + "TextEncoder": true, + "URL": true, + "console.error": true, + "console.log": true, + "console.warn": true, + "crypto": true, + "document.body.appendChild": true, + "document.createElement": true, + "fetch": true + }, + "packages": { + "@metamask/permission-controller": true, + "@metamask/rpc-errors": true, + "@metamask/snaps-sdk": true, + "@metamask/snaps-sdk>@metamask/key-tree": true, + "@metamask/snaps-utils>@metamask/slip44": true, + "@metamask/snaps-utils>cron-parser": true, + "@metamask/snaps-utils>fast-json-stable-stringify": true, + "@metamask/snaps-utils>fast-xml-parser": true, + "@metamask/snaps-utils>marked": true, + "@metamask/snaps-utils>rfdc": true, + "@metamask/snaps-utils>validate-npm-package-name": true, + "@metamask/utils": true, + "@metamask/utils>@scure/base": true, + "@noble/hashes": true, + "chalk": true, + "semver": true, + "superstruct": true + } + }, "@metamask/snaps-sdk": { "globals": { "fetch": true @@ -2988,108 +3094,146 @@ }, "@sentry/browser": { "globals": { - "TextDecoder": true, - "TextEncoder": true, - "XMLHttpRequest": true, + "PerformanceObserver.supportedEntryTypes.includes": true, + "Request": true, + "URL": true, + "XMLHttpRequest.prototype": true, "__SENTRY_DEBUG__": true, "__SENTRY_RELEASE__": true, + "addEventListener": true, + "console.error": true, "indexedDB.open": true, + "performance.timeOrigin": true, "setTimeout": true }, "packages": { - "@sentry/browser>@sentry-internal/tracing": true, + "@sentry/browser>@sentry-internal/browser-utils": true, + "@sentry/browser>@sentry-internal/feedback": true, + "@sentry/browser>@sentry-internal/replay": true, + "@sentry/browser>@sentry-internal/replay-canvas": true, "@sentry/browser>@sentry/core": true, - "@sentry/browser>@sentry/replay": true, "@sentry/utils": true } }, - "@sentry/browser>@sentry-internal/tracing": { + "@sentry/browser>@sentry-internal/browser-utils": { "globals": { - "Headers": true, + "PerformanceEventTiming.prototype": true, "PerformanceObserver": true, - "Request": true, + "XMLHttpRequest.prototype": true, "__SENTRY_DEBUG__": true, "addEventListener": true, - "performance.getEntriesByType": true, - "removeEventListener": true + "clearTimeout": true, + "performance": true, + "removeEventListener": true, + "setTimeout": true }, "packages": { "@sentry/browser>@sentry/core": true, "@sentry/utils": true } }, - "@sentry/browser>@sentry/core": { + "@sentry/browser>@sentry-internal/feedback": { "globals": { + "FormData": true, + "HTMLFormElement": true, "__SENTRY_DEBUG__": true, - "__SENTRY_TRACING__": true, - "clearInterval": true, + "cancelAnimationFrame": true, "clearTimeout": true, - "console.warn": true, - "setInterval": true, + "document.createElement": true, + "document.createElementNS": true, + "document.createTextNode": true, + "isSecureContext": true, + "requestAnimationFrame": true, "setTimeout": true }, "packages": { + "@sentry/browser>@sentry/core": true, "@sentry/utils": true } }, - "@sentry/browser>@sentry/replay": { + "@sentry/browser>@sentry-internal/replay": { "globals": { "Blob": true, "CSSConditionRule": true, "CSSGroupingRule": true, "CSSMediaRule": true, + "CSSRule": true, "CSSSupportsRule": true, + "Document": true, "DragEvent": true, "Element": true, "FormData": true, - "HTMLCanvasElement": true, - "HTMLElement.prototype": true, + "HTMLElement": true, "HTMLFormElement": true, - "HTMLImageElement": true, - "HTMLInputElement.prototype": true, - "HTMLOptionElement.prototype": true, - "HTMLSelectElement.prototype": true, - "HTMLTextAreaElement.prototype": true, "Headers": true, - "ImageData": true, "MouseEvent": true, "MutationObserver": true, + "Node.DOCUMENT_FRAGMENT_NODE": true, "Node.prototype.contains": true, - "PerformanceObserver": true, + "PointerEvent": true, "TextEncoder": true, "URL": true, "URLSearchParams": true, "Worker": true, - "Zone": true, + "__RRWEB_EXCLUDE_IFRAME__": true, + "__RRWEB_EXCLUDE_SHADOW_DOM__": true, "__SENTRY_DEBUG__": true, + "__SENTRY_EXCLUDE_REPLAY_WORKER__": true, "__rrMutationObserver": true, + "addEventListener": true, "clearTimeout": true, + "console.debug": true, "console.error": true, "console.warn": true, + "customElements.get": true, "document": true, "innerHeight": true, "innerWidth": true, "location.href": true, - "pageXOffset": true, - "pageYOffset": true, - "requestAnimationFrame": true, - "setTimeout": true + "location.origin": true, + "parent": true }, "packages": { + "@sentry/browser>@sentry-internal/browser-utils": true, "@sentry/browser>@sentry/core": true, - "@sentry/utils": true, - "browserify>process": true + "@sentry/utils": true } }, - "@sentry/integrations": { + "@sentry/browser>@sentry-internal/replay-canvas": { "globals": { + "Blob": true, + "HTMLCanvasElement": true, + "HTMLImageElement": true, + "ImageData": true, + "URL.createObjectURL": true, + "WeakRef": true, + "Worker": true, + "cancelAnimationFrame": true, + "console.error": true, + "createImageBitmap": true, + "document": true + }, + "packages": { + "@sentry/browser>@sentry/core": true, + "@sentry/utils": true + } + }, + "@sentry/browser>@sentry/core": { + "globals": { + "Headers": true, "Request": true, + "URL": true, "__SENTRY_DEBUG__": true, - "console.log": true + "__SENTRY_TRACING__": true, + "clearInterval": true, + "clearTimeout": true, + "console.log": true, + "console.warn": true, + "setInterval": true, + "setTimeout": true }, "packages": { - "@sentry/utils": true, - "localforage": true + "@sentry/utils": true } }, "@sentry/utils": { @@ -3097,20 +3241,23 @@ "CustomEvent": true, "DOMError": true, "DOMException": true, + "EdgeRuntime": true, "Element": true, "ErrorEvent": true, "Event": true, + "HTMLElement": true, "Headers": true, "Request": true, "Response": true, + "TextDecoder": true, "TextEncoder": true, "URL": true, - "XMLHttpRequest.prototype": true, "__SENTRY_BROWSER_BUNDLE__": true, "__SENTRY_DEBUG__": true, "clearTimeout": true, "console.error": true, "document": true, + "setInterval": true, "setTimeout": true }, "packages": { diff --git a/package.json b/package.json index 86cc48f21f2f..f66f6bbf4730 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "url": "https://github.com/MetaMask/metamask-extension.git" }, "scripts": { + "env:e2e": "SEGMENT_HOST='https://api.segment.io' SEGMENT_WRITE_KEY='FAKE' SENTRY_DSN_DEV=https://fake@sentry.io/0000000 yarn", "start": "yarn build:dev dev --apply-lavamoat=false --snow=false", "start:skip-onboarding": "SKIP_ONBOARDING=true yarn start", "start:mv2": "ENABLE_MV3=false yarn build:dev dev --apply-lavamoat=false --snow=false", @@ -19,20 +20,20 @@ "dist:mmi:debug": "yarn dist --build-type mmi --apply-lavamoat=false --snow=false", "build": "yarn lavamoat:build", "build:dev": "node development/build/index.js", - "start:test": "SEGMENT_HOST='https://api.segment.io' SEGMENT_WRITE_KEY='FAKE' SENTRY_DSN_DEV=https://fake@sentry.io/0000000 yarn build:dev testDev", - "start:test:flask": "SEGMENT_HOST='https://api.segment.io' SEGMENT_WRITE_KEY='FAKE' SENTRY_DSN_DEV=https://fake@sentry.io/0000000 yarn build:dev testDev --build-type flask --apply-lavamoat=false --snow=false", - "start:test:mv2:flask": "ENABLE_MV3=false SEGMENT_HOST='https://api.segment.io' SEGMENT_WRITE_KEY='FAKE' SENTRY_DSN_DEV=https://fake@sentry.io/0000000 yarn build:dev testDev --build-type flask --apply-lavamoat=false --snow=false", - "start:test:mv2": "ENABLE_MV3=false BLOCKAID_FILE_CDN=static.cx.metamask.io/api/v1/confirmations/ppom SEGMENT_HOST='https://api.segment.io' SEGMENT_WRITE_KEY='FAKE' SENTRY_DSN_DEV=https://fake@sentry.io/0000000 yarn build:dev testDev --apply-lavamoat=false --snow=false", + "start:test": "yarn env:e2e build:dev testDev", + "start:test:flask": "yarn start:test --build-type flask", + "start:test:mv2:flask": "ENABLE_MV3=false yarn start:test:flask --apply-lavamoat=false --snow=false", + "start:test:mv2": "ENABLE_MV3=false BLOCKAID_FILE_CDN=static.cx.metamask.io/api/v1/confirmations/ppom yarn start:test --apply-lavamoat=false --snow=false", "benchmark:chrome": "SELENIUM_BROWSER=chrome ts-node test/e2e/benchmark.js", "mv3:stats:chrome": "SELENIUM_BROWSER=chrome ts-node test/e2e/mv3-perf-stats/index.js", "user-actions-benchmark:chrome": "SELENIUM_BROWSER=chrome ts-node test/e2e/user-actions-benchmark.js", "benchmark:firefox": "SELENIUM_BROWSER=firefox ts-node test/e2e/benchmark.js", - "build:test": "SEGMENT_HOST='https://api.segment.io' SEGMENT_WRITE_KEY='FAKE' SENTRY_DSN_DEV=https://fake@sentry.io/0000000 yarn build test", - "build:test:dev": "SEGMENT_HOST='https://api.segment.io' SEGMENT_WRITE_KEY='FAKE' SENTRY_DSN_DEV=https://fake@sentry.io/0000000 yarn build:dev testDev --apply-lavamoat=false", - "build:test:flask": "SEGMENT_HOST='https://api.segment.io' SEGMENT_WRITE_KEY='FAKE' yarn build test --build-type flask", - "build:test:flask:mv2": "ENABLE_MV3=false SEGMENT_HOST='https://api.segment.io' SEGMENT_WRITE_KEY='FAKE' yarn build test --build-type flask", - "build:test:mmi": "SEGMENT_HOST='https://api.segment.io' SEGMENT_WRITE_KEY='FAKE' yarn build test --build-type mmi", - "build:test:mv2": "ENABLE_MV3=false BLOCKAID_FILE_CDN=static.cx.metamask.io/api/v1/confirmations/ppom SEGMENT_HOST='https://api.segment.io' SEGMENT_WRITE_KEY='FAKE' SENTRY_DSN_DEV=https://fake@sentry.io/0000000 yarn build test", + "build:test": "yarn env:e2e build test", + "build:test:dev": "yarn env:e2e build:dev testDev --apply-lavamoat=false", + "build:test:flask": "yarn build:test --build-type flask", + "build:test:flask:mv2": "ENABLE_MV3=false yarn build:test:flask", + "build:test:mmi": "yarn build:test --build-type mmi", + "build:test:mv2": "ENABLE_MV3=false BLOCKAID_FILE_CDN=static.cx.metamask.io/api/v1/confirmations/ppom yarn build:test", "test": "yarn lint && yarn test:unit && yarn test:unit:jest", "dapp": "node development/static-server.js node_modules/@metamask/test-dapp/dist --port 8080", "dapp-chain": "GANACHE_ARGS='-b 2' concurrently -k -n ganache,dapp -p '[{time}][{name}]' 'yarn ganache:start' 'sleep 5 && yarn dapp'", @@ -225,7 +226,7 @@ "@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.0.0", - "@metamask/transaction-controller": "^32.0.0", + "@metamask/transaction-controller": "patch:@metamask/transaction-controller@npm%3A32.0.0#~/.yarn/patches/@metamask-transaction-controller-npm-32.0.0-e23c2c3443.patch", "@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", "@babel/runtime@npm:^7.12.5": "patch:@babel/runtime@npm%3A7.24.0#~/.yarn/patches/@babel-runtime-npm-7.24.0-7eb1dd11a2.patch", @@ -261,7 +262,7 @@ }, "dependencies": { "@babel/runtime": "patch:@babel/runtime@npm%3A7.24.0#~/.yarn/patches/@babel-runtime-npm-7.24.0-7eb1dd11a2.patch", - "@blockaid/ppom_release": "^1.4.9", + "@blockaid/ppom_release": "^1.5.2", "@contentful/rich-text-html-renderer": "^16.3.5", "@ensdomains/content-hash": "^2.5.7", "@ethereumjs/tx": "^4.1.1", @@ -315,7 +316,7 @@ "@metamask/gas-fee-controller": "^18.0.0", "@metamask/jazzicon": "^2.0.0", "@metamask/keyring-api": "^8.0.0", - "@metamask/keyring-controller": "^16.1.0", + "@metamask/keyring-controller": "patch:@metamask/keyring-controller@npm%3A16.1.0#~/.yarn/patches/@metamask-keyring-controller-npm-16.1.0-7043d2dc62.patch", "@metamask/logging-controller": "^3.0.1", "@metamask/logo": "^3.1.2", "@metamask/message-manager": "^7.3.0", @@ -344,8 +345,8 @@ "@metamask/snaps-execution-environments": "^6.5.0", "@metamask/snaps-rpc-methods": "^9.1.4", "@metamask/snaps-sdk": "^6.0.0", - "@metamask/snaps-utils": "^7.7.0", - "@metamask/transaction-controller": "^32.0.0", + "@metamask/snaps-utils": "patch:@metamask/snaps-utils@npm%3A7.7.0#~/.yarn/patches/@metamask-snaps-utils-npm-7.7.0-2cc1f044af.patch", + "@metamask/transaction-controller": "patch:@metamask/transaction-controller@npm%3A32.0.0#~/.yarn/patches/@metamask-transaction-controller-npm-32.0.0-e23c2c3443.patch", "@metamask/user-operation-controller": "^10.0.0", "@metamask/utils": "^8.2.1", "@ngraveio/bc-ur": "^1.1.12", @@ -354,10 +355,9 @@ "@popperjs/core": "^2.4.0", "@reduxjs/toolkit": "patch:@reduxjs/toolkit@npm%3A1.9.7#~/.yarn/patches/@reduxjs-toolkit-npm-1.9.7-b14925495c.patch", "@segment/loosely-validate-event": "^2.0.0", - "@sentry/browser": "^7.53.0", - "@sentry/integrations": "^7.53.0", - "@sentry/types": "^7.53.0", - "@sentry/utils": "^7.53.0", + "@sentry/browser": "^8.19.0", + "@sentry/types": "^8.19.0", + "@sentry/utils": "^8.19.0", "@trezor/connect-web": "patch:@trezor/connect-web@npm%3A9.2.2#~/.yarn/patches/@trezor-connect-web-npm-9.2.2-a4de8e45fc.patch", "@zxing/browser": "^0.1.4", "@zxing/library": "0.20.0", @@ -370,7 +370,6 @@ "bowser": "^2.11.0", "chart.js": "^4.4.1", "classnames": "^2.2.6", - "contentful": "^10.8.7", "copy-to-clipboard": "^3.3.3", "currency-formatter": "^1.4.2", "debounce-stream": "^2.0.0", @@ -535,6 +534,7 @@ "chalk": "^4.1.2", "chokidar": "^3.6.0", "concurrently": "^8.2.2", + "contentful": "^10.8.7", "copy-webpack-plugin": "^12.0.2", "cross-spawn": "^7.0.3", "css-loader": "^6.10.0", diff --git a/shared/constants/metametrics.ts b/shared/constants/metametrics.ts index 7a83ec4f61e2..f4d5aad027f0 100644 --- a/shared/constants/metametrics.ts +++ b/shared/constants/metametrics.ts @@ -517,23 +517,18 @@ export enum MetaMetricsEventName { DecryptionApproved = 'Decryption Approved', DecryptionRejected = 'Decryption Rejected', DecryptionRequested = 'Decryption Requested', - DisablingAccountNotifications = 'Disabling Account Notifications', - EnablingAccountNotifications = 'Enabling Account Notifications', - DisablingNotifications = 'Disabling Notifications', - DismissEnablingNotificationsFlow = 'Dismiss Enabling Notifications Flow', + DisablingNotifications = 'Notifications Disabled', EmptyBuyBannerDisplayed = 'Empty Buy Banner Displayed', EmptyBuyBannerClicked = 'Empty Buy Banner Clicked', EmptyReceiveBannerDisplayed = 'Empty Receive Banner Displayed', EmptyReceiveBannerClicked = 'Empty Receive Banner Clicked', EmptyNftsBannerDisplayed = 'Empty NFTs Banner Displayed', EmptyNftsBannerClicked = 'Empty NFTs Banner Clicked', - EnablingNotifications = 'Enabling Notifications', + EnablingNotifications = 'Notifications Enabled', EncryptionPublicKeyApproved = 'Encryption Approved', EncryptionPublicKeyRejected = 'Encryption Rejected', EncryptionPublicKeyRequested = 'Encryption Requested', ExternalLinkClicked = 'External Link Clicked', - FeatureAnnouncementEnabled = 'Feature Announcement Enabled', - FeatureAnnouncementDisabled = 'Feature Announcement Disabled', KeyExportSelected = 'Key Export Selected', KeyExportRequested = 'Key Export Requested', KeyExportFailed = 'Key Export Failed', @@ -546,7 +541,7 @@ export enum MetaMetricsEventName { KeyGasFeeEstimationBuySwapTokens = 'Key Show Gas Fee Estimation, Buy Crypto and Swap Tokens', KeyAutoDetectTokens = 'Key Autodetect tokens', KeyBatchAccountBalanceRequests = 'Key Batch account balance requests', - MarkAllNotificationsRead = 'Mark All Notifications Read', + MarkAllNotificationsRead = 'Notifications Marked All as Read', MetricsOptIn = 'Metrics Opt In', MetricsOptOut = 'Metrics Opt Out', NavAccountMenuOpened = 'Account Menu Opened', @@ -561,9 +556,6 @@ export enum MetaMetricsEventName { NavSendButtonClicked = 'Send Button Clicked', NavSwapButtonClicked = 'Swap Button Clicked', NftAdded = 'NFT Added', - NotificationPageOpened = 'Notification Page Opened', - NotificationItemClicked = 'Notification Item Clicked', - NotificationDetailClicked = 'Notification Detail Clicked', OnboardingWalletCreationStarted = 'Wallet Setup Selected', OnboardingWalletImportStarted = 'Wallet Import Started', OnboardingWalletCreationAttempted = 'Wallet Password Created', @@ -575,12 +567,8 @@ export enum MetaMetricsEventName { OnboardingWalletSecurityPhraseWrittenDown = 'SRP Backup Confirm Display', OnboardingWalletSecurityPhraseConfirmed = 'SRP Backup Confirmed', OnboardingWalletCreationComplete = 'Wallet Created', - OnboardingWalletCreationCompleteWithAuthenticating = 'Wallet Created with Authenticating', OnboardingWalletSetupComplete = 'Application Opened', OnboardingWalletAdvancedSettings = 'Settings Updated', - OnboardingWalletAdvancedSettingsWithAuthenticating = 'Settings Updated with Authenticating', - OnboardingWalletAdvancedSettingsWithoutAuthenticating = 'Settings Updated without Authenticating', - OnboardingWalletAdvancedSettingsTurnOffProfileSyncing = 'Turn Off Profile Syncing', OnboardingWalletAdvancedSettingsTurnOnProfileSyncing = 'Turn On Profile Syncing', OnboardingWalletImportAttempted = 'Wallet Import Attempted', OnboardingWalletVideoPlay = 'SRP Intro Video Played', @@ -622,7 +610,6 @@ export enum MetaMetricsEventName { SrpCopiedToClipboard = 'Copies SRP to clipboard', SrpToConfirmBackup = 'SRP Backup Confirm Displayed', StakingEntryPointClicked = 'Stake Button Clicked', - StartEnablingNotificationsFlow = 'Start Enabling Notifications Flow', SupportLinkClicked = 'Support Link Clicked', TermsOfUseShown = 'Terms of Use Shown', TermsOfUseAccepted = 'Terms of Use Accepted', @@ -714,13 +701,13 @@ export enum MetaMetricsEventName { TokenAutoDetectionEnableModal = 'Token Autodetection Enabled from modal', TokenAutoDetectionDisableModal = 'Token Autodetection Disabled from modal', ///: END:ONLY_INCLUDE_IF - TurnOffProfileSyncing = 'Turn Off Profile Syncing', - TurnOnProfileSyncing = 'Turn On Profile Syncing', - TurnOnMetaMetrics = 'Turn On MetaMetrics', - TurnOffMetaMetrics = 'Turn Off MetaMetrics', + TurnOnMetaMetrics = 'MetaMetrics Turned On', + TurnOffMetaMetrics = 'MetaMetrics Turned Off', // Notifications - PushNotificationReceived = 'Push Notification Received', - PushNotificationClicked = 'Push Notification Clicked', + NotificationReceived = 'Notification Received', + NotificationsSettingsUpdated = 'Notifications Settings Updated', + NotificationClicked = 'Notification Clicked', + NotificationMenuOpened = 'Notification Menu Opened', NftAutoDetectionEnableModal = 'Nft Autodetection Enabled from modal', NftAutoDetectionDisableModal = 'Nft Autodetection Disabled from modal', @@ -763,7 +750,6 @@ export enum MetaMetricsEventCategory { Messages = 'Messages', Navigation = 'Navigation', Network = 'Network', - EnableNotifications = 'Enable Notifications', Onboarding = 'Onboarding', NotificationInteraction = 'Notification Interaction', NotificationSettings = 'Notification Settings', diff --git a/shared/lib/trace.test.ts b/shared/lib/trace.test.ts new file mode 100644 index 000000000000..a1845cb2fbd7 --- /dev/null +++ b/shared/lib/trace.test.ts @@ -0,0 +1,112 @@ +import { Span, startSpan, withIsolationScope } from '@sentry/browser'; +import { trace } from './trace'; + +jest.mock('@sentry/browser', () => ({ + withIsolationScope: jest.fn(), + startSpan: jest.fn(), +})); + +const NAME_MOCK = 'testTransaction'; +const PARENT_CONTEXT_MOCK = {} as Span; + +const TAGS_MOCK = { + tag1: 'value1', + tag2: true, + tag3: 123, +}; + +const DATA_MOCK = { + data1: 'value1', + data2: true, + data3: 123, +}; + +function mockGetMetaMetricsEnabled(enabled: boolean) { + global.sentry = { + getMetaMetricsEnabled: () => Promise.resolve(enabled), + }; +} + +describe('Trace', () => { + const startSpanMock = jest.mocked(startSpan); + const withIsolationScopeMock = jest.mocked(withIsolationScope); + const setTagsMock = jest.fn(); + + beforeEach(() => { + jest.resetAllMocks(); + + startSpanMock.mockImplementation((_, fn) => fn({} as Span)); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + withIsolationScopeMock.mockImplementation((fn: any) => + fn({ setTags: setTagsMock }), + ); + }); + + describe('trace', () => { + // @ts-expect-error This function is missing from the Mocha type definitions + it.each([ + ['enabled', true], + ['disabled', false], + ])( + 'executes callback if Sentry is %s', + async (_: string, sentryEnabled: boolean) => { + let callbackExecuted = false; + + mockGetMetaMetricsEnabled(sentryEnabled); + + await trace({ name: NAME_MOCK }, async () => { + callbackExecuted = true; + }); + + expect(callbackExecuted).toBe(true); + }, + ); + + // @ts-expect-error This function is missing from the Mocha type definitions + it.each([ + ['enabled', true], + ['disabled', false], + ])( + 'returns value from callback if Sentry is %s', + async (_: string, sentryEnabled: boolean) => { + mockGetMetaMetricsEnabled(sentryEnabled); + + const result = await trace({ name: NAME_MOCK }, async () => { + return true; + }); + + expect(result).toBe(true); + }, + ); + + it('invokes Sentry if enabled', async () => { + mockGetMetaMetricsEnabled(true); + + await trace( + { + name: NAME_MOCK, + tags: TAGS_MOCK, + data: DATA_MOCK, + parentContext: PARENT_CONTEXT_MOCK, + }, + async () => Promise.resolve(), + ); + + expect(withIsolationScopeMock).toHaveBeenCalledTimes(1); + + expect(startSpanMock).toHaveBeenCalledTimes(1); + expect(startSpanMock).toHaveBeenCalledWith( + { + name: NAME_MOCK, + parentSpan: PARENT_CONTEXT_MOCK, + attributes: DATA_MOCK, + }, + expect.any(Function), + ); + + expect(setTagsMock).toHaveBeenCalledTimes(1); + expect(setTagsMock).toHaveBeenCalledWith(TAGS_MOCK); + }); + }); +}); diff --git a/shared/lib/trace.ts b/shared/lib/trace.ts new file mode 100644 index 000000000000..c585edb981ba --- /dev/null +++ b/shared/lib/trace.ts @@ -0,0 +1,54 @@ +import * as Sentry from '@sentry/browser'; +import { Primitive } from '@sentry/types'; +import { createModuleLogger } from '@metamask/utils'; +import { log as sentryLogger } from '../../app/scripts/lib/setupSentry'; + +const log = createModuleLogger(sentryLogger, 'trace'); + +export type TraceRequest = { + data?: Record; + name: string; + parentContext?: unknown; + tags?: Record; +}; + +export async function trace( + request: TraceRequest, + fn: (context?: unknown) => Promise, +): Promise { + const { data: attributes, name, parentContext, tags } = request; + const parentSpan = (parentContext ?? null) as Sentry.Span | null; + + const isSentryEnabled = + (await globalThis.sentry.getMetaMetricsEnabled()) as boolean; + + const callback = async (span: Sentry.Span | null) => { + log('Starting trace', name, request); + + const start = Date.now(); + let error; + + try { + return await fn(span); + } catch (currentError) { + error = currentError; + throw currentError; + } finally { + const end = Date.now(); + const duration = end - start; + + log('Finished trace', name, duration, { error, request }); + } + }; + + if (!isSentryEnabled) { + log('Skipping Sentry trace as metrics disabled', name, request); + return callback(null); + } + + return await Sentry.withIsolationScope(async (scope) => { + scope.setTags(tags as Record); + + return await Sentry.startSpan({ name, parentSpan, attributes }, callback); + }); +} diff --git a/shared/modules/mv3.utils.js b/shared/modules/mv3.utils.js index 4307ebbd8920..e969c5b74228 100644 --- a/shared/modules/mv3.utils.js +++ b/shared/modules/mv3.utils.js @@ -1,18 +1,27 @@ -import browser from 'webextension-polyfill'; +const runtimeManifest = + global.chrome?.runtime.getManifest() || global.browser?.runtime.getManifest(); /** - * A boolean indicating whether the manifest of the current extension - * is set to manifest version 3. + * A boolean indicating whether the manifest of the current extension is set to manifest version 3. + * + * We have found that when this is run early in a service worker process, the runtime manifest is + * unavailable. That's why we have a fallback using the ENABLE_MV3 constant. The fallback is also + * used in unit tests. */ -export const isManifestV3 = - browser.runtime.getManifest().manifest_version === 3; +export const isManifestV3 = runtimeManifest + ? runtimeManifest.manifest_version === 3 + : // Our build system sets this as a boolean, but in a Node.js context (e.g. unit tests) it will + // always be a string + process.env.ENABLE_MV3 === true || + process.env.ENABLE_MV3 === 'true' || + process.env.ENABLE_MV3 === undefined; /** * A boolean indicating whether the browser supports the offscreen document api. * This is only available in when the manifest is version 3, and only in chromium * versions 109 and higher. As of June 7, 2024, it is not available in firefox. */ -export const isOffscreenAvailable = Boolean(browser.offscreen); +export const isOffscreenAvailable = Boolean(global.chrome?.offscreen); /** * A boolean indicating whether the current extension's manifest is version 3 diff --git a/shared/modules/selectors/index.test.ts b/shared/modules/selectors/index.test.ts index 6f1298952d76..4bdacaacd93f 100644 --- a/shared/modules/selectors/index.test.ts +++ b/shared/modules/selectors/index.test.ts @@ -5,6 +5,7 @@ import { getCurrentChainSupportsSmartTransactions, getSmartTransactionsEnabled, getIsSmartTransaction, + getIsSmartTransactionsOptInModalAvailable, } from '.'; describe('Selectors', () => { @@ -24,9 +25,17 @@ describe('Selectors', () => { type: 'Hardware', }, }, + address: '0x123', + type: 'eip155:eoa', }, }, }, + accounts: { + '0x123': { + address: '0x123', + balance: '0x15f6f0b9d4f8d000', + }, + }, providerConfig: { chainId: CHAIN_IDS.MAINNET, }, @@ -250,4 +259,111 @@ describe('Selectors', () => { expect(result).toBe(false); }); }); + + describe('getIsSmartTransactionsOptInModalAvailable', () => { + it('returns true for Ethereum Mainnet + supported RPC URL + null opt-in status and non-zero balance', () => { + const state = createMockState(); + const newState = { + ...state, + metamask: { + ...state.metamask, + preferences: { + ...state.metamask.preferences, + smartTransactionsOptInStatus: null, + }, + }, + }; + expect(getIsSmartTransactionsOptInModalAvailable(newState)).toBe(true); + }); + + it('returns false for Polygon Mainnet + supported RPC URL + null opt-in status and non-zero balance', () => { + const state = createMockState(); + const newState = { + ...state, + metamask: { + ...state.metamask, + preferences: { + ...state.metamask.preferences, + smartTransactionsOptInStatus: null, + }, + providerConfig: { + ...state.metamask.providerConfig, + chainId: CHAIN_IDS.POLYGON, + }, + }, + }; + expect(getIsSmartTransactionsOptInModalAvailable(newState)).toBe(false); + }); + + it('returns false for Ethereum Mainnet + unsupported RPC URL + null opt-in status and non-zero balance', () => { + const state = createMockState(); + const newState = { + ...state, + metamask: { + ...state.metamask, + preferences: { + ...state.metamask.preferences, + smartTransactionsOptInStatus: null, + }, + networkConfigurations: { + 'network-configuration-id-1': { + chainId: CHAIN_IDS.MAINNET, + ticker: CURRENCY_SYMBOLS.ETH, + rpcUrl: 'https://mainnet.quiknode.pro/', + }, + }, + }, + }; + expect(getIsSmartTransactionsOptInModalAvailable(newState)).toBe(false); + }); + + it('returns false for Ethereum Mainnet + supported RPC URL + true opt-in status and non-zero balance', () => { + const state = createMockState(); + expect(getIsSmartTransactionsOptInModalAvailable(state)).toBe(false); + }); + + it('returns false for Ethereum Mainnet + supported RPC URL + null opt-in status and zero balance (0x0)', () => { + const state = createMockState(); + const newState = { + ...state, + metamask: { + ...state.metamask, + preferences: { + ...state.metamask.preferences, + smartTransactionsOptInStatus: null, + }, + accounts: { + ...state.metamask.accounts, + '0x123': { + address: '0x123', + balance: '0x0', + }, + }, + }, + }; + expect(getIsSmartTransactionsOptInModalAvailable(newState)).toBe(false); + }); + + it('returns false for Ethereum Mainnet + supported RPC URL + null opt-in status and zero balance (0x00)', () => { + const state = createMockState(); + const newState = { + ...state, + metamask: { + ...state.metamask, + preferences: { + ...state.metamask.preferences, + smartTransactionsOptInStatus: null, + }, + accounts: { + ...state.metamask.accounts, + '0x123': { + address: '0x123', + balance: '0x00', + }, + }, + }, + }; + expect(getIsSmartTransactionsOptInModalAvailable(newState)).toBe(false); + }); + }); }); diff --git a/shared/modules/selectors/smart-transactions.ts b/shared/modules/selectors/smart-transactions.ts index 24994f1ec203..c510e817dbdb 100644 --- a/shared/modules/selectors/smart-transactions.ts +++ b/shared/modules/selectors/smart-transactions.ts @@ -7,8 +7,10 @@ import { getCurrentChainId, getCurrentNetwork, accountSupportsSmartTx, + getSelectedAccount, } from '../../../ui/selectors/selectors'; // TODO: Migrate shared selectors to this file. import { isProduction } from '../environment'; +import { MultichainState } from '../../../ui/selectors/multichain'; type SmartTransactionsMetaMaskState = { metamask: { @@ -89,13 +91,27 @@ const getIsAllowedRpcUrlForSmartTransactions = ( return rpcUrl?.hostname?.endsWith('.infura.io'); }; +/** + * Checks if the selected account has a non-zero balance. + * + * @param state - The state object containing account information. + * @returns true if the selected account has a non-zero balance, otherwise false. + */ +const hasNonZeroBalance = (state: SmartTransactionsMetaMaskState) => { + const selectedAccount = getSelectedAccount( + state as unknown as MultichainState, + ); + return BigInt(selectedAccount?.balance || '0x0') > 0n; +}; + export const getIsSmartTransactionsOptInModalAvailable = ( state: SmartTransactionsMetaMaskState, ) => { return ( getCurrentChainSupportsSmartTransactions(state) && getIsAllowedRpcUrlForSmartTransactions(state) && - getSmartTransactionsOptInStatus(state) === null + getSmartTransactionsOptInStatus(state) === null && + hasNonZeroBalance(state) ); }; diff --git a/test/e2e/playwright/mmi/specs/navigation.spec.ts b/test/e2e/playwright/mmi/specs/navigation.spec.ts index 276beab17359..06c29f422971 100644 --- a/test/e2e/playwright/mmi/specs/navigation.spec.ts +++ b/test/e2e/playwright/mmi/specs/navigation.spec.ts @@ -18,8 +18,6 @@ const supportContactUs = const mmiHomePage = 'https://metamask.io/institutions/'; const privacyAndNotice = 'https://consensys.io/privacy-notice'; const openSeaTermsOfUse = 'https://opensea.io/securityproviderterms'; -const metamaskAttributions = - 'https://raw.githubusercontent.com/MetaMask/metamask-extension/develop/attribution.txt'; const termsOfUse = 'https://consensys.io/terms-of-use'; const learnMoreArticles = 'https://support.metamask.io/'; @@ -159,12 +157,6 @@ test.describe('MMI Navigation', () => { privacyAndNotice, ); await checkLinkURL(context, mainMenuPage.page, 'Terms of use', termsOfUse); - await checkLinkURL( - context, - mainMenuPage.page, - 'Attributions', - metamaskAttributions, - ); await checkLinkURL( context, mainMenuPage.page, diff --git a/test/e2e/playwright/shared/pageObjects/signup-page.ts b/test/e2e/playwright/shared/pageObjects/signup-page.ts index 7edd859bb660..e4165fc1ab89 100644 --- a/test/e2e/playwright/shared/pageObjects/signup-page.ts +++ b/test/e2e/playwright/shared/pageObjects/signup-page.ts @@ -80,6 +80,5 @@ export class SignUpPage { await this.gotItBtn.click(); await this.nextBtn.click(); await this.doneBtn.click(); - await this.enableButton.click(); } } 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 new file mode 100644 index 000000000000..5ab35138ce7b --- /dev/null +++ b/test/e2e/tests/api-usage/account-tracker-api-usage.spec.ts @@ -0,0 +1,209 @@ +import { strict as assert } from 'assert'; +import { MockedEndpoint } from 'mockttp'; +import { JsonRpcRequest } from '@metamask/utils'; +import { + withFixtures, + defaultGanacheOptions, + unlockWallet, + veryLargeDelayMs, +} from '../../helpers'; +import FixtureBuilder from '../../fixture-builder'; +import { Driver } from '../../webdriver/driver'; +import { Mockttp } from '../../mock-e2e'; + +async function mockInfura(mockServer: Mockttp): Promise { + const blockNumber = { value: 0 }; + + return [ + await mockServer + .forPost(/infura/u) + .withJsonBodyIncluding({ method: 'eth_blockNumber' }) + .times(50) + .thenCallback(() => { + // We need to ensure the mocked block number keeps increasing, + // as this results in block tracker listeners firing, which is + // needed for the potential account tracker network requests being + // tested against here. + blockNumber.value += 1; + + return { + statusCode: 200, + json: { + jsonrpc: '2.0', + id: '1111111111111111', + result: blockNumber.value.toString(16), + }, + }; + }), + await mockServer.forPost(/infura/u).thenCallback(() => { + return { + statusCode: 200, + json: { + jsonrpc: '2.0', + id: '1111111111111111', + result: '0x1', + }, + }; + }), + await mockServer.forGet(/infura/u).thenCallback(() => { + return { + statusCode: 200, + json: { + jsonrpc: '2.0', + id: '1111111111111111', + result: '0x0', + }, + }; + }), + ]; +} + +async function getAllInfuraJsonRpcRequests( + mockedEndpoint: MockedEndpoint[], +): Promise { + const allInfuraJsonRpcRequests: JsonRpcRequest[] = []; + let seenRequests; + let seenProviderRequests; + + for (const m of mockedEndpoint) { + seenRequests = await m.getSeenRequests(); + seenProviderRequests = seenRequests.filter((request) => + request.url.match('infura'), + ); + + for (const r of seenProviderRequests) { + const json = await r.body.getJson(); + if (json !== undefined) { + allInfuraJsonRpcRequests.push(json); + } + } + } + + return allInfuraJsonRpcRequests; +} + +function getSpecifiedJsonRpcRequests( + jsonRpcRequestArray: JsonRpcRequest[], + methodsToGet: string[], +) { + return jsonRpcRequestArray.filter(({ method }) => + methodsToGet.includes(method), + ); +} + +describe('Account Tracker API Usage', function () { + it('should not make eth_call or eth_getBalance requests before the UI is opened and should make those requests after the UI is opened', async function () { + // Note: we are not testing that eth_getBlockByNumber is not called before the UI + // is opened because there is a known bug that results in it being called if the + // user is already onboarded: https://github.com/MetaMask/MetaMask-planning/issues/2151 + // Once that issue is resolved, we can add eth_getBlockByNumber to the below array. + const RPC_METHODS_TO_TEST = ['eth_call', 'eth_getBalance']; + + await withFixtures( + { + fixtures: new FixtureBuilder().withNetworkControllerOnMainnet().build(), + ganacheOptions: defaultGanacheOptions, + title: this.test?.fullTitle(), + testSpecificMock: mockInfura, + }, + async ({ + driver, + mockedEndpoint, + }: { + driver: Driver; + mockedEndpoint: MockedEndpoint[]; + }) => { + await driver.delay(veryLargeDelayMs); + let allInfuraJsonRpcRequests = await getAllInfuraJsonRpcRequests( + mockedEndpoint, + ); + let rpcMethodsToTestRequests = getSpecifiedJsonRpcRequests( + allInfuraJsonRpcRequests, + RPC_METHODS_TO_TEST, + ); + + assert.ok( + rpcMethodsToTestRequests.length === 0, + `An ${RPC_METHODS_TO_TEST.join( + ' or ', + )} request has been made to infura before opening the UI`, + ); + + await unlockWallet(driver); + await driver.delay(veryLargeDelayMs); + + allInfuraJsonRpcRequests = await getAllInfuraJsonRpcRequests( + mockedEndpoint, + ); + rpcMethodsToTestRequests = getSpecifiedJsonRpcRequests( + allInfuraJsonRpcRequests, + RPC_METHODS_TO_TEST, + ); + + assert.ok( + rpcMethodsToTestRequests.length > 0, + `No ${RPC_METHODS_TO_TEST.join( + ' or ', + )} request has been made to infura since opening the UI`, + ); + }, + ); + }); + + it('should not make eth_call or eth_getBalance or eth_getBlockByNumber requests after the UI is closed', async function () { + const RPC_METHODS_TO_TEST = [ + 'eth_getBlockByNumber', + 'eth_call', + 'eth_getBalance', + ]; + + await withFixtures( + { + fixtures: new FixtureBuilder().withNetworkControllerOnMainnet().build(), + ganacheOptions: defaultGanacheOptions, + title: this.test?.fullTitle(), + testSpecificMock: mockInfura, + }, + async ({ + driver, + mockedEndpoint, + }: { + driver: Driver; + mockedEndpoint: MockedEndpoint[]; + }) => { + await unlockWallet(driver); + await driver.delay(veryLargeDelayMs); + const initialInfuraJsonRpcRequests = await getAllInfuraJsonRpcRequests( + mockedEndpoint, + ); + + await driver.openNewURL('about:blank'); + // The delay is intentionally 20000, to ensure we cover at least 1 polling + // loop of time for the block tracker. + await driver.delay(20000); + + const currentInfuraJsonRpcRequests = await getAllInfuraJsonRpcRequests( + mockedEndpoint, + ); + + const initialRpcMethodsToTestRequests = getSpecifiedJsonRpcRequests( + initialInfuraJsonRpcRequests, + RPC_METHODS_TO_TEST, + ); + + const currentRpcMethodsToTestRequests = getSpecifiedJsonRpcRequests( + currentInfuraJsonRpcRequests, + RPC_METHODS_TO_TEST, + ); + + assert.ok( + initialRpcMethodsToTestRequests.length === + currentRpcMethodsToTestRequests.length, + `An ${RPC_METHODS_TO_TEST.join( + ' or ', + )} request has been made to infura after closing the UI`, + ); + }, + ); + }); +}); diff --git a/test/e2e/tests/metrics/errors.spec.js b/test/e2e/tests/metrics/errors.spec.js index d183d5842fed..54e7c2b0e6aa 100644 --- a/test/e2e/tests/metrics/errors.spec.js +++ b/test/e2e/tests/metrics/errors.spec.js @@ -5,7 +5,9 @@ const { get, has, set, unset, cloneDeep } = require('lodash'); const { Browser } = require('selenium-webdriver'); const { format } = require('prettier'); const { isObject } = require('@metamask/utils'); -const { SENTRY_UI_STATE } = require('../../../../app/scripts/lib/setupSentry'); +const { + SENTRY_UI_STATE, +} = require('../../../../app/scripts/constants/sentry-state'); const FixtureBuilder = require('../../fixture-builder'); const { convertToHexValue, @@ -219,7 +221,7 @@ describe('Sentry errors', function () { ], }; - describe('before initialization, after opting out of metrics', function () { + describe('before initialization, after opting out of metrics @no-mmi', function () { it('should NOT send error events in the background', async function () { await withFixtures( { @@ -532,7 +534,7 @@ describe('Sentry errors', function () { }); }); - describe('after initialization, after opting out of metrics', function () { + describe('after initialization, after opting out of metrics @no-mmi', function () { it('should NOT send error events in the background', async function () { await withFixtures( { @@ -670,6 +672,7 @@ describe('Sentry errors', function () { const mockTextBody = (await mockedRequest.body.getText()).split('\n'); const mockJsonBody = JSON.parse(mockTextBody[2]); const appState = mockJsonBody?.extra?.appState; + const { extensionId, installType } = mockJsonBody.extra; assert.deepStrictEqual(Object.keys(appState), [ 'browser', 'version', @@ -685,6 +688,11 @@ describe('Sentry errors', function () { appState?.version.length > 0, 'Invalid version state', ); + assert.ok( + typeof extensionId === 'string' && extensionId.length > 0, + `${extensionId} is not a valid extension ID`, + ); + assert.equal(installType, 'development'); await matchesSnapshot({ data: transformBackgroundState(appState.state), snapshot: 'errors-after-init-opt-in-background-state', @@ -761,6 +769,7 @@ describe('Sentry errors', function () { const mockTextBody = (await mockedRequest.body.getText()).split('\n'); const mockJsonBody = JSON.parse(mockTextBody[2]); const appState = mockJsonBody?.extra?.appState; + const { extensionId, installType } = mockJsonBody.extra; assert.deepStrictEqual(Object.keys(appState), [ 'browser', 'version', @@ -776,6 +785,11 @@ describe('Sentry errors', function () { appState?.version.length > 0, 'Invalid version state', ); + assert.ok( + typeof extensionId === 'string' && extensionId.length > 0, + `${extensionId} is not a valid extension ID`, + ); + assert.equal(installType, 'development'); await matchesSnapshot({ data: transformUiState(appState.state), snapshot: 'errors-after-init-opt-in-ui-state', 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 e84e0ee786e1..226481323a8a 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 @@ -108,6 +108,7 @@ "traits": "object", "previousUserTraits": "object", "fragments": "object", + "dataCollectionForMarketing": "boolean", "segmentApiCalls": "object" }, "MetamaskNotificationsController": { @@ -273,6 +274,7 @@ "swapsQuoteRefreshTime": 60000, "swapsQuotePrefetchingRefreshTime": 60000, "swapsStxBatchStatusRefreshTime": 10000, + "swapsStxStatusDeadline": 180, "swapsStxGetTransactionsRefreshTime": 10000, "swapsStxMaxFeeMultiplier": 2 } @@ -298,7 +300,7 @@ }, "UserOperationController": { "userOperations": "object" }, "UserStorageController": { - "isProfileSyncingEnabled": true, + "isProfileSyncingEnabled": null, "isProfileSyncingUpdateLoading": "boolean" } } 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 f8f5e2eb46c9..d50e1f7c927b 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 @@ -198,7 +198,7 @@ "nameSources": "object", "userOperations": "object", "isSignedIn": "boolean", - "isProfileSyncingEnabled": true, + "isProfileSyncingEnabled": null, "isProfileSyncingUpdateLoading": "boolean", "subscriptionAccountsSeen": "object", "isMetamaskNotificationsFeatureSeen": "boolean", @@ -248,6 +248,7 @@ "swapsQuoteRefreshTime": 60000, "swapsQuotePrefetchingRefreshTime": 60000, "swapsStxBatchStatusRefreshTime": 10000, + "swapsStxStatusDeadline": 180, "swapsStxGetTransactionsRefreshTime": 10000, "swapsStxMaxFeeMultiplier": 2 }, diff --git a/test/e2e/tests/metrics/wallet-created.spec.js b/test/e2e/tests/metrics/wallet-created.spec.js index cff720062818..bc07cfdeea75 100644 --- a/test/e2e/tests/metrics/wallet-created.spec.js +++ b/test/e2e/tests/metrics/wallet-created.spec.js @@ -87,6 +87,8 @@ describe('Wallet Created Events @no-mmi', function () { locale: 'en', chain_id: '0x539', environment_type: 'fullscreen', + is_profile_syncing_enabled: null, + is_signed_in: false, }); }, ); diff --git a/test/e2e/tests/multichain/all-permissions-page.spec.js b/test/e2e/tests/multichain/all-permissions-page.spec.js new file mode 100644 index 000000000000..5bb718bd8d20 --- /dev/null +++ b/test/e2e/tests/multichain/all-permissions-page.spec.js @@ -0,0 +1,98 @@ +const { strict: assert } = require('assert'); +const { + withFixtures, + WINDOW_TITLES, + connectToDapp, + logInWithBalanceValidation, + defaultGanacheOptions, +} = require('../../helpers'); +const FixtureBuilder = require('../../fixture-builder'); + +describe('Permissions Page', function () { + it('should show connected site permissions when a single dapp is connected', async function () { + await withFixtures( + { + dapp: true, + fixtures: new FixtureBuilder().build(), + title: this.test.fullTitle(), + ganacheOptions: defaultGanacheOptions, + }, + async ({ driver, ganacheServer }) => { + await logInWithBalanceValidation(driver, ganacheServer); + await connectToDapp(driver); + + // close test dapp window to avoid future confusion + const windowHandles = await driver.getAllWindowHandles(); + await driver.closeWindowHandle(windowHandles[1]); + // disconnect dapp in fullscreen view + await driver.switchToWindowWithTitle( + WINDOW_TITLES.ExtensionInFullScreenView, + ); + await driver.clickElement( + '[data-testid ="account-options-menu-button"]', + ); + await driver.clickElement({ text: 'All Permissions', tag: 'div' }); + await driver.clickElementAndWaitToDisappear({ + text: 'Got it', + tag: 'button', + }); + const connectedDapp = await driver.isElementPresent({ + text: '127.0.0.1:8080', + tag: 'p', + }); + assert.ok(connectedDapp, 'Account connected to Dapp1'); + }, + ); + }); + + it('should show all permissions listed when experimental settings toggle is off', async function () { + await withFixtures( + { + dapp: true, + fixtures: new FixtureBuilder().build(), + title: this.test.fullTitle(), + ganacheOptions: defaultGanacheOptions, + }, + async ({ driver, ganacheServer }) => { + await logInWithBalanceValidation(driver, ganacheServer); + await connectToDapp(driver); + + // close test dapp window to avoid future confusion + const windowHandles = await driver.getAllWindowHandles(); + await driver.closeWindowHandle(windowHandles[1]); + // disconnect dapp in fullscreen view + await driver.switchToWindowWithTitle( + WINDOW_TITLES.ExtensionInFullScreenView, + ); + 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.clickElement( + '[data-testid="experimental-setting-toggle-request-queue"] label', + ); + await driver.clickElement( + '.settings-page__header__title-container__close-button', + ); + await driver.clickElement( + '[data-testid ="account-options-menu-button"]', + ); + await driver.clickElement({ text: 'All Permissions', tag: 'div' }); + await driver.clickElementAndWaitToDisappear({ + text: 'Got it', + tag: 'button', + }); + const connectedDapp = await driver.isElementPresent({ + text: '127.0.0.1:8080', + tag: 'p', + }); + assert.ok(connectedDapp, 'Account connected to Dapp1'); + }, + ); + }); +}); diff --git a/test/e2e/tests/multichain/asset-picker-send.spec.ts b/test/e2e/tests/multichain/asset-picker-send.spec.ts index 9740cd1c9878..8358e8242f69 100644 --- a/test/e2e/tests/multichain/asset-picker-send.spec.ts +++ b/test/e2e/tests/multichain/asset-picker-send.spec.ts @@ -11,7 +11,7 @@ import { import { Driver } from '../../webdriver/driver'; import { RECIPIENT_ADDRESS_MOCK } from '../simulation-details/types'; -describe('AssetPickerSendFlow', function () { +describe('AssetPickerSendFlow @no-mmi', function () { const chainId = CHAIN_IDS.MAINNET; const fixtures = { diff --git a/test/e2e/tests/request-queuing/ui.spec.js b/test/e2e/tests/request-queuing/ui.spec.js index 423a8a509f29..ae84d7125d02 100644 --- a/test/e2e/tests/request-queuing/ui.spec.js +++ b/test/e2e/tests/request-queuing/ui.spec.js @@ -1,5 +1,5 @@ const { strict: assert } = require('assert'); -const { Browser } = require('selenium-webdriver'); +const { Browser, until } = require('selenium-webdriver'); const FixtureBuilder = require('../../fixture-builder'); const { withFixtures, @@ -125,10 +125,16 @@ async function confirmTransaction(driver) { } async function switchToNetworkByName(driver, networkName) { - await driver.clickElement('[data-testid="network-display"]'); + await driver.clickElement('.mm-picker-network'); await driver.clickElement(`[data-testid="${networkName}"]`); } +async function openPopupWithActiveTabOrigin(driver, origin) { + await driver.openNewPage( + `${driver.extensionUrl}/${PAGES.POPUP}.html?activeTabOrigin=${origin}`, + ); +} + async function validateBalanceAndActivity( driver, expectedBalance, @@ -429,6 +435,178 @@ describe('Request-queue UI changes', function () { ); }); + it('should signal from UI to dapp the network change @no-mmi', async function () { + await withFixtures( + { + dapp: true, + fixtures: new FixtureBuilder() + .withPreferencesControllerUseRequestQueueEnabled() + .withSelectedNetworkControllerPerDomain() + .build(), + ganacheOptions: defaultGanacheOptions, + title: this.test.fullTitle(), + driverOptions: { constrainWindowSize: true }, + }, + async ({ driver }) => { + // Navigate to extension home screen + await unlockWallet(driver); + + // Open the first dapp which starts on chain '0x539 + await openDappAndSwitchChain(driver, DAPP_URL); + + // Ensure the dapp starts on the correct network + await driver.wait( + until.elementTextContains( + await driver.findElement('#chainId'), + '0x539', + ), + ); + + // Open the popup with shimmed activeTabOrigin + await openPopupWithActiveTabOrigin(driver, DAPP_URL); + + // Switch to mainnet + await switchToNetworkByName(driver, 'Ethereum Mainnet'); + + // Switch back to the Dapp tab + await driver.switchToWindowWithUrl(DAPP_URL); + + // Check to make sure the dapp network changed + await driver.wait( + until.elementTextContains( + await driver.findElement('#chainId'), + '0x1', + ), + ); + }, + ); + }); + + it('should autoswitch networks to the last used network for domain', async function () { + const port = 8546; + const chainId = 1338; + await withFixtures( + { + dapp: true, + fixtures: new FixtureBuilder() + .withNetworkControllerDoubleGanache() + .withPreferencesControllerUseRequestQueueEnabled() + .withSelectedNetworkControllerPerDomain() + .build(), + ganacheOptions: { + ...defaultGanacheOptions, + concurrent: [ + { + port, + chainId, + ganacheOptions2: defaultGanacheOptions, + }, + ], + }, + dappOptions: { numberOfDapps: 2 }, + title: this.test.fullTitle(), + }, + async ({ driver }) => { + // Open fullscreen + await unlockWallet(driver); + + // Open the first dapp which starts on chain '0x539 + await openDappAndSwitchChain(driver, DAPP_URL); + + // Open tab 2, switch to Ethereum Mainnet + await openDappAndSwitchChain(driver, DAPP_ONE_URL, '0x1', 4); + + // Open the popup with shimmed activeTabOrigin + await openPopupWithActiveTabOrigin(driver, DAPP_URL); + + // Ensure network was reset to original + await driver.findElement({ + css: '.multichain-app-header__contents--avatar-network .mm-text', + text: 'Localhost 8545', + }); + + // Ensure toast is shown to the user + await driver.findElement({ + css: '.toast-text', + text: 'Localhost 8545 is now active on 127.0.0.1:8080', + }); + }, + ); + }); + + it('should autoswitch networks when last confirmation from another network is rejected', async function () { + const port = 8546; + const chainId = 1338; + + await withFixtures( + { + dapp: true, + fixtures: new FixtureBuilder() + .withNetworkControllerDoubleGanache() + .withPreferencesControllerUseRequestQueueEnabled() + .withSelectedNetworkControllerPerDomain() + .build(), + ganacheOptions: { + ...defaultGanacheOptions, + concurrent: [ + { + port, + chainId, + ganacheOptions2: defaultGanacheOptions, + }, + ], + }, + dappOptions: { numberOfDapps: 2 }, + title: this.test.fullTitle(), + driverOptions: { constrainWindowSize: true }, + }, + async ({ driver }) => { + await unlockWallet(driver); + + // Open the first dapp which starts on chain '0x539 + await openDappAndSwitchChain(driver, DAPP_URL); + + // Open tab 2, switch to Ethereum Mainnet + await openDappAndSwitchChain(driver, DAPP_ONE_URL, '0x1', 4); + await driver.waitForSelector({ + css: '.error-message-text', + text: 'You are on the Ethereum Mainnet.', + }); + await driver.delay(veryLargeDelayMs); + + // Start a Send on Ethereum Mainnet + await driver.clickElement('#sendButton'); + await driver.delay(regularDelayMs); + + // Open the popup with shimmed activeTabOrigin + await openPopupWithActiveTabOrigin(driver, DAPP_URL); + + // Ensure the confirmation pill shows Ethereum Mainnet + await driver.waitForSelector({ + css: '[data-testid="network-display"]', + text: 'Ethereum Mainnet', + }); + + // Reject the confirmation + await driver.clickElement( + '[data-testid="page-container-footer-cancel"]', + ); + + // Wait for network to automatically change to localhost + await driver.waitForSelector({ + css: '.multichain-app-header__contents--avatar-network .mm-text', + text: 'Localhost 8545', + }); + + // Ensure toast is shown to the user + await driver.waitForSelector({ + css: '.toast-text', + text: 'Localhost 8545 is now active on 127.0.0.1:8080', + }); + }, + ); + }); + it('should gracefully handle network connectivity failure for signatures @no-mmi', async function () { const port = 8546; const chainId = 1338; @@ -473,7 +651,7 @@ describe('Request-queue UI changes', function () { await driver.switchToWindowWithTitle( WINDOW_TITLES.ExtensionInFullScreenView, ); - await driver.findElement({ + await driver.waitForSelector({ css: '[data-testid="network-display"]', text: 'Ethereum Mainnet', }); diff --git a/test/e2e/tests/settings/account-token-list.spec.js b/test/e2e/tests/settings/account-token-list.spec.js index 86028d37bf0a..921cacae9e3a 100644 --- a/test/e2e/tests/settings/account-token-list.spec.js +++ b/test/e2e/tests/settings/account-token-list.spec.js @@ -57,6 +57,16 @@ describe('Settings', function () { tag: 'div', }); await driver.clickElement({ text: 'Fiat', tag: 'label' }); + // We now need to enable "Show fiat on testnet" if we are using testnets (and since our custom + // network during test is using a testnet chain ID, it will be considered as a test network) + await driver.clickElement({ + text: 'Advanced', + tag: 'div', + }); + await driver.clickElement('.show-fiat-on-testnets-toggle'); + // Looks like when enabling the "Show fiat on testnet" it takes some time to re-update the + // overview screen, so just wait a bit here: + await driver.delay(1000); await driver.clickElement( '.settings-page__header__title-container__close-button', @@ -68,7 +78,7 @@ describe('Settings', function () { const tokenListAmount = await driver.findElement( '.eth-overview__primary-container', ); - assert.equal(await tokenListAmount.getText(), '25\nETH'); + assert.equal(await tokenListAmount.getText(), '$42,500.00\nUSD'); await driver.clickElement('[data-testid="account-menu-icon"]'); const accountTokenValue = await driver.waitForSelector( '.multichain-account-list-item .multichain-account-list-item__asset', diff --git a/test/e2e/tests/settings/backup-restore.spec.js b/test/e2e/tests/settings/backup-restore.spec.js index 4ad2b9a5b7a8..9385e8c5ada5 100644 --- a/test/e2e/tests/settings/backup-restore.spec.js +++ b/test/e2e/tests/settings/backup-restore.spec.js @@ -1,6 +1,5 @@ const { strict: assert } = require('assert'); const { promises: fs } = require('fs'); -const path = require('path'); const { defaultGanacheOptions, withFixtures, @@ -39,13 +38,6 @@ const getBackupJson = async () => { } }; -const restoreFile = path.join( - __dirname, - '../..', - 'restore', - 'MetaMaskUserData.json', -); - describe('Backup and Restore', function () { it('should backup the account settings', async function () { if (process.env.SELENIUM_BROWSER === 'chrome') { @@ -88,50 +80,4 @@ describe('Backup and Restore', function () { }, ); }); - - it('should restore the account settings', async function () { - if (process.env.SELENIUM_BROWSER === 'chrome') { - // Chrome shows OS level download prompt which can't be dismissed by Selenium - this.skip(); - } - await withFixtures( - { - fixtures: new FixtureBuilder().build(), - ganacheOptions: defaultGanacheOptions, - title: this.test.fullTitle(), - }, - async ({ driver }) => { - await unlockWallet(driver); - - // Restore - await driver.clickElement( - '[data-testid="account-options-menu-button"]', - ); - await driver.clickElement({ text: 'Settings', tag: 'div' }); - await driver.clickElement({ text: 'Advanced', tag: 'div' }); - const restore = await driver.findElement('#restore-file'); - await restore.sendKeys(restoreFile); - - // Dismiss success message - await driver.waitForSelector({ - css: '[data-testid="restore-user-data-banner-alert-description"]', - text: 'Your data has been restored successfully', - }); - await driver.clickElement({ text: 'Dismiss', tag: 'button' }); - - // Verify restore - await driver.clickElement({ text: 'Contacts', tag: 'div' }); - - const recipientLabel = await driver.findElement( - '[data-testid="address-list-item-label"]', - ); - assert.equal('Test Account', await recipientLabel.getText()); - - const recipientAddress = await driver.findElement( - '[data-testid="address-list-item-address"]', - ); - assert.equal('0x0c54F...7AaFb', await recipientAddress.getText()); - }, - ); - }); }); 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 e8b416b9604e..8fbfe1e45dcb 100644 --- a/test/e2e/tests/swap-send/swap-send-erc20.spec.ts +++ b/test/e2e/tests/swap-send/swap-send-erc20.spec.ts @@ -15,7 +15,7 @@ import { SWAP_SEND_QUOTES_RESPONSE_TST_ETH } from './mocks/erc20-data'; const RECIPIENT_ADDRESS = '0xc427D562164062a23a5cFf596A4a3208e72Acd28'; -describe('Swap-Send ERC20', function () { +describe('Swap-Send ERC20 @no-mmi', function () { describe('to non-contract address with data that matches swap data signature', function (this: Suite) { it('submits a transaction successfully', async function () { await withFixtures( 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 e4be22ae1fa3..a0398094ac4e 100644 --- a/test/e2e/tests/swap-send/swap-send-eth.spec.ts +++ b/test/e2e/tests/swap-send/swap-send-eth.spec.ts @@ -13,7 +13,7 @@ import { const RECIPIENT_ADDRESS = '0xc427D562164062a23a5cFf596A4a3208e72Acd28'; -describe('Swap-Send ETH', function () { +describe('Swap-Send ETH @no-mmi', function () { describe('to non-contract address with data that matches swap data signature', function (this: Suite) { it('submits a transaction successfully', async function () { await withFixtures( diff --git a/test/e2e/webdriver/chrome.js b/test/e2e/webdriver/chrome.js index c53ccceda3c7..4a8d41afe9ec 100644 --- a/test/e2e/webdriver/chrome.js +++ b/test/e2e/webdriver/chrome.js @@ -22,7 +22,12 @@ function getProxyServer(proxyPort) { * A wrapper around a {@code WebDriver} instance exposing Chrome-specific functionality */ class ChromeDriver { - static async build({ openDevToolsForTabs, port, proxyPort }) { + static async build({ + openDevToolsForTabs, + constrainWindowSize, + port, + proxyPort, + }) { const args = [ `--proxy-server=${getProxyServer(proxyPort)}`, // Set proxy in the way that doesn't interfere with Selenium Manager '--disable-features=OptimizationGuideModelDownloading,OptimizationHintsFetching,OptimizationTargetPrediction,OptimizationHints,NetworkTimeServiceQuerying', // Stop chrome from calling home so much (auto-downloads of AI models; time sync) @@ -42,6 +47,10 @@ class ChromeDriver { args.push('--auto-open-devtools-for-tabs'); } + if (constrainWindowSize) { + args.push('--window-size=320,600'); + } + args.push('--log-level=3'); args.push('--enable-logging'); diff --git a/test/e2e/webdriver/firefox.js b/test/e2e/webdriver/firefox.js index e202c36c4092..b10c55c598d3 100644 --- a/test/e2e/webdriver/firefox.js +++ b/test/e2e/webdriver/firefox.js @@ -45,10 +45,11 @@ class FirefoxDriver { * @param {object} options - the options for the build * @param options.responsive * @param options.port + * @param options.constrainWindowSize * @param options.proxyPort * @returns {Promise<{driver: !ThenableWebDriver, extensionUrl: string, extensionId: string}>} */ - static async build({ responsive, port, proxyPort }) { + static async build({ responsive, port, constrainWindowSize, proxyPort }) { const templateProfile = fs.mkdtempSync(TEMP_PROFILE_PATH_PREFIX); const options = new firefox.Options().setProfile(templateProfile); @@ -99,7 +100,7 @@ class FirefoxDriver { const extensionId = await fxDriver.installExtension('dist/firefox'); const internalExtensionId = await fxDriver.getInternalId(); - if (responsive) { + if (responsive || constrainWindowSize) { await driver.manage().window().setRect({ width: 320, height: 600 }); } diff --git a/test/e2e/webdriver/index.js b/test/e2e/webdriver/index.js index 4d5197bb0dac..ddc0d5543822 100644 --- a/test/e2e/webdriver/index.js +++ b/test/e2e/webdriver/index.js @@ -6,6 +6,7 @@ const FirefoxDriver = require('./firefox'); async function buildWebDriver({ openDevToolsForTabs, port, + constrainWindowSize, timeOut, proxyPort, } = {}) { @@ -18,6 +19,7 @@ async function buildWebDriver({ } = await buildBrowserWebDriver(browser, { openDevToolsForTabs, port, + constrainWindowSize, proxyPort, }); const driver = new Driver(seleniumDriver, browser, extensionUrl, timeOut); diff --git a/test/env.js b/test/env.js index 18fa4ace11b2..4d1b6f42ca4a 100644 --- a/test/env.js +++ b/test/env.js @@ -15,3 +15,4 @@ process.env.PUSH_NOTIFICATIONS_SERVICE_URL = 'https://mock-test-push-notifications-api.metamask.io'; process.env.PORTFOLIO_URL = 'https://portfolio.test'; process.env.ENABLE_CONFIRMATION_REDESIGN = 'true'; +process.env.METAMASK_VERSION = 'MOCK_VERSION'; diff --git a/types/global.d.ts b/types/global.d.ts index c061fba57001..e580813bdcfa 100644 --- a/types/global.d.ts +++ b/types/global.d.ts @@ -1,6 +1,10 @@ +// Many of the state hooks return untyped raw state. +/* eslint-disable @typescript-eslint/no-explicit-any */ + // In order for variables to be considered on the global scope they must be // declared using var and not const or let, which is why this rule is disabled /* eslint-disable no-var */ + import * as Sentry from '@sentry/browser'; import { Success, @@ -225,16 +229,24 @@ declare class Chrome { } type SentryObject = Sentry & { - // Verifies that the user has opted into metrics and then updates the sentry - // instance to track sessions and begins the session. - startSession: () => void; - - // Verifies that the user has opted out of metrics and then updates the - // sentry instance to NOT track sessions and ends the current session. - endSession: () => void; + getMetaMetricsEnabled: () => Promise; +}; - // Calls either startSession or endSession based on optin status - toggleSession: () => void; +type StateHooks = { + getCleanAppState?: () => Promise; + getLogs?: () => any[]; + getMostRecentPersistedState?: () => any; + getPersistedState: () => Promise; + getSentryAppState?: () => any; + getSentryState: () => { + browser: string; + version: string; + state?: any; + persistedState?: any; + }; + metamaskGetState?: () => Promise; + throwTestBackgroundError?: (msg?: string) => Promise; + throwTestError?: (msg?: string) => void; }; export declare global { @@ -244,6 +256,8 @@ export declare global { var chrome: Chrome; + var stateHooks: StateHooks; + namespace jest { // The interface is being used for declaration merging, which is an acceptable exception to this rule. // eslint-disable-next-line @typescript-eslint/consistent-type-definitions diff --git a/ui/components/app/auto-detect-nft/auto-detect-nft-modal.tsx b/ui/components/app/auto-detect-nft/auto-detect-nft-modal.tsx index 3fa744a72e89..114b7129b2e8 100644 --- a/ui/components/app/auto-detect-nft/auto-detect-nft-modal.tsx +++ b/ui/components/app/auto-detect-nft/auto-detect-nft-modal.tsx @@ -1,6 +1,7 @@ import React, { useCallback, useContext } from 'react'; import { useDispatch, useSelector } from 'react-redux'; + import { Modal, ModalContent, @@ -10,6 +11,7 @@ import { Text, ModalBody, ModalFooter, + ButtonVariant, } from '../../component-library'; import { useI18nContext } from '../../../hooks/useI18nContext'; import { @@ -18,7 +20,6 @@ import { Display, FlexDirection, JustifyContent, - TextAlign, TextVariant, } from '../../../helpers/constants/design-system'; import { setOpenSeaEnabled, setUseNftDetection } from '../../../store/actions'; @@ -93,13 +94,9 @@ function AutoDetectNftModal({ isOpen, onClose }: AutoDetectNftModalProps) { > - + {t('allowMetaMaskToDetectNFTs')} - + {t('immediateAccessToYourNFTs')} @@ -114,15 +111,17 @@ function AutoDetectNftModal({ isOpen, onClose }: AutoDetectNftModalProps) { handleNftAutoDetection(true)} - submitButtonProps={{ - children: t('allow'), - block: true, - }} onCancel={() => handleNftAutoDetection(false)} cancelButtonProps={{ children: t('notRightNow'), block: true, + variant: ButtonVariant.Link, + }} + submitButtonProps={{ + children: t('allow'), + block: true, }} + containerProps={{ flexDirection: FlexDirection.ColumnReverse }} /> diff --git a/ui/components/app/auto-detect-token/auto-detect-token-modal.tsx b/ui/components/app/auto-detect-token/auto-detect-token-modal.tsx index c050d53c64cd..c636fdda7e36 100644 --- a/ui/components/app/auto-detect-token/auto-detect-token-modal.tsx +++ b/ui/components/app/auto-detect-token/auto-detect-token-modal.tsx @@ -10,6 +10,7 @@ import { Text, ModalBody, ModalFooter, + ButtonVariant, } from '../../component-library'; import { useI18nContext } from '../../../hooks/useI18nContext'; import { @@ -18,7 +19,6 @@ import { Display, FlexDirection, JustifyContent, - TextAlign, TextVariant, } from '../../../helpers/constants/design-system'; import { setUseTokenDetection } from '../../../store/actions'; @@ -101,13 +101,9 @@ function AutoDetectTokenModal({ > - + {t('allowMetaMaskToDetectTokens')} - + {t('immediateAccessToYourTokens')} @@ -129,8 +125,10 @@ function AutoDetectTokenModal({ onCancel={() => handleTokenAutoDetection(false)} cancelButtonProps={{ children: t('notRightNow'), + variant: ButtonVariant.Link, block: true, }} + containerProps={{ flexDirection: FlexDirection.ColumnReverse }} /> diff --git a/ui/components/app/confirm/info/__snapshots__/info.test.tsx.snap b/ui/components/app/confirm/info/__snapshots__/info.test.tsx.snap index a257e3ee3fa8..467385b65cc2 100644 --- a/ui/components/app/confirm/info/__snapshots__/info.test.tsx.snap +++ b/ui/components/app/confirm/info/__snapshots__/info.test.tsx.snap @@ -10,7 +10,7 @@ exports[`ConfirmInfo should match snapshot 1`] = ` style="overflow-wrap: anywhere; min-height: 24px;" >

= ({ display={Display.Flex} flexDirection={FlexDirection.Row} justifyContent={JustifyContent.center} - alignItems={AlignItems.center} + alignItems={AlignItems.flexStart} color={color} > diff --git a/ui/components/app/confirm/info/row/text-token-units.stories.tsx b/ui/components/app/confirm/info/row/text-token-units.stories.tsx new file mode 100644 index 000000000000..cc490b683dc5 --- /dev/null +++ b/ui/components/app/confirm/info/row/text-token-units.stories.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import { ConfirmInfoRow } from './row'; +import { ConfirmInfoRowTextTokenUnits } from './text-token-units'; + +export default { + title: 'Components/App/Confirm/InfoRowTextToken', + component: ConfirmInfoRowTextTokenUnits, + decorators: [ + (story) => {story()}, + ], + argTypes: { + value: { + control: 'text', + }, + decimals: { + control: 'number', + }, + }, + args: { + value: '3000198768', + decimals: 5, + }, +}; + +export const DefaultStory = (args: { value: string, decimals: number }) => + ; + diff --git a/ui/components/app/confirm/info/row/text-token-units.test.tsx b/ui/components/app/confirm/info/row/text-token-units.test.tsx new file mode 100644 index 000000000000..311ab0acd9c5 --- /dev/null +++ b/ui/components/app/confirm/info/row/text-token-units.test.tsx @@ -0,0 +1,55 @@ +import React from 'react'; +import { render } from '@testing-library/react'; +import { ConfirmInfoRowTextTokenUnits } from './text-token-units'; + +describe('ConfirmInfoRowTextTokenUnits', () => { + it('renders the value with the correct formatted decimal', () => { + const value = 123.456789; + const decimals = 4; + const { getByText } = render( + , + ); + + expect(getByText('0.0123')).toBeInTheDocument(); + }); + + it('renders the value with the correct formatted number for lengthy decimals', () => { + const value = '300012312312123121'; + const decimals = 18; + const { getByText } = render( + , + ); + + expect(getByText('0.3')).toBeInTheDocument(); + }); + + it('renders the value with the correct formatted non-fractional number', () => { + const value = 123456789; + const decimals = 4; + const { getByText } = render( + , + ); + + expect(getByText('12,346')).toBeInTheDocument(); + }); + + it('renders the value with the correct formatted number', () => { + const value = '30001231231212312138768'; + const decimals = 9; + const { getByText } = render( + , + ); + + expect(getByText('30,001,231,231,...')).toBeInTheDocument(); + }); + + it('renders the value with the correct formatted number and ellipsis', () => { + const value = '30001231231212312138768'; + const decimals = 7; + const { getByText } = render( + , + ); + + expect(getByText('3,000,123,123,1...')).toBeInTheDocument(); + }); +}); diff --git a/ui/components/app/confirm/info/row/text-token-units.tsx b/ui/components/app/confirm/info/row/text-token-units.tsx new file mode 100644 index 000000000000..cb0a620c4b5e --- /dev/null +++ b/ui/components/app/confirm/info/row/text-token-units.tsx @@ -0,0 +1,36 @@ +import React from 'react'; +import { BigNumber } from 'bignumber.js'; + +import { calcTokenAmount } from '../../../../../../shared/lib/transactions-controller-utils'; +import { + formatAmount, + formatAmountMaxPrecision, +} from '../../../../../pages/confirmations/components/simulation-details/formatAmount'; +import { shortenString } from '../../../../../helpers/utils/util'; +import { ConfirmInfoRowText } from './text'; + +type ConfirmInfoRowTextTokenUnitsProps = { + value: number | string | BigNumber; + decimals: number; +}; + +export const ConfirmInfoRowTextTokenUnits: React.FC< + ConfirmInfoRowTextTokenUnitsProps +> = ({ value, decimals }) => { + const tokenValue = calcTokenAmount(value, decimals); + + const tokenText = formatAmount('en-US', tokenValue); + const tokenTextMaxPrecision = formatAmountMaxPrecision('en-US', tokenValue); + + return ( + + ); +}; diff --git a/ui/components/app/confirm/info/row/text.tsx b/ui/components/app/confirm/info/row/text.tsx index a93b053dd80e..23750d8836bc 100644 --- a/ui/components/app/confirm/info/row/text.tsx +++ b/ui/components/app/confirm/info/row/text.tsx @@ -11,18 +11,8 @@ import { import { Box, ButtonIcon, IconName, Text } from '../../../../component-library'; import Tooltip from '../../../../ui/tooltip'; -const InfoText = ({ - isEllipsis, - text, -}: { - isEllipsis: boolean; - text: string; -}) => ( - +const InfoText = ({ text }: { text: string }) => ( + {text} ); @@ -31,14 +21,12 @@ export type ConfirmInfoRowTextProps = { text: string; onEditClick?: () => void; editIconClassName?: string; - isEllipsis?: boolean; tooltip?: string; }; export const ConfirmInfoRowText: React.FC = ({ text, onEditClick, - isEllipsis = false, editIconClassName, tooltip, }) => { @@ -61,10 +49,10 @@ export const ConfirmInfoRowText: React.FC = ({ wrapperStyle={{ minWidth: 0 }} interactive > - + ) : ( - + )} {isEditable ? ( { - this.props.onSave(this.state.alias).then(this.props.hideModal); + onSubmit = async () => { + await this.props.onSave(this.state.alias).then(this.props.hideModal); }; onKeyPress = (e) => { diff --git a/ui/components/app/modals/new-account-modal/new-account-modal.container.js b/ui/components/app/modals/new-account-modal/new-account-modal.container.js index 2a1ba3175e80..13af5f15868b 100644 --- a/ui/components/app/modals/new-account-modal/new-account-modal.container.js +++ b/ui/components/app/modals/new-account-modal/new-account-modal.container.js @@ -1,5 +1,10 @@ import { connect } from 'react-redux'; -import * as actions from '../../../../store/actions'; +import { + addNewAccount, + setAccountLabel, + forceUpdateMetamaskState, + hideModal, +} from '../../../../store/actions'; import NewAccountModal from './new-account-modal.component'; function mapStateToProps(state) { @@ -10,14 +15,14 @@ function mapStateToProps(state) { function mapDispatchToProps(dispatch) { return { - hideModal: () => dispatch(actions.hideModal()), - createAccount: (newAccountName) => { - return dispatch(actions.addNewAccount()).then((newAccountAddress) => { - if (newAccountName) { - dispatch(actions.setAccountLabel(newAccountAddress, newAccountName)); - } - return newAccountAddress; - }); + hideModal: () => dispatch(hideModal()), + createAccount: async (newAccountName) => { + const newAccountAddress = await dispatch(addNewAccount()); + if (newAccountName) { + dispatch(setAccountLabel(newAccountAddress, newAccountName)); + } + await forceUpdateMetamaskState(dispatch); + return newAccountAddress; }, }; } @@ -30,9 +35,9 @@ function mergeProps(stateProps, dispatchProps) { ...stateProps, ...dispatchProps, onSave: (newAccountName) => { - return createAccount(newAccountName).then((newAccountAddress) => - onCreateNewAccount(newAccountAddress), - ); + return createAccount(newAccountName).then((newAccountAddress) => { + onCreateNewAccount(newAccountAddress); + }); }, }; } diff --git a/ui/components/app/modals/new-account-modal/new-account-modal.test.tsx b/ui/components/app/modals/new-account-modal/new-account-modal.test.tsx new file mode 100644 index 000000000000..4c3fdf8de3e2 --- /dev/null +++ b/ui/components/app/modals/new-account-modal/new-account-modal.test.tsx @@ -0,0 +1,87 @@ +import React from 'react'; +import thunk from 'redux-thunk'; +import { waitFor, fireEvent } from '@testing-library/react'; +import configureStore from 'redux-mock-store'; +import { renderWithProvider } from '../../../../../test/jest'; +import mockState from '../../../../../test/data/mock-state.json'; +import messages from '../../../../../app/_locales/en/messages.json'; +import NewAccountModal from './new-account-modal.container'; + +const mockOnCreateNewAccount = jest.fn(); +const mockNewAccountNumber = 2; +const mockNewMetamaskState = { + ...mockState.metamask, + currentLocale: 'en', +}; +const mockAddress = '0x1234567890'; + +const mockSubmitRequestToBackground = jest.fn().mockImplementation((method) => { + switch (method) { + case 'addNewAccount': + return mockAddress; + case 'setAccountLabel': + return {}; + case 'getState': + return mockNewMetamaskState; + default: + return {}; + } +}); + +jest.mock('../../../../store/background-connection', () => ({ + ...jest.requireActual('../../../../store/background-connection'), + submitRequestToBackground: (method: string, args: unknown) => + mockSubmitRequestToBackground(method, args), +})); + +const renderModal = ( + props = { + onCreateNewAccount: mockOnCreateNewAccount, + newAccountNumber: mockNewAccountNumber, + }, +) => { + const state = { + ...mockState, + metamask: { + ...mockState.metamask, + currentLocale: 'en', + }, + appState: { + ...mockState.appState, + modal: { + ...mockState.appState.modal, + modalState: { + name: 'NEW_ACCOUNT', + props, + }, + }, + }, + }; + const middlewares = [thunk]; + const mockStore = configureStore(middlewares); + const store = mockStore(state); + + return { + render: renderWithProvider(, store), + store, + }; +}; + +describe('NewAccountModal', () => { + it('calls forceUpdateMetamaskState after adding account', async () => { + const { render } = renderModal(); + const { getByText } = render; + const addAccountButton = getByText(messages.save.message); + expect(addAccountButton).toBeInTheDocument(); + + fireEvent.click(addAccountButton); + + await waitFor(() => { + expect(mockSubmitRequestToBackground).toHaveBeenNthCalledWith( + 2, + 'getState', + undefined, + ); + }); + }); +}); diff --git a/ui/components/app/modals/turn-on-metamask-notifications/turn-on-metamask-notifications.tsx b/ui/components/app/modals/turn-on-metamask-notifications/turn-on-metamask-notifications.tsx index 2cb2437cc665..39d554759b29 100644 --- a/ui/components/app/modals/turn-on-metamask-notifications/turn-on-metamask-notifications.tsx +++ b/ui/components/app/modals/turn-on-metamask-notifications/turn-on-metamask-notifications.tsx @@ -61,10 +61,12 @@ export default function TurnOnMetamaskNotifications() { setButtonState(true); await createNotifications(); trackEvent({ - category: MetaMetricsEventCategory.EnableNotifications, - event: MetaMetricsEventName.EnablingNotifications, + category: MetaMetricsEventCategory.NotificationInteraction, + event: MetaMetricsEventName.NotificationMenuOpened, properties: { - isProfileSyncingEnabled, + is_profile_syncing_enabled: isProfileSyncingEnabled, + is_notifications_enabled: isNotificationEnabled, + action_type: 'enabled', }, }); }; @@ -72,10 +74,12 @@ export default function TurnOnMetamaskNotifications() { const handleHideModal = () => { hideModal(); trackEvent({ - category: MetaMetricsEventCategory.EnableNotifications, - event: MetaMetricsEventName.DismissEnablingNotificationsFlow, + category: MetaMetricsEventCategory.NotificationInteraction, + event: MetaMetricsEventName.NotificationMenuOpened, properties: { - isProfileSyncingEnabled, + is_profile_syncing_enabled: isProfileSyncingEnabled, + is_notifications_enabled: isNotificationEnabled, + action_type: 'dismissed', }, }); }; diff --git a/ui/components/app/nfts-items/nfts-items.js b/ui/components/app/nfts-items/nfts-items.js index 7f2cfce7c7cb..5559fe15c86a 100644 --- a/ui/components/app/nfts-items/nfts-items.js +++ b/ui/components/app/nfts-items/nfts-items.js @@ -164,17 +164,22 @@ export default function NftsItems({ }; const onSendNft = async (nft) => { - trackEvent({ - event: MetaMetricsEventName.sendAssetSelected, - category: MetaMetricsEventCategory.Send, - properties: { - ...sendAnalytics, - is_destination_asset_picker_modal: false, - new_asset_symbol: nft.name, - new_asset_address: nft.address, - is_nft: true, + trackEvent( + { + event: MetaMetricsEventName.sendAssetSelected, + category: MetaMetricsEventCategory.Send, + properties: { + is_destination_asset_picker_modal: false, + is_nft: true, + }, + sensitiveProperties: { + ...sendAnalytics, + new_asset_symbol: nft.name, + new_asset_address: nft.address, + }, }, - }); + { excludeMetaMetricsId: false }, + ); await dispatch( updateSendAsset({ type: AssetType.NFT, diff --git a/ui/components/app/smart-transactions/smart-transactions-opt-in-modal.test.tsx b/ui/components/app/smart-transactions/smart-transactions-opt-in-modal.test.tsx index cfeac0b5811d..15546c3aa09d 100644 --- a/ui/components/app/smart-transactions/smart-transactions-opt-in-modal.test.tsx +++ b/ui/components/app/smart-transactions/smart-transactions-opt-in-modal.test.tsx @@ -2,14 +2,12 @@ import React from 'react'; import { fireEvent } from '@testing-library/react'; import thunk from 'redux-thunk'; import configureMockStore from 'redux-mock-store'; -import { useHistory } from 'react-router-dom'; import { renderWithProvider, createSwapsMockStore, } from '../../../../test/jest'; import { setSmartTransactionsOptInStatus } from '../../../store/actions'; -import { ADVANCED_ROUTE } from '../../../helpers/constants/routes'; import SmartTransactionsOptInModal from './smart-transactions-opt-in-modal'; const middleware = [thunk]; @@ -32,18 +30,14 @@ describe('SmartTransactionsOptInModal', () => { store, ); expect(getByText('Enable')).toBeInTheDocument(); - expect(getByText('Manage in settings')).toBeInTheDocument(); + expect(getByText('No thanks')).toBeInTheDocument(); expect(container).toMatchSnapshot(); }); - it('calls setSmartTransactionsOptInStatus with false when the "Manage in settings" link is clicked and redirects to Advanced Settings', () => { + it('calls setSmartTransactionsOptInStatus with false when the "No thanks" link is clicked', () => { (setSmartTransactionsOptInStatus as jest.Mock).mockImplementationOnce(() => jest.fn(), ); - const historyPushMock = jest.fn(); - (useHistory as jest.Mock).mockImplementationOnce(() => ({ - push: historyPushMock, - })); const store = configureMockStore(middleware)(createSwapsMockStore()); const { getByText } = renderWithProvider( { />, store, ); - const manageInSettingsLink = getByText('Manage in settings'); - fireEvent.click(manageInSettingsLink); + const noThanksLink = getByText('No thanks'); + fireEvent.click(noThanksLink); expect(setSmartTransactionsOptInStatus).toHaveBeenCalledWith(false); - expect(historyPushMock).toHaveBeenCalledWith( - `${ADVANCED_ROUTE}#smart-transactions`, - ); }); it('calls setSmartTransactionsOptInStatus with true when the "Enable" button is clicked', () => { diff --git a/ui/components/app/smart-transactions/smart-transactions-opt-in-modal.tsx b/ui/components/app/smart-transactions/smart-transactions-opt-in-modal.tsx index 982c3d463a71..5b8ff09f9237 100644 --- a/ui/components/app/smart-transactions/smart-transactions-opt-in-modal.tsx +++ b/ui/components/app/smart-transactions/smart-transactions-opt-in-modal.tsx @@ -1,5 +1,4 @@ import React, { useCallback, useEffect } from 'react'; -import { useHistory } from 'react-router-dom'; import { useDispatch } from 'react-redux'; import { useI18nContext } from '../../../hooks/useI18nContext'; @@ -31,7 +30,6 @@ import { } from '../../component-library'; import { setSmartTransactionsOptInStatus } from '../../../store/actions'; import { SMART_TRANSACTIONS_LEARN_MORE_URL } from '../../../../shared/constants/smartTransactions'; -import { ADVANCED_ROUTE } from '../../../helpers/constants/routes'; export type SmartTransactionsOptInModalProps = { isOpen: boolean; @@ -75,10 +73,10 @@ const EnableSmartTransactionsButton = ({ ); }; -const ManageInSettingsLink = ({ - handleManageInSettingsLinkClick, +const NoThanksLink = ({ + handleNoThanksLinkClick, }: { - handleManageInSettingsLinkClick: () => void; + handleNoThanksLinkClick: () => void; }) => { const t = useI18nContext(); return ( @@ -87,11 +85,11 @@ const ManageInSettingsLink = ({ type="link" variant={ButtonVariant.Link} color={TextColor.textAlternative} - onClick={handleManageInSettingsLinkClick} + onClick={handleNoThanksLinkClick} width={BlockSize.Full} className="mm-smart-transactions-opt-in-modal__no-thanks-link" > - {t('manageInSettings')} + {t('noThanks')} ); }; @@ -166,16 +164,14 @@ export default function SmartTransactionsOptInModal({ }: SmartTransactionsOptInModalProps) { const t = useI18nContext(); const dispatch = useDispatch(); - const history = useHistory(); const handleEnableButtonClick = useCallback(() => { dispatch(setSmartTransactionsOptInStatus(true)); }, [dispatch]); - const handleManageInSettingsLinkClick = useCallback(() => { + const handleNoThanksLinkClick = useCallback(() => { // Set the Smart Transactions opt-in status to false, so the opt-in modal is not shown again. dispatch(setSmartTransactionsOptInStatus(false)); - history.push(`${ADVANCED_ROUTE}#smart-transactions`); }, [dispatch]); useEffect(() => { @@ -215,9 +211,7 @@ export default function SmartTransactionsOptInModal({ - + diff --git a/ui/components/app/wallet-overview/coin-buttons.tsx b/ui/components/app/wallet-overview/coin-buttons.tsx index 5d710b83adfb..f874df4daa34 100644 --- a/ui/components/app/wallet-overview/coin-buttons.tsx +++ b/ui/components/app/wallet-overview/coin-buttons.tsx @@ -211,16 +211,19 @@ const CoinButtons = ({ ///: END:ONLY_INCLUDE_IF const handleSendOnClick = useCallback(async () => { - trackEvent({ - event: MetaMetricsEventName.NavSendButtonClicked, - category: MetaMetricsEventCategory.Navigation, - properties: { - token_symbol: 'ETH', - location: 'Home', - text: 'Send', - chain_id: chainId, + trackEvent( + { + event: MetaMetricsEventName.NavSendButtonClicked, + category: MetaMetricsEventCategory.Navigation, + properties: { + token_symbol: 'ETH', + location: 'Home', + text: 'Send', + chain_id: chainId, + }, }, - }); + { excludeMetaMetricsId: false }, + ); await dispatch(startNewDraftTransaction({ type: AssetType.native })); history.push(SEND_ROUTE); }, [chainId]); diff --git a/ui/components/multichain/account-list-item/__snapshots__/account-list-item.test.js.snap b/ui/components/multichain/account-list-item/__snapshots__/account-list-item.test.js.snap index ae53a1353084..c14fb8a0c42d 100644 --- a/ui/components/multichain/account-list-item/__snapshots__/account-list-item.test.js.snap +++ b/ui/components/multichain/account-list-item/__snapshots__/account-list-item.test.js.snap @@ -534,17 +534,17 @@ exports[`AccountListItem renders AccountListItem component and shows account nam

- $880.18 + 0.006 - USD + ETH
diff --git a/ui/components/multichain/account-list-item/account-list-item.js b/ui/components/multichain/account-list-item/account-list-item.js index 2b48bcb79baf..49d78fed8af4 100644 --- a/ui/components/multichain/account-list-item/account-list-item.js +++ b/ui/components/multichain/account-list-item/account-list-item.js @@ -51,12 +51,13 @@ import { getUseBlockie, } from '../../../selectors'; import { + getMultichainIsTestnet, getMultichainNativeCurrency, getMultichainNativeCurrencyImage, getMultichainNetwork, + getMultichainShouldShowFiat, } from '../../../selectors/multichain'; import { useMultichainAccountTotalFiatBalance } from '../../../hooks/useMultichainAccountTotalFiatBalance'; -import { TEST_NETWORKS } from '../../../../shared/constants/network'; import { ConnectedStatus } from '../connected-status/connected-status'; ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) import { getCustodianIconForAddress } from '../../../selectors/institutional/selectors'; @@ -90,17 +91,19 @@ export const AccountListItem = ({ useState(); const useBlockie = useSelector(getUseBlockie); - const { network: currentNetwork, isEvmNetwork } = useMultichainSelector( - getMultichainNetwork, - account, - ); + const { isEvmNetwork } = useMultichainSelector(getMultichainNetwork, account); const setAccountListItemMenuRef = (ref) => { setAccountListItemMenuElement(ref); }; + const isTestnet = useMultichainSelector(getMultichainIsTestnet, account); + const isMainnet = !isTestnet; + const shouldShowFiat = useMultichainSelector( + getMultichainShouldShowFiat, + account, + ); const showFiatInTestnets = useSelector(getShowFiatInTestnets); const showFiat = - !isEvmNetwork || - (TEST_NETWORKS.includes(currentNetwork?.nickname) && !showFiatInTestnets); + shouldShowFiat && (isMainnet || (isTestnet && showFiatInTestnets)); const accountTotalFiatBalances = useMultichainAccountTotalFiatBalance(account); const mappedOrderedTokenList = accountTotalFiatBalances.orderedTokenList.map( @@ -108,12 +111,9 @@ export const AccountListItem = ({ avatarValue: item.iconUrl, }), ); - let balanceToTranslate = isEvmNetwork - ? accountTotalFiatBalances.totalWeiBalance + const balanceToTranslate = isEvmNetwork + ? account.balance : accountTotalFiatBalances.totalBalance; - if (showFiat && isEvmNetwork) { - balanceToTranslate = account.balance; - } ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) const custodianIcon = useSelector((state) => @@ -307,9 +307,7 @@ export const AccountListItem = ({ ethNumberOfDecimals={MAXIMUM_CURRENCY_DECIMALS} value={balanceToTranslate} type={PRIMARY} - showFiat={ - !showFiat || !TEST_NETWORKS.includes(currentNetwork?.nickname) - } + showFiat={showFiat} data-testid="first-currency-display" /> 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 b8ae5573bf5f..7bfbbd76d97c 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 @@ -210,6 +210,9 @@ describe('AccountListItem', () => { chainId: CHAIN_IDS.SEPOLIA, nickname: SEPOLIA_DISPLAY_NAME, }, + preferences: { + showFiatInTestnets: false, + }, }, }, ); @@ -263,7 +266,7 @@ describe('AccountListItem', () => { '[data-testid="avatar-group"]', ); - const expectedBalance = '$0.00'; + const expectedBalance = '$3.31'; expect(firstCurrencyDisplay).toBeInTheDocument(); expect(firstCurrencyDisplay.firstChild.textContent).toContain( @@ -275,9 +278,18 @@ describe('AccountListItem', () => { }); it('renders fiat for non-EVM account', () => { - const { container } = render({ - account: mockNonEvmAccount, - }); + const { container } = render( + { + account: mockNonEvmAccount, + }, + { + metamask: { + preferences: { + showFiatInTestnets: true, + }, + }, + }, + ); const firstCurrencyDisplay = container.querySelector( '[data-testid="first-currency-display"]', diff --git a/ui/components/multichain/app-header/app-header-locked-content.tsx b/ui/components/multichain/app-header/app-header-locked-content.tsx index 9285529c8d39..9923b2d31281 100644 --- a/ui/components/multichain/app-header/app-header-locked-content.tsx +++ b/ui/components/multichain/app-header/app-header-locked-content.tsx @@ -6,13 +6,10 @@ import MetafoxLogo from '../../ui/metafox-logo'; import { PickerNetwork } from '../../component-library'; import { DEFAULT_ROUTE } from '../../../helpers/constants/routes'; import { getTestNetworkBackgroundColor } from '../../../selectors'; -import { - MultichainProviderConfig, - ProviderConfigWithImageUrl, -} from '../../../../shared/constants/multichain/networks'; +import { MultichainNetwork } from '../../../selectors/multichain'; type AppHeaderLockedContentProps = { - currentNetwork: ProviderConfigWithImageUrl | MultichainProviderConfig; + currentNetwork: MultichainNetwork; networkOpenCallback: () => void; }; @@ -36,7 +33,7 @@ export const AppHeaderLockedContent = ({ }} aria-label={`${t('networkMenu')} ${currentNetwork?.nickname}`} label={currentNetwork?.nickname ?? ''} - src={currentNetwork?.rpcPrefs?.imageUrl} + src={currentNetwork?.network?.rpcPrefs?.imageUrl} onClick={(e: React.MouseEvent) => { e.stopPropagation(); e.preventDefault(); diff --git a/ui/components/multichain/app-header/app-header-unlocked-content.tsx b/ui/components/multichain/app-header/app-header-unlocked-content.tsx index 2be2d5ffd342..03e73b6e757d 100644 --- a/ui/components/multichain/app-header/app-header-unlocked-content.tsx +++ b/ui/components/multichain/app-header/app-header-unlocked-content.tsx @@ -51,16 +51,13 @@ import { MetaMetricsContext } from '../../../contexts/metametrics'; import { useCopyToClipboard } from '../../../hooks/useCopyToClipboard'; import { MINUTE } from '../../../../shared/constants/time'; import { NotificationsTagCounter } from '../notifications-tag-counter'; -import { - MultichainProviderConfig, - ProviderConfigWithImageUrl, -} from '../../../../shared/constants/multichain/networks'; import { CONNECTIONS } from '../../../helpers/constants/routes'; +import { MultichainNetwork } from '../../../selectors/multichain'; type AppHeaderUnlockedContentProps = { popupStatus: boolean; isEvmNetwork: boolean; - currentNetwork: ProviderConfigWithImageUrl | MultichainProviderConfig; + currentNetwork: MultichainNetwork; networkOpenCallback: () => void; disableNetworkPicker: boolean; disableAccountPicker: boolean; @@ -135,7 +132,7 @@ export const AppHeaderUnlockedContent = ({ className="multichain-app-header__contents--avatar-network" ref={menuRef} as="button" - src={currentNetwork?.rpcPrefs?.imageUrl ?? ''} + src={currentNetwork?.network?.rpcPrefs?.imageUrl ?? ''} label={currentNetwork?.nickname ?? ''} aria-label={`${t('networkMenu')} ${currentNetwork?.nickname}`} labelProps={{ @@ -162,7 +159,7 @@ export const AppHeaderUnlockedContent = ({ margin={2} aria-label={`${t('networkMenu')} ${currentNetwork?.nickname}`} label={currentNetwork?.nickname ?? ''} - src={currentNetwork?.rpcPrefs?.imageUrl} + src={currentNetwork?.network?.rpcPrefs?.imageUrl} onClick={(e: React.MouseEvent) => { e.stopPropagation(); e.preventDefault(); diff --git a/ui/components/multichain/app-header/app-header.js b/ui/components/multichain/app-header/app-header.js index 45f05aadff86..87935e70ccb3 100644 --- a/ui/components/multichain/app-header/app-header.js +++ b/ui/components/multichain/app-header/app-header.js @@ -40,13 +40,8 @@ export const AppHeader = ({ location }) => { const menuRef = useRef(null); const isUnlocked = useSelector(getIsUnlocked); - const { - chainId, - // Used for network icon / dropdown - network: currentNetwork, - // Used for network icon / dropdown - isEvmNetwork, - } = useSelector(getMultichainNetwork); + const multichainNetwork = useSelector(getMultichainNetwork); + const { chainId, isEvmNetwork } = multichainNetwork; const dispatch = useDispatch(); @@ -149,7 +144,7 @@ export const AppHeader = ({ location }) => { { /> ) : ( )} diff --git a/ui/components/multichain/app-header/app-header.test.js b/ui/components/multichain/app-header/app-header.test.js index 5b74f17858cd..d72d65510d96 100644 --- a/ui/components/multichain/app-header/app-header.test.js +++ b/ui/components/multichain/app-header/app-header.test.js @@ -23,6 +23,8 @@ jest.mock('react-router-dom', () => ({ const render = ({ stateChanges = {}, + provider = {}, + networkConfigurations = {}, location = {}, isUnlocked = true, } = {}) => { @@ -30,12 +32,20 @@ const render = ({ ...mockState, metamask: { ...mockState.metamask, - isUnlocked, + providerConfig: { + ...mockState.metamask.providerConfig, + ...(provider ?? {}), + }, + networkConfigurations: { + ...mockState.metamask.networkConfigurations, + ...(networkConfigurations ?? {}), + }, + isUnlocked: isUnlocked ?? true, }, activeTab: { origin: 'https://remix.ethereum.org', }, - ...stateChanges, + ...(stateChanges ?? {}), }); return renderWithProvider(, store); }; @@ -175,4 +185,48 @@ describe('App Header', () => { expect(connectionPickerButton).not.toBeInTheDocument(); }); }); + + 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, + isUnlocked: true, + }); + expect(getByText(mockProviderConfig.nickname)).toBeInTheDocument(); + }); + + 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, + isUnlocked: true, + }); + expect(getByText(mockProviderConfig.rpcUrl)).toBeInTheDocument(); + }); + }); }); 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 190da522cad2..12be182cfbd3 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker-amount.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker-amount.tsx @@ -41,6 +41,7 @@ type AssetPickerAmountProps = OverridingUnion< asset: Asset; amount: Amount; isAmountLoading?: boolean; + error?: string; /** * Callback for when the amount changes; disables the input when undefined */ @@ -57,6 +58,7 @@ export const AssetPickerAmount = ({ amount, onAmountChange, isAmountLoading, + error: passedError, ...assetPickerProps }: AssetPickerAmountProps) => { const selectedAccount = useSelector(getSelectedInternalAccount); @@ -170,7 +172,9 @@ export const AssetPickerAmount = ({ {/* Only show balance if mutable */} - {onAmountChange && } + {onAmountChange && ( + + )} {isSwapsErrorShown && ( {t(swapQuotesError)} 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 0700f62a4cec..b0c2b6a4566d 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 @@ -60,6 +60,7 @@ import { import { isEqualCaseInsensitive } from '../../../../../shared/modules/string-utils'; import { Asset, Collection, Token } from './types'; import { AssetPickerModalNftTab } from './asset-picker-modal-nft-tab'; + import AssetList from './AssetList'; type AssetPickerModalProps = { @@ -122,17 +123,22 @@ export function AssetPickerModal({ const handleAssetChange = useCallback( (token: Token) => { onAssetChange(token); - trackEvent({ - event: MetaMetricsEventName.sendAssetSelected, - category: MetaMetricsEventCategory.Send, - properties: { - ...sendAnalytics, - is_destination_asset_picker_modal: Boolean(isDest), - new_asset_symbol: token.symbol, - new_asset_address: token.address, - is_nft: false, + 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], 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 5cf53d522d98..3ce7f37b591e 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 @@ -36,7 +36,10 @@ import { 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, @@ -66,7 +69,9 @@ export function AssetPicker({ sendingAsset, isDisabled = false, }: 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); @@ -119,6 +124,16 @@ export function AssetPicker({ const currentNetwork = useSelector(getCurrentNetwork); const testNetworkBackgroundColor = useSelector(getTestNetworkBackgroundColor); + const handleAssetPickerTitle = (): string | undefined => { + ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) + if (isDisabled) { + return t('swapTokenNotAvailable'); + } + ///: END:ONLY_INCLUDE_IF + + return undefined; + }; + return ( <> {/* This is the Modal that ask to choose token to send */} @@ -147,14 +162,19 @@ export function AssetPicker({ backgroundColor={BackgroundColor.transparent} onClick={() => { setShowAssetPickerModal(true); - trackEvent({ - event: MetaMetricsEventName.sendTokenModalOpened, - category: MetaMetricsEventCategory.Send, - properties: { - ...sendAnalytics, - is_destination_asset_picker_modal: Boolean(sendingAsset), + trackEvent( + { + event: MetaMetricsEventName.sendTokenModalOpened, + category: MetaMetricsEventCategory.Send, + properties: { + is_destination_asset_picker_modal: Boolean(sendingAsset), + }, + sensitiveProperties: { + ...sendAnalytics, + }, }, - }); + { excludeMetaMetricsId: false }, + ); }} endIconName={IconName.ArrowDown} endIconProps={{ @@ -162,14 +182,14 @@ export function AssetPicker({ marginInlineStart: 0, display: isDisabled ? Display.None : Display.InlineBlock, }} - title={isDisabled ? t('swapTokenNotAvailable') : undefined} + title={handleAssetPickerTitle()} > { if (shouldShowEnableModal) { trackEvent({ - category: MetaMetricsEventCategory.EnableNotifications, - event: MetaMetricsEventName.StartEnablingNotificationsFlow, + category: MetaMetricsEventCategory.NotificationInteraction, + event: MetaMetricsEventName.NotificationMenuOpened, properties: { - isProfileSyncingEnabled, - isMetamaskNotificationsEnabled, + is_profile_syncing_enabled: isProfileSyncingEnabled, + is_notifications_enabled: isMetamaskNotificationsEnabled, }, }); dispatch(showConfirmTurnOnMetamaskNotifications()); + closeMenu(); return; } @@ -159,10 +160,10 @@ export const GlobalMenu = ({ closeMenu, anchorElement, isOpen }) => { // Otherwise we can navigate to the notifications page trackEvent({ category: MetaMetricsEventCategory.NotificationInteraction, - event: MetaMetricsEventName.NotificationPageOpened, + event: MetaMetricsEventName.NotificationMenuOpened, properties: { - isProfileSyncingEnabled, - isMetamaskNotificationsEnabled, + is_profile_syncing_enabled: isProfileSyncingEnabled, + is_notifications_enabled: isMetamaskNotificationsEnabled, }, }); history.push(NOTIFICATIONS_ROUTE); diff --git a/ui/components/multichain/import-tokens-modal/import-tokens-modal.js b/ui/components/multichain/import-tokens-modal/import-tokens-modal.js index 4ec483f13ff8..102977dc02d0 100644 --- a/ui/components/multichain/import-tokens-modal/import-tokens-modal.js +++ b/ui/components/multichain/import-tokens-modal/import-tokens-modal.js @@ -26,7 +26,7 @@ import { getTokenList, getCurrentNetwork, getTestNetworkBackgroundColor, - contractExchangeRateSelector, + getTokenExchangeRates, } from '../../../selectors'; import { addImportedTokens, @@ -137,7 +137,7 @@ export const ImportTokensModal = ({ onClose }) => { const accounts = useSelector(getInternalAccounts); const tokens = useSelector((state) => state.metamask.tokens); const rpcPrefs = useSelector(getRpcPrefsForCurrentProvider); - const contractExchangeRates = useSelector(contractExchangeRateSelector); + const contractExchangeRates = useSelector(getTokenExchangeRates); const [customAddress, setCustomAddress] = useState(''); const [customAddressError, setCustomAddressError] = useState(null); diff --git a/ui/components/multichain/network-list-menu/network-list-menu.js b/ui/components/multichain/network-list-menu/network-list-menu.js index 6f511b6a8bcd..88d28325f901 100644 --- a/ui/components/multichain/network-list-menu/network-list-menu.js +++ b/ui/components/multichain/network-list-menu/network-list-menu.js @@ -34,6 +34,7 @@ import { getUseRequestQueue, getNetworkConfigurations, getEditedNetwork, + getAllDomains, } from '../../../selectors'; import ToggleButton from '../../ui/toggle-button'; import { @@ -99,6 +100,7 @@ export const NetworkListMenu = ({ onClose }) => { const selectedTabOrigin = useSelector(getOriginOfCurrentTab); const useRequestQueue = useSelector(getUseRequestQueue); const networkConfigurations = useSelector(getNetworkConfigurations); + const domains = useSelector(getAllDomains); const dispatch = useDispatch(); const history = useHistory(); @@ -294,10 +296,14 @@ export const NetworkListMenu = ({ onClose }) => { dispatch(setActiveNetwork(network.id)); } - // If presently on a dapp, communicate a change to - // the dapp via silent switchEthereumChain that the - // network has changed due to user action - if (useRequestQueue && selectedTabOrigin) { + // If presently on and connected to a dapp, communicate a change to + // the dapp via silent switchEthereumChain that the network has + // changed due to user action + if ( + useRequestQueue && + selectedTabOrigin && + domains[selectedTabOrigin] + ) { setNetworkClientIdForDomain(selectedTabOrigin, network.id); } 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 678745defa25..913079ec0d91 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 @@ -40,6 +40,7 @@ const render = ({ providerConfigId = 'chain5', isUnlocked = true, origin = MOCK_ORIGIN, + selectedTabOriginInDomainsState = true, } = {}) => { const state = { metamask: { @@ -54,6 +55,11 @@ const render = ({ showTestNetworks, }, useRequestQueue: true, + domains: { + ...(selectedTabOriginInDomainsState + ? { [origin]: providerConfigId } + : {}), + }, }, activeTab: { origin, @@ -228,4 +234,33 @@ describe('NetworkListMenu', () => { expect(queryByText('Sepolia')).not.toBeInTheDocument(); }); }); + + describe('selectedTabOrigin is connected to wallet', () => { + it('fires setNetworkClientIdForDomain when network item is clicked', () => { + const { getByText } = render(); + fireEvent.click(getByText(MAINNET_DISPLAY_NAME)); + expect(mockSetNetworkClientIdForDomain).toHaveBeenCalledWith( + MOCK_ORIGIN, + NETWORK_TYPES.MAINNET, + ); + }); + + it('fires setNetworkClientIdForDomain when test network item is clicked', () => { + const { getByText } = render({ showTestNetworks: true }); + fireEvent.click(getByText(SEPOLIA_DISPLAY_NAME)); + expect(mockSetNetworkClientIdForDomain).toHaveBeenCalledWith( + MOCK_ORIGIN, + NETWORK_TYPES.SEPOLIA, + ); + }); + }); + + describe('selectedTabOrigin is not connected to wallet', () => { + it('does not fire setNetworkClientIdForDomain when network item is clicked', () => { + jest.clearAllMocks(); + const { getByText } = render({ selectedTabOriginInDomainsState: false }); + fireEvent.click(getByText(MAINNET_DISPLAY_NAME)); + expect(mockSetNetworkClientIdForDomain).not.toHaveBeenCalled(); + }); + }); }); diff --git a/ui/components/multichain/nft-item/nft-item.js b/ui/components/multichain/nft-item/nft-item.js index d955db8a32f3..6d87658026ef 100644 --- a/ui/components/multichain/nft-item/nft-item.js +++ b/ui/components/multichain/nft-item/nft-item.js @@ -116,7 +116,7 @@ NftItem.propTypes = { /** * Image that represents the network */ - networkSrc: PropTypes.string.isRequired, + networkSrc: PropTypes.string, /** * Token ID of the NFT */ 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 23237a70a0c7..949a2edd2255 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 @@ -26,7 +26,8 @@ export const NotificationDetailBlockExplorerButton = ({ const t = useI18nContext(); const chainIdHex = decimalToHex(chainId); - const { nativeBlockExplorerUrl } = getNetworkDetailsByChainId( + + const { blockExplorerConfig } = getNetworkDetailsByChainId( `0x${chainIdHex}` as keyof typeof CHAIN_IDS, ); @@ -36,7 +37,21 @@ export const NotificationDetailBlockExplorerButton = ({ }, [defaultNetworks]); const blockExplorerUrl = - defaultNetwork?.rpcPrefs?.blockExplorerUrl ?? nativeBlockExplorerUrl; + defaultNetwork?.rpcPrefs?.blockExplorerUrl ?? blockExplorerConfig?.url; + + const getBlockExplorerButtonText = () => { + if (defaultNetwork?.rpcPrefs?.blockExplorerUrl) { + return t('notificationItemCheckBlockExplorer'); + } + if (blockExplorerConfig?.name) { + return t('notificationTransactionSuccessView', [ + blockExplorerConfig.name, + ]); + } + return t('notificationItemCheckBlockExplorer'); + }; + + const blockExplorerButtonText = getBlockExplorerButtonText(); if (!blockExplorerUrl) { return null; @@ -46,7 +61,7 @@ export const NotificationDetailBlockExplorerButton = ({ { trackEvent({ category: MetaMetricsEventCategory.NotificationInteraction, - event: MetaMetricsEventName.NotificationDetailClicked, + event: MetaMetricsEventName.NotificationClicked, properties: { - notificationId: notification.id, - notificationType: notification.type, + notification_id: notification.id, + notification_type: notification.type, + ...(notification.type !== TRIGGER_TYPES.FEATURES_ANNOUNCEMENT && { + chain_id: notification?.chain_id, + }), + notification_is_read: notification.isRead, + click_type: 'detail', }, }); }; diff --git a/ui/components/multichain/pages/connections/__snapshots__/connections.test.tsx.snap b/ui/components/multichain/pages/connections/__snapshots__/connections.test.tsx.snap index 3864de9c921e..afd02098086f 100644 --- a/ui/components/multichain/pages/connections/__snapshots__/connections.test.tsx.snap +++ b/ui/components/multichain/pages/connections/__snapshots__/connections.test.tsx.snap @@ -6,7 +6,7 @@ exports[`Connections Content should render correctly 1`] = ` class="mm-box multichain-page mm-box--display-flex mm-box--flex-direction-row mm-box--justify-content-center mm-box--width-full mm-box--height-full mm-box--background-color-background-alternative" >
- $880.18 + 966.988 - USD + ETH
diff --git a/ui/components/multichain/pages/connections/connections.tsx b/ui/components/multichain/pages/connections/connections.tsx index 6dc382288ef4..6e75c222c59d 100644 --- a/ui/components/multichain/pages/connections/connections.tsx +++ b/ui/components/multichain/pages/connections/connections.tsx @@ -191,7 +191,10 @@ export const Connections = () => { ); return ( - +
{ }); return ( - +
+ > + $0.00 + @@ -500,7 +502,7 @@ exports[`SendPage render and initialization should render correctly even when a
- ? + E
- $880.18 + 966.988 - USD + ETH
@@ -538,17 +538,17 @@ exports[`SendPageYourAccounts render renders correctly 1`] = `
- $0.00 + 0 - USD + ETH
@@ -835,17 +835,17 @@ exports[`SendPageYourAccounts render renders correctly 1`] = `
- $0.00 + 0 - USD + ETH
@@ -1141,17 +1141,17 @@ exports[`SendPageYourAccounts render renders correctly 1`] = `
- $0.00 + 0 - USD + ETH
@@ -1438,17 +1438,17 @@ exports[`SendPageYourAccounts render renders correctly 1`] = `
- $0.00 + 0 - USD + ETH
@@ -1748,17 +1748,17 @@ exports[`SendPageYourAccounts render renders correctly 1`] = `
- $0.00 + 0 - USD + ETH
diff --git a/ui/components/multichain/pages/send/components/address-book.tsx b/ui/components/multichain/pages/send/components/address-book.tsx index 84e29e0a1ed5..8100646486ca 100644 --- a/ui/components/multichain/pages/send/components/address-book.tsx +++ b/ui/components/multichain/pages/send/components/address-book.tsx @@ -99,14 +99,17 @@ export const SendPageAddressBook = () => { `sendFlow - User clicked recipient from ${type}. address: ${address}, nickname ${nickname}`, ), ); - trackEvent({ - event: MetaMetricsEventName.sendRecipientSelected, - category: MetaMetricsEventCategory.Send, - properties: { - location: 'address book', - inputType: type, + trackEvent( + { + event: MetaMetricsEventName.sendRecipientSelected, + category: MetaMetricsEventCategory.Send, + properties: { + location: 'address book', + inputType: type, + }, }, - }); + { excludeMetaMetricsId: false }, + ); dispatch(updateRecipient({ address, nickname })); dispatch(updateRecipientUserInput(address)); }; diff --git a/ui/components/multichain/pages/send/components/network-picker.tsx b/ui/components/multichain/pages/send/components/network-picker.tsx index fd2f52f985a7..f4aaa0f16ebf 100644 --- a/ui/components/multichain/pages/send/components/network-picker.tsx +++ b/ui/components/multichain/pages/send/components/network-picker.tsx @@ -12,7 +12,7 @@ export const SendPageNetworkPicker = () => { return ( dispatch(toggleNetworkMenu())} data-testid="send-page-network-picker" diff --git a/ui/components/multichain/pages/send/components/quote-card/index.tsx b/ui/components/multichain/pages/send/components/quote-card/index.tsx index d3815fd3d64c..99b84e42a29b 100644 --- a/ui/components/multichain/pages/send/components/quote-card/index.tsx +++ b/ui/components/multichain/pages/send/components/quote-card/index.tsx @@ -85,14 +85,19 @@ export function QuoteCard({ scrollRef }: QuoteCardProps) { } if (bestQuote) { - trackEvent({ - event: MetaMetricsEventName.sendSwapQuoteFetched, - category: MetaMetricsEventCategory.Send, - properties: { - ...sendAnalytics, - is_first_fetch: isQuoteJustLoaded, + trackEvent( + { + event: MetaMetricsEventName.sendSwapQuoteFetched, + category: MetaMetricsEventCategory.Send, + properties: { + is_first_fetch: isQuoteJustLoaded, + }, + sensitiveProperties: { + ...sendAnalytics, + }, }, - }); + { excludeMetaMetricsId: false }, + ); setTimeLeft(REFRESH_INTERVAL); } else { setTimeLeft(undefined); diff --git a/ui/components/multichain/pages/send/components/recipient-content.tsx b/ui/components/multichain/pages/send/components/recipient-content.tsx index 097214d7cd25..419cba588b47 100644 --- a/ui/components/multichain/pages/send/components/recipient-content.tsx +++ b/ui/components/multichain/pages/send/components/recipient-content.tsx @@ -1,4 +1,10 @@ -import React, { useCallback, useMemo, useRef } from 'react'; +import React, { + useCallback, + ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) + useMemo, + ///: END:ONLY_INCLUDE_IF + useRef, +} from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { BannerAlert, @@ -11,9 +17,11 @@ import { acknowledgeRecipientWarning, getBestQuote, getCurrentDraftTransaction, - getIsSwapAndSendDisabledForNetwork, getSendAsset, + ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) + getIsSwapAndSendDisabledForNetwork, getSwapsBlockedTokens, + ///: END:ONLY_INCLUDE_IF } from '../../../../../ducks/send'; import { AssetType } from '../../../../../../shared/constants/transaction'; import { CONTRACT_ADDRESS_LINK } from '../../../../../helpers/constants/common'; @@ -21,10 +29,13 @@ import { Display } from '../../../../../helpers/constants/design-system'; import { useI18nContext } from '../../../../../hooks/useI18nContext'; import { AssetPickerAmount } from '../../..'; import { decimalToHex } from '../../../../../../shared/modules/conversion.utils'; +///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) import { getIsSwapsChain, 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 { SendHexData, SendPageRow, QuoteCard } from '.'; @@ -45,6 +56,13 @@ export const SendPageRecipientContent = ({ isSwapQuoteLoading, } = useSelector(getCurrentDraftTransaction); + let isSwapAllowed; + + ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) + isSwapAllowed = false; + ///: END:ONLY_INCLUDE_IF + + ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) const isBasicFunctionality = useSelector(getUseExternalServices); const isSwapsChain = useSelector(getIsSwapsChain); const isSwapAndSendDisabledForNetwork = useSelector( @@ -54,12 +72,14 @@ export const SendPageRecipientContent = ({ const memoizedSwapsBlockedTokens = useMemo(() => { return new Set(swapsBlockedTokens); }, [swapsBlockedTokens]); - const isSwapAllowed = + + isSwapAllowed = isSwapsChain && !isSwapAndSendDisabledForNetwork && [AssetType.token, AssetType.native].includes(sendAsset.type) && isBasicFunctionality && !memoizedSwapsBlockedTokens.has(sendAsset.details?.address?.toLowerCase()); + ///: END:ONLY_INCLUDE_IF const bestQuote: Quote = useSelector(getBestQuote); diff --git a/ui/components/multichain/pages/send/components/recipient-input.tsx b/ui/components/multichain/pages/send/components/recipient-input.tsx index debcdbbdca36..4d7d8cee633b 100644 --- a/ui/components/multichain/pages/send/components/recipient-input.tsx +++ b/ui/components/multichain/pages/send/components/recipient-input.tsx @@ -46,14 +46,17 @@ export const SendPageRecipientInput = () => { addHistoryEntry(`sendFlow - Valid address typed ${address}`), ); await dispatch(updateRecipientUserInput(address)); - trackEvent({ - event: MetaMetricsEventName.sendRecipientSelected, - category: MetaMetricsEventCategory.Send, - properties: { - location: 'send page recipient input', - inputType: 'user input', + trackEvent( + { + event: MetaMetricsEventName.sendRecipientSelected, + category: MetaMetricsEventCategory.Send, + properties: { + location: 'send page recipient input', + inputType: 'user input', + }, }, - }); + { excludeMetaMetricsId: false }, + ); dispatch(updateRecipient({ address, nickname: '' })); }} internalSearch={isUsingMyAccountsForRecipientSearch} diff --git a/ui/components/multichain/pages/send/components/recipient.tsx b/ui/components/multichain/pages/send/components/recipient.tsx index 971f7ec41a72..f2049493133a 100644 --- a/ui/components/multichain/pages/send/components/recipient.tsx +++ b/ui/components/multichain/pages/send/components/recipient.tsx @@ -24,6 +24,7 @@ import { MetaMetricsEventCategory, MetaMetricsEventName, } from '../../../../../../shared/constants/metametrics'; + import { MetaMetricsContext } from '../../../../../contexts/metametrics'; import { DomainInputResolutionCell } from './domain-input-resolution-cell'; import { SendPageAddressBook, SendPageRow, SendPageYourAccounts } from '.'; @@ -66,14 +67,17 @@ export const SendPageRecipient = () => { `sendFlow - User clicked recipient from ${type}. address: ${address}, nickname ${nickname}`, ), ); - trackEvent({ - event: MetaMetricsEventName.sendRecipientSelected, - category: MetaMetricsEventCategory.Send, - properties: { - location: 'send page recipient screen', - inputType: type, + trackEvent( + { + event: MetaMetricsEventName.sendRecipientSelected, + category: MetaMetricsEventCategory.Send, + properties: { + location: 'send page recipient screen', + inputType: type, + }, }, - }); + { excludeMetaMetricsId: false }, + ); dispatch(updateRecipient({ address, nickname })); dispatch(updateRecipientUserInput(address)); }; diff --git a/ui/components/multichain/pages/send/components/your-accounts.tsx b/ui/components/multichain/pages/send/components/your-accounts.tsx index a881e8922634..d05926e7ae87 100644 --- a/ui/components/multichain/pages/send/components/your-accounts.tsx +++ b/ui/components/multichain/pages/send/components/your-accounts.tsx @@ -42,14 +42,17 @@ export const SendPageYourAccounts = () => { `sendFlow - User clicked recipient from my accounts. address: ${account.address}, nickname ${account.name}`, ), ); - trackEvent({ - event: MetaMetricsEventName.sendRecipientSelected, - category: MetaMetricsEventCategory.Send, - properties: { - location: 'my accounts', - inputType: 'click', + trackEvent( + { + event: MetaMetricsEventName.sendRecipientSelected, + category: MetaMetricsEventCategory.Send, + properties: { + location: 'my accounts', + inputType: 'click', + }, }, - }); + { excludeMetaMetricsId: false }, + ); dispatch( updateRecipient({ address: account.address, diff --git a/ui/components/multichain/pages/send/send.js b/ui/components/multichain/pages/send/send.js index c724a042b1d3..3195dceab5d3 100644 --- a/ui/components/multichain/pages/send/send.js +++ b/ui/components/multichain/pages/send/send.js @@ -58,6 +58,7 @@ import { AssetPickerAmount } from '../..'; import useUpdateSwapsState from '../../../../hooks/useUpdateSwapsState'; import { getIsDraftSwapAndSend } from '../../../../ducks/send/helpers'; import { smartTransactionsListSelector } from '../../../../selectors'; +import { TRANSACTION_ERRORED_EVENT } from '../../../app/transaction-activity-log/transaction-activity-log.constants'; import { SendPageAccountPicker, SendPageRecipientContent, @@ -91,6 +92,7 @@ export const SendPage = () => { const sendAnalytics = useSelector(getSendAnalyticProperties); const [isSubmitting, setIsSubmitting] = useState(false); + const [error, setError] = useState(undefined); const handleSelectToken = useCallback( (token, isReceived) => { @@ -151,6 +153,7 @@ export const SendPage = () => { const cleanup = useCallback(() => { dispatch(resetSendState()); setIsSubmitting(false); + setError(undefined); }, [dispatch]); /** @@ -198,13 +201,16 @@ export const SendPage = () => { } dispatch(resetSendState()); - trackEvent({ - event: MetaMetricsEventName.sendFlowExited, - category: MetaMetricsEventCategory.Send, - properties: { - ...sendAnalytics, + trackEvent( + { + event: MetaMetricsEventName.sendFlowExited, + category: MetaMetricsEventCategory.Send, + sensitiveProperties: { + ...sendAnalytics, + }, }, - }); + { excludeMetaMetricsId: false }, + ); const nextRoute = sendStage === SEND_STAGES.EDIT ? DEFAULT_ROUTE : mostRecentOverviewPage; @@ -213,13 +219,16 @@ export const SendPage = () => { useEffect(() => { if (swapQuotesError) { - trackEvent({ - event: MetaMetricsEventName.sendSwapQuoteError, - category: MetaMetricsEventCategory.Send, - properties: { - ...sendAnalytics, + trackEvent( + { + event: MetaMetricsEventName.sendSwapQuoteError, + category: MetaMetricsEventCategory.Send, + sensitiveProperties: { + ...sendAnalytics, + }, }, - }); + { excludeMetaMetricsId: false }, + ); } // sendAnalytics should not result in the event refiring // eslint-disable-next-line react-hooks/exhaustive-deps @@ -229,20 +238,28 @@ export const SendPage = () => { event.preventDefault(); setIsSubmitting(true); - await dispatch(signTransaction(history)); - // prevents state update on unmounted component error - if (isSubmitting) { - setIsSubmitting(false); + setError(undefined); + + try { + await dispatch(signTransaction(history)); + + trackEvent({ + category: MetaMetricsEventCategory.Transactions, + event: 'Complete', + properties: { + ...sendAnalytics, + action: isSwapAndSend ? 'Submit Immediately' : 'Edit Screen', + legacy_event: true, + }, + }); + } catch { + setError(TRANSACTION_ERRORED_EVENT); + } finally { + // prevents state update on unmounted component error + if (isSubmitting) { + setIsSubmitting(false); + } } - trackEvent({ - category: MetaMetricsEventCategory.Transactions, - event: 'Complete', - properties: { - ...sendAnalytics, - action: isSwapAndSend ? 'Submit Immediately' : 'Edit Screen', - legacy_event: true, - }, - }); }; // Submit button @@ -285,8 +302,10 @@ export const SendPage = () => { useUpdateSwapsState(); const onAmountChange = useCallback( - (newAmountRaw, newAmountFormatted) => - dispatch(updateSendAmount(newAmountRaw, newAmountFormatted)), + (newAmountRaw, newAmountFormatted) => { + dispatch(updateSendAmount(newAmountRaw, newAmountFormatted)); + setError(undefined); + }, [dispatch], ); @@ -316,6 +335,7 @@ export const SendPage = () => { {isSendFormShown && ( { chainId: CHAIN_IDS.GOERLI, nickname: GOERLI_DISPLAY_NAME, type: NETWORK_TYPES.GOERLI, + ticker: 'ETH', }, }, }; 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 a80941b84c1a..b209e4d0603d 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 @@ -26,7 +26,7 @@ exports[`TokenListItem should render correctly 1`] = ` class="mm-box mm-text mm-avatar-base mm-avatar-base--size-xs mm-avatar-network mm-text--body-xs mm-text--text-transform-uppercase mm-box--display-flex mm-box--justify-content-center mm-box--align-items-center mm-box--color-text-default mm-box--background-color-background-alternative mm-box--rounded-full mm-box--border-color-border-muted box--border-style-solid box--border-width-1" > Ethereum Mainnet logo diff --git a/ui/components/ui/deprecated-networks/deprecated-networks.js b/ui/components/ui/deprecated-networks/deprecated-networks.js index 3d596709f934..a67f8258317e 100644 --- a/ui/components/ui/deprecated-networks/deprecated-networks.js +++ b/ui/components/ui/deprecated-networks/deprecated-networks.js @@ -17,7 +17,7 @@ import { DEPRECATED_NETWORKS, NEAR_AURORA_MAINNET_IMAGE_URL, } from '../../../../shared/constants/network'; -import { editAndSetNetworkConfiguration } from '../../../store/actions'; +import { upsertNetworkConfiguration } from '../../../store/actions'; import { MetaMetricsNetworkEventSource } from '../../../../shared/constants/metametrics'; export default function DeprecatedNetworks() { @@ -59,9 +59,9 @@ export default function DeprecatedNetworks() { actionButtonOnClick: async () => { setIsClosed(true); await dispatch( - editAndSetNetworkConfiguration( + upsertNetworkConfiguration( { - networkConfigurationId: id, + id, chainId: CHAIN_IDS.AURORA, nickname: AURORA_DISPLAY_NAME, rpcUrl: 'https://mainnet.aurora.dev', @@ -71,7 +71,10 @@ export default function DeprecatedNetworks() { blockExplorerUrl: 'https://aurorascan.dev', }, }, - { source: MetaMetricsNetworkEventSource.DeprecatedNetworkModal }, + { + source: MetaMetricsNetworkEventSource.DeprecatedNetworkModal, + setActive: true, + }, ), ); }, diff --git a/ui/contexts/snaps/snap-interface.tsx b/ui/contexts/snaps/snap-interface.tsx index 45af0572a03e..2b46781e1aa4 100644 --- a/ui/contexts/snaps/snap-interface.tsx +++ b/ui/contexts/snaps/snap-interface.tsx @@ -5,6 +5,7 @@ import { UserInputEventType, } from '@metamask/snaps-sdk'; import { encodeBase64 } from '@metamask/snaps-utils'; + import { Json } from '@metamask/utils'; import { debounce, throttle } from 'lodash'; import React, { diff --git a/ui/ducks/send/send.js b/ui/ducks/send/send.js index 8cfb7f4d5356..b915907f5b42 100644 --- a/ui/ducks/send/send.js +++ b/ui/ducks/send/send.js @@ -989,6 +989,10 @@ const slice = createSlice({ const draftTransaction = state.draftTransactions[state.currentTransactionUUID]; + if (!draftTransaction) { + return; + } + // use maxFeePerGas as the multiplier if working with a FEE_MARKET transaction // otherwise use gasPrice if ( @@ -1080,7 +1084,7 @@ const slice = createSlice({ const draftTransaction = state.draftTransactions[state.currentTransactionUUID]; let amount = '0x0'; - if (draftTransaction.sendAsset.type === AssetType.token) { + if (draftTransaction?.sendAsset.type === AssetType.token) { const decimals = draftTransaction.sendAsset.details?.decimals ?? 0; const multiplier = Math.pow(10, Number(decimals)); @@ -1292,7 +1296,7 @@ const slice = createSlice({ state.gasTotalForLayer1 = action.payload; if ( state.amountMode === AMOUNT_MODES.MAX && - draftTransaction.sendAsset.type === AssetType.native + draftTransaction?.sendAsset.type === AssetType.native ) { slice.caseReducers.updateAmountToMax(state); } @@ -1456,6 +1460,10 @@ const slice = createSlice({ const draftTransaction = state.draftTransactions[state.currentTransactionUUID]; + if (!draftTransaction) { + return; + } + const amountValue = new Numeric(draftTransaction.amount.value, 16); switch (true) { @@ -1557,6 +1565,11 @@ const slice = createSlice({ validateGasField: (state) => { const draftTransaction = state.draftTransactions[state.currentTransactionUUID]; + + if (!draftTransaction) { + return; + } + const insufficientFunds = !isBalanceSufficient({ amount: draftTransaction.sendAsset.type === AssetType.native diff --git a/ui/ducks/send/send.test.js b/ui/ducks/send/send.test.js index ac31dba417f6..65953d9a41fb 100644 --- a/ui/ducks/send/send.test.js +++ b/ui/ducks/send/send.test.js @@ -105,14 +105,6 @@ jest.mock('lodash', () => ({ debounce: (fn) => fn, })); -setBackgroundConnection({ - addPollingTokenToAppState: jest.fn(), - addTransaction: jest.fn((_u, _v, cb) => { - cb(null, { transactionMeta: null }); - }), - updateTransactionSendFlowHistory: jest.fn((_x, _y, _z, cb) => cb(null)), -}); - const getTestUUIDTx = (state) => state.draftTransactions['test-uuid']; describe('Send Slice', () => { @@ -124,6 +116,14 @@ describe('Send Slice', () => { let setDefaultHomeActiveTabNameStub; beforeEach(() => { + setBackgroundConnection({ + addPollingTokenToAppState: jest.fn(), + addTransaction: jest.fn((_u, _v, cb) => { + cb(null, { transactionMeta: null }); + }), + updateTransactionSendFlowHistory: jest.fn((_x, _y, _z, cb) => cb(null)), + }); + jest.useFakeTimers(); getTokenStandardAndDetailsStub = jest .spyOn(Actions, 'getTokenStandardAndDetails') @@ -283,7 +283,28 @@ describe('Send Slice', () => { expect(draft.amount.value).toStrictEqual('0xadf7'); expect(draft.status).toStrictEqual(SEND_STATUSES.VALID); }); + + it('should not error when draft transaction is not defined', () => { + const state = getInitialSendStateWithExistingTxState({ + gas: { + gasPrice: '0x1', + maxFeePerGas: '0x2', + gasLimit: GAS_LIMITS.SIMPLE, + }, + }); + + delete state.draftTransactions['test-uuid']; + + const action = { + type: 'send/calculateGasTotal', + }; + + const runAction = () => sendReducer(state, action); + + expect(runAction).not.toThrow(); + }); }); + describe('resetSendState', () => { it('should set the state back to a blank slate matching the initialState object', () => { const action = { @@ -1076,6 +1097,29 @@ describe('Send Slice', () => { ); expect(draftTransaction.status).toBe(SEND_STATUSES.INVALID); }); + + it('should not throw error when draft transaction does not exist', () => { + const tokenAssetState = getInitialSendStateWithExistingTxState({ + amount: { + value: '0x77359400', // 2000000000 + }, + sendAsset: { + type: AssetType.token, + balance: '0x6fc23ac0', // 1875000000 + details: { + decimals: 0, + }, + }, + }); + + const action = { + type: 'send/validateAmountField', + }; + + delete tokenAssetState.draftTransactions['test-uuid']; + + expect(() => sendReducer(tokenAssetState, action)).not.toThrow(); + }); }); describe('validateGasField', () => { @@ -1101,6 +1145,24 @@ describe('Send Slice', () => { INSUFFICIENT_FUNDS_ERROR, ); }); + it('should not throw error when draft transaction does not exist', () => { + const gasFieldState = getInitialSendStateWithExistingTxState({ + account: { + balance: '0x0', + }, + gas: { + gasTotal: '0x1319718a5000', // 21000000000000 + }, + }); + + delete gasFieldState.draftTransactions['test-uuid']; + + const action = { + type: 'send/validateGasField', + }; + + expect(() => sendReducer(gasFieldState, action)).not.toThrow(); + }); }); // TODO: update this @@ -3020,6 +3082,74 @@ describe('Send Slice', () => { addTransactionAndRouteToConfirmationPageStub.mock.calls[0][0].to, ).toStrictEqual('0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'); }); + it('should rethrow addTransactionAndRouteToConfirmationPage errors', async () => { + const tokenTransferTxState = { + metamask: { + providerConfig: { + chainId: CHAIN_IDS.GOERLI, + }, + transactions: [ + { + id: 1, + chainId: CHAIN_IDS.GOERLI, + status: 'unapproved', + txParams: { + value: 'oldTxValue', + }, + }, + ], + }, + send: { + ...getInitialSendStateWithExistingTxState({ + id: 1, + sendAsset: { + details: { + address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', + }, + type: 'TOKEN', + }, + receiveAsset: { + details: { + address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', + }, + type: 'TOKEN', + }, + recipient: { + address: '4F90e18605Fd46F9F9Fab0e225D88e1ACf5F5324', + }, + amount: { + value: '0x1', + }, + }), + stage: SEND_STAGES.DRAFT, + selectedAccount: { + address: '0x6784e8507A1A46443f7bDc8f8cA39bdA92A675A6', + }, + }, + }; + + jest.mock('../../store/actions.ts'); + + const store = mockStore(tokenTransferTxState); + + const ERROR = new Error('rejected'); + + const history = { push: jest.fn() }; + + setBackgroundConnection({ + addPollingTokenToAppState: jest.fn(), + addTransaction: jest.fn((_u, _v) => { + throw new Error(ERROR); + }), + updateTransactionSendFlowHistory: jest.fn((_x, _y, _z, cb) => + cb(null), + ), + }); + + await expect( + store.dispatch(signTransaction(history)), + ).rejects.toThrow('rejected'); + }); }); describe('with swap+send', () => { diff --git a/ui/helpers/constants/metamask-notifications/metamask-notifications.ts b/ui/helpers/constants/metamask-notifications/metamask-notifications.ts new file mode 100644 index 000000000000..0b7169ce57a8 --- /dev/null +++ b/ui/helpers/constants/metamask-notifications/metamask-notifications.ts @@ -0,0 +1,53 @@ +import { CHAIN_IDS } from '../../../../shared/constants/network'; + +/** + * Configuration for a block explorer. + * + * @property {string} url - The URL of the block explorer. + * @property {string} name - The name of the block explorer. + */ +export type BlockExplorerConfig = { + url: string; + name: string; +}; + +/** + * Map of all supported block explorers for notifications. + */ +export const SUPPORTED_NOTIFICATION_BLOCK_EXPLORERS = { + // ETHEREUM + [CHAIN_IDS.MAINNET]: { + url: 'https://etherscan.io', + name: 'Etherscan', + }, + // OPTIMISM + [CHAIN_IDS.OPTIMISM]: { + url: 'https://optimistic.etherscan.io', + name: 'Optimistic Etherscan', + }, + // BSC + [CHAIN_IDS.BSC]: { + url: 'https://bscscan.com', + name: 'BscScan', + }, + // POLYGON + [CHAIN_IDS.POLYGON]: { + url: 'https://polygonscan.com', + name: 'PolygonScan', + }, + // ARBITRUM + [CHAIN_IDS.ARBITRUM]: { + url: 'https://arbiscan.io', + name: 'Arbiscan', + }, + // AVALANCHE + [CHAIN_IDS.AVALANCHE]: { + url: 'https://snowtrace.io', + name: 'Snowtrace', + }, + // LINEA + [CHAIN_IDS.LINEA_MAINNET]: { + url: 'https://lineascan.build', + name: 'LineaScan', + }, +} as const; diff --git a/ui/helpers/utils/metrics.js b/ui/helpers/utils/metrics.js index 7e66f88d6857..32d7d3c18c42 100644 --- a/ui/helpers/utils/metrics.js +++ b/ui/helpers/utils/metrics.js @@ -1,8 +1,10 @@ +import { TransactionType } from '@metamask/transaction-controller'; import { BlockaidReason, BlockaidResultType, } from '../../../shared/constants/security-provider'; import { MetaMetricsEventUiCustomization } from '../../../shared/constants/metametrics'; +import { calcTokenAmount } from '../../../shared/lib/transactions-controller-utils'; export function getMethodName(camelCase) { if (!camelCase || typeof camelCase !== 'string') { @@ -89,3 +91,42 @@ export const getBlockaidMetricsProps = ({ securityAlertResponse }) => { return params; }; + +export const getSwapAndSendMetricsProps = (transactionMeta) => { + if (transactionMeta.type !== TransactionType.swapAndSend) { + return {}; + } + + const { + chainId, + sourceTokenAmount, + sourceTokenDecimals, + destinationTokenAmount, + destinationTokenDecimals, + sourceTokenSymbol, + destinationTokenAddress, + destinationTokenSymbol, + sourceTokenAddress, + } = transactionMeta; + + const params = { + chain_id: chainId, + token_amount_source: + sourceTokenAmount && sourceTokenDecimals + ? calcTokenAmount(sourceTokenAmount, sourceTokenDecimals).toString() + : undefined, + token_amount_dest_estimate: + destinationTokenAmount && destinationTokenDecimals + ? calcTokenAmount( + destinationTokenAmount, + destinationTokenDecimals, + ).toString() + : undefined, + token_symbol_source: sourceTokenSymbol, + token_symbol_destination: destinationTokenSymbol, + token_address_source: sourceTokenAddress, + token_address_destination: destinationTokenAddress, + }; + + return params; +}; diff --git a/ui/helpers/utils/metrics.test.js b/ui/helpers/utils/metrics.test.js index db5bec5143b8..d7d7e610ccb7 100644 --- a/ui/helpers/utils/metrics.test.js +++ b/ui/helpers/utils/metrics.test.js @@ -4,7 +4,11 @@ import { BlockaidResultType, SecurityAlertSource, } from '../../../shared/constants/security-provider'; -import { getBlockaidMetricsProps, getMethodName } from './metrics'; +import { + getBlockaidMetricsProps, + getMethodName, + getSwapAndSendMetricsProps, +} from './metrics'; describe('getMethodName', () => { it('gets correct method names', () => { @@ -181,3 +185,39 @@ describe('getBlockaidMetricsProps', () => { }); }); }); + +describe('getSwapAndSendMetricsProps', () => { + it('returns an empty object when transaction type is not swapAndSend', () => { + const transactionMeta = { + type: 'other', + }; + const result = getSwapAndSendMetricsProps(transactionMeta); + expect(result).toStrictEqual({}); + }); + + it('returns the expected metrics props when transaction type is swapAndSend', () => { + const transactionMeta = { + type: 'swapAndSend', + chainId: 1, + sourceTokenAmount: '1000000000000', + sourceTokenDecimals: 12, + destinationTokenAmount: '200', + destinationTokenDecimals: 2, + sourceTokenSymbol: 'ETH', + destinationTokenAddress: '0x123', + destinationTokenSymbol: 'ABC', + sourceTokenAddress: '0x456', + }; + const expectedMetricsProps = { + chain_id: 1, + token_amount_source: '1', + token_amount_dest_estimate: '2', + token_symbol_source: 'ETH', + token_symbol_destination: 'ABC', + token_address_source: '0x456', + token_address_destination: '0x123', + }; + const result = getSwapAndSendMetricsProps(transactionMeta); + expect(result).toStrictEqual(expectedMetricsProps); + }); +}); diff --git a/ui/helpers/utils/notification.util.ts b/ui/helpers/utils/notification.util.ts index 5ff78c8e496c..1efe78adf3bb 100644 --- a/ui/helpers/utils/notification.util.ts +++ b/ui/helpers/utils/notification.util.ts @@ -5,7 +5,6 @@ import { CHAIN_IDS, CHAIN_ID_TO_CURRENCY_SYMBOL_MAP, CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP, - ETHERSCAN_SUPPORTED_NETWORKS, NETWORK_TO_NAME_MAP, FEATURED_RPCS, MAINNET_RPC_URL, @@ -16,17 +15,30 @@ import { LINEA_MAINNET_RPC_URL, LOCALHOST_RPC_URL, } 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, hexWEIToDecETH, decimalToHex, } from '../../../shared/modules/conversion.utils'; +/** + * Type guard to ensure a key is present in an object. + * + * @param object - The object to check. + * @param key - The key to check for. + * @returns True if the key is present, false otherwise. + */ +function isKey(object: T, key: PropertyKey): key is keyof T { + return key in object; +} + /** * Checks if 2 date objects are on the same day * @@ -301,7 +313,7 @@ export function getNetworkDetailsByChainId(chainId?: keyof typeof CHAIN_IDS): { nativeCurrencySymbol: string; nativeCurrencyLogo: string; nativeCurrencyAddress: string; - nativeBlockExplorerUrl?: string; + blockExplorerConfig?: BlockExplorerConfig; } { const fullNativeCurrencyName = NETWORK_TO_NAME_MAP[chainId as keyof typeof NETWORK_TO_NAME_MAP] ?? ''; @@ -315,22 +327,16 @@ export function getNetworkDetailsByChainId(chainId?: keyof typeof CHAIN_IDS): { chainId as keyof typeof CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP ]; const nativeCurrencyAddress = '0x0000000000000000000000000000000000000000'; - const blockExplorerConfig = - ETHERSCAN_SUPPORTED_NETWORKS[ - chainId as keyof typeof ETHERSCAN_SUPPORTED_NETWORKS - ]; - let nativeBlockExplorerUrl; - if (blockExplorerConfig) { - nativeBlockExplorerUrl = `https://www.${blockExplorerConfig.domain}`; - } - + chainId && isKey(SUPPORTED_NOTIFICATION_BLOCK_EXPLORERS, chainId) + ? SUPPORTED_NOTIFICATION_BLOCK_EXPLORERS[chainId] + : undefined; return { nativeCurrencyName, nativeCurrencySymbol, nativeCurrencyLogo, nativeCurrencyAddress, - nativeBlockExplorerUrl, + blockExplorerConfig, }; } @@ -409,9 +415,14 @@ export const getNetworkFees = async (notification: OnChainRawNotification) => { } const chainId = decimalToHex(notification.chain_id); - const provider = new JsonRpcProvider( - getRpcUrlByChainId(`0x${chainId}` as HexChainId), - ); + const rpcUrl = getRpcUrlByChainId(`0x${chainId}` as HexChainId); + const connection = { + url: rpcUrl, + headers: { + 'Infura-Source': 'metamask/metamask', + }, + }; + const provider = new JsonRpcProvider(connection); if (!provider) { throw new Error(`No provider found for chainId ${chainId}`); diff --git a/ui/helpers/utils/util.js b/ui/helpers/utils/util.js index cc5e6e182403..9cf3920b0785 100644 --- a/ui/helpers/utils/util.js +++ b/ui/helpers/utils/util.js @@ -223,24 +223,30 @@ export function getRandomFileName() { * @param {number} options.truncatedCharLimit - The maximum length of the string. * @param {number} options.truncatedStartChars - The number of characters to preserve at the beginning. * @param {number} options.truncatedEndChars - The number of characters to preserve at the end. + * @param {boolean} options.skipCharacterInEnd - Skip the character at the end. * @returns {string} The shortened string. */ export function shortenString( stringToShorten = '', - { truncatedCharLimit, truncatedStartChars, truncatedEndChars } = { + { + truncatedCharLimit, + truncatedStartChars, + truncatedEndChars, + skipCharacterInEnd, + } = { truncatedCharLimit: TRUNCATED_NAME_CHAR_LIMIT, truncatedStartChars: TRUNCATED_ADDRESS_START_CHARS, truncatedEndChars: TRUNCATED_ADDRESS_END_CHARS, + skipCharacterInEnd: false, }, ) { if (stringToShorten.length < truncatedCharLimit) { return stringToShorten; } - return `${stringToShorten.slice( - 0, - truncatedStartChars, - )}...${stringToShorten.slice(-truncatedEndChars)}`; + return `${stringToShorten.slice(0, truncatedStartChars)}...${ + skipCharacterInEnd ? '' : stringToShorten.slice(-truncatedEndChars) + }`; } /** @@ -259,6 +265,7 @@ export function shortenAddress(address = '') { truncatedCharLimit: TRUNCATED_NAME_CHAR_LIMIT, truncatedStartChars: TRUNCATED_ADDRESS_START_CHARS, truncatedEndChars: TRUNCATED_ADDRESS_END_CHARS, + skipCharacterInEnd: false, }); } diff --git a/ui/helpers/utils/util.test.js b/ui/helpers/utils/util.test.js index 76811f432f81..2ad0db6e50a0 100644 --- a/ui/helpers/utils/util.test.js +++ b/ui/helpers/utils/util.test.js @@ -1081,5 +1081,16 @@ describe('util', () => { }), ).toStrictEqual('0x12...7890'); }); + + it('should shorten the string and remove all characters from the end if skipCharacterInEnd is true', () => { + expect( + util.shortenString('0x1234567890123456789012345678901234567890', { + truncatedCharLimit: 10, + truncatedStartChars: 4, + truncatedEndChars: 4, + skipCharacterInEnd: true, + }), + ).toStrictEqual('0x12...'); + }); }); }); diff --git a/ui/hooks/metamask-notifications/useMetametrics.test.tsx b/ui/hooks/metamask-notifications/useMetametrics.test.tsx index a9778212a801..3d4afffd03e7 100644 --- a/ui/hooks/metamask-notifications/useMetametrics.test.tsx +++ b/ui/hooks/metamask-notifications/useMetametrics.test.tsx @@ -40,7 +40,15 @@ describe('useMetametrics', () => { jest.clearAllMocks(); }); - it('should enable MetaMetrics when user is not signed in', async () => { + it('should enable MetaMetrics when user is not signed in and profile syncing enabled', async () => { + store.getState = () => ({ + metamask: { + participateInMetaMetrics: true, + isProfileSyncingEnabled: true, + isSignedIn: false, + }, + }); + const { result, waitForNextUpdate } = renderHook( () => useEnableMetametrics(), { @@ -62,11 +70,11 @@ describe('useMetametrics', () => { expect(result.current.loading).toBe(false); }); - it('should disable MetaMetrics and sign out if profile syncing is disabled', async () => { + it('should disable MetaMetrics and sign out if profile syncing is enabled', async () => { store.getState = () => ({ metamask: { participateInMetaMetrics: true, - isProfileSyncingEnabled: false, + isProfileSyncingEnabled: true, isSignedIn: true, }, }); diff --git a/ui/hooks/metamask-notifications/useMetametrics.ts b/ui/hooks/metamask-notifications/useMetametrics.ts index 7cf3f17b6a19..0f9f1c2f666f 100644 --- a/ui/hooks/metamask-notifications/useMetametrics.ts +++ b/ui/hooks/metamask-notifications/useMetametrics.ts @@ -23,6 +23,7 @@ export function useEnableMetametrics(): { error: string | null; } { const dispatch = useDispatch(); + const isProfileSyncingEnabled = useSelector(selectIsProfileSyncingEnabled); const isUserSignedIn = useSelector(selectIsSignedIn); const [loading, setLoading] = useState(false); @@ -34,7 +35,7 @@ export function useEnableMetametrics(): { setError(null); try { - if (!isUserSignedIn) { + if (isProfileSyncingEnabled && !isUserSignedIn) { await dispatch(performSignIn()); } @@ -81,7 +82,7 @@ export function useDisableMetametrics(): { setError(null); try { - if (!isProfileSyncingEnabled) { + if (isProfileSyncingEnabled) { await dispatch(performSignOut()); } await dispatch(setParticipateInMetaMetrics(false)); diff --git a/ui/hooks/metamask-notifications/useSwitchNotifications.ts b/ui/hooks/metamask-notifications/useSwitchNotifications.ts index 0d60b16714a2..53e121473bac 100644 --- a/ui/hooks/metamask-notifications/useSwitchNotifications.ts +++ b/ui/hooks/metamask-notifications/useSwitchNotifications.ts @@ -1,5 +1,5 @@ -import { useState, useCallback } from 'react'; -import { useDispatch } from 'react-redux'; +import { useState, useCallback, useMemo, useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; import log from 'loglevel'; import { setFeatureAnnouncementsEnabled, @@ -8,6 +8,7 @@ import { updateOnChainTriggersByAccount, hideLoadingIndication, } from '../../store/actions'; +import { getIsUpdatingMetamaskNotificationsAccount } from '../../selectors/metamask-notifications/metamask-notifications'; export function useSwitchFeatureAnnouncementsChange(): { onChange: (state: boolean) => Promise; @@ -114,3 +115,75 @@ export function useSwitchAccountNotificationsChange(): { error, }; } + +function useRefetchAccountSettings() { + const dispatch = useDispatch(); + + const getAccountSettings = useCallback(async (accounts: string[]) => { + try { + const result = (await dispatch( + checkAccountsPresence(accounts), + )) as unknown as UseSwitchAccountNotificationsData; + + return result; + } catch { + return {}; + } + }, []); + + return getAccountSettings; +} + +/** + * Account Settings Hook. + * Gets initial loading states, and returns enable/disable account states. + * Also exposes an update() method so each switch can be manually updated. + * + * @param accounts - the accounts we are checking to see if notifications are enabled/disabled + * @returns props for settings page + */ +export function useAccountSettingsProps(accounts: string[]) { + const accountsBeingUpdated = useSelector( + getIsUpdatingMetamaskNotificationsAccount, + ); + const fetchAccountSettings = useRefetchAccountSettings(); + const [data, setData] = useState({}); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + + // Memoize the accounts array to avoid unnecessary re-fetching + const jsonAccounts = useMemo(() => JSON.stringify(accounts), [accounts]); + + const update = useCallback(async (addresses: string[]) => { + try { + setLoading(true); + setError(null); + const res = await fetchAccountSettings(addresses); + setData(res); + } catch { + setError('Failed to get account settings'); + } finally { + setLoading(false); + } + }, []); + + // Effect - async get if accounts are enabled/disabled + useEffect(() => { + try { + const memoAccounts: string[] = JSON.parse(jsonAccounts); + update(memoAccounts); + } catch { + setError('Failed to get account settings'); + } finally { + setLoading(false); + } + }, [jsonAccounts, fetchAccountSettings]); + + return { + data, + initialLoading: loading, + error, + accountsBeingUpdated, + update, + }; +} diff --git a/ui/hooks/snaps/useSignatureInsights.js b/ui/hooks/snaps/useSignatureInsights.js index ae5679466be3..c0e297fcb216 100644 --- a/ui/hooks/snaps/useSignatureInsights.js +++ b/ui/hooks/snaps/useSignatureInsights.js @@ -108,7 +108,7 @@ export function useSignatureInsights({ txData }) { } } } - if (Object.keys(txData).length > 0) { + if (Object.keys(txData).length > 0 && txData.msgParams !== undefined) { fetchInsight(); } return () => { diff --git a/ui/hooks/useFiatFormatter.test.ts b/ui/hooks/useFiatFormatter.test.ts index bf7fc670b5ed..7e6423f1df34 100644 --- a/ui/hooks/useFiatFormatter.test.ts +++ b/ui/hooks/useFiatFormatter.test.ts @@ -35,6 +35,44 @@ describe('useFiatFormatter', () => { expect(formatFiat(0)).toBe('$0.00'); }); + describe('shorten the fiat', () => { + it('when currency symbol on the left for given locale', () => { + mockGetIntlLocale.mockReturnValue('en-US'); + mockGetCurrentCurrency.mockReturnValue('USD'); + + const { result } = renderHook(() => useFiatFormatter()); + const formatFiat = result.current; + + expect(formatFiat(100000000000000000, { shorten: true })).toBe( + '$100,000,000,...', + ); + }); + + it('when currency symbol on the right for given locale', () => { + mockGetIntlLocale.mockReturnValue('es-ES'); + mockGetCurrentCurrency.mockReturnValue('EUR'); + + const { result } = renderHook(() => useFiatFormatter()); + const formatFiat = result.current; + + expect(formatFiat(100000000000000000, { shorten: true })).toBe( + '100.000.000....€', + ); + }); + + it('handle unknown currencies by returning amount followed by currency code', () => { + mockGetCurrentCurrency.mockReturnValue('storj'); + mockGetIntlLocale.mockReturnValue('en-US'); + + const { result } = renderHook(() => useFiatFormatter()); + const formatFiat = result.current; + + expect(formatFiat(100000, { shorten: true })).toBe('100,000 storj'); + expect(formatFiat(500.5, { shorten: true })).toBe('500.5 storj'); + expect(formatFiat(0, { shorten: true })).toBe('0 storj'); + }); + }); + it('should use the current locale and currency from the mocked functions', () => { mockGetIntlLocale.mockReturnValue('fr-FR'); mockGetCurrentCurrency.mockReturnValue('EUR'); @@ -47,12 +85,13 @@ describe('useFiatFormatter', () => { it('should gracefully handle unknown currencies by returning amount followed by currency code', () => { mockGetCurrentCurrency.mockReturnValue('storj'); + mockGetIntlLocale.mockReturnValue('en-US'); const { result } = renderHook(() => useFiatFormatter()); const formatFiat = result.current; // Testing the fallback formatting for an unknown currency - expect(formatFiat(1000)).toBe('1000 storj'); + expect(formatFiat(100000)).toBe('100,000 storj'); expect(formatFiat(500.5)).toBe('500.5 storj'); expect(formatFiat(0)).toBe('0 storj'); }); diff --git a/ui/hooks/useFiatFormatter.ts b/ui/hooks/useFiatFormatter.ts index 0f5e37151670..0c9bd33ffee5 100644 --- a/ui/hooks/useFiatFormatter.ts +++ b/ui/hooks/useFiatFormatter.ts @@ -1,35 +1,98 @@ import { useSelector } from 'react-redux'; import { getIntlLocale } from '../ducks/locale/locale'; import { getCurrentCurrency } from '../selectors'; +import { shortenString } from '../helpers/utils/util'; /** * Returns a function that formats a fiat amount as a localized string. + * The hook takes an optional options object to configure the formatting. * * Example usage: * * ``` * const formatFiat = useFiatFormatter(); * const formattedAmount = formatFiat(1000); + * + * const shorteningFiatFormatter = useFiatFormatter(); + * const shortenedAmount = shorteningFiatFormatter(100000000000000000, { shorten: true }); * ``` * * @returns A function that takes a fiat amount as a number and returns a formatted string. */ -type FiatFormatter = (fiatAmount: number) => string; +const TRUNCATED_CHAR_LIMIT_FOR_SHORTENED_FIAT = 15; +const TRUNCATED_START_CHARS_FOR_SHORTENED_FIAT = 12; + +type FiatFormatterOptions = { + shorten?: boolean; +}; + +type FiatFormatter = ( + fiatAmount: number, + options?: FiatFormatterOptions, +) => string; export const useFiatFormatter = (): FiatFormatter => { const locale = useSelector(getIntlLocale); const fiatCurrency = useSelector(getCurrentCurrency); - return (fiatAmount: number) => { + return (fiatAmount: number, options: FiatFormatterOptions = {}) => { + const { shorten } = options; + try { - return new Intl.NumberFormat(locale, { + const formatter = new Intl.NumberFormat(locale, { style: 'currency', currency: fiatCurrency, - }).format(fiatAmount); + }); + + if (!shorten) { + return formatter.format(fiatAmount); + } + + const parts = formatter.formatToParts(fiatAmount); + + let currencySymbol = ''; + let numberString = ''; + + parts.forEach((part) => { + if (part.type === 'currency') { + currencySymbol += part.value; + } else { + numberString += part.value; + } + }); + + // Shorten the number part while preserving commas + const shortenedNumberString = shortenString(numberString, { + truncatedCharLimit: TRUNCATED_CHAR_LIMIT_FOR_SHORTENED_FIAT, + truncatedStartChars: TRUNCATED_START_CHARS_FOR_SHORTENED_FIAT, + truncatedEndChars: 0, + skipCharacterInEnd: true, + }); + + // Determine the position of the currency symbol + const currencyBeforeNumber = + parts.findIndex((part) => part.type === 'currency') < + parts.findIndex((part) => part.type === 'integer'); + + // Reassemble the formatted string + return currencyBeforeNumber + ? `${currencySymbol}${shortenedNumberString}` + : `${shortenedNumberString}${currencySymbol}`; } catch (error) { // Fallback for unknown or unsupported currencies - return `${fiatAmount} ${fiatCurrency}`; + const formattedNumber = new Intl.NumberFormat(locale).format(fiatAmount); + const shortenedNumberString = shortenString(formattedNumber, { + truncatedCharLimit: TRUNCATED_CHAR_LIMIT_FOR_SHORTENED_FIAT, + truncatedStartChars: TRUNCATED_START_CHARS_FOR_SHORTENED_FIAT, + truncatedEndChars: 0, + skipCharacterInEnd: true, + }); + + if (shorten) { + return `${shortenedNumberString} ${fiatCurrency}`; + } + return `${formattedNumber} ${fiatCurrency}`; } }; }; diff --git a/ui/index.js b/ui/index.js index 0ee95c12d2d1..907fcea66b89 100644 --- a/ui/index.js +++ b/ui/index.js @@ -8,7 +8,7 @@ import browser from 'webextension-polyfill'; import { getEnvironmentType } from '../app/scripts/lib/util'; import { AlertTypes } from '../shared/constants/alerts'; import { maskObject } from '../shared/modules/object.utils'; -import { SENTRY_UI_STATE } from '../app/scripts/lib/setupSentry'; +import { SENTRY_UI_STATE } from '../app/scripts/constants/sentry-state'; import { ENVIRONMENT_TYPE_POPUP } from '../shared/constants/app'; import { COPY_OPTIONS } from '../shared/constants/copy'; import switchDirection from '../shared/lib/switch-direction'; @@ -241,7 +241,11 @@ async function startApp(metamaskState, backgroundConnection, opts) { * @param {object} store - The Redux store. */ function setupStateHooks(store) { - if (process.env.METAMASK_DEBUG || process.env.IN_TEST) { + if ( + process.env.METAMASK_DEBUG || + process.env.IN_TEST || + process.env.ENABLE_SETTINGS_PAGE_DEV_OPTIONS + ) { /** * The following stateHook is a method intended to throw an error, used in * our E2E test to ensure that errors are attempted to be sent to sentry. @@ -263,7 +267,7 @@ function setupStateHooks(store) { window.stateHooks.throwTestBackgroundError = async function ( msg = 'Test Error', ) { - store.dispatch(actions.throwTestBackgroundError(msg)); + await actions.throwTestBackgroundError(msg); }; } 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 fe4e339f903b..88be64f74f37 100644 --- a/ui/pages/asset/components/__snapshots__/asset-page.test.tsx.snap +++ b/ui/pages/asset/components/__snapshots__/asset-page.test.tsx.snap @@ -192,7 +192,7 @@ exports[`AssetPage should render a native asset 1`] = ` class="mm-box mm-text mm-avatar-base mm-avatar-base--size-xs mm-avatar-network mm-text--body-xs mm-text--text-transform-uppercase mm-box--display-flex mm-box--justify-content-center mm-box--align-items-center mm-box--color-text-default mm-box--background-color-background-alternative mm-box--rounded-full mm-box--border-color-border-muted box--border-style-solid box--border-width-1" > Ethereum Mainnet logo @@ -476,7 +476,7 @@ exports[`AssetPage should render an ERC20 asset without prices 1`] = ` class="mm-box mm-text mm-avatar-base mm-avatar-base--size-xs mm-avatar-network mm-text--body-xs mm-text--text-transform-uppercase mm-box--display-flex mm-box--justify-content-center mm-box--align-items-center mm-box--color-text-default mm-box--background-color-background-alternative mm-box--rounded-full mm-box--border-color-border-muted box--border-style-solid box--border-width-1" > Ethereum Mainnet logo @@ -953,7 +953,7 @@ exports[`AssetPage should render an ERC20 token with prices 1`] = ` class="mm-box mm-text mm-avatar-base mm-avatar-base--size-xs mm-avatar-network mm-text--body-xs mm-text--text-transform-uppercase mm-box--display-flex mm-box--justify-content-center mm-box--align-items-center mm-box--color-text-default mm-box--background-color-background-alternative mm-box--rounded-full mm-box--border-color-border-muted box--border-style-solid box--border-width-1" > Ethereum Mainnet logo diff --git a/ui/pages/asset/components/token-buttons.tsx b/ui/pages/asset/components/token-buttons.tsx index 6cd78fab7693..cfa4abee87f0 100644 --- a/ui/pages/asset/components/token-buttons.tsx +++ b/ui/pages/asset/components/token-buttons.tsx @@ -181,16 +181,19 @@ const TokenButtons = ({ { - trackEvent({ - event: MetaMetricsEventName.NavSendButtonClicked, - category: MetaMetricsEventCategory.Navigation, - properties: { - token_symbol: token.symbol, - location: MetaMetricsSwapsEventSource.TokenView, - text: 'Send', - chain_id: chainId, + trackEvent( + { + event: MetaMetricsEventName.NavSendButtonClicked, + category: MetaMetricsEventCategory.Navigation, + properties: { + token_symbol: token.symbol, + location: MetaMetricsSwapsEventSource.TokenView, + text: 'Send', + chain_id: chainId, + }, }, - }); + { excludeMetaMetricsId: false }, + ); try { await dispatch( startNewDraftTransaction({ diff --git a/ui/pages/confirmations/components/confirm/info/__snapshots__/info.test.tsx.snap b/ui/pages/confirmations/components/confirm/info/__snapshots__/info.test.tsx.snap index 76b15cccf95f..c388d2e9d3cd 100644 --- a/ui/pages/confirmations/components/confirm/info/__snapshots__/info.test.tsx.snap +++ b/ui/pages/confirmations/components/confirm/info/__snapshots__/info.test.tsx.snap @@ -10,7 +10,7 @@ exports[`Info renders info section for personal sign request 1`] = ` style="overflow-wrap: anywhere; min-height: 24px; background: transparent;" >

renders component for contract interaction style="overflow-wrap: anywhere; min-height: 24px;" >

renders component for contract interaction style="overflow-wrap: anywhere; min-height: 24px;" >

renders component for contract interaction style="overflow-wrap: anywhere; min-height: 24px;" >

renders component for contract interaction style="overflow-wrap: anywhere; min-height: 24px;" >

renders component for contract interaction style="overflow-wrap: anywhere; min-height: 24px;" >

renders component for contract interaction style="overflow-wrap: anywhere; min-height: 24px;" >

renders component for contract interaction style="overflow-wrap: anywhere; min-height: 24px;" >

renders component for contract interaction style="overflow-wrap: anywhere; min-height: 24px;" >

renders component for contract interaction style="overflow-wrap: anywhere; min-height: 24px;" >

renders component for contract interaction style="overflow-wrap: anywhere; min-height: 24px;" >

renders component for contract interaction style="overflow-wrap: anywhere; min-height: 24px;" >

renders component for contract interaction style="overflow-wrap: anywhere; min-height: 24px;" >

does not render component for advanced transaction style="overflow-wrap: anywhere; min-height: 24px;" >

does not render component for advanced transaction style="overflow-wrap: anywhere; min-height: 24px;" >

renders component for advanced transaction details style="overflow-wrap: anywhere; min-height: 24px;" >

renders component for advanced transaction details style="overflow-wrap: anywhere; min-height: 24px;" >

renders component 1`] = ` style="overflow-wrap: anywhere; min-height: 24px;" >

renders component for gas fees section with advanced style="overflow-wrap: anywhere; min-height: 24px;" >

renders component for gas fees section with advanced style="overflow-wrap: anywhere; min-height: 24px;" >

renders component for gas fees section with advanced style="overflow-wrap: anywhere; min-height: 24px;" >

renders component for gas fees section with advanced style="overflow-wrap: anywhere; min-height: 24px;" >

renders component for gas fees section with advanced style="overflow-wrap: anywhere; min-height: 24px;" >

renders component 1`] = ` style="overflow-wrap: anywhere; min-height: 24px;" >

renders component for gas fees section with advanced style="overflow-wrap: anywhere; min-height: 24px;" >

renders component for gas fees section with advanced style="overflow-wrap: anywhere; min-height: 24px;" >

renders component for gas fees section with advanced style="overflow-wrap: anywhere; min-height: 24px;" >

renders component for gas fees section with advanced style="overflow-wrap: anywhere; min-height: 24px;" >

renders component for gas fees section with advanced style="overflow-wrap: anywhere; min-height: 24px;" >

renders component for transaction details 1`] = style="overflow-wrap: anywhere; min-height: 24px;" >

renders component for transaction details 1`] = style="overflow-wrap: anywhere; min-height: 24px;" >

3,000

@@ -422,7 +423,7 @@ exports[`TypedSignInfo correctly renders permit sign type 1`] = ` style="overflow-wrap: anywhere; min-height: 24px; padding-right: 0px;" >

30

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 97b212d08256..59b46303dbc0 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,8 +1,8 @@ import React, { useMemo } from 'react'; import { useSelector } from 'react-redux'; import { NameType } from '@metamask/name-controller'; -import { BigNumber } from 'bignumber.js'; +import { calcTokenAmount } from '../../../../../../../../shared/lib/transactions-controller-utils'; import { parseTypedDataMessage } from '../../../../../../../../shared/modules/transaction.utils'; import { Numeric } from '../../../../../../../../shared/modules/Numeric'; import Name from '../../../../../../../components/app/name/name'; @@ -10,6 +10,7 @@ import { ConfirmInfoRow, ConfirmInfoRowText, } from '../../../../../../../components/app/confirm/info/row'; +import { shortenString } from '../../../../../../../helpers/utils/util'; import { useI18nContext } from '../../../../../../../hooks/useI18nContext'; import { currentConfirmationSelector } from '../../../../../../../selectors'; import { Box, Text } from '../../../../../../../components/component-library'; @@ -53,15 +54,11 @@ const PermitSimulation: React.FC<{ }, [exchangeRate, value]); const { tokenValue, tokenValueMaxPrecision } = useMemo(() => { - const valueBN = new BigNumber(value); - const diviserBN = new BigNumber(10).pow(tokenDecimals); - const resultBn = valueBN.div(diviserBN); + const tokenAmount = calcTokenAmount(value, tokenDecimals); - // FIXME - Precision may be lost for large values when using formatAmount - /** @see {@link https://github.com/MetaMask/metamask-extension/issues/25755} */ return { - tokenValue: formatAmount('en-US', resultBn), - tokenValueMaxPrecision: formatAmountMaxPrecision('en-US', resultBn), + tokenValue: formatAmount('en-US', tokenAmount), + tokenValueMaxPrecision: formatAmountMaxPrecision('en-US', tokenAmount), }; }, [tokenDecimals, value]); @@ -91,17 +88,24 @@ const PermitSimulation: React.FC<{ backgroundColor={BackgroundColor.backgroundAlternative} borderRadius={BorderRadius.XL} paddingInline={2} + style={{ paddingTop: '1px', paddingBottom: '1px' }} textAlign={TextAlign.Center} - ellipsis > - {tokenValue} + {shortenString(tokenValue || '', { + truncatedCharLimit: 15, + truncatedStartChars: 15, + truncatedEndChars: 0, + skipCharacterInEnd: true, + })} - {fiatValue && } + {fiatValue && ( + + )} 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 b8d470123220..24ea8d5cb9a9 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 @@ -10,7 +10,7 @@ exports[`DataTree correctly renders reverse strings 1`] = ` style="overflow-wrap: anywhere; min-height: 24px; padding-right: 0px;" >

| TreeData[]; @@ -81,21 +77,8 @@ const DataField = memo( ); } if (isPermit && label === 'value') { - const valueBN = new BigNumber(value); - const diviserBN = new BigNumber(10).pow(tokenDecimals); - const resultBn = valueBN.div(diviserBN); - - // FIXME - Precision may be lost for large values when using formatAmount - /** @see {@link https://github.com/MetaMask/metamask-extension/issues/25755} */ - const tokenValue = formatAmount('en-US', resultBn); - const tokenValueMaxPrecision = formatAmountMaxPrecision('en-US', valueBN); - return ( - + ); } if (isPermit && label === 'deadline') { diff --git a/ui/pages/confirmations/components/confirm/row/typed-sign-data-v1/__snapshots__/typedSignDataV1.test.tsx.snap b/ui/pages/confirmations/components/confirm/row/typed-sign-data-v1/__snapshots__/typedSignDataV1.test.tsx.snap index 21d11d1453ec..822cc5c7d388 100644 --- a/ui/pages/confirmations/components/confirm/row/typed-sign-data-v1/__snapshots__/typedSignDataV1.test.tsx.snap +++ b/ui/pages/confirmations/components/confirm/row/typed-sign-data-v1/__snapshots__/typedSignDataV1.test.tsx.snap @@ -17,7 +17,7 @@ exports[`ConfirmInfoRowTypedSignData should match snapshot 1`] = ` style="overflow-wrap: anywhere; min-height: 24px; padding-right: 0px;" >

@@ -151,7 +151,7 @@ exports[`Blockaid Banner Alert should render 'warning' UI when securityAlertResp Something doesn't look right? @@ -248,7 +248,7 @@ exports[`Blockaid Banner Alert should render 'warning' UI when securityAlertResp Something doesn't look right? @@ -346,7 +346,7 @@ exports[`Blockaid Banner Alert should render details section even when features Something doesn't look right? @@ -457,7 +457,7 @@ exports[`Blockaid Banner Alert should render details when provided 1`] = ` Something doesn't look right? @@ -556,7 +556,7 @@ exports[`Blockaid Banner Alert should render link to report url 1`] = ` Something doesn't look right? diff --git a/ui/pages/confirmations/components/simulation-details/amount-pill.tsx b/ui/pages/confirmations/components/simulation-details/amount-pill.tsx index 5e86018f5e58..a8ad71cfadc6 100644 --- a/ui/pages/confirmations/components/simulation-details/amount-pill.tsx +++ b/ui/pages/confirmations/components/simulation-details/amount-pill.tsx @@ -59,6 +59,7 @@ export const AmountPill: React.FC<{ truncatedCharLimit: 11, truncatedStartChars: 4, truncatedEndChars: 4, + skipCharacterInEnd: false, }); const shortenedTokenIdPart = `#${shortenedDecimalTokenId}`; diff --git a/ui/pages/confirmations/components/simulation-details/fiat-display.tsx b/ui/pages/confirmations/components/simulation-details/fiat-display.tsx index 5143cd5ed06b..66e9148c676e 100644 --- a/ui/pages/confirmations/components/simulation-details/fiat-display.tsx +++ b/ui/pages/confirmations/components/simulation-details/fiat-display.tsx @@ -32,12 +32,14 @@ export function calculateTotalFiat(fiatAmounts: FiatAmount[]): number { /** * Displays the fiat value of a single balance change. * - * @param props - * @param props.fiatAmount + * @param props - The props object. + * @param props.fiatAmount - The fiat amount to display. + * @param props.shorten - Whether to shorten the fiat amount. */ -export const IndividualFiatDisplay: React.FC<{ fiatAmount: FiatAmount }> = ({ - fiatAmount, -}) => { +export const IndividualFiatDisplay: React.FC<{ + fiatAmount: FiatAmount; + shorten?: boolean; +}> = ({ fiatAmount, shorten = false }) => { const hideFiatForTestnet = useHideFiatForTestnet(); const fiatFormatter = useFiatFormatter(); @@ -50,7 +52,7 @@ export const IndividualFiatDisplay: React.FC<{ fiatAmount: FiatAmount }> = ({ } const absFiat = Math.abs(fiatAmount); - return {fiatFormatter(absFiat)}; + return {fiatFormatter(absFiat, { shorten })}; }; /** diff --git a/ui/pages/confirmations/components/simulation-details/formatAmount.test.ts b/ui/pages/confirmations/components/simulation-details/formatAmount.test.ts index f7a627e46e5c..4ae9b7ee7278 100644 --- a/ui/pages/confirmations/components/simulation-details/formatAmount.test.ts +++ b/ui/pages/confirmations/components/simulation-details/formatAmount.test.ts @@ -2,54 +2,52 @@ import { BigNumber } from 'bignumber.js'; import { formatAmount } from './formatAmount'; describe('formatAmount', () => { - const locale = 'en-US'; + describe('#formatAmount', () => { + const locale = 'en-US'; - it('returns "0" for zero amount', () => { - expect(formatAmount(locale, new BigNumber(0))).toBe('0'); - }); + it('returns "0" for zero amount', () => { + expect(formatAmount(locale, new BigNumber(0))).toBe('0'); + }); - it('returns "<0.000001" for 0 < amount < MIN_AMOUNT', () => { - expect(formatAmount(locale, new BigNumber(0.0000009))).toBe('<0.000001'); - }); + it('returns "<0.000001" for 0 < amount < MIN_AMOUNT', () => { + expect(formatAmount(locale, new BigNumber(0.0000009))).toBe('<0.000001'); + }); - // @ts-expect-error This is missing from the Mocha type definitions - it.each([ - [0.0000456, '0.000046'], - [0.0004567, '0.000457'], - [0.003456, '0.00346'], - [0.023456, '0.0235'], - [0.125456, '0.125'], - ])( - 'formats amount less than 1 with maximum significant digits (%s => %s)', - (amount: number, expected: string) => { - expect(formatAmount(locale, new BigNumber(amount))).toBe(expected); - }, - ); + // @ts-expect-error This is missing from the Mocha type definitions + it.each([ + [0.0000456, '0.000046'], + [0.0004567, '0.000457'], + [0.003456, '0.00346'], + [0.023456, '0.0235'], + [0.125456, '0.125'], + ])( + 'formats amount less than 1 with maximum significant digits (%s => %s)', + (amount: number, expected: string) => { + expect(formatAmount(locale, new BigNumber(amount))).toBe(expected); + }, + ); - // @ts-expect-error This is missing from the Mocha type definitions - it.each([ - [1.0034, '1.003'], - [1.034, '1.034'], - [1.3034, '1.303'], - [12.0345, '12.03'], - [121.456, '121.5'], - [1034.123, '1,034'], - [47361034.006, '47,361,034'], - ['12130982923409.5', '12,130,982,923,410'], - ['1213098292340944.5', '1,213,098,292,340,945'], - // Precision is lost after the value is greator than Number.MAX_SAFE_INTEGER. The digits after - // the 15th digit become 0's. - // TODO fix the precision - /** @see {@link https://github.com/MetaMask/metamask-extension/issues/25755} */ - ['30001231231212312138768', '30,001,231,231,212,312,000,000'], - [ - '115792089237316195423570985008687907853269984665640564039457584007913129639935', - '115,792,089,237,316,200,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000', - ], - ])( - 'formats amount greater than or equal to 1 with appropriate decimal precision (%s => %s)', - (amount: number, expected: string) => { - expect(formatAmount(locale, new BigNumber(amount))).toBe(expected); - }, - ); + // @ts-expect-error This is missing from the Mocha type definitions + it.each([ + [1.0034, '1.003'], + [1.034, '1.034'], + [1.3034, '1.303'], + [12.0345, '12.03'], + [121.456, '121.5'], + [1034.123, '1,034'], + [47361034.006, '47,361,034'], + ['12130982923409.5', '12,130,982,923,410'], + ['1213098292340944.5', '1,213,098,292,340,945'], + ['30001231231212312138768', '30,001,231,231,212,312,138,768'], + [ + '1157920892373161954235709850086879078532699846656405640394575.84007913129639935', + '1,157,920,892,373,161,954,235,709,850,086,879,078,532,699,846,656,405,640,394,576', + ], + ])( + 'formats amount greater than or equal to 1 with appropriate decimal precision (%s => %s)', + (amount: number, expected: string) => { + expect(formatAmount(locale, new BigNumber(amount))).toBe(expected); + }, + ); + }); }); diff --git a/ui/pages/confirmations/components/simulation-details/formatAmount.ts b/ui/pages/confirmations/components/simulation-details/formatAmount.ts index 1314b3b5f911..213b55d52083 100644 --- a/ui/pages/confirmations/components/simulation-details/formatAmount.ts +++ b/ui/pages/confirmations/components/simulation-details/formatAmount.ts @@ -13,9 +13,18 @@ export function formatAmountMaxPrecision( locale: string, num: number | BigNumber, ): string { - return new Intl.NumberFormat(locale, { - minimumSignificantDigits: 1, - }).format(new BigNumber(num.toString()).toNumber()); + const bigNumberValue = new BigNumber(num); + const numberOfDecimals = bigNumberValue.decimalPlaces(); + const formattedValue = bigNumberValue.toFixed(numberOfDecimals); + + const [integerPart, fractionalPart] = formattedValue.split('.'); + const formattedIntegerPart = new Intl.NumberFormat(locale).format( + integerPart as unknown as number, + ); + + return fractionalPart + ? `${formattedIntegerPart}.${fractionalPart}` + : formattedIntegerPart; } /** @@ -82,5 +91,10 @@ export function formatAmount(locale: string, amount: BigNumber): string { return new Intl.NumberFormat(locale, { maximumFractionDigits, - } as Intl.NumberFormatOptions).format(amount.toNumber()); + } as Intl.NumberFormatOptions).format( + // string is valid parameter for format function + // for some reason it gives TS issue + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/format#number + amount.toFixed(maximumFractionDigits) as unknown as number, + ); } 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 0e472aad11db..41fa86d0e454 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 @@ -302,7 +302,7 @@ exports[`ConfirmSendEther should render correct information for for confirm send

- ? + T

+ > + Test initial state + + > + 966.988 + - USD + ETH
@@ -372,7 +374,7 @@ exports[`remove-snap-account confirmation should match snapshot 1`] = `
- ? + E
- ? + T
+ > + Test initial state + - ? + T
+ > + Test initial state + - {showWhatsNew ? : null} {!showWhatsNew && showRecoveryPhraseReminder ? ( Promise; }; +function useUpdateAccountSetting( + address: string, + refetchAccountSettings: () => Promise, +) { + const { onChange: switchAccountNotifications, error } = + useSwitchAccountNotificationsChange(); + const { listNotifications: refetch } = useListNotifications(); + + // Local states + const [loading, setLoading] = useState(false); + + const toggleAccount = useCallback( + async (state: boolean) => { + setLoading(true); + try { + await switchAccountNotifications([address], state); + await refetchAccountSettings(); + refetch(); + } catch { + // Do nothing (we don't need to propagate this) + } + setLoading(false); + }, + [address, refetch, refetchAccountSettings, switchAccountNotifications], + ); + + return { toggleAccount, loading, error }; +} + export const NotificationsSettingsPerAccount = ({ address, name, - disabled, - loading, + isEnabled, + isLoading, + disabledSwitch, + refetchAccountSettings, }: NotificationsSettingsPerAccountProps) => { - const { listNotifications } = useMetamaskNotificationsContext(); const trackEvent = useContext(MetaMetricsContext); - // Hooks const { - onChange: onChangeAccountNotifications, - error: errorAccountNotificationsChange, - } = useSwitchAccountNotificationsChange(); - const { switchAccountNotifications, error: errorSwitchAccountNotifications } = - useSwitchAccountNotifications(); - - const [data, setData] = useState< - UseSwitchAccountNotificationsData | undefined - >(undefined); - const [isLoading, setIsLoading] = useState(false); - - useEffect(() => { - const fetchData = async () => { - const fetchedData = await switchAccountNotifications([address]); - setData(fetchedData || {}); - }; - fetchData(); - }, [address, switchAccountNotifications]); - - useEffect(() => { - setIsLoading(loading); - }, [loading]); + toggleAccount, + loading: isUpdatingAccount, + error: accountError, + } = useUpdateAccountSetting(address, refetchAccountSettings); - const error = - errorAccountNotificationsChange || errorSwitchAccountNotifications; + const loading = isLoading || isUpdatingAccount; + const error = accountError; const handleToggleAccountNotifications = useCallback(async () => { - const originalValue = data?.[address]; - await onChangeAccountNotifications([address], !originalValue); trackEvent({ category: MetaMetricsEventCategory.NotificationSettings, - event: originalValue - ? MetaMetricsEventName.DisablingAccountNotifications - : MetaMetricsEventName.EnablingAccountNotifications, + event: MetaMetricsEventName.NotificationsSettingsUpdated, properties: { - address, + setting_type: 'account_notifications', + old_value: isEnabled, + new_value: !isEnabled, }, }); - const fetchedData = await switchAccountNotifications([address]); - setData(fetchedData || {}); - listNotifications(); - }, [address, data, onChangeAccountNotifications]); + await toggleAccount(!isEnabled); + }, [address, isEnabled, toggleAccount, trackEvent]); return ( <> diff --git a/ui/pages/notifications-settings/notifications-settings-types.tsx b/ui/pages/notifications-settings/notifications-settings-types.tsx index 8225bd14ec24..b00390e1cc46 100644 --- a/ui/pages/notifications-settings/notifications-settings-types.tsx +++ b/ui/pages/notifications-settings/notifications-settings-types.tsx @@ -55,10 +55,13 @@ export function NotificationsSettingsTypes({ try { onChangeFeatureAnnouncements(!featureAnnouncementsEnabled); trackEvent({ - category: MetaMetricsEventCategory.NotificationInteraction, - event: featureAnnouncementsEnabled - ? MetaMetricsEventName.FeatureAnnouncementDisabled - : MetaMetricsEventName.FeatureAnnouncementEnabled, + category: MetaMetricsEventCategory.NotificationSettings, + event: MetaMetricsEventName.NotificationsSettingsUpdated, + properties: { + setting_type: 'feature_announcements', + old_value: featureAnnouncementsEnabled, + new_value: !featureAnnouncementsEnabled, + }, }); listNotifications(); } catch (error) { diff --git a/ui/pages/notifications-settings/notifications-settings.tsx b/ui/pages/notifications-settings/notifications-settings.tsx index c4e71e97f273..7261daeed02f 100644 --- a/ui/pages/notifications-settings/notifications-settings.tsx +++ b/ui/pages/notifications-settings/notifications-settings.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useMemo, useState } from 'react'; import { useSelector } from 'react-redux'; import { useHistory } from 'react-router-dom'; import type { InternalAccount } from '@metamask/keyring-api'; @@ -26,9 +26,9 @@ import { Content, Header } from '../../components/multichain/pages/page'; import { selectIsMetamaskNotificationsEnabled, getIsUpdatingMetamaskNotifications, - getIsUpdatingMetamaskNotificationsAccount, } from '../../selectors/metamask-notifications/metamask-notifications'; import { getInternalAccounts } from '../../selectors'; +import { useAccountSettingsProps } from '../../hooks/metamask-notifications/useSwitchNotifications'; import { NotificationsSettingsAllowNotifications } from './notifications-settings-allow-notifications'; import { NotificationsSettingsTypes } from './notifications-settings-types'; import { NotificationsSettingsPerAccount } from './notifications-settings-per-account'; @@ -56,36 +56,23 @@ export default function NotificationsSettings() { const isUpdatingMetamaskNotifications = useSelector( getIsUpdatingMetamaskNotifications, ); - const isUpdatingMetamaskNotificationsAccount = useSelector( - getIsUpdatingMetamaskNotificationsAccount, - ); const accounts: AccountType[] = useSelector(getInternalAccounts); // States const [loadingAllowNotifications, setLoadingAllowNotifications] = useState(isUpdatingMetamaskNotifications); - const [updatingAccountList, setUpdatingAccountList] = useState([]); - const [updatingAccount, setUpdatingAccount] = useState(false); - - useEffect(() => { - if (updatingAccountList.length > 0) { - setUpdatingAccount(true); - } else { - setUpdatingAccount(false); - } - }, [updatingAccountList]); - useEffect(() => { - if (isUpdatingMetamaskNotifications) { - setLoadingAllowNotifications(isUpdatingMetamaskNotifications); - } - }, [isUpdatingMetamaskNotifications]); + const accountAddresses = useMemo( + () => accounts.map((a) => a.address), + [accounts], + ); - useEffect(() => { - if (isUpdatingMetamaskNotificationsAccount) { - setUpdatingAccountList(isUpdatingMetamaskNotificationsAccount); - } - }, [isUpdatingMetamaskNotificationsAccount]); + // Account Settings + const accountSettingsProps = useAccountSettingsProps(accountAddresses); + const updatingAccounts = accountSettingsProps.accountsBeingUpdated.length > 0; + const refetchAccountSettings = async () => { + await accountSettingsProps.update(accountAddresses); + }; return ( @@ -108,7 +95,7 @@ export default function NotificationsSettings() { loading={loadingAllowNotifications} setLoading={setLoadingAllowNotifications} data-testid="notifications-settings-allow-notifications" - disabled={updatingAccount} + disabled={updatingAccounts} /> {/* Notifications settings per types */} {/* Notifications settings per account */} @@ -160,8 +147,18 @@ export default function NotificationsSettings() { key={account.id} address={account.address} name={account.metadata.name} - disabled={updatingAccountList.length > 0} - loading={updatingAccountList.includes(account.address)} + disabledSwitch={ + accountSettingsProps.initialLoading || updatingAccounts + } + isLoading={accountSettingsProps.accountsBeingUpdated.includes( + account.address, + )} + isEnabled={ + accountSettingsProps.data?.[ + account.address.toLowerCase() + ] ?? false + } + refetchAccountSettings={refetchAccountSettings} /> ))} diff --git a/ui/pages/notifications/notification-components/snap/snap.tsx b/ui/pages/notifications/notification-components/snap/snap.tsx index bdd7b4576694..beb9151af10d 100644 --- a/ui/pages/notifications/notification-components/snap/snap.tsx +++ b/ui/pages/notifications/notification-components/snap/snap.tsx @@ -29,11 +29,12 @@ export const SnapComponent = ({ snapNotification }: SnapComponentProps) => { dispatch(markNotificationsAsRead([snapNotification.id])); trackEvent({ category: MetaMetricsEventCategory.NotificationInteraction, - event: MetaMetricsEventName.NotificationDetailClicked, + event: MetaMetricsEventName.NotificationClicked, properties: { - notificationId: snapNotification.id, - notificationType: snapNotification.type, - notificationIsRead: snapNotification.isRead, + notification_id: snapNotification.id, + notification_type: snapNotification.type, + notification_is_read: snapNotification.isRead, + click_type: 'item', }, }); }; @@ -42,11 +43,12 @@ export const SnapComponent = ({ snapNotification }: SnapComponentProps) => { dispatch(markNotificationsAsRead([snapNotification.id])); trackEvent({ category: MetaMetricsEventCategory.NotificationInteraction, - event: MetaMetricsEventName.NotificationItemClicked, + event: MetaMetricsEventName.NotificationClicked, properties: { - notificationId: snapNotification.id, - notificationType: snapNotification.type, - notificationIsRead: snapNotification.isRead, + notification_id: snapNotification.id, + notification_type: snapNotification.type, + notification_is_read: snapNotification.isRead, + click_type: 'item', }, }); history.push(getSnapRoute(snapNotification.data.origin)); diff --git a/ui/pages/notifications/notifications-list-item.tsx b/ui/pages/notifications/notifications-list-item.tsx index fd8e628d9869..b3ffba845ebf 100644 --- a/ui/pages/notifications/notifications-list-item.tsx +++ b/ui/pages/notifications/notifications-list-item.tsx @@ -14,6 +14,7 @@ import { } from '../../helpers/constants/design-system'; import { NOTIFICATIONS_ROUTE } from '../../helpers/constants/routes'; import { useMarkNotificationAsRead } from '../../hooks/metamask-notifications/useNotifications'; +import { TRIGGER_TYPES } from '../../../app/scripts/controllers/metamask-notifications/constants/notification-schema'; import { NotificationComponents, hasNotificationComponents, @@ -32,11 +33,15 @@ export function NotificationsListItem({ const handleNotificationClick = useCallback(() => { trackEvent({ category: MetaMetricsEventCategory.NotificationInteraction, - event: MetaMetricsEventName.NotificationItemClicked, + event: MetaMetricsEventName.NotificationClicked, properties: { - notificationId: notification.id, - notificationType: notification.type, - notificationIsRead: notification.isRead, + notification_id: notification.id, + notification_type: notification.type, + notification_is_read: notification.isRead, + ...(notification.type !== TRIGGER_TYPES.FEATURES_ANNOUNCEMENT && { + chain_id: notification?.chain_id, + }), + click_type: 'item', }, }); markNotificationAsRead([ diff --git a/ui/pages/onboarding-flow/creation-successful/creation-successful.js b/ui/pages/onboarding-flow/creation-successful/creation-successful.js index c645b93078dc..515637e79ccd 100644 --- a/ui/pages/onboarding-flow/creation-successful/creation-successful.js +++ b/ui/pages/onboarding-flow/creation-successful/creation-successful.js @@ -25,7 +25,7 @@ import { import { MetaMetricsContext } from '../../../contexts/metametrics'; import { useCreateSession } from '../../../hooks/metamask-notifications/useCreateSession'; import { selectIsProfileSyncingEnabled } from '../../../selectors/metamask-notifications/profile-syncing'; -import { selectParticipateInMetaMetrics } from '../../../selectors/metamask-notifications/authentication'; +import { selectIsSignedIn } from '../../../selectors/metamask-notifications/authentication'; export default function CreationSuccessful() { const history = useHistory(); @@ -36,7 +36,7 @@ export default function CreationSuccessful() { const { createSession } = useCreateSession(); const isProfileSyncingEnabled = useSelector(selectIsProfileSyncingEnabled); - const participateInMetaMetrics = useSelector(selectParticipateInMetaMetrics); + const isSignedIn = useSelector(selectIsSignedIn); return (
@@ -116,19 +116,10 @@ export default function CreationSuccessful() { event: MetaMetricsEventName.OnboardingWalletCreationComplete, properties: { method: firstTimeFlowType, + is_signed_in: isSignedIn, + is_profile_syncing_enabled: isProfileSyncingEnabled, }, }); - if (isProfileSyncingEnabled || participateInMetaMetrics) { - trackEvent({ - category: MetaMetricsEventCategory.Onboarding, - event: - MetaMetricsEventName.OnboardingWalletCreationCompleteWithAuthenticating, - properties: { - isProfileSyncingEnabled, - partedInMetaMetrics: participateInMetaMetrics, - }, - }); - } createSession(); history.push(ONBOARDING_PIN_EXTENSION_ROUTE); }} diff --git a/ui/pages/onboarding-flow/metametrics/__snapshots__/metametrics.test.js.snap b/ui/pages/onboarding-flow/metametrics/__snapshots__/metametrics.test.js.snap index 516b27b2a38b..5c8c445397e7 100644 --- a/ui/pages/onboarding-flow/metametrics/__snapshots__/metametrics.test.js.snap +++ b/ui/pages/onboarding-flow/metametrics/__snapshots__/metametrics.test.js.snap @@ -16,6 +16,18 @@ exports[`Onboarding Metametrics Component should match snapshot 1`] = ` > We’d like to gather basic usage and diagnostics data to improve MetaMask. Know that we never sell the data you provide here.

+

@@ -123,20 +135,20 @@ exports[`Onboarding Metametrics Component should match snapshot 1`] = `

- +
@@ -158,6 +170,18 @@ exports[`Onboarding Metametrics Component should match snapshot after new policy > We’d like to gather basic usage and diagnostics data to improve MetaMask. Know that we never sell the data you provide here.

+

@@ -265,20 +289,20 @@ exports[`Onboarding Metametrics Component should match snapshot after new policy

- +
diff --git a/ui/pages/onboarding-flow/metametrics/index.scss b/ui/pages/onboarding-flow/metametrics/index.scss index 8860d03ad572..49ee6aa8ab28 100644 --- a/ui/pages/onboarding-flow/metametrics/index.scss +++ b/ui/pages/onboarding-flow/metametrics/index.scss @@ -40,10 +40,6 @@ button { margin-bottom: 24px; width: 50%; - - @include design-system.screen-sm-min { - width: 200px; - } } } } diff --git a/ui/pages/onboarding-flow/metametrics/metametrics.js b/ui/pages/onboarding-flow/metametrics/metametrics.js index 8fe4246ca332..422e9f32cb95 100644 --- a/ui/pages/onboarding-flow/metametrics/metametrics.js +++ b/ui/pages/onboarding-flow/metametrics/metametrics.js @@ -6,8 +6,11 @@ import { TypographyVariant, FONT_WEIGHT, TEXT_ALIGN, + Display, + FlexDirection, TextColor, IconColor, + BlockSize, } from '../../../helpers/constants/design-system'; import Button from '../../../components/ui/button'; import { useI18nContext } from '../../../hooks/useI18nContext'; @@ -30,10 +33,12 @@ import { import { MetaMetricsContext } from '../../../contexts/metametrics'; import { + Box as BoxComponent, Checkbox, Icon, IconName, IconSize, + Text, } from '../../../components/component-library'; import Box from '../../../components/ui/box/box'; @@ -122,6 +127,17 @@ export default function OnboardingMetametrics() { > {t('onboardingMetametricsDescription')} + + + {t('onboardingMetametricsPrivacyDescription')} + + -
- + -
+ +
); } diff --git a/ui/pages/onboarding-flow/metametrics/metametrics.test.js b/ui/pages/onboarding-flow/metametrics/metametrics.test.js index 5318d0ca1821..ffa66646bc2e 100644 --- a/ui/pages/onboarding-flow/metametrics/metametrics.test.js +++ b/ui/pages/onboarding-flow/metametrics/metametrics.test.js @@ -6,7 +6,7 @@ import { renderWithProvider } from '../../../../test/lib/render-helpers'; import { ONBOARDING_CREATE_PASSWORD_ROUTE } from '../../../helpers/constants/routes'; import { onboardingMetametricsAgree, - onboardingMetametricsDisagree, + noThanks, } from '../../../../app/_locales/en/messages.json'; import { setParticipateInMetaMetrics, @@ -102,7 +102,7 @@ describe('Onboarding Metametrics Component', () => { mockStore, ); - const confirmCancel = queryByText(onboardingMetametricsDisagree.message); + const confirmCancel = queryByText(noThanks.message); fireEvent.click(confirmCancel); @@ -120,7 +120,7 @@ describe('Onboarding Metametrics Component', () => { mockStore, ); - const confirmCancel = queryByText(onboardingMetametricsDisagree.message); + const confirmCancel = queryByText(noThanks.message); fireEvent.click(confirmCancel); diff --git a/ui/pages/onboarding-flow/privacy-settings/privacy-settings.js b/ui/pages/onboarding-flow/privacy-settings/privacy-settings.js index ea15950451c5..1b22fc53f43c 100644 --- a/ui/pages/onboarding-flow/privacy-settings/privacy-settings.js +++ b/ui/pages/onboarding-flow/privacy-settings/privacy-settings.js @@ -210,23 +210,11 @@ export default function PrivacySettings() { category: MetaMetricsEventCategory.Onboarding, event: MetaMetricsEventName.OnboardingWalletAdvancedSettings, properties: { + is_profile_syncing_enabled: profileSyncingProps.isProfileSyncingEnabled, show_incoming_tx: incomingTransactionsPreferences, use_phising_detection: usePhishingDetection, turnon_token_detection: turnOnTokenDetection, - }, - }); - - const eventName = - profileSyncingProps.isProfileSyncingEnabled || participateInMetaMetrics - ? MetaMetricsEventName.OnboardingWalletAdvancedSettingsWithAuthenticating - : MetaMetricsEventName.OnboardingWalletAdvancedSettingsWithoutAuthenticating; - - trackEvent({ - category: MetaMetricsEventCategory.Onboarding, - event: eventName, - properties: { - isProfileSyncingEnabled: profileSyncingProps.isProfileSyncingEnabled, - participateInMetaMetrics, + settings_group: 'advanced', }, }); @@ -242,10 +230,12 @@ export default function PrivacySettings() { profileSyncingProps.setIsProfileSyncingEnabled(false); trackEvent({ category: MetaMetricsEventCategory.Onboarding, - event: - MetaMetricsEventName.OnboardingWalletAdvancedSettingsTurnOffProfileSyncing, + event: MetaMetricsEventName.OnboardingWalletAdvancedSettings, properties: { - participateInMetaMetrics, + settings_group: 'advanced', + settings_type: 'profile_syncing', + old_value: true, + new_value: false, }, }); }, @@ -255,10 +245,12 @@ export default function PrivacySettings() { profileSyncingProps.setIsProfileSyncingEnabled(true); trackEvent({ category: MetaMetricsEventCategory.Onboarding, - event: - MetaMetricsEventName.OnboardingWalletAdvancedSettingsTurnOnProfileSyncing, + event: MetaMetricsEventName.OnboardingWalletAdvancedSettings, properties: { - participateInMetaMetrics, + settings_group: 'advanced', + settings_type: 'profile_syncing', + old_value: false, + new_value: true, }, }); } diff --git a/ui/pages/routes/routes.component.js b/ui/pages/routes/routes.component.js index 510d8322244c..bdf1d8187cd6 100644 --- a/ui/pages/routes/routes.component.js +++ b/ui/pages/routes/routes.component.js @@ -624,6 +624,7 @@ export default class Routes extends Component { const onAutoHideToast = () => { setHideNftEnablementToast(false); }; + if (!this.onHomeScreen()) { return null; } diff --git a/ui/pages/settings/advanced-tab/__snapshots__/advanced-tab.component.test.js.snap b/ui/pages/settings/advanced-tab/__snapshots__/advanced-tab.component.test.js.snap index 52b0c314dd58..8faac5e8f71a 100644 --- a/ui/pages/settings/advanced-tab/__snapshots__/advanced-tab.component.test.js.snap +++ b/ui/pages/settings/advanced-tab/__snapshots__/advanced-tab.component.test.js.snap @@ -231,7 +231,7 @@ exports[`AdvancedTab Component should match snapshot 1`] = ` class="settings-page__content-item-col" >
-
-
- - Restore user data - - - You can restore data like contacts and preferences from a backup file. - -
-
-
- - -
-
-
{ const { fileName, data } = await this.props.backupUserData(); exportAsFile(fileName, data, ExportableContentType.JSON); @@ -360,6 +320,7 @@ export default class AdvancedTab extends PureComponent { } offLabel={t('off')} onLabel={t('on')} + className="show-fiat-on-testnets-toggle" />
@@ -678,75 +639,6 @@ export default class AdvancedTab extends PureComponent { ); } - renderRestoreUserData() { - const { t } = this.context; - const { showResultMessage, restoreSuccessful, restoreMessage } = this.state; - - const defaultRestoreMessage = restoreSuccessful - ? t('restoreSuccessful') - : t('restoreFailed'); - const restoreMessageToRender = - restoreMessage === CORRUPT_JSON_FILE - ? t('dataBackupSeemsCorrupt') - : defaultRestoreMessage; - - return ( - -
- {t('restoreUserData')} - - {t('restoreUserDataDescription')} - -
-
-
- - this.handleFileUpload(e)} - /> -
- {showResultMessage && ( - { - this.setState({ - showResultMessage: false, - restoreSuccessful: true, - restoreMessage: null, - }); - }} - /> - )} -
-
- ); - } - render() { const { warning } = this.props; @@ -763,7 +655,6 @@ export default class AdvancedTab extends PureComponent { {this.renderUseNonceOptIn()} {this.renderAutoLockTimeLimit()} {this.renderUserDataBackup()} - {this.renderRestoreUserData()} {this.renderDismissSeedBackupReminderControl()} {this.renderToggleEthSignControl()}
diff --git a/ui/pages/settings/advanced-tab/advanced-tab.component.test.js b/ui/pages/settings/advanced-tab/advanced-tab.component.test.js index ccb194fbd984..8f894856ad6b 100644 --- a/ui/pages/settings/advanced-tab/advanced-tab.component.test.js +++ b/ui/pages/settings/advanced-tab/advanced-tab.component.test.js @@ -8,12 +8,15 @@ import AdvancedTab from '.'; const mockSetAutoLockTimeLimit = jest.fn().mockReturnValue({ type: 'TYPE' }); const mockSetShowTestNetworks = jest.fn(); +const mockSetShowFiatConversionOnTestnetsPreference = jest.fn(); const mockSetStxOptIn = jest.fn(); jest.mock('../../../store/actions.ts', () => { return { setAutoLockTimeLimit: (...args) => mockSetAutoLockTimeLimit(...args), setShowTestNetworks: () => mockSetShowTestNetworks, + setShowFiatConversionOnTestnetsPreference: () => + mockSetShowFiatConversionOnTestnetsPreference, setSmartTransactionsOptInStatus: () => mockSetStxOptIn, }; }); @@ -33,12 +36,6 @@ describe('AdvancedTab Component', () => { expect(backupButton).toBeInTheDocument(); }); - it('should render restore button', () => { - const { queryByTestId } = renderWithProvider(, mockStore); - const restoreFile = queryByTestId('restore-file'); - expect(restoreFile).toBeInTheDocument(); - }); - it('should default the auto-lockout time to 0', () => { const { queryByTestId } = renderWithProvider(, mockStore); const autoLockoutTime = queryByTestId('auto-lockout-time'); @@ -74,6 +71,16 @@ describe('AdvancedTab Component', () => { expect(mockSetAutoLockTimeLimit).toHaveBeenCalledWith(0); }); + it('should toggle show fiat on test networks', () => { + const { queryAllByRole } = renderWithProvider(, mockStore); + + const testShowFiatOnTestnets = queryAllByRole('checkbox')[2]; + + fireEvent.click(testShowFiatOnTestnets); + + expect(mockSetShowFiatConversionOnTestnetsPreference).toHaveBeenCalled(); + }); + it('should toggle show test networks', () => { const { queryAllByRole } = renderWithProvider(, mockStore); diff --git a/ui/pages/settings/advanced-tab/advanced-tab.container.js b/ui/pages/settings/advanced-tab/advanced-tab.container.js index a0dcd4a7bcc3..ce818363baa1 100644 --- a/ui/pages/settings/advanced-tab/advanced-tab.container.js +++ b/ui/pages/settings/advanced-tab/advanced-tab.container.js @@ -6,7 +6,6 @@ import { getPreferences } from '../../../selectors'; import { backupUserData, displayWarning, - restoreUserData, setAutoLockTimeLimit, setDisabledRpcMethodPreference, setDismissSeedBackUpReminder, @@ -56,7 +55,6 @@ export const mapStateToProps = (state) => { export const mapDispatchToProps = (dispatch) => { return { backupUserData: () => backupUserData(), - restoreUserData: (jsonString) => restoreUserData(jsonString), setHexDataFeatureFlag: (shouldShow) => dispatch(setFeatureFlag('sendHexData', shouldShow)), displayWarning: (warning) => dispatch(displayWarning(warning)), diff --git a/ui/pages/settings/contact-list-tab/add-contact/add-contact.component.js b/ui/pages/settings/contact-list-tab/add-contact/add-contact.component.js index 78c3054df933..5221aff38d04 100644 --- a/ui/pages/settings/contact-list-tab/add-contact/add-contact.component.js +++ b/ui/pages/settings/contact-list-tab/add-contact/add-contact.component.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import { debounce } from 'lodash'; import TextField from '../../../../components/ui/text-field'; import { CONTACT_LIST_ROUTE } from '../../../../helpers/constants/routes'; -import { IS_FLASK, isValidDomainName } from '../../../../helpers/utils/util'; +import { isValidDomainName } from '../../../../helpers/utils/util'; import DomainInput from '../../../confirmations/send/send-content/add-recipient/domain-input'; import PageContainerFooter from '../../../../components/ui/page-container/page-container-footer'; import { @@ -11,7 +11,7 @@ import { isValidHexAddress, } from '../../../../../shared/modules/hexstring-utils'; import { INVALID_RECIPIENT_ADDRESS_ERROR } from '../../../confirmations/send/send.constants'; -import { DomainInputResolutionCell } from '../../../../components/multichain/pages/send/components/domain-input-resolution-cell'; +import { DomainInputResolutionCell } from '../../../../components/multichain/pages/send/components'; export default class AddContact extends PureComponent { static contextTypes = { @@ -68,8 +68,10 @@ export default class AddContact extends PureComponent { isValidHexAddress(input, { mixedCaseUseChecksum: true }); const validEnsAddress = isValidDomainName(input); - if (!IS_FLASK && !validEnsAddress && !valid) { + if (!validEnsAddress && !valid) { this.setState({ error: INVALID_RECIPIENT_ADDRESS_ERROR }); + } else { + this.setState({ error: null }); } }; @@ -104,6 +106,10 @@ export default class AddContact extends PureComponent { this.props; const errorToRender = domainError || this.state.error; + const newAddress = this.state.selectedAddress || this.state.input; + const validAddress = + !isBurnAddress(newAddress) && + isValidHexAddress(newAddress, { mixedCaseUseChecksum: true }); return (
@@ -150,7 +156,7 @@ export default class AddContact extends PureComponent { domainName={domainName} onClick={() => { this.setState({ - selectedAddress: resolvedAddress, + input: resolvedAddress, newName: this.state.newName || domainName, }); this.props.resetDomainResolution(); @@ -171,15 +177,10 @@ export default class AddContact extends PureComponent { { - await addToAddressBook( - this.state.selectedAddress, - this.state.newName, - ); + await addToAddressBook(newAddress, this.state.newName); history.push(CONTACT_LIST_ROUTE); }} onCancel={() => { diff --git a/ui/pages/settings/contact-list-tab/add-contact/add-contact.test.js b/ui/pages/settings/contact-list-tab/add-contact/add-contact.test.js index 29f9fc7722ad..2ab1a7111704 100644 --- a/ui/pages/settings/contact-list-tab/add-contact/add-contact.test.js +++ b/ui/pages/settings/contact-list-tab/add-contact/add-contact.test.js @@ -47,7 +47,7 @@ describe('AddContact component', () => { fireEvent.change(input, { target: { value: 'invalid address' } }); setTimeout(() => { expect(getByText('Recipient address is invalid')).toBeInTheDocument(); - }, 100); + }, 600); }); it('should get disabled submit button when username field is empty', () => { @@ -60,4 +60,47 @@ describe('AddContact component', () => { const saveButton = getByText('Save'); expect(saveButton).toBeDisabled(); }); + + it('should enable submit button when input is valid', () => { + const store = configureMockStore(middleware)(state); + const { getByText, getByTestId } = renderWithProvider( + , + store, + ); + + const nameInput = document.getElementById('nickname'); + fireEvent.change(nameInput, { target: { value: 'friend' } }); + + const addressInput = getByTestId('ens-input'); + fireEvent.change(addressInput, { + target: { value: '0x1234Bf0BBa69C63E2657cF94693cC4A907085678' }, + }); + + const saveButton = getByText('Save'); + expect(saveButton).not.toBeDisabled(); + }); + + it('should disable submit button when input is not a valid address', () => { + const store = configureMockStore(middleware)(state); + const { getByText, getByTestId } = renderWithProvider( + , + store, + ); + + const nameInput = document.getElementById('nickname'); + fireEvent.change(nameInput, { target: { value: 'friend' } }); + + const addressInput = getByTestId('ens-input'); + fireEvent.change(addressInput, { + // invalid length + target: { value: '0x1234' }, + }); + expect(getByText('Save')).toBeDisabled(); + + fireEvent.change(addressInput, { + // wrong checksum + target: { value: '0x1234bf0bba69C63E2657cF94693cC4A907085678' }, + }); + expect(getByText('Save')).toBeDisabled(); + }); }); diff --git a/ui/pages/settings/contact-list-tab/index.scss b/ui/pages/settings/contact-list-tab/index.scss index 8f4ed5795c0d..dadf594ff9dc 100644 --- a/ui/pages/settings/contact-list-tab/index.scss +++ b/ui/pages/settings/contact-list-tab/index.scss @@ -212,6 +212,7 @@ padding-bottom: 0 !important; width: 100%; height: 100%; + width: 100%; padding-top: 0; &__content { diff --git a/ui/pages/settings/developer-options-tab/__snapshots__/developer-options-tab.test.tsx.snap b/ui/pages/settings/developer-options-tab/__snapshots__/developer-options-tab.test.tsx.snap index 262707e4c051..f7f9b38a0235 100644 --- a/ui/pages/settings/developer-options-tab/__snapshots__/developer-options-tab.test.tsx.snap +++ b/ui/pages/settings/developer-options-tab/__snapshots__/developer-options-tab.test.tsx.snap @@ -308,6 +308,147 @@ exports[`Develop options tab should match snapshot 1`] = `
+

+ Sentry +

+
+
+
+
+ + + Generate an unhandled + + TestError + + in this window. + + +
+
+
+ +
+
+
+
+
+
+
+
+
+ + + Generate an unhandled + + TestError + + in the service worker. + + +
+
+
+ +
+
+
+
+
+
+
+
+
+ + + Generate a + + Developer Test + + Sentry trace. + + +
+
+
+ +
+
+
+
+
+
+
`; diff --git a/ui/pages/settings/developer-options-tab/developer-options-tab.tsx b/ui/pages/settings/developer-options-tab/developer-options-tab.tsx index dcfdad78828f..1637fec74ce0 100644 --- a/ui/pages/settings/developer-options-tab/developer-options-tab.tsx +++ b/ui/pages/settings/developer-options-tab/developer-options-tab.tsx @@ -36,6 +36,7 @@ import { getEnvironmentType } from '../../../../app/scripts/lib/util'; import { ENVIRONMENT_TYPE_POPUP } from '../../../../shared/constants/app'; import { getIsRedesignedConfirmationsDeveloperEnabled } from '../../confirmations/selectors/confirm'; import ToggleRow from './developer-options-toggle-row-component'; +import { SentryTest } from './sentry-test'; const DeveloperOptionsTab = () => { const t = useI18nContext(); @@ -272,6 +273,7 @@ const DeveloperOptionsTab = () => { {renderNetworkMenuRedesign()} {renderEnableConfirmationsRedesignToggle()}
+
); }; diff --git a/ui/pages/settings/developer-options-tab/sentry-test.tsx b/ui/pages/settings/developer-options-tab/sentry-test.tsx new file mode 100644 index 000000000000..ab9a7702fcb5 --- /dev/null +++ b/ui/pages/settings/developer-options-tab/sentry-test.tsx @@ -0,0 +1,187 @@ +import React, { useState, useCallback, ReactElement } from 'react'; +import { ButtonVariant } from '@metamask/snaps-sdk'; +import { + Box, + Button, + Icon, + IconName, + IconSize, + Text, +} from '../../../components/component-library'; +import { + AlignItems, + Display, + FlexDirection, + IconColor, + JustifyContent, +} from '../../../helpers/constants/design-system'; +import { trace } from '../../../../shared/lib/trace'; +import { useI18nContext } from '../../../hooks/useI18nContext'; + +export function SentryTest() { + return ( + <> + + Sentry + +
+ + + +
+ + ); +} + +function GenerateUIError() { + const t = useI18nContext(); + + const handleClick = useCallback(async () => { + await window.stateHooks.throwTestError?.('Developer Options'); + }, []); + + return ( + TestError, + ])} + onClick={handleClick} + expectError + /> + ); +} + +function GenerateBackgroundError() { + const t = useI18nContext(); + + const handleClick = useCallback(async () => { + await window.stateHooks.throwTestBackgroundError?.('Developer Options'); + }, []); + + return ( + TestError], + )} + onClick={handleClick} + expectError + /> + ); +} + +function GenerateTrace() { + const t = useI18nContext(); + + const handleClick = useCallback(async () => { + await trace( + { + name: 'Developer Test', + data: { 'test.data.number': 123 }, + tags: { 'test.tag.number': 123 }, + }, + async (context) => { + await trace( + { + name: 'Nested Test 1', + data: { 'test.data.boolean': true }, + tags: { 'test.tag.boolean': true }, + parentContext: context, + }, + () => sleep(1000), + ); + + await trace( + { + name: 'Nested Test 2', + data: { 'test.data.string': 'test' }, + tags: { 'test.tag.string': 'test' }, + parentContext: context, + }, + () => sleep(500), + ); + }, + ); + }, []); + + return ( + Developer Test, + ])} + onClick={handleClick} + /> + ); +} + +function TestButton({ + name, + description, + onClick, + expectError, +}: { + name: string; + description: ReactElement; + onClick: () => Promise; + expectError?: boolean; +}) { + const [isComplete, setIsComplete] = useState(false); + + const handleClick = useCallback(async () => { + let hasError = false; + + try { + await onClick(); + } catch (error) { + hasError = true; + throw error; + } finally { + if (expectError || !hasError) { + setIsComplete(true); + } + } + }, [onClick]); + + return ( + +
+
{description}
+
+
+ +
+
+ + +
+
+ ); +} + +function sleep(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} diff --git a/ui/pages/settings/info-tab/info-tab.component.js b/ui/pages/settings/info-tab/info-tab.component.js index c3c44e425561..121f29e7c52c 100644 --- a/ui/pages/settings/info-tab/info-tab.component.js +++ b/ui/pages/settings/info-tab/info-tab.component.js @@ -29,7 +29,7 @@ import { export default class InfoTab extends PureComponent { state = { - version: global.platform?.getVersion() ?? '', + version: process.env.METAMASK_VERSION, }; static contextTypes = { @@ -113,7 +113,7 @@ export default class InfoTab extends PureComponent {
{ - const t = useI18nContext(); const trackEvent = useContext(MetaMetricsContext); + const t = useI18nContext(); const dispatch = useDispatch(); const { enableProfileSyncing, error: enableProfileSyncingError } = useEnableProfileSyncing(); @@ -59,7 +58,6 @@ const ProfileSyncToggle = () => { const error = enableProfileSyncingError || disableProfileSyncingError; const isProfileSyncingEnabled = useSelector(selectIsProfileSyncingEnabled); - const participateInMetaMetrics = useSelector(selectParticipateInMetaMetrics); const isProfileSyncingUpdateLoading = useSelector( selectIsProfileSyncingUpdateLoading, ); @@ -73,9 +71,12 @@ const ProfileSyncToggle = () => { disableProfileSyncing(); trackEvent({ category: MetaMetricsEventCategory.Settings, - event: MetaMetricsEventName.TurnOffProfileSyncing, + event: MetaMetricsEventName.SettingsUpdated, properties: { - participateInMetaMetrics, + settings_group: 'security', + settings_type: 'profile_syncing', + old_value: true, + new_value: false, }, }); }, @@ -85,10 +86,12 @@ const ProfileSyncToggle = () => { await enableProfileSyncing(); trackEvent({ category: MetaMetricsEventCategory.Settings, - event: MetaMetricsEventName.TurnOnProfileSyncing, + event: MetaMetricsEventName.SettingsUpdated, properties: { - isProfileSyncingEnabled, - participateInMetaMetrics, + settings_group: 'security', + settings_type: 'profile_syncing', + old_value: false, + new_value: true, }, }); } diff --git a/ui/pages/swaps/smart-transaction-status/smart-transaction-status.js b/ui/pages/swaps/smart-transaction-status/smart-transaction-status.js index ad60188f4ce1..157190687f31 100644 --- a/ui/pages/swaps/smart-transaction-status/smart-transaction-status.js +++ b/ui/pages/swaps/smart-transaction-status/smart-transaction-status.js @@ -110,7 +110,6 @@ export default function SmartTransactionStatusPage() { cancellationFeeWei = latestSmartTransaction?.statusMetadata?.cancellationFeeWei; } - const [timeLeftForPendingStxInSec, setTimeLeftForPendingStxInSec] = useState( swapsNetworkConfig.stxStatusDeadline, ); diff --git a/ui/selectors/confirm-transaction.js b/ui/selectors/confirm-transaction.js index 4a3b82c9e475..7e5182414b6b 100644 --- a/ui/selectors/confirm-transaction.js +++ b/ui/selectors/confirm-transaction.js @@ -34,6 +34,7 @@ import { checkNetworkAndAccountSupports1559, getCurrentChainId, getMetaMaskAccounts, + getTokenExchangeRates, } from './selectors'; import { getUnapprovedTransactions, @@ -153,19 +154,6 @@ export const txDataSelector = (state) => state.confirmTransaction.txData; const tokenDataSelector = (state) => state.confirmTransaction.tokenData; const tokenPropsSelector = (state) => state.confirmTransaction.tokenProps; -const contractExchangeRatesSelector = (state) => { - const chainId = getCurrentChainId(state); - const contractMarketData = state.metamask.marketData?.[chainId]; - - return Object.entries(contractMarketData).reduce( - (acc, [address, marketData]) => { - acc[address] = marketData?.price ?? null; - return acc; - }, - {}, - ); -}; - const tokenDecimalsSelector = createSelector( tokenPropsSelector, (tokenProps) => tokenProps && tokenProps.decimals, @@ -218,7 +206,7 @@ export const sendTokenTokenAmountAndToAddressSelector = createSelector( ); export const contractExchangeRateSelector = createSelector( - contractExchangeRatesSelector, + (state) => getTokenExchangeRates(state), tokenAddressSelector, (contractExchangeRates, tokenAddress) => { return contractExchangeRates[ diff --git a/ui/selectors/multichain.test.ts b/ui/selectors/multichain.test.ts index 6ffed9c18c04..86b4d85ad8a9 100644 --- a/ui/selectors/multichain.test.ts +++ b/ui/selectors/multichain.test.ts @@ -1,6 +1,9 @@ import { Cryptocurrency } from '@metamask/assets-controllers'; import { InternalAccount } from '@metamask/keyring-api'; -import { getNativeCurrency } from '../ducks/metamask/metamask'; +import { + getNativeCurrency, + getProviderConfig, +} from '../ducks/metamask/metamask'; import { MULTICHAIN_PROVIDER_CONFIGS, MultichainNetworks, @@ -12,7 +15,14 @@ import { MOCK_ACCOUNT_BIP122_P2WPKH, MOCK_ACCOUNT_BIP122_P2WPKH_TESTNET, } from '../../test/data/mock-accounts'; -import { CHAIN_IDS } from '../../shared/constants/network'; + +import { + CHAIN_IDS, + ETH_TOKEN_IMAGE_URL, + MAINNET_DISPLAY_NAME, + TEST_NETWORK_IDS, +} from '../../shared/constants/network'; + import { MultichainNativeAssets } from '../../shared/constants/multichain/assets'; import { AccountsState } from './accounts'; import { @@ -30,10 +40,10 @@ import { getMultichainShouldShowFiat, getMultichainIsBitcoin, getMultichainSelectedAccountCachedBalanceIsZero, + getMultichainIsTestnet, } from './multichain'; import { getCurrentCurrency, - getCurrentNetwork, getSelectedAccountCachedBalance, getShouldShowFiat, } from '.'; @@ -157,6 +167,47 @@ describe('Multichain Selectors', () => { const network = getMultichainNetwork(state); expect(network.isEvmNetwork).toBe(true); }); + + it('returns a EVM network with the correct network image', () => { + const state = getEvmState(); + + const network = getMultichainNetwork(state); + expect(network.network.rpcPrefs?.imageUrl).toBe(ETH_TOKEN_IMAGE_URL); + }); + + it('returns a nickname for default networks', () => { + const state = getEvmState(); + + const network = getMultichainNetwork(state); + expect(network.nickname).toBe(MAINNET_DISPLAY_NAME); + }); + + it('returns rpcUrl as its nickname if its not defined', () => { + const mockNetworkRpc = 'https://mock-rpc.com'; + const mockNetwork = { + id: 'mock-network', + type: 'rpc', + ticker: 'MOCK', + chainId: '0x123123123', + rpcUrl: mockNetworkRpc, + // `nickname` is undefined here + }; + + const state = { + ...getEvmState(), + metamask: { + ...getEvmState().metamask, + providerConfig: mockNetwork, + networkConfigurations: { + [mockNetwork.id]: mockNetwork, + }, + }, + }; + + const network = getMultichainNetwork(state); + expect(network.nickname).toBe(network.network.rpcUrl); + expect(network.nickname).toBe(mockNetworkRpc); + }); }); describe('getMultichainIsEvm', () => { @@ -177,9 +228,7 @@ describe('Multichain Selectors', () => { it('returns a ProviderConfig if account is EVM', () => { const state = getEvmState(); - // NOTE: We do fallback to `getCurrentNetwork` (using the "original" list - // of network) when using EVM context, so check against this value here - const evmMainnetNetwork = getCurrentNetwork(state); + const evmMainnetNetwork = getProviderConfig(state); expect(getMultichainProviderConfig(state)).toBe(evmMainnetNetwork); }); @@ -332,6 +381,44 @@ describe('Multichain Selectors', () => { ); }); + describe('getMultichainIsTestnet', () => { + it('returns false if account is EVM (mainnet)', () => { + const state = getEvmState(); + + expect(getMultichainIsTestnet(state)).toBe(false); + }); + + // @ts-expect-error This is missing from the Mocha type definitions + it.each(TEST_NETWORK_IDS)( + 'returns true if account is EVM (testnet): %s', + (chainId: string) => { + const state = getEvmState(); + + state.metamask.providerConfig.chainId = chainId; + expect(getMultichainIsTestnet(state)).toBe(true); + }, + ); + + // @ts-expect-error This is missing from the Mocha type definitions + it.each([ + { isTestnet: false, account: MOCK_ACCOUNT_BIP122_P2WPKH }, + { isTestnet: true, account: MOCK_ACCOUNT_BIP122_P2WPKH_TESTNET }, + ])( + 'returns $isTestnet if non-EVM account address "$account.address" is compatible with mainnet', + ({ + isTestnet, + account, + }: { + isTestnet: boolean; + account: InternalAccount; + }) => { + const state = getNonEvmState(account); + + expect(getMultichainIsTestnet(state)).toBe(isTestnet); + }, + ); + }); + describe('getMultichainSelectedAccountCachedBalance', () => { it('returns cached balance if account is EVM', () => { const state = getEvmState(); diff --git a/ui/selectors/multichain.ts b/ui/selectors/multichain.ts index 6e42ca4fcb08..ef452aa3ac3c 100644 --- a/ui/selectors/multichain.ts +++ b/ui/selectors/multichain.ts @@ -1,9 +1,11 @@ import PropTypes from 'prop-types'; import { InternalAccount, isEvmAccountType } from '@metamask/keyring-api'; -import { ProviderConfig } from '@metamask/network-controller'; +import { + NetworkConfiguration, + ProviderConfig, +} from '@metamask/network-controller'; import type { RatesControllerState } from '@metamask/assets-controllers'; import { CaipChainId, KnownCaipNamespace } from '@metamask/utils'; -import { ChainId } from '@metamask/controller-utils'; import { createSelector } from '@reduxjs/toolkit'; import { Numeric } from '../../shared/modules/Numeric'; import { @@ -19,17 +21,26 @@ import { } from '../ducks/metamask/metamask'; import { BalancesControllerState } from '../../app/scripts/lib/accounts/BalancesController'; import { MultichainNativeAssets } from '../../shared/constants/multichain/assets'; + +import { + CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP, + NETWORK_TO_NAME_MAP, + NETWORK_TYPES, + TEST_NETWORK_IDS, +} from '../../shared/constants/network'; + import { AccountsState } from './accounts'; import { - getAllNetworks, getCurrentChainId, getCurrentCurrency, getIsMainnet, getMaybeSelectedInternalAccount, getNativeCurrencyImage, + getNetworkConfigurations, getSelectedAccountCachedBalance, getSelectedInternalAccount, getShouldShowFiat, + getShowFiatInTestnets, } from '.'; export type RatesState = { @@ -42,15 +53,19 @@ export type BalancesState = { export type MultichainState = AccountsState & RatesState & BalancesState; +// TODO: Remove after updating to @metamask/network-controller 20.0.0 +export type ProviderConfigWithImageUrlAndExplorerUrl = ProviderConfig & { + rpcPrefs?: { blockExplorerUrl?: string; imageUrl?: string }; +}; + +// TODO: Remove after updating to @metamask/network-controller 20.0.0 +export type NetworkConfigurationWithId = NetworkConfiguration & { id: string }; + export type MultichainNetwork = { nickname: string; isEvmNetwork: boolean; chainId: CaipChainId; - network: // TODO: Maybe updates ProviderConfig to add rpcPrefs.imageUrl field - | (ProviderConfig & { - rpcPrefs?: { blockExplorerUrl?: string; imageUrl?: string }; - }) - | MultichainProviderConfig; + network: ProviderConfigWithImageUrlAndExplorerUrl | MultichainProviderConfig; }; export const MultichainNetworkPropType = PropTypes.shape({ @@ -111,17 +126,55 @@ export function getMultichainNetwork( ): MultichainNetwork { const isEvm = getMultichainIsEvm(state, account); - // EVM networks - const evmNetworks: ProviderConfig[] = getAllNetworks(state); - const evmChainId: ChainId = getCurrentChainId(state); - if (isEvm) { - const evmNetwork: ProviderConfig = - evmNetworks.find((provider) => provider.chainId === evmChainId) ?? - getProviderConfig(state); // We fallback to the original selector otherwise + // EVM networks + const evmChainId: string = getCurrentChainId(state); + + // TODO: Update to use network configurations when @metamask/network-controller is updated to 20.0.0 + // ProviderConfig will be deprecated to use NetworkConfigurations + // When a user updates a network name its only updated in the NetworkConfigurations. + const evmNetwork: ProviderConfigWithImageUrlAndExplorerUrl = + getProviderConfig(state); + // Fallback to a known network image if network configuration does not defined it + const evmChainIdKey = + evmChainId as keyof typeof CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP; + if ( + !evmNetwork?.rpcPrefs?.imageUrl && + evmChainIdKey in CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP + ) { + evmNetwork.rpcPrefs = { + ...evmNetwork.rpcPrefs, + imageUrl: CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP[evmChainIdKey], + }; + } + + let nickname; + if (evmNetwork.type === NETWORK_TYPES.RPC) { + // These are custom networks defined by the user. + // If there aren't any nicknames, the RPC URL is displayed. + + // Could be undefined for default configurations. + const evmNetworkConfigurations = getNetworkConfigurations(state); + const evmNetworkConfiguration = + // id will always be defined for custom networks. + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + evmNetworkConfigurations?.[evmNetwork.id!]; + nickname = + evmNetworkConfiguration?.nickname ?? + evmNetwork.nickname ?? + // rpcUrl will always be defined for custom networks. + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + evmNetwork.rpcUrl!; + } else { + // These are the default networks, they do not have nicknames + + // Nickname is "optional", so it might be undefined here + nickname = NETWORK_TO_NAME_MAP[evmNetwork.type]; + } return { - nickname: 'Ethereum', + // Current behavior is to display RPC URL as nickname if its not defined. + nickname, isEvmNetwork: true, // We assume the chain ID is `string` or `number`, so we convert it to a // `Number` to be compliant with EIP155 CAIP chain ID @@ -154,7 +207,6 @@ export function getMultichainNetwork( // TODO: Adapt this for other non-EVM networks nickname: nonEvmNetwork.nickname, isEvmNetwork: false, - // FIXME: We should use CAIP-2 chain ID here, and not only the reference part chainId: nonEvmNetwork?.chainId, network: nonEvmNetwork, }; @@ -261,10 +313,13 @@ export function getMultichainShouldShowFiat( state: MultichainState, account?: InternalAccount, ) { - return getMultichainIsEvm(state, account) + const selectedAccount = account ?? getSelectedInternalAccount(state); + const isTestnet = getMultichainIsTestnet(state, selectedAccount); + const isMainnet = !isTestnet; + + return getMultichainIsEvm(state, selectedAccount) ? getShouldShowFiat(state) - : // For now we force this for non-EVM - true; + : isMainnet || (isTestnet && getShowFiatInTestnets(state)); } export function getMultichainDefaultToken( @@ -290,13 +345,35 @@ export function getMultichainIsMainnet( ) { const selectedAccount = account ?? getSelectedInternalAccount(state); const providerConfig = getMultichainProviderConfig(state, selectedAccount); - return getMultichainIsEvm(state) + return getMultichainIsEvm(state, account) ? getIsMainnet(state) : // TODO: For now we only check for bitcoin, but we will need to // update this for other non-EVM networks later! providerConfig.chainId === MultichainNetworks.BITCOIN; } +export function getMultichainIsTestnet( + state: MultichainState, + account?: InternalAccount, +) { + // NOTE: Since there are 2 different implementations for `IsTestnet` and `IsMainnet` we follow + // the same pattern here too! + const selectedAccount = account ?? getSelectedInternalAccount(state); + const providerConfig = getMultichainProviderConfig(state, selectedAccount); + return getMultichainIsEvm(state, account) + ? // FIXME: There are multiple ways of checking for an EVM test network, but + // current implementation differ between each other. So we do not use + // `getIsTestnet` here and uses the actual `TEST_NETWORK_IDS` which seems + // more up-to-date + (TEST_NETWORK_IDS as string[]).includes( + (providerConfig as ProviderConfig).chainId, + ) + : // TODO: For now we only check for bitcoin, but we will need to + // update this for other non-EVM networks later! + (providerConfig as MultichainProviderConfig).chainId === + MultichainNetworks.BITCOIN_TESTNET; +} + export function getMultichainBalances( state: MultichainState, ): BalancesState['metamask']['balances'] { diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js index f28d0507265b..915abbe2db5d 100644 --- a/ui/selectors/selectors.js +++ b/ui/selectors/selectors.js @@ -1498,24 +1498,6 @@ export const getConnectedSitesList = createDeepEqualSelector( }, ); -export const getConnectedSitesListWithNetworkInfo = createDeepEqualSelector( - getConnectedSitesList, - getAllDomains, - getAllNetworks, - (sitesList, domains, networks) => { - Object.keys(sitesList).forEach((siteKey) => { - const connectedNetwork = networks.find( - (network) => network.id === domains[siteKey], - ); - // For the testnets, if we do not have an image, we will have a fallback string - sitesList[siteKey].networkIconUrl = - connectedNetwork.rpcPrefs?.imageUrl || ''; - sitesList[siteKey].networkName = connectedNetwork.nickname; - }); - return sitesList; - }, -); - export const getConnectedSnapsList = createDeepEqualSelector( getSnapsList, (snapsData) => { @@ -1820,12 +1802,57 @@ export function getNumberOfAllUnapprovedTransactionsAndMessages(state) { export const getCurrentNetwork = createDeepEqualSelector( getAllNetworks, getProviderConfig, + /** + * Get the current network configuration. + * + * @param {Record[]} allNetworks - All network configurations. + * @param {Record} providerConfig - The configuration for the current network's provider. + * @returns {{ + * chainId: `0x${string}`; + * id?: string; + * nickname?: string; + * providerType?: string; + * rpcPrefs?: { blockExplorerUrl?: string; imageUrl?: string; }; + * rpcUrl: string; + * ticker: string; + * }} networkConfiguration - Configuration for the current network. + */ (allNetworks, providerConfig) => { const filter = providerConfig.type === 'rpc' ? (network) => network.id === providerConfig.id : (network) => network.id === providerConfig.type; - return allNetworks.find(filter); + return ( + allNetworks.find(filter) ?? { + chainId: providerConfig.chainId, + nickname: providerConfig.nickname, + rpcPrefs: providerConfig.rpcPrefs, + rpcUrl: providerConfig.rpcUrl, + ticker: providerConfig.ticker, + } + ); + }, +); + +export const getConnectedSitesListWithNetworkInfo = createDeepEqualSelector( + getConnectedSitesList, + getAllDomains, + getAllNetworks, + getCurrentNetwork, + (sitesList, domains, networks, currentNetwork) => { + Object.keys(sitesList).forEach((siteKey) => { + const connectedNetwork = networks.find( + (network) => network.id === domains[siteKey], + ); + // For the testnets, if we do not have an image, we will have a fallback string + sitesList[siteKey].networkIconUrl = + connectedNetwork?.rpcPrefs?.imageUrl || + currentNetwork?.rpcPrefs?.imageUrl || + ''; + sitesList[siteKey].networkName = + connectedNetwork?.nickname || currentNetwork?.nickname || ''; + }); + return sitesList; }, ); diff --git a/ui/selectors/selectors.test.js b/ui/selectors/selectors.test.js index 127116b56b3b..4b658812a0f5 100644 --- a/ui/selectors/selectors.test.js +++ b/ui/selectors/selectors.test.js @@ -784,6 +784,104 @@ describe('Selectors', () => { }); describe('#getCurrentNetwork', () => { + it('returns built-in network configuration', () => { + const modifiedMockState = { + ...mockState, + metamask: { + ...mockState.metamask, + providerConfig: { + ...mockState.metamask.providerConfig, + chainId: '0x1', + type: 'sepolia', + }, + }, + }; + const currentNetwork = selectors.getCurrentNetwork(modifiedMockState); + + // Replace rpcUrl by type for consistency between environments because it's determined + // by environment variable. + currentNetwork.rpcUrl = typeof currentNetwork.rpcUrl; + expect(currentNetwork).toMatchInlineSnapshot(` + { + "chainId": "0xaa36a7", + "id": "sepolia", + "nickname": "Sepolia", + "providerType": "sepolia", + "removable": false, + "rpcUrl": "string", + "ticker": "SepoliaETH", + } + `); + }); + + it('returns custom network configuration', () => { + const mockNetworkConfigurationId = 'mock-network-config-id'; + const modifiedMockState = { + ...mockState, + metamask: { + ...mockState.metamask, + networkConfigurations: { + ...mockState.networkConfigurations, + [mockNetworkConfigurationId]: { + rpcUrl: 'https://mock-rpc-endpoint.test', + chainId: '0x9999', + ticker: 'TST', + id: mockNetworkConfigurationId, + }, + }, + providerConfig: { + rpcUrl: 'https://mock-rpc-endpoint.test', + chainId: '0x9999', + ticker: 'TST', + id: mockNetworkConfigurationId, + type: 'rpc', + }, + }, + }; + + const currentNetwork = selectors.getCurrentNetwork(modifiedMockState); + + expect(currentNetwork).toMatchInlineSnapshot(` + { + "chainId": "0x9999", + "id": "mock-network-config-id", + "removable": true, + "rpcPrefs": { + "imageUrl": undefined, + }, + "rpcUrl": "https://mock-rpc-endpoint.test", + "ticker": "TST", + } + `); + }); + + it('returns custom network configuration that is missing from networkConfigurations state', () => { + const modifiedMockState = { + ...mockState, + metamask: { + ...mockState.metamask, + providerConfig: { + rpcUrl: 'https://mock-rpc-endpoint.test', + chainId: '0x9999', + ticker: 'TST', + type: 'rpc', + }, + }, + }; + + const currentNetwork = selectors.getCurrentNetwork(modifiedMockState); + + expect(currentNetwork).toMatchInlineSnapshot(` + { + "chainId": "0x9999", + "nickname": undefined, + "rpcPrefs": undefined, + "rpcUrl": "https://mock-rpc-endpoint.test", + "ticker": "TST", + } + `); + }); + it('returns the correct custom network when there is a chainId collision', () => { const modifiedMockState = { ...mockState, diff --git a/ui/store/actions.test.js b/ui/store/actions.test.js index 255d0b26adce..a4211c1c3874 100644 --- a/ui/store/actions.test.js +++ b/ui/store/actions.test.js @@ -1113,114 +1113,6 @@ describe('Actions', () => { }); }); - describe('#editAndSetNetworkConfiguration', () => { - afterEach(() => { - sinon.restore(); - }); - - it('removes then re-adds the given network configuration', async () => { - const store = mockStore(); - - const removeNetworkConfigurationStub = sinon - .stub() - .callsFake((_, cb) => cb()); - - const upsertNetworkConfigurationStub = sinon - .stub() - .callsFake((_, cb) => cb()); - - background.getApi.returns({ - removeNetworkConfiguration: removeNetworkConfigurationStub, - upsertNetworkConfiguration: upsertNetworkConfigurationStub, - }); - setBackgroundConnection(background.getApi()); - - const networkConfiguration = { - rpcUrl: 'newRpc', - chainId: '0x', - ticker: 'ETH', - nickname: 'nickname', - rpcPrefs: { blockExplorerUrl: 'etherscan.io' }, - }; - - await store.dispatch( - actions.editAndSetNetworkConfiguration( - { - ...networkConfiguration, - networkConfigurationId: 'networkConfigurationId', - }, - { source: 'https://test-dapp.com' }, - ), - ); - expect( - removeNetworkConfigurationStub.calledOnceWith('networkConfigurationId'), - ).toBe(true); - expect( - upsertNetworkConfigurationStub.calledOnceWith(networkConfiguration, { - setActive: true, - referrer: ORIGIN_METAMASK, - source: 'https://test-dapp.com', - }), - ).toBe(true); - }); - - it('displays warning when removeNetworkConfiguration throws', async () => { - const store = mockStore(); - - const upsertNetworkConfigurationStub = sinon - .stub() - .callsFake((_, cb) => cb()); - - const removeNetworkConfigurationStub = sinon - .stub() - .callsFake((_, cb) => cb(new Error('error'))); - - background.getApi.returns({ - removeNetworkConfiguration: removeNetworkConfigurationStub, - upsertNetworkConfiguration: upsertNetworkConfigurationStub, - }); - - setBackgroundConnection(background.getApi()); - - const expectedActions = [ - { type: 'DISPLAY_WARNING', payload: 'Had a problem removing network!' }, - ]; - - await store.dispatch( - actions.editAndSetNetworkConfiguration( - { - networkConfigurationId: 'networkConfigurationId', - rpcUrl: 'newRpc', - chainId: '0x', - ticker: 'ETH', - nickname: 'nickname', - rpcPrefs: { blockExplorerUrl: 'etherscan.io' }, - }, - { source: 'https://test-dapp.com' }, - ), - ); - expect(store.getActions()).toStrictEqual(expectedActions); - }); - - it('throws when no options object is passed as a second argument', async () => { - const store = mockStore(); - await expect(() => - store.dispatch( - actions.editAndSetNetworkConfiguration({ - networkConfigurationId: 'networkConfigurationId', - rpcUrl: 'newRpc', - chainId: '0x', - ticker: 'ETH', - nickname: 'nickname', - rpcPrefs: { blockExplorerUrl: 'etherscan.io' }, - }), - ), - ).toThrow( - "Cannot destructure property 'source' of 'undefined' as it is undefined.", - ); - }); - }); - describe('#upsertNetworkConfiguration', () => { afterEach(() => { sinon.restore(); @@ -1239,6 +1131,7 @@ describe('Actions', () => { setBackgroundConnection(background.getApi()); const networkConfiguration = { + id: 'networkConfigurationId', rpcUrl: 'newRpc', chainId: '0x', ticker: 'ETH', @@ -1266,7 +1159,7 @@ describe('Actions', () => { await expect(() => store.dispatch( actions.upsertNetworkConfiguration({ - networkConfigurationId: 'networkConfigurationId', + id: 'networkConfigurationId', rpcUrl: 'newRpc', chainId: '0x', ticker: 'ETH', @@ -1726,12 +1619,6 @@ describe('Actions', () => { }); describe('#setParticipateInMetaMetrics', () => { - beforeAll(() => { - window.sentry = { - toggleSession: jest.fn(), - endSession: jest.fn(), - }; - }); it('sets participateInMetaMetrics to true', async () => { const store = mockStore(); const setParticipateInMetaMetricsStub = jest.fn((_, cb) => cb()); @@ -1747,7 +1634,6 @@ describe('Actions', () => { true, expect.anything(), ); - expect(window.sentry.toggleSession).toHaveBeenCalled(); }); }); diff --git a/ui/store/actions.ts b/ui/store/actions.ts index 4159b4db1441..4738645199ff 100644 --- a/ui/store/actions.ts +++ b/ui/store/actions.ts @@ -1011,6 +1011,7 @@ export function addTransactionAndRouteToConfirmationPage( } catch (error) { dispatch(hideLoadingIndication()); dispatch(displayWarning(error)); + throw error; } return null; }; @@ -2382,12 +2383,14 @@ export function setProviderType( export function upsertNetworkConfiguration( { + id, rpcUrl, chainId, nickname, rpcPrefs, ticker = EtherDenomination.ETH, }: { + id?: string; rpcUrl: string; chainId: string; nickname: string; @@ -2404,14 +2407,21 @@ export function upsertNetworkConfiguration( ): ThunkAction { return async (dispatch) => { log.debug( - `background.upsertNetworkConfiguration: ${rpcUrl} ${chainId} ${ticker} ${nickname}`, + `background.upsertNetworkConfiguration: ${id} ${rpcUrl} ${chainId} ${ticker} ${nickname}`, ); let networkConfigurationId; try { networkConfigurationId = await submitRequestToBackground( 'upsertNetworkConfiguration', [ - { rpcUrl, chainId, ticker, nickname: nickname || rpcUrl, rpcPrefs }, + { + id, + rpcUrl, + chainId, + ticker, + nickname: nickname || rpcUrl, + rpcPrefs, + }, { setActive, source, referrer: ORIGIN_METAMASK }, ], ); @@ -2423,56 +2433,6 @@ export function upsertNetworkConfiguration( }; } -export function editAndSetNetworkConfiguration( - { - networkConfigurationId, - rpcUrl, - chainId, - nickname, - rpcPrefs, - ticker = EtherDenomination.ETH, - }: { - networkConfigurationId: string; - rpcUrl: string; - chainId: string; - nickname: string; - rpcPrefs: RPCDefinition['rpcPrefs']; - ticker: string; - }, - { source }: { source: string }, -): ThunkAction { - return async (dispatch) => { - log.debug( - `background.removeNetworkConfiguration: ${networkConfigurationId}`, - ); - try { - await submitRequestToBackground('removeNetworkConfiguration', [ - networkConfigurationId, - ]); - } catch (error) { - logErrorWithMessage(error); - dispatch(displayWarning('Had a problem removing network!')); - return; - } - - try { - await submitRequestToBackground('upsertNetworkConfiguration', [ - { - rpcUrl, - chainId, - ticker, - nickname: nickname || rpcUrl, - rpcPrefs, - }, - { setActive: true, referrer: ORIGIN_METAMASK, source }, - ]); - } catch (error) { - logErrorWithMessage(error); - dispatch(displayWarning('Had a problem changing networks!')); - } - }; -} - export function setActiveNetwork( networkConfigurationId: string, ): ThunkAction { @@ -3263,17 +3223,12 @@ export function setParticipateInMetaMetrics( reject(err); return; } - /** - * We need to inform sentry that the user's optin preference may have - * changed. The logic to determine which way to toggle is in the - * toggleSession handler in setupSentry.js. - */ - window.sentry?.toggleSession(); dispatch({ type: actionConstants.SET_PARTICIPATE_IN_METAMETRICS, value: participationPreference, }); + resolve([participationPreference, metaMetricsId as string]); }, ); @@ -5167,7 +5122,7 @@ export function setName( * Throw an error in the background for testing purposes. * * @param message - The error message. - * @deprecated This is only mean to facilitiate E2E testing. We should not use + * @deprecated This is only meant to facilitiate E2E testing. We should not use * this for handling errors. */ export async function throwTestBackgroundError(message: string): Promise { diff --git a/yarn.lock b/yarn.lock index c8112a2f3586..48ebf778edbc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1710,10 +1710,10 @@ __metadata: languageName: node linkType: hard -"@blockaid/ppom_release@npm:^1.4.9": - version: 1.4.9 - resolution: "@blockaid/ppom_release@npm:1.4.9" - checksum: 10/5aff4d0397632eb875a96e5bf1d13ebb17b11a457a4fe5f1b9a6a80d4612be233d0c328ecf36fff4d126326b0ea555590e8112515c3df7703f79b376288d5559 +"@blockaid/ppom_release@npm:^1.5.2": + version: 1.5.2 + resolution: "@blockaid/ppom_release@npm:1.5.2" + checksum: 10/b47af74e8a315d996abb8371a7b28f9923bf78272cc408b2307024e2050b57f36c53b6b9a57b276b0910b449ebb384897e293abad356131ccf65c6c3c5ca31ae languageName: node linkType: hard @@ -5648,7 +5648,7 @@ __metadata: languageName: node linkType: hard -"@metamask/keyring-controller@npm:^16.0.0, @metamask/keyring-controller@npm:^16.1.0": +"@metamask/keyring-controller@npm:16.1.0, @metamask/keyring-controller@npm:^16.0.0": version: 16.1.0 resolution: "@metamask/keyring-controller@npm:16.1.0" dependencies: @@ -5690,6 +5690,27 @@ __metadata: languageName: node linkType: hard +"@metamask/keyring-controller@patch:@metamask/keyring-controller@npm%3A16.1.0#~/.yarn/patches/@metamask-keyring-controller-npm-16.1.0-7043d2dc62.patch": + version: 16.1.0 + resolution: "@metamask/keyring-controller@patch:@metamask/keyring-controller@npm%3A16.1.0#~/.yarn/patches/@metamask-keyring-controller-npm-16.1.0-7043d2dc62.patch::version=16.1.0&hash=d6d94c" + dependencies: + "@ethereumjs/util": "npm:^8.1.0" + "@keystonehq/metamask-airgapped-keyring": "npm:^0.14.1" + "@metamask/base-controller": "npm:^5.0.2" + "@metamask/browser-passworder": "npm:^4.3.0" + "@metamask/eth-hd-keyring": "npm:^7.0.1" + "@metamask/eth-sig-util": "npm:^7.0.1" + "@metamask/eth-simple-keyring": "npm:^6.0.1" + "@metamask/keyring-api": "npm:^6.1.1" + "@metamask/message-manager": "npm:^9.0.0" + "@metamask/utils": "npm:^8.3.0" + async-mutex: "npm:^0.5.0" + ethereumjs-wallet: "npm:^1.0.1" + immer: "npm:^9.0.6" + checksum: 10/1c22b5e39c662f6f05d517bd25a9e4f8fdfb71fdaba4607e04967bb6b5f14b9670e8f2790e4e86cb9e4a6573dd34588dc208bfa8b55a83bbef7e1b4df0e1f58a + languageName: node + linkType: hard + "@metamask/logging-controller@npm:^3.0.1": version: 3.0.1 resolution: "@metamask/logging-controller@npm:3.0.1" @@ -6383,7 +6404,7 @@ __metadata: languageName: node linkType: hard -"@metamask/snaps-utils@npm:^7.4.0, @metamask/snaps-utils@npm:^7.7.0": +"@metamask/snaps-utils@npm:7.7.0, @metamask/snaps-utils@npm:^7.4.0, @metamask/snaps-utils@npm:^7.7.0": version: 7.7.0 resolution: "@metamask/snaps-utils@npm:7.7.0" dependencies: @@ -6414,6 +6435,37 @@ __metadata: languageName: node linkType: hard +"@metamask/snaps-utils@patch:@metamask/snaps-utils@npm%3A7.7.0#~/.yarn/patches/@metamask-snaps-utils-npm-7.7.0-2cc1f044af.patch": + version: 7.7.0 + resolution: "@metamask/snaps-utils@patch:@metamask/snaps-utils@npm%3A7.7.0#~/.yarn/patches/@metamask-snaps-utils-npm-7.7.0-2cc1f044af.patch::version=7.7.0&hash=5f2735" + dependencies: + "@babel/core": "npm:^7.23.2" + "@babel/types": "npm:^7.23.0" + "@metamask/base-controller": "npm:^6.0.0" + "@metamask/key-tree": "npm:^9.1.1" + "@metamask/permission-controller": "npm:^10.0.0" + "@metamask/rpc-errors": "npm:^6.2.1" + "@metamask/slip44": "npm:^3.1.0" + "@metamask/snaps-registry": "npm:^3.1.0" + "@metamask/snaps-sdk": "npm:^6.0.0" + "@metamask/utils": "npm:^8.3.0" + "@noble/hashes": "npm:^1.3.1" + "@scure/base": "npm:^1.1.1" + chalk: "npm:^4.1.2" + cron-parser: "npm:^4.5.0" + fast-deep-equal: "npm:^3.1.3" + fast-json-stable-stringify: "npm:^2.1.0" + fast-xml-parser: "npm:^4.3.4" + marked: "npm:^12.0.1" + rfdc: "npm:^1.3.0" + semver: "npm:^7.5.4" + ses: "npm:^1.1.0" + superstruct: "npm:^1.0.3" + validate-npm-package-name: "npm:^5.0.0" + checksum: 10/9ac16da1c2c1c7e2b857078ff4d9d450db8d5dbf650143ffc7953d2aea70fd58c87d1c1f2429a5a1c1418334d27e87d4a6a03089a55ba86840c417dfdb73b2fe + languageName: node + linkType: hard + "@metamask/swappable-obj-proxy@npm:^2.2.0": version: 2.2.0 resolution: "@metamask/swappable-obj-proxy@npm:2.2.0" @@ -6451,7 +6503,7 @@ __metadata: languageName: node linkType: hard -"@metamask/transaction-controller@npm:^32.0.0": +"@metamask/transaction-controller@npm:32.0.0": version: 32.0.0 resolution: "@metamask/transaction-controller@npm:32.0.0" dependencies: @@ -6486,6 +6538,41 @@ __metadata: languageName: node linkType: hard +"@metamask/transaction-controller@patch:@metamask/transaction-controller@npm%3A32.0.0#~/.yarn/patches/@metamask-transaction-controller-npm-32.0.0-e23c2c3443.patch": + version: 32.0.0 + resolution: "@metamask/transaction-controller@patch:@metamask/transaction-controller@npm%3A32.0.0#~/.yarn/patches/@metamask-transaction-controller-npm-32.0.0-e23c2c3443.patch::version=32.0.0&hash=27b27a" + dependencies: + "@ethereumjs/common": "npm:^3.2.0" + "@ethereumjs/tx": "npm:^4.2.0" + "@ethereumjs/util": "npm:^8.1.0" + "@ethersproject/abi": "npm:^5.7.0" + "@ethersproject/contracts": "npm:^5.7.0" + "@ethersproject/providers": "npm:^5.7.0" + "@metamask/approval-controller": "npm:^7.0.0" + "@metamask/base-controller": "npm:^6.0.0" + "@metamask/controller-utils": "npm:^11.0.0" + "@metamask/eth-query": "npm:^4.0.0" + "@metamask/gas-fee-controller": "npm:^17.0.0" + "@metamask/metamask-eth-abis": "npm:^3.1.1" + "@metamask/network-controller": "npm:^19.0.0" + "@metamask/nonce-tracker": "npm:^5.0.0" + "@metamask/rpc-errors": "npm:^6.2.1" + "@metamask/utils": "npm:^8.3.0" + async-mutex: "npm:^0.5.0" + bn.js: "npm:^5.2.1" + eth-method-registry: "npm:^4.0.0" + fast-json-patch: "npm:^3.1.1" + lodash: "npm:^4.17.21" + uuid: "npm:^8.3.2" + peerDependencies: + "@babel/runtime": ^7.23.9 + "@metamask/approval-controller": ^7.0.0 + "@metamask/gas-fee-controller": ^17.0.0 + "@metamask/network-controller": ^19.0.0 + checksum: 10/f8e1907ed697406fc1af7dca3627e5d28f213a3b4875258ec58abdaa0ae48c4714ad79face61519ab1f3475e6aa57e8c80c9622e2eb18f65b1cd5b123962e5ea + languageName: node + linkType: hard + "@metamask/user-operation-controller@npm:^10.0.0": version: 10.0.0 resolution: "@metamask/user-operation-controller@npm:10.0.0" @@ -7831,29 +7918,64 @@ __metadata: languageName: node linkType: hard -"@sentry-internal/tracing@npm:7.53.0": - version: 7.53.0 - resolution: "@sentry-internal/tracing@npm:7.53.0" +"@sentry-internal/browser-utils@npm:8.19.0": + version: 8.19.0 + resolution: "@sentry-internal/browser-utils@npm:8.19.0" + dependencies: + "@sentry/core": "npm:8.19.0" + "@sentry/types": "npm:8.19.0" + "@sentry/utils": "npm:8.19.0" + checksum: 10/d6df6cb6edc6b2ddb7362daee39770a51b255d343b3dcb80dc98f77dc43a7cc66f29076e14d1a0ac162a51a4f620b876493a04c23a530f57170009364b6464ea + languageName: node + linkType: hard + +"@sentry-internal/feedback@npm:8.19.0": + version: 8.19.0 + resolution: "@sentry-internal/feedback@npm:8.19.0" dependencies: - "@sentry/core": "npm:7.53.0" - "@sentry/types": "npm:7.53.0" - "@sentry/utils": "npm:7.53.0" - tslib: "npm:^1.9.3" - checksum: 10/0c449ee967ca568128842649f1eb16e6674c562cbf54436b74f02a2f5ef62ca9d402622a955cdfcfc9b2e158f5d98b21964f6be8164001a2c53b67c1bd00d290 + "@sentry/core": "npm:8.19.0" + "@sentry/types": "npm:8.19.0" + "@sentry/utils": "npm:8.19.0" + checksum: 10/e10cf1f63d49a41072aaa1b7b007241a273bd4bfa6d2c628e50d621c8cde836e6743bdefbf9ba7e96684b6dd18ad49e17841f4420fc33757e7c119ec88b4ac15 languageName: node linkType: hard -"@sentry/browser@npm:^7.53.0": - version: 7.53.0 - resolution: "@sentry/browser@npm:7.53.0" +"@sentry-internal/replay-canvas@npm:8.19.0": + version: 8.19.0 + resolution: "@sentry-internal/replay-canvas@npm:8.19.0" dependencies: - "@sentry-internal/tracing": "npm:7.53.0" - "@sentry/core": "npm:7.53.0" - "@sentry/replay": "npm:7.53.0" - "@sentry/types": "npm:7.53.0" - "@sentry/utils": "npm:7.53.0" - tslib: "npm:^1.9.3" - checksum: 10/4902ddbaab5281e0f1de121bc6120e812cd86ae62139980c62316235f9f685c5f377ccbabdc7160062bb496dc4e7f2e6799fb1a762a89692a177a4457225c55f + "@sentry-internal/replay": "npm:8.19.0" + "@sentry/core": "npm:8.19.0" + "@sentry/types": "npm:8.19.0" + "@sentry/utils": "npm:8.19.0" + checksum: 10/1f379c141884b448c56fcd663b8acc0ff1c12d50a2b9db37f9552eb2bc8c99a970114f80e58c8c4fcd61f933f9a15f58dc6cbe6f4297bb574d6772be8f41c5bf + languageName: node + linkType: hard + +"@sentry-internal/replay@npm:8.19.0": + version: 8.19.0 + resolution: "@sentry-internal/replay@npm:8.19.0" + dependencies: + "@sentry-internal/browser-utils": "npm:8.19.0" + "@sentry/core": "npm:8.19.0" + "@sentry/types": "npm:8.19.0" + "@sentry/utils": "npm:8.19.0" + checksum: 10/dc9bef6997d1f40fb0402f52c9d14f72cf050ec140fda27e00057c59ddd1a6144e78e40aeb5e0223dd48651bf02f809db26cf6e866dd5c8ec5c6bbbf76c6f1aa + languageName: node + linkType: hard + +"@sentry/browser@npm:^8.19.0": + version: 8.19.0 + resolution: "@sentry/browser@npm:8.19.0" + dependencies: + "@sentry-internal/browser-utils": "npm:8.19.0" + "@sentry-internal/feedback": "npm:8.19.0" + "@sentry-internal/replay": "npm:8.19.0" + "@sentry-internal/replay-canvas": "npm:8.19.0" + "@sentry/core": "npm:8.19.0" + "@sentry/types": "npm:8.19.0" + "@sentry/utils": "npm:8.19.0" + checksum: 10/2412e938454bd5cc505bbbe7092a17bf5fde4b222ecfedaf3d54fb963a6c875c78661921d8f6e998498c85a9a52e616db75fd706867f76d38bf3f95714775aa6 languageName: node linkType: hard @@ -7872,54 +7994,36 @@ __metadata: languageName: node linkType: hard -"@sentry/core@npm:7.53.0": - version: 7.53.0 - resolution: "@sentry/core@npm:7.53.0" - dependencies: - "@sentry/types": "npm:7.53.0" - "@sentry/utils": "npm:7.53.0" - tslib: "npm:^1.9.3" - checksum: 10/3c69eca4c0f4215a041170c7632bf55fc70d01a888dce0de7f9876a6333aa1db93f5ef59147719e01f1a54f74e40f1201fad9570a17cf01349d1b1fc91e0ee0d - languageName: node - linkType: hard - -"@sentry/integrations@npm:^7.53.0": - version: 7.53.0 - resolution: "@sentry/integrations@npm:7.53.0" +"@sentry/core@npm:8.19.0": + version: 8.19.0 + resolution: "@sentry/core@npm:8.19.0" dependencies: - "@sentry/types": "npm:7.53.0" - "@sentry/utils": "npm:7.53.0" - localforage: "npm:^1.8.1" - tslib: "npm:^1.9.3" - checksum: 10/ea183a235f3fca9503d2dd11d1799525ccf38a20989bff083dcdf61947823be59ae5c36e080fab69f9537ed912feaaa4f554451add0abe223e566a6e9f56d70d + "@sentry/types": "npm:8.19.0" + "@sentry/utils": "npm:8.19.0" + checksum: 10/708ef5abd81a9ab5288a4b258411e78591a7fec4854fc582c34f087fce62f5cd74e1086fbbc27a9f55da77d113dde137fbf9649f5b7df3d1a22886850702adbd languageName: node linkType: hard -"@sentry/replay@npm:7.53.0": - version: 7.53.0 - resolution: "@sentry/replay@npm:7.53.0" - dependencies: - "@sentry/core": "npm:7.53.0" - "@sentry/types": "npm:7.53.0" - "@sentry/utils": "npm:7.53.0" - checksum: 10/8ba1e72ae42eb20ab779554ad39cdcd6e74cc4457250599e7b7d8db411dd2f706d11b9e25bda6116640273529b28e5328498730ae1d3f8c130e1e276933d715d +"@sentry/types@npm:8.19.0": + version: 8.19.0 + resolution: "@sentry/types@npm:8.19.0" + checksum: 10/8812f7394c6c031197abc04d80e5b5b3693742dc065b877c535a9ceb538aabd60ee27fc2b13824e2b8fc264819868109bbd4de3642fd1c7bf30d304fb0c21aa9 languageName: node linkType: hard -"@sentry/types@npm:7.53.0, @sentry/types@npm:^7.53.0": - version: 7.53.0 - resolution: "@sentry/types@npm:7.53.0" - checksum: 10/ea05e60594529bd7b59c6ba9cfecb5066a2a20af60194b67772769e4e02d54fd2da2e8124c90b3f0b4fbabd47499e7ccea833efaa819f7643b9d45c50aead5c3 +"@sentry/types@npm:^8.19.0": + version: 8.20.0 + resolution: "@sentry/types@npm:8.20.0" + checksum: 10/c7d7ed17975f0fc0b4bf5aece58084953c2a76e8f417923a476fe1fd42a2c9339c548d701edbc4b938c9252cf680d3eff4c6c2a986bc7ac62649aebf656c5b64 languageName: node linkType: hard -"@sentry/utils@npm:7.53.0, @sentry/utils@npm:^7.53.0": - version: 7.53.0 - resolution: "@sentry/utils@npm:7.53.0" +"@sentry/utils@npm:8.19.0, @sentry/utils@npm:^8.19.0": + version: 8.19.0 + resolution: "@sentry/utils@npm:8.19.0" dependencies: - "@sentry/types": "npm:7.53.0" - tslib: "npm:^1.9.3" - checksum: 10/38732598c57db770f307e5869c4d68e95f53715970b3cf0d24e37ced5ef3d43af3560b2efd5a1362891a5340224ce34cf073695986b3a8b1958f4a09b614ee6e + "@sentry/types": "npm:8.19.0" + checksum: 10/abd507e5b37c7753534865f74a1a622fdbe2d71cfa61fd009703f4c9c90634fb6d26e3b2f8e09904631d4692e3735de451ed914c505c31700a6f5504a61e649e languageName: node linkType: hard @@ -18561,13 +18665,13 @@ __metadata: linkType: hard "fast-xml-parser@npm:^4.3.4": - version: 4.3.4 - resolution: "fast-xml-parser@npm:4.3.4" + version: 4.4.1 + resolution: "fast-xml-parser@npm:4.4.1" dependencies: strnum: "npm:^1.0.5" bin: fxparser: src/cli/cli.js - checksum: 10/ef859101980cdd02b111fce09e25949a80e373654a6c424091355930f0d364abec144d8bb722d250a0c070416566518e621e198204a6b976db68f20c16d9300b + checksum: 10/0c05ab8703630d8c857fafadbd78d0020d3a8e54310c3842179cd4a0d9d97e96d209ce885e91241f4aa9dd8dfc2fd924a682741a423d65153cad34da2032ec44 languageName: node linkType: hard @@ -24213,7 +24317,7 @@ __metadata: languageName: node linkType: hard -"localforage@npm:^1.8.1, localforage@npm:^1.9.0": +"localforage@npm:^1.9.0": version: 1.10.0 resolution: "localforage@npm:1.10.0" dependencies: @@ -25137,7 +25241,7 @@ __metadata: "@babel/preset-typescript": "npm:^7.23.2" "@babel/register": "npm:^7.22.15" "@babel/runtime": "patch:@babel/runtime@npm%3A7.24.0#~/.yarn/patches/@babel-runtime-npm-7.24.0-7eb1dd11a2.patch" - "@blockaid/ppom_release": "npm:^1.4.9" + "@blockaid/ppom_release": "npm:^1.5.2" "@contentful/rich-text-html-renderer": "npm:^16.3.5" "@ensdomains/content-hash": "npm:^2.5.7" "@ethereumjs/tx": "npm:^4.1.1" @@ -25205,7 +25309,7 @@ __metadata: "@metamask/gas-fee-controller": "npm:^18.0.0" "@metamask/jazzicon": "npm:^2.0.0" "@metamask/keyring-api": "npm:^8.0.0" - "@metamask/keyring-controller": "npm:^16.1.0" + "@metamask/keyring-controller": "patch:@metamask/keyring-controller@npm%3A16.1.0#~/.yarn/patches/@metamask-keyring-controller-npm-16.1.0-7043d2dc62.patch" "@metamask/logging-controller": "npm:^3.0.1" "@metamask/logo": "npm:^3.1.2" "@metamask/message-manager": "npm:^7.3.0" @@ -25235,10 +25339,10 @@ __metadata: "@metamask/snaps-execution-environments": "npm:^6.5.0" "@metamask/snaps-rpc-methods": "npm:^9.1.4" "@metamask/snaps-sdk": "npm:^6.0.0" - "@metamask/snaps-utils": "npm:^7.7.0" + "@metamask/snaps-utils": "patch:@metamask/snaps-utils@npm%3A7.7.0#~/.yarn/patches/@metamask-snaps-utils-npm-7.7.0-2cc1f044af.patch" "@metamask/test-bundler": "npm:^1.0.0" "@metamask/test-dapp": "npm:^8.4.0" - "@metamask/transaction-controller": "npm:^32.0.0" + "@metamask/transaction-controller": "patch:@metamask/transaction-controller@npm%3A32.0.0#~/.yarn/patches/@metamask-transaction-controller-npm-32.0.0-e23c2c3443.patch" "@metamask/user-operation-controller": "npm:^10.0.0" "@metamask/utils": "npm:^8.2.1" "@ngraveio/bc-ur": "npm:^1.1.12" @@ -25253,11 +25357,10 @@ __metadata: "@popperjs/core": "npm:^2.4.0" "@reduxjs/toolkit": "patch:@reduxjs/toolkit@npm%3A1.9.7#~/.yarn/patches/@reduxjs-toolkit-npm-1.9.7-b14925495c.patch" "@segment/loosely-validate-event": "npm:^2.0.0" - "@sentry/browser": "npm:^7.53.0" + "@sentry/browser": "npm:^8.19.0" "@sentry/cli": "npm:^2.19.4" - "@sentry/integrations": "npm:^7.53.0" - "@sentry/types": "npm:^7.53.0" - "@sentry/utils": "npm:^7.53.0" + "@sentry/types": "npm:^8.19.0" + "@sentry/utils": "npm:^8.19.0" "@storybook/addon-a11y": "npm:^7.6.19" "@storybook/addon-actions": "npm:^7.6.19" "@storybook/addon-designs": "npm:^7.0.9" @@ -33924,7 +34027,7 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^1.13.0, tslib@npm:^1.8.1, tslib@npm:^1.9.0, tslib@npm:^1.9.3": +"tslib@npm:^1.13.0, tslib@npm:^1.8.1, tslib@npm:^1.9.0": version: 1.14.1 resolution: "tslib@npm:1.14.1" checksum: 10/7dbf34e6f55c6492637adb81b555af5e3b4f9cc6b998fb440dac82d3b42bdc91560a35a5fb75e20e24a076c651438234da6743d139e4feabf0783f3cdfe1dddb